Skip to content

fix(intent): refuse any non-cargo program - #321

Merged
ScriptedAlchemy merged 3 commits into
masterfrom
fix/reject-non-cargo-program
Sep 26, 2026
Merged

ScriptedAlchemy merged 3 commits into
masterfrom
fix/reject-non-cargo-program

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Why

hauler exec -- ls -la and hauler request -- ls ran the real ls. The result card then called it a cargo run. parseCargoArgv in src/internal/cargo/intent.ts refused only a path-shaped first word or a program after a peeled env prefix. A bare word such as ls became the cargo subcommand, while buildCommand in src/internal/cargo/execution/executor.ts spawned argv[0] as the program. The two decisions drifted.

A second path ran it too. localQueryReason treats any parse error as "cargo resolves this locally", and every client passthrough spawns argv in place without parsing. Rejecting in the parser alone would have moved ls from the daemon to the client.

Scope

parseCargoArgv now throws ProgramNotCargoError with the existing program must be cargo, got X message for any first word that is not a cargo executable. The only exception is a recognized bash -c / sh -c wrapper, whose zero or several cargo statements stay accepted with subcommand bash as before. This one branch replaces the two narrower ones.

runExecCommand in src/scripts/hauler.ts is the only caller of runExecClient. It now parses the argv once and refuses ProgramNotCargoError with exit 2 before the client picks a local query, direct, or daemon-unreachable passthrough. Other parse errors keep their current handling.

No supported caller submits argv without a leading cargo. The hook rewrite in src/internal/host-hooks/inspect.ts inserts hauler exec -- immediately before the cargo word. The PATH shim passes the absolute real cargo. hauler_request and hauler exec forward the user argv as given. The only in-repo argv without cargo was a test convenience in tests/unit/cargo/intent.test.ts, now prefixed with cargo.

Blast Radius

hauler exec -- bash script.sh is now refused. It was never a wrapper the parser modeled. Scripts passed as bash -c '...' are unchanged. env NAME=value cargo ... and absolute cargo paths are unchanged.

hauler request -- ls is refused by the daemon with bad-intent. The routed request CLI exits 1 for any daemon rejection, which this PR does not change.

Verification

Failing first. parseCargoArgv(['ls', '-la']) failed with expected [Function] to throw an error. run(['exec', '--', 'ls', '-la']) returned code 0 with empty output instead of code 2.

After the fix, from the built CLI with an isolated state dir:

$ hauler exec -- ls -la
[cargo-hauler] program must be cargo, got ls        exit=2
$ hauler exec -- ls --help
[cargo-hauler] program must be cargo, got ls        exit=2
$ hauler request -- ls
hauler daemon rejected the request (bad-intent): program must be cargo, got ls   exit=1
$ hauler exec -- cargo --version
cargo 1.97.0-nightly                                 exit=0

pnpm run check passed. Two earlier full rstest runs each hit a different integration timeout in daemon-ticket-log, daemon-fold-trailers, and daemon-reattach. The three files pass 28/28 alone and touch no code in this diff.

Principles

Fix Root Causes. The parser owns the program decision, and the exec entry consults it once instead of each passthrough mode guarding separately.

Test Behavior, Not Implementation. Both tests call the parser and the hauler script entry with literal argv and assert the literal message and exit code.

Laziness Protocol. One parser branch replaces two, and the client check is one parse at the single exec entry.

@changeset-bot

changeset-bot Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f9bd2e7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
cargo-hauler Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 26, 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-09-26T00:30:35.241367Z b11a33d 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: b11a33d0c0

ℹ️ 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".

"cargo-hauler": patch
---

`hauler exec` and `hauler request` now refuse a program that is not cargo, such as `hauler exec -- ls -la`, with `program must be cargo, got ls` and exit code 2. Before, the daemon or the local passthrough ran it and reported it as a cargo run.

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 Correct the request exit code in the release note

In the documented hauler request -- ls scenario, the daemon rejection is converted to a regular error by runTicketEffect, so the routed CLI exits with code 1; only hauler exec maps a bad-intent response to code 2. Claiming that both commands exit 2 gives scripts consuming the release note the wrong contract, so distinguish the two exit codes or change the request command accordingly.

Useful? React with 👍 / 👎.

@ScriptedAlchemy
ScriptedAlchemy force-pushed the fix/reject-non-cargo-program branch from b11a33d to f9bd2e7 Compare September 26, 2026 00:53
@ScriptedAlchemy
ScriptedAlchemy merged commit e3e9eec into master Sep 26, 2026
@ScriptedAlchemy
ScriptedAlchemy deleted the fix/reject-non-cargo-program branch September 26, 2026 00:53
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