fix: defer flow transform variable() fetches to child step jobs#10005
Draft
hugocasa wants to merge 3 commits into
Draft
fix: defer flow transform variable() fetches to child step jobs#10005hugocasa wants to merge 3 commits into
hugocasa wants to merge 3 commits into
Conversation
…jobs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deploying windmill with
|
| Latest commit: |
9a74690
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://706f0460.windmill.pages.dev |
| Branch Preview URL: | https://hugo-win-2145-propagate-secr.windmill.pages.dev |
…interpolate args Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… eager Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes WIN-2145.
When
variable()is called inside a flow step's input transform expression (e.g.mycli --password=${variable("f/secrets/some_password")}), the secret was registered for masking only against jobs currently running on that worker — i.e. the parent flow job. The child step job is created afterwards bypush(), so when it started (on any worker) it got a fresh empty mask set and the secret appeared unmasked in the child job's logs.This PR propagates the flow job's accumulated secret masks to each pushed child job via
push_args.extraunder a_secret_maskskey, following the_TEMP_SCRIPT_REFS/WM_TRACEPARENTpropagation pattern. The child's worker decrypts and registers the masks right afterregister_running_job, before the job produces any logs.Two deliberate deviations from the issue's proposed fix:
push_args.extrais persisted to the child'sv2_job.args, so a plaintext JSON array would have stored raw secret values in the DB and shown them in the run UI — a worse leak than the logs. The blob is encrypted with the workspace key + root job id, the exact scheme already used for$encrypted:args, and the child derives the same key viaget_root_job_id.push_next_flow_job. For steps after the first,push_next_flow_jobruns on the step-completion path where the flow job is not in the running-jobs set (it was unregistered right after pushing step 1), sovariable()fetches during transforms would register nothing and there would be nothing to propagate. Atrack_job_for_masksRAII guard registers the flow job for the duration of the invocation (no-op when already tracked) and seeds it with masks inherited from its own args, so subflows keep propagating transitively.Changes
windmill-common/src/sensitive_log_masks.rs:SECRET_MASKS_ARGconstant,get_secrets_for_job(sorted snapshot), andtrack_job_for_masks+MaskTrackingGuard(RAII, only unregisters what it registered).windmill-worker/src/worker_flow.rs: guard + args-seeding at the top ofpush_next_flow_job; per-iteration snapshot in the push loop (parallel for-loop iterations can register more secrets via their own simple input transforms) with the encrypted blob cached and only rebuilt when the set changes; injected intopush_args.extra.windmill-worker/src/worker.rs: afterregister_running_job, decrypt and register propagated masks (best-effort; skipped on agent workers since an HTTP connection cannot fetch the workspace key — same pre-existing limitation as$encrypted:args).windmill-worker/src/common.rs:register_secret_masks_from_argshelper; strip_secret_masksfromargs.jsonalongside_MODULES/_TEMP_SCRIPT_REFS.frontend/src/lib/components/JobArgs.svelte: hide_secret_masksfrom the args table and show a small "secret masks" badge instead (same treatment as_TEMP_SCRIPT_REFS).test_flow_transform_secret_maskingcovering both push paths: step "a" (flow job pushed while running underhandle_flow) and step "b" (pushed from the step-completion path), asserting masked child logs, no plaintext in logs, and no plaintext in the stored_secret_masksarg. Added aquickjsfeature to the test crates so the JS-evaluator-gated test can run without the heavydeno_corebuild; CI enables both..sqlx: two new offline cache entries for the test queries (via./update_sqlx.sh).Test plan
cargo test -p windmill-api-integration-tests --features quickjs --test sensitive_log_masking— new flow test + full pre-existing masking suite passcargo check -p windmill-common -p windmill-workernpm run check— 0 errorse2e*****9w1, the plaintext appears nowhere injob_logs, and the stored_secret_masksarg is an encrypted blob_secret_maskshidden from the inputs table, "secret masks" badge shownScreenshots
Child step job of the E2E flow (the visible
e2e_secret_password_…value in thecmdarg is a throwaway test string; transform results landing in child args is pre-existing behavior — this PR is about logs):Known limitations
push_next_flow_jobinvocation (in-memory, per-process): a secret fetched by step 1's transform is masked in step 1's job, but not in a later step's job unless that step's transform fetches it again. Cross-step accumulation would require persisting masks in the flow status; out of scope here.$encrypted:args limitation.🤖 Generated with Claude Code
Summary by cubic
Propagates secret masks from flow input transforms to child step jobs and defers eligible
variable()fetches to child workers, so secrets are masked in child logs and never stored in child args. Addresses WIN-2145.New Features
variable()in flow input transforms to$var:or{"$interpolate": [...]}args that the child worker resolves at start, registering masks and keeping raw secrets out of stored child args.cache_ttlsteps, and gated by a min-version check for$interpolatesupport; non-deferable expressions fall back to eager evaluation.Bug Fixes
push_args.extra._secret_masks, encrypted with the workspace key + root job id, and register them right afterregister_running_joband before any logs; agent workers skip if they can’t decrypt.push_next_flow_jobon the completion path and seed from its own args so transforms can register masks; supports subflows._secret_masksfromargs.jsonand hide it in the UI; add an integration test covering$interpolate,$var, and eager-fallback cases; enablequickjsfor tests; update.sqlxcache.Written for commit 9a74690. Summary will update on new commits.