mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-21 16:02:28 +00:00
339c259fce
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>
14 lines
694 B
TypeScript
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
|
|
}
|