Repository navigation
sec(ladder): LadderExecutionEnabled follows #1765's config-driven-scheduled-execution pattern (currently inert) #178
Description
Summary
Found while investigating LeanerCloud/cloud-commitments-cli#1765 (config-driven scheduled auto-exchange bypasses the execute:ri-exchange carve-out). The commitment-laddering feature has the identical pattern -- a config write gated only on update:config controls whether a scheduled task's write side is armed with real cloud-provider clients -- but it is not exploitable today because the planning engine never actually calls the write-side methods. This issue exists so the gap is discoverable by whoever wires that engine, rather than only living as a comment on a different issue's thread.
The pattern
GlobalConfig.LadderingEnabled + LadderExecutionEnabled (internal/config/types.go:67-80) are both writable only via update:config (PUT /api/config -> updateConfig, internal/api/handler_config.go:61). Both are threaded into the scheduled TaskLadderRun task:
handleLadderRun (internal/server/handler_ladder.go:83)
-> runLadderConfigs -> processOneLadderConfig
-> buildAndWireCapability(ctx, region, accountID, executionEnabled) (handler_ladder.go:254)
-> wireLadderWriteSide(ctx, executionEnabled, ...) (internal/server/ladder_write.go:116)
When executionEnabled (== globalCfg.LadderExecutionEnabled) is true, wireLadderWriteSide calls awsladder.WireWriteSide, which wires real EC2 and Savings Plans clients plus an exchangeRunnerAdapter that can trigger RI-exchange auto-execution (the same exchange.RunAutoExchange at the center of LeanerCloud/cloud-commitments-cli#1765) onto the LadderCapability. This is exactly LeanerCloud/cloud-commitments-cli#1765's shape: a config write gated only on update:config controls whether a scheduled task can act with real money-moving authority, with no execute:purchases/Purchaser-group check anywhere on the config-write path.
Why it is not exploitable today
handleLadderRun's own docstring states it plainly: "plan-only: no PurchaseLayer, ReshapeBuffer, email, or approval tokens" (internal/server/handler_ladder.go:82). Confirmed by grep: there are zero non-test callers of .PurchaseLayer( or .ReshapeBuffer( on the LadderCapability interface anywhere in the codebase. wireLadderWriteSide builds and threads the write-side wiring end-to-end, but nothing in the current planning engine invokes it -- LadderExecutionEnabled = true currently wires real AWS clients that are never called.
Why this needs its own issue instead of a note on LeanerCloud/cloud-commitments-cli#1765
Inert-today is exactly the state that stops being true the moment someone wires the ladder engine to actually call PurchaseLayer / ReshapeBuffer (the later phase of issue LeanerCloud/cloud-commitments-go#27). Whoever does that work needs to know this landmine exists before shipping it, and a comment buried in LeanerCloud/cloud-commitments-cli#1765's thread is invisible to someone searching the ladder code or working LeanerCloud/cloud-commitments-go#27. This issue is the thing that surfaces when they look.
Suggested fix direction
When the ladder engine is wired to actually execute (future work on LeanerCloud/cloud-commitments-go#27), apply the same fix LeanerCloud/cloud-commitments-cli#1765 recommends there: require execute:purchases (or Purchaser-group membership) specifically for the write that flips LadderExecutionEnabled (or LadderingEnabled + LadderExecutionEnabled together) to true, via a shared helper analogous to whatever LeanerCloud/cloud-commitments-cli#1765 lands, rather than relying on update:config alone. Do not block on this issue before LeanerCloud/cloud-commitments-cli#1765 -- LeanerCloud/cloud-commitments-cli#1765 is the live, exploitable instance; this one is preventive.
Refs LeanerCloud/cloud-commitments-cli#1765, LeanerCloud/cloud-commitments-cli#1758, LeanerCloud/cloud-commitments-cli#1644, LeanerCloud/cloud-commitments-go#27.
Activity