Files
pi-extensions/docs/adr/0002-env-defers-to-ai-judge.md
T
SikongJueluo 1afcbd3118 refactor(permission): consume structured bash payload
- require @gotgenes/pi-permission-system >=25.3.0 and read the complete local bash command from PromptPermissionDetails.payload instead of session-walking recovery
- remove the @sikongjueluo/pi-permission-shared package
- pass the triggering command unit to handlers via HandlerContext.unit in place of details.command
- add shadow-only AI judge modules for evidence projection, structured verdict requests, and prompt building, with vitest coverage
- record ADR 0004 and mark the ADR 0001 recovery mechanism superseded
- exclude pi-permission-system 25.3.0 from the pnpm minimumReleaseAge guard
2026-08-16 23:08:45 +08:00

2.9 KiB

status
status
accepted

Defer env to the AI judge; inner-cmd never unwraps it

env is the second command type considered for pi-permission-inner-cmd's deterministic unwrapping. We decided inner-cmd always defers env — it never strips modifiers or re-evaluates an inner command — and lets the AI judge authority handle env commands together with their full environment context.

Why

env is a non-transparent wrapper: its modifier args (NAME=VALUE, -i, -u, -C, -S) form an unbounded, implicit input channel that the command-string policy cannot see. Stripping them and re-evaluating the inner command is unsound, because the modifiers can change what actually runs without changing the inner command string:

  • PATH=/evil — resolve the inner name to a different binary;
  • LD_PRELOAD=/x.so, LD_LIBRARY_PATH — inject arbitrary native code into the inner binary before its main();
  • BASH_ENV, PYTHONPATH, NODE_OPTIONS, PERL5OPT, … — source scripts or inject code into the inner command's runtime;
  • -i, -u NAME, -C DIR — alter the execution context.

A "detect dangerous variables and defer" denylist is infeasible: the dangerous set is open-ended (new runtimes keep introducing new *_OPT / *_PATH injection variables), so any denylist is permanently incomplete, and one miss is an RCE. Detecting "does any modifier exist" is trivial and complete, but the only sound unwrap case is bare env <cmd> (zero modifiers), which is too rare in practice to justify the shell parsing it would require (assignment/flag detection, -S, --, /usr/bin/env basename, quoting).

Considered options

  • Detect a PATH= override, otherwise unwrap — rejected as false security: LD_PRELOAD and the interpreter-injection variables defeat it without touching PATH.
  • Unwrap only bare env <cmd> (zero modifiers) — sound, but bare env is rarely written and the boundary parsing adds real complexity for near-zero coverage. Not worth it.
  • Always defer (chosen) — zero parsing risk, zero soundness surface; non-transparent commands get the semantic judgement they require from the AI judge.

Consequences

This establishes a clean division between the two permission authorities:

  • inner-cmd (deterministic) unwraps only transparent wrappers (timeout) — sound, fast, narrow. Every uncertain or non-transparent command defers fail-closed.
  • The AI judge authority receives deferred commands (including env) and reasons about the full command with its environment, where non-transparency can be understood semantically rather than stripped.

This is why the AI judge consumes the complete command from permission-system's structured prompt payload (ADR 0004) — it needs the complete input, modifiers included, to judge non-transparent wrappers. See CONTEXT.md for the transparent / non-transparent wrapper distinction.