mirror of
https://github.com/SikongJueluo/pi-extensions.git
synced 2026-10-05 20:02:55 +08:00
- 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
62 lines
2.9 KiB
Markdown
62 lines
2.9 KiB
Markdown
---
|
|
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.
|