Skip to content

Intercept rule matching uses hand-rolled strict-prefix argv comparison, bypassable via global flags before subcommand #1328

Description

@kipz

What happened?

resolve_intercept_action (crates/nono-cli/src/tool-sandbox/policy.rs)
matches an InterceptRuleConfig.args pattern against the shim's argv via
a hand-rolled strict-prefix comparison starting at index 0 of
argv[1..]:

if shim_args.len() >= rule.args.len()
    && rule
        .args
        .iter()
        .zip(shim_args.iter())
        .all(|(expected, actual)| expected.as_bytes() == *actual)
{
    ...
}

This matcher is local to intercept rules — it doesn't reuse the
ArgvMatcherConfig type (exact / prefix / contains) already used
for invocation_policy rules in command_policy.rs, so intercept rules
have no contains-style option.

Because matching assumes the subcommand is always shim_args[0], an
intercept rule (e.g. args: ["clone"], carrying a narrowed sandbox
override or a deny/approve action) matches git clone ... but not
git -c protocol.ext.allow=always clone ..., since shim_args[0] is
-c. When no rule matches, resolution falls through to
ResolvedInterceptAction::passthrough() — no sandbox override, no
intercept action — so the broader default sandbox applies and any
gating on that subcommand is silently skipped.

Pre-existing behavior (confirmed unchanged vs. main via git show,
originally added in #1105) — not introduced by #1324, which only adds
the sandbox field without touching the matcher. Flagged by automated
review on #1324: #1324 (comment)

What did you expect to happen?

An intercept rule targeting a subcommand should match regardless of
global flags/options inserted before that subcommand, so its
sandbox/deny/approve action can't be bypassed by argv shape alone.

Steps to reproduce

  1. Configure an intercept rule for a command (e.g. git) with
    args: ["push", "--force"] and a deny action or narrow sandbox
    override.
  2. Invoke git -c foo=bar push --force ... under nono's tool-sandbox.
  3. Observe the rule doesn't match; the invocation falls through to the
    catch-all/passthrough action and default sandbox instead.

Relevant output or logs

N/A — logic/design issue found via code review, not a crash.

nono version

dev build at 832224b (branch kipz/per-intercept-sandbox, PR #1324)

Operating system

Other (specify in additional context)

OS version / distro

N/A — bug is in argv comparison, not the platform-specific sandbox backend.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions