Skip to content

P0-3: Encrypt sensitive output variables in checkpoint JSON

Summary

  • Real production risk: pre-fix, IsSensitive=true output variables emitted by user scripts (API keys, passwords) were stored as plaintext in DeploymentExecutionCheckpoint.OutputVariablesJson. DBAs / ops engineers / anyone with leaked DB credentials could see secrets directly — violating the at-rest encryption principle followed elsewhere in the pipeline (variable sets, account credentials, agent configs).
  • Fix: ExecuteStepsPhase.SerializeOutputVariables now encrypts Value of sensitive variables via IVariableEncryptionService.EncryptAsync (V2 envelope, 600k PBKDF2 iterations, random per-payload salt) before JSON serialization. ResumeCheckpointPhase.RestoreOutputVariablesAsync decrypts on read.
  • Backward compat: pre-fix plaintext checkpoints resume cleanly via the IsValidEncryptedValue guard (un-prefixed text passes through). Operators upgrading from 1.6.5 → 1.6.6 mid-deployment will not see broken resume.
  • Non-sensitive values stay plaintext: operators need to inspect them to debug stuck deploys; encrypting all would block that workflow without security benefit.

Test plan

  • Unit (CheckpointSensitiveVarEncryptionTests): 4 round-trip cases
    • RoundTrip_SensitiveValue_DecryptsBackToOriginal — pin: ciphertext NOT plaintext in JSON; decrypt restores original
    • RoundTrip_NonSensitiveValue_StaysPlaintextInJson — pin: non-sensitive NOT encrypted (operator inspectability)
    • Resume_PreFixPlaintextCheckpoint_RestoresUnchanged — pin: backward compat for in-flight upgrade
    • RoundTrip_MixedVariables_OnlySensitiveAreEncrypted — pin: mixed batch handled correctly
  • All 16 affected test files patched to provide IVariableEncryptionService to new constructor signatures (ExecuteStepsPhase + ResumeCheckpointPhase)
  • Full unit suite: 5112/5112 pass

Rollback

Single-PR revert is clean — no schema migration. New checkpoints written by 1.6.6 carry the SQUID_ENCRYPTED_V2: prefix; reverting to 1.6.5 leaves those entries unreadable, but only for in-flight deployments (completed deployments don't need checkpoint resume). Risk window is minutes (one batch persist interval).

Notes on V2 envelope behaviour

Documented in code comment: V2 envelope embeds a random per-payload salt, so the variableSetId (we pass ServerTaskId) is advisory on decrypt — security rests on master-key custody, not on ID isolation. The argument is preserved for legacy V1 ciphertext compatibility.

🤖 Generated with Claude Code

Merge request reports

Loading