Skip to content

fix: refuse to write a sensitive variable that would clear its value - #450

Draft
NickJosevski wants to merge 1 commit into
mainfrom
fix/449-sensitive-variable-guard
Draft

fix: refuse to write a sensitive variable that would clear its value#450
NickJosevski wants to merge 1 commit into
mainfrom
fix/449-sensitive-variable-guard

Conversation

@NickJosevski

Copy link
Copy Markdown
Contributor

Fixes #449.

A sensitive variable written with an empty Value and no Id had its stored secret replaced with an empty string. Nothing errored, and the loss was undetectable on read because the server never returns a sensitive value. A caller who rebuilds a variable set instead of round-tripping the objects from GetAll produces exactly that payload.

Both Update paths now reject it. Everything else is unchanged: sending the Id keeps the existing value, and sending an explicit value sets it.

What the server actually does

Measured against a live 2026.x instance, using a runbook that printed ${#VALUE} so masking could not hide the result. Starting value 18 characters:

Id sent Value sent Resulting length
yes "" 18 — preserved
yes null 18 — preserved
yes "newvalue123" 11 — updated
no "" 0 — wiped
no null 0 — wiped
no "newvalue123" 11 — updated

The Id is the sole determinant. null and "" are indistinguishable.

That is why this doesn't take #261's approach of changing Value to *string — rows 4 and 5 show sending null wipes the secret just the same, so it would be a breaking change to every caller of Variable.Value that fixes nothing. #261 also bundles GitHub credential support that has since landed independently as credentials.GitHubApp.

Verified live

Six flows through the client, before cleanup:

1. round-trip untouched (IDs intact)          err=<nil>
2. same set, sensitive variable's ID stripped  err=... would clear the stored value
3. explicit value, no ID                       err=<nil>
4. new sensitive variable with a value         err=<nil>
5. new sensitive variable with no value        err=... would clear the stored value
6. newclient path, ID stripped                 err=... would clear the stored value

Case 5 is newly rejected: creating a sensitive variable with no value. That was already a no-op on the server and is indistinguishable from case 2, which is the destructive one.

Test fixtures were removed afterwards. pkg/variables has the same failures before and after — TestLibraryVariableSetServiceDeleteAll fails on both, being a destructive integration test that cannot delete variable sets in use by projects on the instance.

🤖 Generated with Claude Code

The server matches a variable without an ID by name, so writing a
sensitive variable with an empty value and no ID replaced the stored
secret with an empty string. Nothing errored and the loss was invisible
on read, because a sensitive value is never returned.

Both Update paths now reject that payload. Sending an ID, or an explicit
value, is unaffected.

Fixes #449

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Writing a sensitive variable without its Id silently wipes the stored value

1 participant