P0-3: Encrypt sensitive output variables in checkpoint JSON
Summary
-
Real production risk: pre-fix,
IsSensitive=trueoutput variables emitted by user scripts (API keys, passwords) were stored as plaintext inDeploymentExecutionCheckpoint.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.SerializeOutputVariablesnow encryptsValueof sensitive variables viaIVariableEncryptionService.EncryptAsync(V2 envelope, 600k PBKDF2 iterations, random per-payload salt) before JSON serialization.ResumeCheckpointPhase.RestoreOutputVariablesAsyncdecrypts on read. -
Backward compat: pre-fix plaintext checkpoints resume cleanly via the
IsValidEncryptedValueguard (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 IVariableEncryptionServiceto 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.