From 3f9d5b4eadfcac572413c45844dd327aee98db8a Mon Sep 17 00:00:00 2001 From: 0xJeff Date: Mon, 24 Aug 2026 17:44:18 +0800 Subject: [PATCH 1/4] fix: allow AgentGuard builds in DSH profile --- README.md | 2 +- docs/dsh.md | 6 +++++- src/installers.ts | 5 ++++- src/tests/cli-init.test.ts | 12 ++++++++---- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4fdce74..acf5f30 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ Reports include DSH identification confidence, plugin kind, explainable risk lev Install AgentGuard as a native DSH tool plugin, then restart the profile: ```bash -dsh plugin --profile web add @goplus/agentguard +dsh plugin --profile web add --allow-build=@goplus/agentguard @goplus/agentguard ``` DSH will expose the read-only `agentguard_dsh_scan` tool for scanning local plugin directories and HTTPS GitHub repositories before installation. diff --git a/docs/dsh.md b/docs/dsh.md index d28416a..341ef71 100644 --- a/docs/dsh.md +++ b/docs/dsh.md @@ -18,9 +18,13 @@ When invoked inside DSH, bare `agentguard init` auto-detects DSH and performs th The equivalent low-level DSH command is: ```bash -dsh plugin --profile web add @goplus/agentguard +dsh plugin --profile web add --allow-build=@goplus/agentguard @goplus/agentguard ``` +The scoped `--allow-build` approval lets pnpm run AgentGuard's reviewed npm +postinstall lifecycle and records that approval in this DSH profile. The +high-level `agentguard init` commands add it automatically. + For local development, link the checkout instead: ```bash diff --git a/src/installers.ts b/src/installers.ts index 35fed32..ca46b44 100644 --- a/src/installers.ts +++ b/src/installers.ts @@ -27,7 +27,10 @@ export function installAgentTemplates(agent: AgentInstaller, options: { cwd?: st } function installDsh(root: string): InstallResult { - const args = ['plugin', '--profile', 'web', 'add', '@goplus/agentguard']; + const args = [ + 'plugin', '--profile', 'web', 'add', + '--allow-build=@goplus/agentguard', '@goplus/agentguard', + ]; const result = spawnSync('dsh', args, { cwd: root, stdio: 'inherit', diff --git a/src/tests/cli-init.test.ts b/src/tests/cli-init.test.ts index c00c64f..bc6209a 100644 --- a/src/tests/cli-init.test.ts +++ b/src/tests/cli-init.test.ts @@ -222,7 +222,8 @@ describe('init CLI', () => { assert.equal(config.agentHost, 'dsh'); assert.deepEqual(config.agentHosts, ['dsh']); assert.deepEqual(JSON.parse(readFileSync(callLog, 'utf8')), [ - 'plugin', '--profile', 'web', 'add', '@goplus/agentguard', + 'plugin', '--profile', 'web', 'add', + '--allow-build=@goplus/agentguard', '@goplus/agentguard', ]); assert.match(stdout, /Installed dsh integration in profile web/); assert.match(stdout, /Restart DSH/); @@ -402,7 +403,8 @@ describe('init CLI', () => { assert.equal(config.agentHost, 'dsh'); assert.deepEqual(config.agentHosts, ['dsh']); assert.deepEqual(JSON.parse(readFileSync(callLog, 'utf8')), [ - 'plugin', '--profile', 'web', 'add', '@goplus/agentguard', + 'plugin', '--profile', 'web', 'add', + '--allow-build=@goplus/agentguard', '@goplus/agentguard', ]); assert.match(stdout, /Installed dsh integration in profile web/); }); @@ -433,7 +435,8 @@ describe('init CLI', () => { assert.equal(config.agentHost, 'dsh'); assert.deepEqual(config.agentHosts, ['dsh']); assert.deepEqual(JSON.parse(readFileSync(callLog, 'utf8')), [ - 'plugin', '--profile', 'web', 'add', '@goplus/agentguard', + 'plugin', '--profile', 'web', 'add', + '--allow-build=@goplus/agentguard', '@goplus/agentguard', ]); assert.match(stdout, /Installed dsh integration in profile web/); }); @@ -462,7 +465,8 @@ describe('init CLI', () => { }); assert.deepEqual(JSON.parse(readFileSync(callLog, 'utf8')), [ - 'plugin', '--profile', 'web', 'add', '@goplus/agentguard', + 'plugin', '--profile', 'web', 'add', + '--allow-build=@goplus/agentguard', '@goplus/agentguard', ]); assert.match(stdout, /Installed dsh integration in profile web/); }); From a0db3d0f8f47bf25e40ffe45a077d8fb205a45ab Mon Sep 17 00:00:00 2001 From: 0xJeff Date: Mon, 24 Aug 2026 18:05:33 +0800 Subject: [PATCH 2/4] runtime.mode: protect --- README.md | 2 +- docs/dsh-complete-candidate.md | 6 +++--- docs/dsh.md | 4 ++-- dsh.cordis.patch.yml | 2 +- scripts/test-dsh-package.mjs | 2 +- src/tests/dsh-plugin.test.ts | 27 ++++++++++++++++++++++++++- 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index acf5f30..caf3aea 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ Use `agentguard_dsh_compare` or the `agentguard dsh-compare` CLI command to iden Update or remove it from the same profile with `dsh plugin --profile web update @goplus/agentguard` or `dsh plugin --profile web remove @goplus/agentguard`. The [DSH operations and report guide](docs/dsh.md#operate-the-dsh-installation) includes verification and troubleshooting steps. -> **DSH runtime guard:** the packaged composition uses non-disruptive `observe` mode. Startup logs and the input-redacted `agentguard_dsh_runtime_summary` tool explicitly show the current configured mode and whether pre-execute enforcement is active. An explicit `runtime.mode: protect` applies AgentGuard's shared allow/warn/require-approval/block policy before DSH dispatches a tool, using DSH's native one-shot approval service and monotonic composition with other policies. Optional `runtime.postResponseMode: block-malicious` suppresses only block-class malicious network results; approval-class post results remain audit-only because DSH has no resumable post-result approval protocol. Exact operator-configured `runtime.attribution.toolOwners` bindings add source ownership without guessing, and `runtime.ownerPolicies` can impose per-owner minimum decisions without weakening shared security policy. Unmapped tools remain `unknown` until DSH exposes a reliable native owner field. See the [DSH runtime guide](docs/dsh-runtime.md). +> **DSH runtime guard:** the packaged composition enables `protect` mode by default, applying AgentGuard's shared allow/warn/require-approval/block policy before DSH dispatches a tool. Startup logs and the input-redacted `agentguard_dsh_runtime_summary` tool explicitly show the current configured mode and whether pre-execute enforcement is active. Set `runtime.mode: observe` only when audit-only shadow evaluation is required. DSH protection uses DSH's native one-shot approval service and monotonic composition with other policies. Optional `runtime.postResponseMode: block-malicious` suppresses only block-class malicious network results; approval-class post results remain audit-only because DSH has no resumable post-result approval protocol. Exact operator-configured `runtime.attribution.toolOwners` bindings add source ownership without guessing, and `runtime.ownerPolicies` can impose per-owner minimum decisions without weakening shared security policy. Unmapped tools remain `unknown` until DSH exposes a reliable native owner field. See the [DSH runtime guide](docs/dsh-runtime.md). The complete candidate scope, activation override, acceptance gates, and intentional boundaries are collected in [AgentGuard for DSH complete candidate](docs/dsh-complete-candidate.md). diff --git a/docs/dsh-complete-candidate.md b/docs/dsh-complete-candidate.md index 665c920..c10ef8c 100644 --- a/docs/dsh-complete-candidate.md +++ b/docs/dsh-complete-candidate.md @@ -11,7 +11,7 @@ This candidate completes the agreed installation-time scanner and DSH-native pre - Bounded batch scanning and version/report comparison. - DSH-native tools: `agentguard_dsh_scan`, `agentguard_dsh_scan_batch`, `agentguard_dsh_compare`, and `agentguard_dsh_runtime_summary`. - Native pre/post lifecycle observation with shared AgentGuard policy semantics. -- Opt-in `protect` mode for pre-execute allow, warn, DSH-native approval, and block. +- Default `protect` mode for pre-execute allow, warn, DSH-native approval, and block. - Optional block-class malicious network-response containment without returning untrusted response content. - Exact operator-configured tool ownership attribution and monotonic per-owner policy floors. - Fail-closed unexpected evaluator errors by default, with an explicit compatibility override. @@ -21,7 +21,7 @@ This candidate completes the agreed installation-time scanner and DSH-native pre ## Installation posture -The packaged `dsh.cordis.patch.yml` remains on `observe`. This avoids turning an ordinary plugin update into an unexpected behavior-changing policy rollout. +The packaged `dsh.cordis.patch.yml` enables `protect` by default, so a standard DSH installation enforces the configured runtime policy immediately. Operators can explicitly switch to `observe` for audit-only shadow evaluation. To confirm protection in a profile, add this complete config override to that profile's `cordis.patch.yml`: @@ -34,7 +34,7 @@ To confirm protection in a profile, add this complete config override to that pr postResponseMode: block-malicious ``` -DSH profile patches replace the row's entire `config`, so the complete runtime configuration is restated. Optional `attribution.toolOwners` entries must contain only exact owner bindings trusted by the operator; a corresponding `ownerPolicies..minimumDecision` can raise that owner's calls to `warn`, `require_approval`, or `block`, but cannot weaken the shared policy. Removing the override returns the bundle to its packaged `observe` configuration after recomposition/restart. +DSH profile patches replace the row's entire `config`, so the complete runtime configuration is restated. Optional `attribution.toolOwners` entries must contain only exact owner bindings trusted by the operator; a corresponding `ownerPolicies..minimumDecision` can raise that owner's calls to `warn`, `require_approval`, or `block`, but cannot weaken the shared policy. Removing the override returns the bundle to its packaged `protect` configuration after recomposition/restart. ## Acceptance status diff --git a/docs/dsh.md b/docs/dsh.md index 341ef71..ec6713a 100644 --- a/docs/dsh.md +++ b/docs/dsh.md @@ -75,10 +75,10 @@ If `http://127.0.0.1:3080/` returns `ERR_CONNECTION_REFUSED`, the DSH web proces | Preserve workspace and request context | Runtime | Uses the DSH session cwd plus shell workdir and network method/header/body fields supported by the shared evaluator. | | Observe network responses | Runtime | Uses native `tools/post-execute`; status, content type, headers, bounded text preview, and explicit byte counts feed shared anomaly detection. | | Summarize recent runtime decisions | Runtime | Bounded local aggregation; raw tool input and reason evidence are omitted. | -| Apply allow, warn, approve, or block decisions inside DSH | Opt-in `protect` | Pre-execute decisions use DSH native `allow`/`ask`/`deny`; optional `postResponseMode: block-malicious` suppresses block-class malicious network results while approval-class post results remain audit-only. | +| Apply allow, warn, approve, or block decisions inside DSH | Default `protect` | Pre-execute decisions use DSH native `allow`/`ask`/`deny`; optional `postResponseMode: block-malicious` suppresses block-class malicious network results while approval-class post results remain audit-only. | | Attribute a call to its source plugin | Partial | Exact operator-configured tool-owner bindings are recorded; unmapped tools remain `unknown` and AgentGuard does not guess. | -Installing the bundle is non-disruptive because its packaged composition uses `observe`. Changing the runtime row to `protect` is the explicit opt-in for real-time pre-execute enforcement. +Installing the bundle enables real-time pre-execute enforcement because its packaged composition uses `protect`. Change the runtime row to `observe` only for audit-only shadow evaluation. ## Why this exists diff --git a/dsh.cordis.patch.yml b/dsh.cordis.patch.yml index 34522f1..8d012e0 100644 --- a/dsh.cordis.patch.yml +++ b/dsh.cordis.patch.yml @@ -4,4 +4,4 @@ name: '@goplus/agentguard/dist/dsh/plugin.js' config: runtime: - mode: observe + mode: protect diff --git a/scripts/test-dsh-package.mjs b/scripts/test-dsh-package.mjs index b9d9107..2646fe8 100644 --- a/scripts/test-dsh-package.mjs +++ b/scripts/test-dsh-package.mjs @@ -78,7 +78,7 @@ try { const { stdout: composed } = await dsh(['web', '--dump-config']); assert.match(composed, /id:\s*agentguard-dsh-plugin/); - assert.match(composed, /runtime:\s*\n\s+mode:\s*observe/); + assert.match(composed, /runtime:\s*\n\s+mode:\s*protect/); const installedPlugin = join(profileDir, 'node_modules/@goplus/agentguard/dist/dsh/plugin.js'); const plugin = await import(`${pathToFileURL(installedPlugin).href}?package=${Date.now()}`); const registeredTools = []; diff --git a/src/tests/dsh-plugin.test.ts b/src/tests/dsh-plugin.test.ts index 2ed5ff2..0456b4f 100644 --- a/src/tests/dsh-plugin.test.ts +++ b/src/tests/dsh-plugin.test.ts @@ -1,8 +1,10 @@ import { afterEach, describe, it } from 'node:test'; import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; import { mkdtemp, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; -import { join } from 'node:path'; +import { join, resolve } from 'node:path'; +import { parse } from 'yaml'; import { apply, createAgentGuardDshBatchTool, @@ -20,6 +22,29 @@ afterEach(async () => { }); describe('AgentGuard DSH runtime plugin', () => { + it('enables pre-execute protection in the packaged DSH integration', () => { + const manifest = JSON.parse(readFileSync(resolve('package.json'), 'utf8')) as { + dsh: { bundle: { patch: string } }; + }; + const bundlePatch = parse(readFileSync(resolve(manifest.dsh.bundle.patch), 'utf8')) as Array<{ + insert?: Array<{ id?: string; config?: Parameters[1] }>; + }>; + const config = bundlePatch + .flatMap(operation => operation.insert ?? []) + .find(entry => entry.id === 'agentguard-dsh-plugin') + ?.config; + assert.ok(config, 'packaged DSH plugin config is missing'); + + const logs: string[] = []; + apply({ + tools: { register() {} }, + on() {}, + logger: { info(message: string) { logs.push(message); }, warn() {} }, + }, config); + + assert.match(logs.at(-1) ?? '', /mode: protect.*enforcement active/i); + }); + it('registers the read-only scanner tool', () => { const registered: Array<{ name: string }> = []; apply({ tools: { register(tool) { registered.push(tool); } } }); From edc8903191885ffa2ebb131424c5a673f76ef3e5 Mon Sep 17 00:00:00 2001 From: 0xJeff Date: Tue, 25 Aug 2026 10:38:04 +0800 Subject: [PATCH 3/4] fix: adapt to DSH rc.2 --- README.md | 2 +- docs/dsh-complete-candidate.md | 6 +++--- docs/dsh-runtime.md | 6 +++--- docs/dsh.md | 8 ++++---- scripts/test-dsh-package.mjs | 8 +++++--- scripts/test-dsh-plugin-lifecycle.mjs | 2 +- src/dsh/runtime.ts | 2 +- src/tests/dsh-runtime.test.ts | 1 + 8 files changed, 19 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index caf3aea..3738977 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ Update or remove it from the same profile with `dsh plugin --profile web update The complete candidate scope, activation override, acceptance gates, and intentional boundaries are collected in [AgentGuard for DSH complete candidate](docs/dsh-complete-candidate.md). -The enforcing adapter maps approval decisions to DSH's native `ask` contract, emits bounded evidence-free reasons, preserves stronger downstream policies, fails closed on unexpected evaluator errors by default, and is registered only when `protect` is explicitly selected. +The enforcing adapter maps approval decisions to DSH's native `ask` contract, emits bounded evidence-free reasons, preserves stronger downstream policies, fails closed on unexpected evaluator errors by default, and is registered whenever `protect` is configured, including by the packaged default composition. Native contract gates cover the full pre-execute approval outcome matrix, concurrent and nested calls, failures, unload, and post-execute result containment. Block-class malicious responses can be suppressed explicitly; approval-class post results remain audit-only because DSH currently exposes no native post-approval resume primitive. The complete candidate passed all 11 guided DSH UAT cases, including native approval/rejection, pre-execute blocking, response containment, redaction, and service stability. diff --git a/docs/dsh-complete-candidate.md b/docs/dsh-complete-candidate.md index c10ef8c..aec1c65 100644 --- a/docs/dsh-complete-candidate.md +++ b/docs/dsh-complete-candidate.md @@ -1,6 +1,6 @@ # AgentGuard for DSH complete candidate -This candidate completes the agreed installation-time scanner and DSH-native pre-execute runtime guard without changing the bundle's non-disruptive installation default. +This candidate completes the agreed installation-time scanner and DSH-native pre-execute runtime guard with `protect` as the packaged installation default. ## Included @@ -78,8 +78,8 @@ npm run benchmark:dsh Confirm this candidate if the following product contract is acceptable: -1. installation remains observation-first; -2. protection is explicit and fails closed on unexpected evaluator errors; +1. installation enables pre-execute protection by default, with `observe` available as an explicit audit-only mode; +2. protection fails closed on unexpected evaluator errors; 3. approval is owned by DSH rather than a duplicate AgentGuard queue; 4. pre-execute protection is real, and optional post-response containment applies only to block-class results; 5. unattributed calls never receive plugin-specific trust automatically. diff --git a/docs/dsh-runtime.md b/docs/dsh-runtime.md index 1515fd6..42277b3 100644 --- a/docs/dsh-runtime.md +++ b/docs/dsh-runtime.md @@ -9,10 +9,10 @@ The runtime integration accepts three explicit modes: | Mode | Pre-execute | Post-execute | Intended use | |---|---|---|---| | `off` | no listener | no listener | scanner tools only | -| `observe` | evaluate and audit; preserve downstream decision | evaluate network responses and audit | packaged default and rollout baseline | -| `protect` | apply `allow`, `warn`, native `ask`, or `deny` | audit by default; optionally suppress block-class malicious results | explicit real-time protection | +| `observe` | evaluate and audit; preserve downstream decision | evaluate network responses and audit | explicit audit-only evaluation | +| `protect` | apply `allow`, `warn`, native `ask`, or `deny` | audit by default; optionally suppress block-class malicious results | packaged default and real-time protection | -The npm bundle continues to compose `observe` by default so installing an update does not silently change tool execution. Enable protection in a custom DSH composition: +The npm bundle composes `protect` by default. A custom DSH composition can restate the protected runtime settings and add optional attribution or policy controls: ```yaml - insert: diff --git a/docs/dsh.md b/docs/dsh.md index ec6713a..1c2e8d2 100644 --- a/docs/dsh.md +++ b/docs/dsh.md @@ -2,7 +2,7 @@ AgentGuard for DeepSeek Harness (DSH) is an installation-time trust layer for the DSH plugin ecosystem. It identifies DSH bundles, profiles, client extensions, and Cordis configuration, then combines that context with AgentGuard's existing static rules to produce an explainable security report. -The Phase 1 scanner is intentionally read-only: it scans source, classifies capabilities, and recommends an installation posture. It never installs the target, executes package lifecycle scripts, evaluates Cordis `!!js` expressions, or starts DSH. The separate runtime integration can observe DSH tool calls or, when explicitly configured as `protect`, enforce pre-execute policy through DSH's native approval protocol. +The Phase 1 scanner is intentionally read-only: it scans source, classifies capabilities, and recommends an installation posture. It never installs the target, executes package lifecycle scripts, evaluates Cordis `!!js` expressions, or starts DSH. The separately installed runtime integration enforces pre-execute policy through DSH's native approval protocol in its packaged default `protect` mode; operators can switch it to `observe` for audit-only evaluation. ## Install in DSH @@ -33,7 +33,7 @@ dsh plugin --profile web add link:/absolute/path/to/agentguard The profile then exposes `agentguard_dsh_scan`, which accepts a local directory or HTTPS GitHub repository URL, an optional GitHub `ref`, and a Markdown or JSON format. It also exposes `agentguard_dsh_scan_batch` for sequentially scanning up to 10 targets, `agentguard_dsh_compare` for comparing an approved version with a candidate, and `agentguard_dsh_runtime_summary` for input-redacted runtime audit aggregates. For example, ask DSH: “Use AgentGuard to compare tags `v1.2.3` and `v1.3.0` of `https://github.com/owner/plugin` before I update.” -The three static AgentGuard DSH tools preserve the Phase 1 boundary: they do not install or execute the target plugin. The fourth tool only summarizes local runtime audit events and never returns raw tool input. The installed bundle enables `observe` by default; [DSH runtime guard](dsh-runtime.md) documents explicit `protect` configuration. +The three static AgentGuard DSH tools preserve the Phase 1 boundary: they do not install or execute the target plugin. The fourth tool only summarizes local runtime audit events and never returns raw tool input. The installed bundle enables `protect` by default; the [DSH runtime guard](dsh-runtime.md) documents audit-only `observe` mode and the available protection settings. ### Operate the DSH installation @@ -57,7 +57,7 @@ Verification checklist: 1. `dsh web --dump-config` contains `id: agentguard-dsh-plugin` and the `@goplus/agentguard/dist/dsh/plugin.js` entry. 2. DSH exposes the `agentguard_dsh_scan`, `agentguard_dsh_scan_batch`, `agentguard_dsh_compare`, and `agentguard_dsh_runtime_summary` tools. 3. A JSON scan contains `scanner.version`, `scanner.phase`, and `scanner.rulesBaseline`. Keep these fields with a saved report so later rescans can be compared to the same implementation. -4. `~/.agentguard/audit.jsonl` receives DSH events with `agentHost: "dsh"`. The default composition records `runtimeMode: "observe"`; an explicit protected composition records pre-execute events with `runtimeMode: "protect"` and `enforcementApplied: true`. +4. `~/.agentguard/audit.jsonl` receives DSH events with `agentHost: "dsh"`. The default composition records pre-execute events with `runtimeMode: "protect"` and `enforcementApplied: true`; an explicit audit-only composition records `runtimeMode: "observe"` and does not apply pre-execute enforcement. 5. After removal and restart, the AgentGuard composition row, tools, and runtime listener are absent. If `http://127.0.0.1:3080/` returns `ERR_CONNECTION_REFUSED`, the DSH web process is not listening; it is not evidence of a scanner failure. Start or restart DSH and inspect its terminal output. If the tool is missing while DSH is running, check the explicit profile with `--dump-config`, then confirm the package appears in that profile's dependencies. @@ -109,7 +109,7 @@ Phase 1 does not include: - Installing a plugin or resolving its lifecycle scripts. - Fetching a package by npm name or comparing an npm tarball with its source repository. - Resolving every layer of an already-installed DSH profile into one effective runtime tree. -- Automatically enabling runtime enforcement when the scanner is installed. Runtime protection is an explicit composition choice documented separately. +- Using a static scan to enable, disable, or alter runtime enforcement. The separately installed DSH bundle defaults to `protect` and can be switched explicitly to `observe`. - Persisting scan history or integrating with a DSH marketplace. ## Command line diff --git a/scripts/test-dsh-package.mjs b/scripts/test-dsh-package.mjs index 2646fe8..237753d 100644 --- a/scripts/test-dsh-package.mjs +++ b/scripts/test-dsh-package.mjs @@ -1,8 +1,8 @@ import assert from 'node:assert/strict'; import { execFile } from 'node:child_process'; -import { access, mkdtemp, readFile, rm } from 'node:fs/promises'; +import { access, mkdtemp, readFile, realpath, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; -import { dirname, join, resolve } from 'node:path'; +import { dirname, join, relative, resolve } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { promisify } from 'node:util'; @@ -71,7 +71,9 @@ try { for (const path of required) assert.ok(archiveFiles.has(path), `tarball is missing ${path}`); assert.ok(![...archiveFiles].some(path => path.startsWith('package/dist/tests/')), 'tarball contains compiled tests'); - await dsh(['plugin', '--profile', 'web', 'add', tarball]); + await dsh(['plugin', '--profile', 'web', 'install', '--lockfile-only']); + const tarballBuildKey = `@goplus/agentguard@file:${relative(await realpath(profileDir), tarball).replaceAll('\\', '/')}`; + await dsh(['plugin', '--profile', 'web', 'add', `--allow-build=${tarballBuildKey}`, tarball]); const installedManifest = JSON.parse(await readFile(join(profileDir, 'package.json'), 'utf8')); assert.ok(installedManifest.dependencies?.['@goplus/agentguard']); assert.ok(installedManifest.dsh?.profile?.bundles?.includes('@goplus/agentguard')); diff --git a/scripts/test-dsh-plugin-lifecycle.mjs b/scripts/test-dsh-plugin-lifecycle.mjs index 994587b..8bb7d4d 100644 --- a/scripts/test-dsh-plugin-lifecycle.mjs +++ b/scripts/test-dsh-plugin-lifecycle.mjs @@ -34,7 +34,7 @@ try { const { stdout: composed } = await dsh(['web', '--dump-config']); assert.match(composed, /id:\s*agentguard-dsh-plugin/); assert.match(composed, /@goplus\/agentguard\/dist\/dsh\/plugin\.js/); - assert.match(composed, /runtime:\s*\n\s+mode:\s*observe/); + assert.match(composed, /runtime:\s*\n\s+mode:\s*protect/); const installedPlugin = join(profileDir, 'node_modules/@goplus/agentguard/dist/dsh/plugin.js'); await access(installedPlugin); diff --git a/src/dsh/runtime.ts b/src/dsh/runtime.ts index 31a2615..0ae0e72 100644 --- a/src/dsh/runtime.ts +++ b/src/dsh/runtime.ts @@ -118,7 +118,7 @@ export interface DshRuntimeObservation { } const SHELL_TOOLS = new Set([ - 'bash', 'terminal', 'shell', 'exec', 'exec_command', 'execute_command', 'execute_code', + 'bash', 'pwsh', 'terminal', 'shell', 'exec', 'exec_command', 'execute_command', 'execute_code', 'run_command', 'run_shell_command', 'spawn_process', ]); const READ_TOOLS = new Set([ diff --git a/src/tests/dsh-runtime.test.ts b/src/tests/dsh-runtime.test.ts index 5da9c24..ba27bca 100644 --- a/src/tests/dsh-runtime.test.ts +++ b/src/tests/dsh-runtime.test.ts @@ -59,6 +59,7 @@ function decision(value: RuntimeDecision['decision'] = 'block'): RuntimeDecision describe('DSH runtime Phase 2A observer', () => { it('normalizes common DSH tools into the shared RuntimeAction vocabulary', () => { assert.equal(mapDshToolToRuntimeAction('bash'), 'shell'); + assert.equal(mapDshToolToRuntimeAction('pwsh'), 'shell'); assert.equal(mapDshToolToRuntimeAction('read_file'), 'file_read'); assert.equal(mapDshToolToRuntimeAction('read_image'), 'file_read'); assert.equal(mapDshToolToRuntimeAction('view_image'), 'file_read'); From f444cea95742bbf9738be4fe7bf057842850781e Mon Sep 17 00:00:00 2001 From: 0xJeff Date: Tue, 25 Aug 2026 11:09:32 +0800 Subject: [PATCH 4/4] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 268fca1..76eb3d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Changed `curl/wget | bash/sh` handling to require approval by default and block only when hard indicators or multiple suspicious signals are present. ### Fixed +- Updated compatibility with DSH `0.1.1-rc.2`: profile installs now authorize AgentGuard package builds, tarball acceptance uses pnpm's exact local-file build key, and the persistent PowerShell `pwsh` tool is classified as shell execution for runtime policy evaluation. - DSH protect hooks now exempt only the four exact AgentGuard-owned tool names, preventing third-party `agentguard_*` tools from bypassing runtime policy evaluation. - DSH runtime scanning now includes referenced generated `dist/` and `build/` files, while protect mode requires explicit decisions for MCP, skill-install, and unknown tools. - DSH scans now fail closed with `DSH_SCAN_INCOMPLETE` and `expert-review-required` when matching files are omitted by the file-count limit, exceed the per-file byte limit, or cannot be read, preventing incomplete scans from returning `safe-to-try`.