@klarkxy/dsh-safe-auto 0.1.0-rc.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -130
- package/README.zh-CN.md +82 -119
- package/docs/ADR-0001.md +1 -1
- package/docs/ADR-0002.md +1 -1
- package/docs/ADR-0003.md +1 -1
- package/docs/ADR-0004.md +33 -0
- package/docs/ADR-0005.md +31 -0
- package/docs/ADR-0006.md +58 -0
- package/dsh.plugin.json +9 -4
- package/lib/client.js +1738 -494
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +18 -10
- package/src/approval-review.js +24 -2
- package/src/config.js +12 -35
- package/src/control.js +29 -17
- package/src/credential-paths.js +29 -0
- package/src/credential-reads.js +227 -0
- package/src/credential-text.js +361 -0
- package/src/execution-evidence.js +115 -0
- package/src/gate.js +37 -31
- package/src/host-runtime.js +159 -0
- package/src/human-approval.js +64 -0
- package/src/index.js +175 -203
- package/src/model-route.js +10 -29
- package/src/policy.js +39 -78
- package/src/review-context.js +35 -0
- package/src/review-paths.js +53 -0
- package/src/reviewer.js +61 -120
- package/src/shell-command.js +547 -0
- package/src/ui-host.js +60 -43
- package/src/escalation.js +0 -118
package/docs/ADR-0003.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ADR-0003: conversation-following and independent reviewer routes
|
|
2
2
|
|
|
3
|
-
Status: implemented in RC3. This supersedes the HTTP-only routing statements in ADR-0001/0002, not their remaining execution restrictions or deployment acceptance gates.
|
|
3
|
+
Status: implemented in RC3. This supersedes the HTTP-only routing statements in ADR-0001/0002, not their remaining execution restrictions or deployment acceptance gates. **`0.2.0-rc.0` narrowed the routing surface to a single reviewer route** (`provider`/`model`/`reasoningEffort`, defaulting to the conversation): the deep stage, the HTTP transport and the fast/deep split described below were removed.
|
|
4
4
|
|
|
5
5
|
## Requirement
|
|
6
6
|
|
package/docs/ADR-0004.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Semantic authorization and bounded execution evidence
|
|
2
|
+
|
|
3
|
+
Status: implemented for `0.2.0-rc.2`; its no-human-fallback decision is superseded by [ADR-0005](ADR-0005.md). Live-model accuracy and installed-profile acceptance remain separate from automated contracts. Sources checked on 2026-10-05.
|
|
4
|
+
|
|
5
|
+
## Reference and decision
|
|
6
|
+
|
|
7
|
+
Codex separates the exact proposed action, retained human authorization and untrusted implementation evidence. Its [review policy template](https://github.com/openai/codex/blob/main/codex-rs/prompts/templates/guardian/policy_template.md) judges authorization by material semantics and recognizes necessary implementation steps. Its [prompt builder](https://github.com/openai/codex/blob/main/codex-rs/core/src/guardian/prompt.rs) supplies a compact transcript and the exact request. [Auto-review documentation](https://learn.chatgpt.com/docs/sandboxing/auto-review) describes read-only investigation and distinguishes timeout from an unsafe action.
|
|
8
|
+
|
|
9
|
+
Adopt those principles inside the existing single-call gate. No new approval service, persistent authority store, general shell interpreter, reviewer tool execution or command whitelist is introduced. Safe Auto retains a stricter floor than Codex's default: at least medium direct authorization, bounded effects, and only low/medium risk qualify for automatic permission.
|
|
10
|
+
|
|
11
|
+
## Authority
|
|
12
|
+
|
|
13
|
+
Collect direct-human text in chronological order after excluding child sessions by durable header metadata. Retain earlier authorization across follow-ups and apply later restrictions and revocations. Assistant messages, tool results, plugin feedback and quoted text do not grant permission. Bind the full selected authority to the live execution and recheck it after awaits.
|
|
14
|
+
|
|
15
|
+
Retain all human text within 12 KiB; do not preserve an old grant by dropping an intervening restriction. Sensitive or nontext messages and overflow mark authority incomplete and prevent review. This intentionally requires a fresh, fully scoped conversation rather than a model-generated summary that could change authority. Only retained session events are available; the plugin cannot recover instructions removed by host compaction.
|
|
16
|
+
|
|
17
|
+
## Evidence
|
|
18
|
+
|
|
19
|
+
Use the pinned host filesystem's `resolve`, `contains`, `stat`, `readBytes` and `processPath` APIs. Read package manifests for the effective working directory and workspace root, plus directly referenced local code and file references in relevant package scripts/hooks. The hint scanner only locates evidence; it does not prove shell semantics or authorize execution. Maintained POSIX `shell-quote` and argument-string packages were considered, but they do not supply PowerShell semantics and add a dependency without improving this bounded locator.
|
|
20
|
+
|
|
21
|
+
Resolve canonical targets inside the canonical workspace root; exclude protected paths before and after resolution. Cap at eight files, 6 KiB per file and 12 KiB overall. Decode strict UTF-8 and withhold other encodings and control-byte content. Screen known secret patterns, including quoted and decoded JSON keys, before sending evidence. This detector is not a guarantee against arbitrary secret encodings. Mark unavailable, excessive and withheld evidence explicitly. Do not recursively inspect imports, dependencies or every workspace package.
|
|
22
|
+
|
|
23
|
+
Record canonical paths, opaque filesystem versions and SHA-256 content digests, including absent manifests. Re-resolve and reread before returning `allowed-once`; changes invalidate the review. This closes stale-evidence grants during review, not the race between approval and actual execution.
|
|
24
|
+
|
|
25
|
+
## Failure and feedback
|
|
26
|
+
|
|
27
|
+
Explicit model denial remains `rejected`; uncertainty, timeout, malformed response, missing reviewer and exhausted budget return native `unavailable`. Neither delegates or grants permission. Only semantic denials increment the denial streak. All attempted model calls keep their reservation; non-denials reset the streak.
|
|
28
|
+
|
|
29
|
+
The `tools/execute` wrapper adds bounded classification and recovery feedback to failed result content. The real ToolRuntime contract verifies that feedback survives normalization. The `tools/result` event remains a read-only observer. Model rationale is labeled untrusted and never copied to logs; secret-bearing rationale is withheld.
|
|
30
|
+
|
|
31
|
+
DSH 0.1.7-rc.2 approval and user-question services both dispatch arbitrary composed waterfall answerers. Neither proves human provenance. A direct human fallback therefore remains unsupported; uncertainty is not delegated through either chain. No fake human channel is added.
|
|
32
|
+
|
|
33
|
+
The default combined prompt/input budget increases from 8 KiB to 32 KiB. Existing explicit profile limits remain valid. Reviewer route, effort, single-call execution identity, cancellation, lifetime, native `never`, settings revocation and protected-file controls remain enforced.
|
package/docs/ADR-0005.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Timed human confirmation
|
|
2
|
+
|
|
3
|
+
Status: implemented in the unpublished `0.2.0-rc.2` work, following explicit user approval on 2026-10-05. Supersedes ADR-0004's no-human-fallback limitation.
|
|
4
|
+
|
|
5
|
+
## Behavior
|
|
6
|
+
|
|
7
|
+
Keep automatic model passes. For a structurally eligible call, a model refusal, uncertainty, timeout, unavailable reviewer or exhausted model budget offers a manual confirmation. Model risk classification remains an opinion; the user can explicitly approve that exact action. Native `never`, deterministic hard refusals, unsupported/unbound execution, changed evidence, audit failure and cancellation remain terminal.
|
|
8
|
+
|
|
9
|
+
`AUTHORITY_CONTEXT_INCOMPLETE` blocks automatic review but may receive fresh one-call UI authorization. Rerun every action/host/schema/secret check with only that historical-input flag waived; keep the original live binding. Incomplete private history is never sent to the reviewer or dialog. No other structural refusal is waived.
|
|
10
|
+
|
|
11
|
+
Default `humanApprovalTimeoutMs` is 60000, operator-owned and separate from the model deadline; zero disables takeover. Publish the exact action, permission change and bounded reviewer opinion to a runtime-local queue. Start the server-monotonic deadline when publishing. Only the first session-bound answer consumes the unpredictable request ID. No answer before expiry means rejection; late/duplicate answers never grant.
|
|
12
|
+
|
|
13
|
+
The existing authenticated `/dsh-safe-auto` browser route owns `approval.list` and `approval.answer`. It bypasses the settings serializer so waiting never blocks approval or revocation. Direct browser JSON requests require fetch mode (`cors` or `same-origin`), destination (`empty`) and same-origin/same-site metadata. The DSH 0.2.0-rc.2 desktop carrier checks the application origin itself, removes Origin and Sec-Fetch-Site, and forwards with its Host-issued cookie. Installed Electron 44 protocol requests omit Fetch Metadata; the carrier's Node fetch adds `mode=cors` but no destination. Missing destination is admitted only with that mode and both Origin and site absent, after Host authentication and source checks. Explicitly invalid metadata remains rejected. No automatic waterfall answerer is consulted and no model tool is registered for answering.
|
|
14
|
+
|
|
15
|
+
## Lifetime and integrity
|
|
16
|
+
|
|
17
|
+
Existing execution identity, complete arguments, human-context and control revision bindings remain live throughout the wait. Cancellation, settings changes, permission changes, session replacement, detach and runtime disposal invalidate requests. The queue has a fixed 32-request cap, is not persisted, and clears timers/listeners when each request settles.
|
|
18
|
+
|
|
19
|
+
Approval claims the queue entry synchronously before file checks. Reverify original captured evidence using a fresh bounded signal combined with execution/runtime cancellation. Never recollect and silently replace old evidence. Check deadline and live binding immediately before the grant. The 30-second model lease applies only to model passes; it does not expire an otherwise valid 60-second human window. A successful manual grant resets the semantic denial streak but does not refill model-call budgets.
|
|
20
|
+
|
|
21
|
+
## UI
|
|
22
|
+
|
|
23
|
+
The composer permission component polls serially while Safe Auto is enabled, including before a request exists. An official body-portaled Modal shows exact plain-text shell commands or complete write/edit effects, cwd, permission change, model opinion and countdown. It remains usable while the agent locks the composer. Reject receives initial focus; closing via Escape, mask or close button rejects. Approve says “only this call.” Writes are never retried automatically; uncertain transport results trigger a fresh read. Session changes/unmount discard stale responses.
|
|
24
|
+
|
|
25
|
+
The browser countdown derives from server `remainingMs` and a local monotonic clock, with network time subtracted. It cannot extend the server deadline.
|
|
26
|
+
|
|
27
|
+
## Trust and limitations
|
|
28
|
+
|
|
29
|
+
Authentication, origin checks, Fetch Metadata and request IDs protect the browser channel against accidental and cross-site answers. They do not prove physical human action. Authenticated clients/carriers and same-process plugins remain trusted; an actor able to steal client authentication or forge a trusted client can forge an answer. The plugin does not claim confinement against that actor. Transports missing fetch mode cannot use manual confirmation; pending requests then expire. Missing site metadata is accepted only when Origin is also absent; missing destination additionally requires `mode=cors`, matching the authenticated desktop carrier. Headless execution with no UI controls retains the non-grant path.
|
|
30
|
+
|
|
31
|
+
Evidence remains partial and review-to-execution file races remain possible. No new persistent storage, credential access, service, framework or dependency is introduced. Local disable/unload remains the rollback.
|
package/docs/ADR-0006.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Static shell proof: read-only fast path and AST-level hard rules
|
|
2
|
+
|
|
3
|
+
Status: implemented in the unpublished `0.2.0-rc.3` work, following explicit user approval on 2026-10-06. Complements ADR-0001 (0.1.x preflight removal) and ADR-0002 (execution-bound review).
|
|
4
|
+
|
|
5
|
+
## Context
|
|
6
|
+
|
|
7
|
+
0.1.x carried a static layer (ordinary-command review, workspace/shell candidates, `shadow`/`smart`/`unattended` modes). ADR-0001 removed it because it was a fuzzy candidate-rule system: enrolled strings pretended to be policy, and three modes made the effective boundary unreadable. Two real problems remained after the removal, however:
|
|
8
|
+
|
|
9
|
+
- The hard-rule layer recognized dangerous programs only as the first word of a bare, whitespace-clean command. `ls | rm x`, `sudo rm x` or `time rm x` in a compound command bypassed the deterministic layer entirely and leaned on the model to notice.
|
|
10
|
+
- Every escalation, including a provably read-only `git status`, paid one reviewer call of latency and budget.
|
|
11
|
+
|
|
12
|
+
Two sibling harnesses informed the redesign. ZCode's build/edit modes downgrade provably read-only Bash invocations through a strict AST walk plus per-command policies, and its reserved `auto` mode (never implemented, fail-closed `mode.auto.unimplemented`) shows the intended shape of "low risk goes automatic." Codex's execpolicy separates a strict fully-literal parse (allowed to prove safety) from lossy heuristics (allowed only to flag danger), aggregates compound commands with every-semantics, and fails closed when parsing cannot explain the whole command.
|
|
13
|
+
|
|
14
|
+
## Decision
|
|
15
|
+
|
|
16
|
+
Parse shell commands with `unbash` (Apache-2.0, zero-dependency bash parser, runtime dependency) in a new `src/shell-command.js`. The analysis accepts only a fully literal command: Statement/AndOr/Pipeline/Command/Time nodes, no backgrounding, no heredoc bodies, and every word, assignment value and redirect target free of expansions, globs and tilde prefixes. Anything else returns `ok: false` and changes nothing about the existing pipeline.
|
|
17
|
+
|
|
18
|
+
**Static fast path.** When the structural assessment reaches `review`, a fully literal bash escalation is granted `allowed-once` with `STATIC_READONLY` only if every invocation satisfies both its complete no-mutation grammar and the separate `isSafeStaticExposure` metadata/literal-output check. Exact case-sensitive POSIX executable names select policies. `rootName` normalization (paths, case and Windows suffixes) is only for danger detection. Registered options have exact arity and value validators; each operation checks input/output operand counts and incompatible modes. Unsupported syntax falls back to the existing reviewer/manual channel, not a hard denial.
|
|
19
|
+
|
|
20
|
+
`ls -la`, `pwd` and `find . -name "*.md"` retain the fast path. File-content readers, transformations, recursive content searches, process arguments and environment enumeration require review: no mutation does not imply no credential exposure. `printenv` is excluded. `rg --pre`, archive/helper modes, output operands, executable pagers and mixed Git read/write operations also fail the no-mutation grammar. `date --file` cannot pass the exposure check because parse errors may repeat source content. Bash `printf -v` and `%n` cannot pass because they assign shell variables.
|
|
21
|
+
|
|
22
|
+
Environment prefixes are checked before handling empty argv. Only explicit bounded values for CI, locale, color, terminal dimensions and UTC/GMT timezone settings are allowed, without duplicate names. Assignment-only statements do not qualify, including safe-looking assignments, because they persist into later compound invocations. Thus `PATH=.; ls` and `PAGER=node; git log` cannot alter subsequent resolution inside a supposedly read-only compound. PAGER, GIT_PAGER, Git configuration/transport and dynamic-loader environment prefixes are excluded.
|
|
23
|
+
|
|
24
|
+
**Git proof scope.** A read-like subcommand is insufficient. Worktree status/diff can invoke configured clean/process filters. The no-mutation grammar retains explicit controls for object/index commands, but content, history, remote URLs and configuration enumeration fail the separate exposure check and continue to review. Metadata listings retain their original disabling controls. Exact version calls and `git --no-pager config --get user.name` / `user.email` are narrow static cases. Controls are recognized in unchanged argv; nothing is prepended or rewritten.
|
|
25
|
+
|
|
26
|
+
`pwsh` always uses review because its grammar differs. The static grant keeps the complete execution binding, one claim, cancellation, control epoch, audit and recheck-before-grant machinery. It consumes no model review budget and touches no denial streak. Profile key `staticReadonly` (default `true`, operator-owned, not UI-editable) disables the fast path.
|
|
27
|
+
|
|
28
|
+
**Native approval ownership.** In pinned DSH 0.2.0-rc.2, ApprovalService applies native policy and cancellation, then dispatches `approval/request` through a waterfall. A genuine foreign request (Blueprint order or Classmates model approval) without a Safe Auto execution store calls `next()`. Any existing owned store stays fail-closed on mismatch, inactivity, staleness or a prior claim, and absent-store requests impersonating owned bash/pwsh/write/edit tools reject. Owned human takeover continues only through the authenticated plugin queue, never a downstream automatic answerer.
|
|
29
|
+
|
|
30
|
+
**AST-level hard rules.** A refusal-only AST walk checks literal program roots (basename, Windows extensions stripped, `command`/`builtin`/`nohup`/`time` wrappers unwrapped over the known prefix, depth-capped) against the dangerous-root table and denies `DANGEROUS_PROGRAM`. Unknown later arguments do not soften a known dangerous root. Unparseable commands keep the legacy bare-command check. Unexplained expansions still require review; partial decoding never grants permission. Hard refusals stay unsoftenable by model or human.
|
|
31
|
+
|
|
32
|
+
Hard protected-path/secret checks consume decoded literal argv, assignment and redirect values, including quote concatenation and option/object-path values. A refusal-only AST walk retains known literals even when a different dynamic fragment or control-flow node prevents a whole-command proof. Its 32768-character parse bound covers the maximum 32768-byte reviewable argument budget; static proof retains its stricter 10000-character bound. These hard refusals apply to every approval route and cannot publish a reviewer/manual override. Quote context is preserved in proof: unquoted glob/brace fragments invalidate it, while fully quoted filename patterns remain literal. File-opening `/dev/tcp` or `/dev/udp` redirections cannot prove read-only because Bash implements them as sockets, including quoted and numbered-fd input redirects. They fall back to review; ordinary file input, fd merges and `/dev/null` output keep their rules.
|
|
33
|
+
|
|
34
|
+
`git ls-files -m`/`--modified` is also excluded: Git's `show_files` calls `ie_modified` and content conversion for that mode. The admitted cached/stage listing does not inspect worktree content; deleted/other listings only stat or enumerate with the same fixed controls. EOL, custom-format and submodule-recursion modes remain unsupported.
|
|
35
|
+
|
|
36
|
+
## Relationship to ADR-0001
|
|
37
|
+
|
|
38
|
+
This is not the return of candidate rules. Nothing is enrolled, persisted, prefixed or remembered: every call is re-proven from its exact bound arguments, and the safety proof requires a fully literal command. Refusal-only partial decoding may recognize a known hazard, never establish safety. Unprovable commands flow into the unchanged reviewer path with the same budgets, and the human confirmation channel of ADR-0005 is untouched. The 0.1.x modes and profile keys remain retired and still fail validation.
|
|
39
|
+
|
|
40
|
+
## Trust and limitations
|
|
41
|
+
|
|
42
|
+
The accepted trust boundary remains operator-trusted PATH resolution and installed runtimes. This is a proof over unchanged command text, not binary identity, shell-function integrity or runtime attestation. Unknown commands cost review; known dedicated credential targets remain hard refusals. Parser or grammar defects may produce incorrect proofs; strict parsing and positive rules reduce that risk but do not establish an absolute guarantee.
|
|
43
|
+
|
|
44
|
+
## Credential reads (2026-10-13 correction)
|
|
45
|
+
|
|
46
|
+
Read protection follows dedicated credential stores and recognized values, not whole agent directories. Write protection retains the prior stricter configuration/instruction boundary. Native `read` runs its own validation, caps and observation logic; the around-execution layer replaces successful structured values before post-execute, rendering and persistence. Complete-source parsers preserve original character positions, allowing the original offset, line and byte windows to remain exact. Native grep results are similarly projected before its formatted-result spill callback. Secret-bearing `str_replace_editor view` calls direct callers to `read`.
|
|
47
|
+
|
|
48
|
+
The verified native read body retains raw content only in memory before returning; its `fs/observed` event contains kind/version, not content. The wrapper returns a fresh minimal result, without copying raw content or metadata. The mounted provider resolves canonical targets and source versions. Complete structured sources are bounded at 1 MiB; large JSONL uses complete selected lines, with per-line/window caps. Errors and incoherent windows withhold content. The implementation uses `jsonc-parser`, `yaml` and `toml-eslint-parser` for grammar/range handling, not a custom configuration parser.
|
|
49
|
+
|
|
50
|
+
Shell jobs may retain raw output before around-execution returns. Therefore known credential or secret-bearing absolute file inputs are refused before execution; no post-output secrecy guarantee is claimed. Relative and opaque shell programs are not proven safe by the new static path. Arbitrary Full Access scripts, custom/remote readers and opaque secrets remain outside this recognizer's guarantee. The README documents supported formats and size limits.
|
|
51
|
+
|
|
52
|
+
The fast path skips execution evidence collection and the model assessment row. Its approval audit row carries `STATIC_READONLY` with `static` as the source. Local `staticReadonly: false`, disable or unload remains the rollback. Tests challenge helper/value/cluster alternatives, output operands, persistent assignments, executable identity and Git configuration effects, then exercise the actual adapter fallback and pinned Cordis/ToolRuntime/ApprovalService waterfall. These are source and fixture-runtime checks, not live OS confinement or real-user acceptance.
|
|
53
|
+
|
|
54
|
+
## Source basis
|
|
55
|
+
|
|
56
|
+
- Read-only ZCode mirror: `apps/zcode-cli/packages/core/src/tool/handlers/bash-readonly-policy-argv-git.ts` and `tool/executor/approval-gate.ts`; design reference only, no source copied.
|
|
57
|
+
- Pinned DSH 0.2.0-rc.2: `@deepseek-ai/dsh-user-approval/lib/index.js` (`ApprovalService.request`/`decide`) and `@deepseek-ai/dsh-sandbox` native `approveEscalation`; the native regression runs these installed packages.
|
|
58
|
+
- [Git command controls](https://git-scm.com/docs/git), [diff controls](https://git-scm.com/docs/git-diff), and upstream source [`diff.c`](https://github.com/git/git/blob/master/diff.c), [`read-cache.c`](https://github.com/git/git/blob/master/read-cache.c), [`convert.c`](https://github.com/git/git/blob/master/convert.c), [`builtin/diff.c`](https://github.com/git/git/blob/master/builtin/diff.c), [`builtin/ls-files.c` at v2.46.0](https://github.com/git/git/blob/v2.46.0/builtin/ls-files.c): worktree conversions apply clean filters, while cached diff reads index/object content. The grammar remains a conservative supported subset, not universal Git safety.
|
package/dsh.plugin.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "@klarkxy/dsh-safe-auto",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"main": "src/index.js",
|
|
5
|
-
"description": "Safe Auto:
|
|
6
|
-
"engines": {
|
|
7
|
-
|
|
5
|
+
"description": "Safe Auto: an independent reviewer vets each native sandbox escalation; only a pass auto-approves that one call.",
|
|
6
|
+
"engines": {
|
|
7
|
+
"dsh": ">=0.2.0-rc.2"
|
|
8
|
+
},
|
|
9
|
+
"contributes": {
|
|
10
|
+
"tools": [],
|
|
11
|
+
"skills": []
|
|
12
|
+
}
|
|
8
13
|
}
|