Skip to content

fix(O170): block shell-exec env assignments (BASH_ENV etc.) and tmux/screen spawn forms - #158

Merged
QiaolongLi1201 merged 4 commits into
mainfrom
fix/o170-indirect-exec-bypass
Oct 18, 2026
Merged

QiaolongLi1201 merged 4 commits into
mainfrom
fix/o170-indirect-exec-bypass

Conversation

@QiaolongLi1201

Copy link
Copy Markdown
Collaborator

O170 (P2): indirect execution bypasses that already existed on main

  • BASH_ENV=<file> bash -c ':' makes bash source the file; ENV= does the
    same for POSIX sh/dash; PROMPT_COMMAND= runs before every prompt of an
    interactive bash; ZDOTDIR moves zsh's .zshenv; an exported
    BASH_FUNC_name%%=() { … } body is code itself.
  • tmux new-session / new-window / split-window / run-shell /
    send-keys, and screen carrying a command, run it outside Moss's
    process tree
    .

None of these tripped the exec-capable gate on main — a probe planted in
BASH_ENV ran while the command looked like an inert assignment.

Fix (src/safety/exec-capable-read.ts)

  • isShellExecEnvName + isBashFuncWord (line-level: tokenizers split on
    the parens) collect the assignments from every shape — prefix
    assignments, env, export/declare, set, bare statements, pwsh
    $env: / [Environment]::SetEnvironmentVariable — and the forms are
    reported only when a POSIX shell head runs on the same line: the common
    app-config name ENV must not false-positive (ENV=production npm start
    stays green; pwsh heads don't read these variables).
  • shellSpawnForm reports tmux/screen only when they carry a command:
    tmux ls, tmux attach, screen -ls are unaffected.

Tests — test/o170-indirect-exec-bypass.spec.mjs (39)

  • RED: every env shape above × shell heads; tmux new-session/new-window/
    split-window/run-shell/send-keys; screen -dmS and -X stuff.
  • GREEN: ENV=production npm start, BASH_ENV=… node x.js (no shell head),
    plain bash -c/zsh -c, FOO=1 bash -c, command-less tmux/screen.
  • Process (real dist/cli.js -p, marker probe): BASH_ENV=<probe> bash -c ':' — probe never runs, exit 9 「被拦」; control bash -c ':' exit 0.

Revert-red (local, macOS)

Neutralizing isShellExecEnvName + shellSpawnForm → 43 red; with the
fix 39/39. Related: o151 204/204, o101 24/24, o117 51/51,
hooks-fail-closed 25/25, npm run check green.

Head eec8b962 includes main at 9d455d94 (#155).

BASH_ENV / ENV / PROMPT_COMMAND / ZDOTDIR assignments (every shape:
prefix, env, export, set, bare, pwsh $env:) and exported BASH_FUNC_*
bodies make a POSIX shell run code; tmux new-session/new-window/
split-window/run-shell/send-keys and screen with a command spawn outside
Moss's tree. None tripped the exec-capable gate on main — a BASH_ENV
probe ran. The env forms fire only when a shell head runs on the same
line (ENV=production npm start stays green); command-less tmux/screen
forms are unaffected.
…c-bypass

# Conflicts:
#	CHANGELOG.md
#	README.en.md
#	README.md
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 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-18T18:02:51.789805Z eec8b96 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: eec8b9628e

ℹ️ 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 on lines +1119 to +1121
for (const w of words) {
const m = ASSIGN.exec(w) ?? w.match(/^\$env:([A-Za-z_][A-Za-z0-9_]*)=/i);
if (m && isShellExecEnvName(m[1]!)) shellExecEnv.add(m[1]!.toUpperCase());

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 Exclude quoted shell payloads from assignment collection

For a normal app-config invocation such as bash -c 'ENV=production node -p process.env.ENV', this loop treats the entire quoted command-string argument as an environment assignment because it starts with ENV=. The outer bash then satisfies sawShellHead, even though the assignment happens inside its payload and only applies to Node, not to a shell startup. I verified that this command returns no forms before this commit but now produces an exec-capable protected-path hit, causing legitimate headless commands to be denied. Let the existing nested scan interpret shell payloads instead of treating arbitrary argument words as assignments.

Useful? React with 👍 / 👎.

Comment on lines +133 to +136
while (i < words.length && words[i]!.startsWith('-')) i += 1;
// `screen -dmS name cmd …`: an -S option consumed the session name.
if (i < words.length && /^-[a-zA-Z]*S$/.test(words[i - 1] ?? '')) i += 1;
if (i < words.length) return 'screen command';

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 Parse screen option operands before identifying a spawned command

For existing-session operations such as screen -r build, this loop stops at the session selector build and incorrectly reports screen command. The GNU Screen manual documents -r [pid.sessionname] as “Resume a detached screen session,” not spawning a command. Query operations such as screen -S build -Q windows are also misclassified because parsing does not continue past the -S operand. I verified that both previously returned no forms and now produce exec-capable protected-path hits, introducing unnecessary interactive approvals and headless denials. Consume option operands and distinguish resume/query modes from actual command-spawning forms.

Useful? React with 👍 / 👎.

…c-bypass

# Conflicts:
#	CHANGELOG.md
#	README.en.md
#	README.md
@QiaolongLi1201
QiaolongLi1201 merged commit ac2c3da into main Oct 18, 2026
13 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