Repository navigation
hookprobe: a run can no longer rewrite the files configuration points at, and a secret's value is not printed by accident - #41
Merged
Conversation
…onfiguration points at, and read secret values A run could write its own MCP config when a deployment kept it on the run's volume, and every later run started the command it named: measured on the probe image with the work compose's setting and a recording stand-in model, with no tool call for the guards or the audit to see. The input guard now protects every configured file by its path on all three runtimes, the before-and-after digest covers them, and the startup posture names any the service user could rewrite (writable_inputs, paged once). The work compose reads both MCP configs from the read-only secrets mounts, and the configuration doc stops offering the volume. A read-only identity still reads secret values. Posture records reads_secrets beside the verdict (can-i get|list secrets, and a second simulation of Secrets Manager, SSM, KMS and S3 reads) without letting it decide anything, and the bash guard refuses printing a secret's value under every posture. Whether a secret exists and when it changed stays open. The console's posture card shows both. Read back from the branch image: the planted config is refused and nothing runs after it; with the config on a read-only mount the posture reports nothing and the kernel refuses the write. Two decision notes, four guard mutations; the 2026-09-09 note that left Secret reads allowed is corrected in place.
…de scanning reads as secret data CodeQL followed `_SECRET_RULES` into the refusal text, which the gate prints and the audit stores, and raised clear-text logging and storage of sensitive data on both (alerts 15 and 16). The table holds patterns and fixed descriptions, never a value: its name was the only "secret" in the flow. The guard's table is `_REVEAL_RULES` and its resource pattern `_RESOURCE_WORD` (the bandit annotation the old name needed goes with it), and posture's questions are `KUBE_READ_QUESTIONS`, `READ_VERBS` and `AWS_READ_ACTIONS`, with a comment beside each saying why. The record fields an operator reads keep their names.
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.
Two things a read-only investigator could still do, both found by asking a second deployment's audit questions of this code, and both closed here.
1. A run could rewrite the files configuration points at. The work compose kept the planner's and the watcher's MCP config on each node's own volume. The engine rereads that file at every run and starts the stdio servers it names, and the input guard protected only fixed names in the workdir. Measured on the probe image with that setting and a recording stand-in for the model API:
The fix has three parts:
writable_inputs), and the alarm channel says so once. It is reported, not refused, so a node configured from the old docs still starts.Read back from this branch's image:
writable_inputs/data/mcp.json, paged once2. A read-only identity still reads secret values.
reads_secretsbeside the verdict and never lets it decide anything. The questions arekubectl auth can-i get|list secrets -Aplus wildcard rules, and a second AWS simulation of Secrets Manager values, SSM parameters, KMS decryption and S3 object reads. Their failures go toread_errors, never to the errors that make a verdict unverifiable.This overturns the 2026-09-09 note that left
kubectl get secret … -o yamlallowed on purpose. That note is corrected in place, and the output-guard test's premise now says which read is refused and why every other read stays open.Shown. The console's posture card adds secret-read counts and names any config a run could rewrite, in both languages.
Checked.
Not changed: codex keeps its hooks in a home on the run's volume, which is already its own containment row. Nothing is deployed. The two local MCP configs move to the read-only mounts after this merges, and the stack stays stopped.
CodeQL. The first run raised two high alerts, clear-text logging and clear-text storage of sensitive data, at the gate's output and the audit writer. The flow started at the guard's rule table, whose name held the word code scanning treats as secret data, and carried its fixed descriptions into the refusal text. The second commit renames the rule and question tables. Every field an operator reads keeps its name.
CI read back before merging.