Repository navigation
0.1.42: credentials on a command line stay on the machine; in-line variables are read; preflight stops guessing at steps it cannot read - #65
Merged
Conversation
…longer guesses at steps it cannot read
From a measured dogfooding log (687 commands of one Claude Code session, ekbasis-claude-hook 0.1.41 in log mode).
The git guard and the shell guard read $NAME / ${NAME} with its value when the line itself sets NAME earlier to a
known value (shell.resolve): a command made only of NAME=value words, at the top level, unconditional, the only place
the line sets NAME. Anything else stays "cannot foresee" as before (environment, set twice, used before set,
export/read/for/local, loops, conditions, pipelines, subshells, command substitution, eval/source/functions). A
resolved word keeps the lexer's quoted flag, so `$S/hooktest.*` still expands as a pattern; the shell guard's state
names the values it used.
Preflight: a step that runs code given inline to an interpreter (python -c, a program on stdin or in a
here-document, node -e, ...) or runs on another machine (ssh, scp/rsync host:) is never named as the failing step
(Step.opaque); the other steps are still judged, and when none of them changes anything the line is "cannot judge"
(silent unless EKBASIS_PREFLIGHT_FAIL_CLOSED=1). Root cause of "for ... do ... done ... likely to fail (checked in
code)": one-line chains were cut at ; into commands named for/do/done/case/esac, which shell_facts found "not
installed"; loops and conditions on one line are now "cannot judge", as in scripts. Two more code facts that
misfired in the replay: a redirection into a folder an earlier step creates (mkdir -p out; ... > out/log), and a
command given by its path (./node_modules/.bin/tsc) taken as "not installed".
Replay through 0.1.41 and 0.1.42, hosted API, same state: lines flagged 299 -> 137; preflight warnings 171 -> 52
(on commands that really failed 6 -> 3, the 3 kept name the failing step); shell guard "could not foresee" 64 -> 19;
git guard "cannot foresee" 76 -> 15. Lines whose flag went away because a variable was read, re-run with the work at
stake put back: 15 of 15 flagged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…the machine or land in the logs The command line went verbatim into the state the client sends (hook, CLI, MCP tools, Python API) and into the hook's log: a key typed on it (printf 'vck_...' > f, export TOKEN=..., curl -H "Authorization: Bearer ...", https://user:token@..., --password=...) reached the server and the disk. ekbasis.redact.redact_line now runs on every state Ekbasis.ask sends, on feedback notes, on the hook's log record, reasons and messages, and on the monitor's log. It replaces credential-looking values with placeholders made of [A-Za-z0-9_] (REDACTED_1, vck_REDACTED_2; the same value gets the same placeholder): known prefixes (sk-ant-, sk-, sk_live_, vck_, hf_, ghp_/gho_/ghs_, github_pat_, xox?-, AKIA/ASIA, glpat-, npm_, pypi-AgE, AIza, JWTs), PEM private key bodies, Authorization/X-Api-Key/X-Auth-Token and Cookie header values, URL passwords, --password/--token/--api-key/--secret values, values of assignments and JSON/YAML keys named like token/secret/passw/api_key/auth(not author)/credential/private_key (8+ characters, not a number, boolean or path), and long random-looking words as a last resort. Variables and substitutions ($TOKEN, $(cat key)), paths, git refs, commit ids, sha256 sums and UUIDs stay. Quotes, spaces, redirections and paths stay where they are, so the parsed line has the same shape (tested). The hook's log records "redacted": n. EKBASIS_REDACT=0 turns it off. Measured: on the 687 logged lines it changes none (no credential left in that log; no path, ref or hash touched). On 14 of them with made-up credentials put on them, run through the hook with and without redaction on the hosted API (one line at a time, at most 2 requests in flight): the same decision and the same kinds of warning for all 14, probabilities within 17 points; the redacted logs never held the credential. README and docs/SECURITY.md say what leaves the machine and the limits of redaction. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…) and of which tools are installed On Linux the shell guard lists a path under the folder as relative, on macOS (/var -> /private/var) as absolute: the test now accepts either. The one-line loop test checks that no reserved word is called "not installed", not that no command is (ps is missing from slim images). Full suite in python:3.12-slim: 389 passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ls restored in the answers Ekbasis.ask redacted the state but not the questions, which can carry runtime values: recap() puts the caller's rules into `instructions`, and the MCP predict_consequences tool takes caller-written questions and criteria. Every string of every question is now redacted before sending (instructions, criteria labels and descriptions; "type" is untouched). Labels are redacted in one pass per question so distinct labels stay distinct, and a redacted label is mapped back to the caller's in the answer (value, probabilities, calibrated probabilities). Test: a recap()'d question with a key in the rules and an MCP-style choice question with keys in a label and a description; nothing leaves, and the answer carries the caller's label. CHANGELOG and SECURITY say so. Co-Authored-By: Claude Opus 5.5 (1M context) <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.
Client 0.1.42, from a measured dogfooding log: 687 commands of one Claude Code session run through
ekbasis-claude-hook0.1.41 in log mode (shell guard and preflight on, hosted API).Security: credentials typed on a command line stay on the machine
Until 0.1.41 the command line went verbatim into the state the client sends (hook, CLI, MCP tools, Python API) and into the hook's log in log mode. A key typed on it (
printf '%s' 'vck_…' > f,export TOKEN=…,curl -H "Authorization: Bearer …",https://user:token@…,--password=…) reached the server and the disk. In that session's log it happened once.ekbasis.redact.redact_linenow runs on every stateEkbasis.asksends, on feedback notes, on the hook's log record, reasons and messages, and on the monitor's log. It replaces credential-looking values with placeholders made of[A-Za-z0-9_](REDACTED_1,vck_REDACTED_2; the same value gets the same placeholder), and keeps quotes, spaces, redirections and paths. The parsed line keeps the same shape (tested).--password,--token,--api-key,--secretvalues;$TOKEN,$(cat key)), paths, git refs, commit ids, sha256 sums and UUIDs."redacted": n, andEKBASIS_REDACT=0turns redaction off.The guards read a variable set earlier on the same line
The git guard and the shell guard (
shell.resolve, used byparseand by the hook's line reader) read$NAME/${NAME}with its value when the line sets NAME before, to a known value:G=/repo; git -C "$G" status,D=build; rm -rf "$D".The assignment must be:
Everything else is still not guessed and stays "cannot foresee", as before:
The shell guard's state names the values it used.
Preflight does not guess at a step it cannot read
python3 -c,python3 - <<'EOF',node -e, …) or runs on another machine (ssh, scp/rsync to or from a host) is never named as the failing step. The other steps are still judged. When none of them changes anything, the line is "cannot judge": silent, or one warning withEKBASIS_PREFLIGHT_FAIL_CLOSED=1.python3 assets_src/make_chart_jev.pylikely fails, step 2 (an inline edit) already ran", and the session's transcript shows exactly that failure.for f in …; do …; doneis likely to fail (checked in code)": one-line chains were cut at;into commands named for/do/done/case/esac. Code then found them "not installed" and decided that the step fails. Such lines are now "cannot judge", as scripts already were.mkdir -p out; … > out/log);./node_modules/.bin/tsc), which code took as "not installed".Replay: 0.1.41 and 0.1.42 on the same commands and state
What disappeared: 246 flags on 221 lines.
True-positive check:
Known follow-up: 10 new preflight warnings, all on commands that succeeded. They sit on lines where 0.1.41's git guard gave up first and preflight never ran. Their cause: preflight's listing does not follow a
cdfrom an earlier step (an issue will be opened).Checks
_version.py,pyproject.toml,plugin.jsonandserver.json.🤖 Generated with Claude Code