Skip to content

Match versioned interpreter names, and record denied macOS execs - #84

Merged
ihavespoons merged 1 commit into
mainfrom
macos-wrap-python-reexec
Nov 3, 2026
Merged

ihavespoons merged 1 commit into
mainfrom
macos-wrap-python-reexec

Conversation

@ihavespoons

Copy link
Copy Markdown
Contributor

The problem

Under agentmon wrap, Homebrew's python3 exited 255 before it ran any code, and no event said why (SECENG-311).

ES reports the resolved executable. /opt/homebrew/bin/python3 resolves to /opt/homebrew/Cellar/python@3.15/3.15.0/bin/python3.15. agent-default names python3, so the engine denied python3.15 with default-deny-commands. The extension denied the exec and recorded nothing.

The fix

  • compiledCommandRule.matchesCommand checks the basename as it is, then without .real, then without a trailing version suffix such as .15. python3.15 matches a rule for python3, and perl5.34 matches a rule for perl5. A deny rule matches the versioned name too.
  • matchCommandRules and checkExecve each had their own copy of the match logic, and only the first stripped .real. Both now call matchesCommand.
  • The extension sends an execve event with argv, parent_pid, the rule and the message when the daemon denies an exec. This follows the Linux execve handler.
  • test-macos-process-access.sh and test-macos-wrap-session-reuse.sh run plain python3 again, instead of the Python.app binary.

Risk

The version suffix adds no new way past a rule. A process can already pass any basename check, because it can name a binary python3. Only .<digits> parts are stripped, and the part that stays must hold a letter.

Verification

  • scripts/test-macos-wrap-python.sh failed 3 of 3 on build 1677 and passes 3 of 3 on build 1678. It runs python3 under wrap, and expects a denied /usr/bin/banner and its execve event.
  • On build 1678: test-macos-wrap-session-reuse.sh passes 4 of 4 with plain python3, test-macos-socket-signal-rules.sh prints 13 ok, and test-macos-network-proxy.sh passes 6 of 6.
  • New Go tests: TestStripVersionSuffix, TestCommandNameForms and TestVersionedInterpreterMatchesRule.
  • go test -tags nofuse ./... passes, and golangci-lint reports 0 issues.
  • test-macos-process-access.sh needs sudo, and it has not run with plain python3 yet.

Fixes SECENG-311.

🤖 Generated with Claude Code

Under `agentmon wrap`, Homebrew's python3 exited 255, and no event said
why (SECENG-311). ES reports the resolved executable, and
/opt/homebrew/bin/python3 resolves to .../bin/python3.15. agent-default
names python3, so the exec fell through to default-deny-commands.

The command matcher now also tries the basename without a trailing
version suffix such as ".15", after the existing ".real" form. Both
match loops in the engine had their own copy of this logic, and only
one stripped ".real". They now share compiledCommandRule.matchesCommand.

The extension now sends an execve event when the daemon denies an exec,
as the Linux execve handler does.

The process access and wrap reuse test scripts run plain python3 again.
scripts/test-macos-wrap-python.sh failed 3 of 3 on build 1677 and passes
3 of 3 on build 1678.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ihavespoons
ihavespoons merged commit 7c3d556 into main Nov 3, 2026
8 checks passed
@ihavespoons
ihavespoons deleted the macos-wrap-python-reexec branch November 3, 2026 08:09
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