Files
windmill/frontend/src/lib/encryptedDraft.ts
T
Diego Imbert 339c259fce fix(drafts): close variable draft-secret laundering oracle (sentinel + rehydrate)
save_draft encrypts secret variable values with the workspace key, but
the ciphertext was round-tripped to the client and the deploy endpoints
decrypted whatever $encrypted: ciphertext the client submitted
(variables.rs create/update). Any workspace member who can write a
variable path could take an arbitrary workspace-key ciphertext (another
user's secret draft via GET /drafts/get with only path-read, or a
deployed secret's stored value) and submit it as their own secret
variable's value — the server decrypted it and, since they own the path,
they read the plaintext back. That bypasses the audited decrypt_secret
permission.

Fix: the ciphertext never leaves the server. get_variable swaps a draft
secret's $encrypted: value for an opaque $draft_secret sentinel (both the
draft overlay and the draft-only inner stand-in). On deploy the client
sends the sentinel back and the server rehydrates the plaintext from the
caller's OWN draft row — the only ciphertext it ever decrypts is one it
encrypted for this exact (workspace, path, email). A raw $encrypted:
submitted by a client is now rejected outright.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 17:29:41 +02:00

14 lines
694 B
TypeScript

/** Opaque placeholder the server sends in place of a draft secret's
* value (mirrors `DRAFT_SECRET_SENTINEL` in
* `backend/windmill-common/src/user_drafts.rs`). The real secret —
* encrypted at rest with the workspace key — NEVER leaves the server:
* `get_variable` swaps the ciphertext for this sentinel. Deploying sends
* the sentinel back and the server rehydrates the plaintext from the
* caller's own draft row. A field holding this value is "secret set,
* hidden, unchanged" — masked in the UI, not editable in place. */
export const DRAFT_SECRET_SENTINEL = '$draft_secret'
export function isEncryptedDraftValue(v: unknown): boolean {
return v === DRAFT_SECRET_SENTINEL
}