Skip to content

2.0.1: scan a journal after a shell command changes it - #2

Merged
dctmfoo merged 1 commit into
mainfrom
fix/scan-bash-journal-writes
Oct 21, 2026
Merged

dctmfoo merged 1 commit into
mainfrom
fix/scan-bash-journal-writes

Conversation

@dctmfoo

@dctmfoo dctmfoo commented Oct 21, 2026

Copy link
Copy Markdown
Owner

Addresses the Codex review finding on #1 (P1, hooks/session-journal.py:1010): a credential written to a journal by an opaque shell command (F=<journal>; cat notes > "$F") escaped the deny-mode check.

  • After any Bash/shell command that changed a journal, PostToolUse scans the journal file itself and tells the agent to remove what matched (values never echoed).
  • In deny mode the journal is remembered on the lease and Stop blocks until it is clean.
  • New scenario test reproduces the reviewer's case end to end; SECURITY.md and CHANGELOG updated; VERSION 2.0.1.

A shell write such as F=<journal>; cat notes > "$F" hides both the
target and the content from PreToolUse, so in deny mode a credential
could land in a journal unchecked. After any shell command that changed
a journal, PostToolUse now scans the journal file itself and tells the
agent to remove what matched; in deny mode Stop blocks until the
journal is clean. Values are never echoed. Bumps to 2.0.1.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-21T18:45:46.697205Z ec84673 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ec84673aa5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread hooks/session-journal.py
Comment on lines +1009 to +1012
if cmd is not None and ctx.secrets != 'off':
# A shell write can carry a credential PreToolUse never saw (`cat secret > "$F"`): scan the journal itself.
for jn in names:
hits = scan_secrets(read(ctx.jpath(jn)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Scan Bash calls whose command text hides the journal path

With the installed Bash hooks, a command such as ./update-journal.sh or . /tmp/vars; cat /tmp/notes > "$F" can modify a journal without containing the literal journal-directory name or matching the landing regex. run_hook() then exits through tool_may_matter() for both PreToolUse and PostToolUse, so no pending timestamp is recorded and this new scan never runs; the credential remains and Stop has no leaked entry. Route opaque Bash calls through the before/after detection path as well.

Useful? React with 👍 / 👎.

Comment thread hooks/session-journal.py
Comment on lines +1067 to +1070
if leaks and not inp.get('stop_hook_active'):
ctx.save_lease(lease)
ctx.trace(ev='stop', sid=sid, result='secret', journal=leaks[-1][0])
return {'decision': 'block', 'reason': 'SESSION-JOURNAL-SECRETS: ' + '; '.join(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep blocking while a recorded leak remains

After the first Stop block, the runtime's next Stop invocation has stop_hook_active=true; if the agent failed, refused, or only partially completed the requested cleanup, this condition skips the secret block and the session can finish with the credential still present. The newly added scenario reproduces this by leaving the file untouched after the first block: the active Stop returns no decision while lease['leaked'] remains populated, contradicting deny mode's promise to block until clean.

Useful? React with 👍 / 👎.

Comment thread hooks/session-journal.py
Comment on lines +1011 to +1014
for jn in names:
hits = scan_secrets(read(ctx.jpath(jn)))
if hits:
leaks.append((jn, hits))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve bulk changes as credential-scan candidates

When an opaque shell command modifies three or more journals, journals_changed_since() returns more than two extras and the existing bulk guard discards the entire set before this loop. Thus names is empty, none of the modified journals is scanned, and credentials written by the bulk command produce neither a warning nor a Stop block. Keep the candidates used for security scanning separate from the smaller set allowed to drive session binding.

Useful? React with 👍 / 👎.

@dctmfoo
dctmfoo merged commit a37217d into main Oct 21, 2026
6 checks passed
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.

1 participant