Skip to content

pi-permission-system: execution-modifier wrappers (time/timeout/nice/stdbuf/setsid) inherit the inner command's verdict #963

Description

@gotgenes

Package

pi-permission-system

What do you want to change?

A wrapper that only changes how the same visible command runs — time, timeout, nice, stdbuf, setsid — should inherit the inner command's verdict outright, instead of being floored to <indirection-bash-wrapper> unless the inner command happens to be a pure reader.

Today at 5ba2ba5e (33.0.5):

time pnpm run lint >/tmp/lintout.txt 2>&1; echo "lint rc=$?"; tail -3 /tmp/lintout.txt

asks with matchedPattern: <indirection-bash-wrapper>, while the same command without time resolves by its own rules. The exemption that would let it through exists — resolveWrapperUnit (src/handlers/gates/bash-command.ts) already resolves executedUnit on the bash surface when floorExemption holds — but isTransparentWrapper (src/access-intent/bash/wrapper-analysis.ts) admits a unit only when the inner head word is in PURE_READER_CORE and the unit has no output redirect (ADR 0013 §11, #803). pnpm is argument-dependent by design, so time pnpm … fails the bar; test/access-intent/bash/wrapper-analysis.test.ts pins it as ["time pnpm test", "the inner command is not in the core"].

Why?

INDIRECTION_WRAPPER_NAMES conflates two classes that #490 floored uniformly:

Class Members What the wrapper changes
Changes what runs, as whom, or with which operands sudo, doas, env, xargs, parallel, rush, rust-parallel, find -exec, fd -x, watch privilege, environment (env PATH=… git), an argument feed the enumerator cannot see, repetition
Changes only how the same visible command runs time, timeout, nice, stdbuf, setsid timing, kill deadline, scheduling, buffering, session

The floor's reason (#490) is that a wrapper "hides the command that should be gated". For the second class that is false whatever the inner command's effect: every operand is on the command line, and the wrapper adds no privilege, no environment, and no argument feed. §11 keys transparency on the inner command (argument-independence); this is a second, wrapper-keyed clause — still package-audited per wrapper, so it does not widen to user declarations, which #880 deliberately keeps outside the floor.

With the strides made on compound-command opacity, these two are now the wrappers I most often approve by hand for a command that would have passed on its own.

How? (optional)

  • Split the second class out of INDIRECTION_WRAPPER_NAMES into its own set (or tag), and let isTransparentWrapper admit a unit whose outermost wrapper is in it whenever unwrapIndirection peels cleanly (no opaque payload, layers ≥ 1) — the inner verdict is then whatever resolveWrapperUnit already computes.
    A nested wrapper from the first class (time sudo …) stops the peel at sudo, which keeps its floor.
  • time: the shell keyword takes only -p, but wrapperName basenames /usr/bin/time, whose -o/--output/-a/--append write a file — refuse the exemption when one is present.
  • The writesViaRedirect refusal exists because the §11 exemption classifies the unit as read. This clause inherits the inner verdict instead, and the redirect destination is gated by the path surfaces exactly as for the unwrapped command — so I expect it not to apply here, but the plan should verify that redirect analysis on a wrapper unit is independent of the floor before dropping it.
  • nohup (writes nohup.out only when stdout is a tty) and flock (creates its lock-file operand) stay floored; either can be revisited separately.
  • The metamorphic pin in test/handlers/gates/bash-command-metamorphic.test.ts (time ${cmd} never loosens the verdict) should keep holding, since the unit's verdict becomes exactly the unwrapped command's.
  • ADR 0013 §11 gains the second clause; docs/architecture/architecture.md's wrapper-analysis.ts entry and the <indirection-bash-wrapper> description in docs/configuration.md follow.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions