Back to marketplace

dsh-auto-approval-plugin

Security

StyxNether/dsh-auto-approval-plugin

Adds an auto-approval permission tier between workspace-write and full access, auto-approving harmless commands and trusted-area targets while asking for everything else.

  • deepseek-harness
  • dsh-plugin
GitHub Stars
3GitHub
Views
0DSH Plugin Hub
Forks
1GitHub
Open issues
0GitHub Issues
Manifest version
2.1.0dsh-auto-approval-plugin
Latest push
Aug 22, 2026GitHub
License
MITJavaScript
Plugin type
Host + ClientRuns in both Host and Web Client

README

View source

dsh-auto-approval-plugin

๐ŸŒ Language: English | ็ฎ€ไฝ“ไธญๆ–‡

A middle permission tier for DeepSeek Harness, between Workspace Write and Full access (danger-full-access): it adds a auto-approval preset to the permission settings and backs it with an automated approval answerer that approves harmless commands and operations whose target lies inside configured trusted areas โ€” including areas outside the current workspace โ€” and asks the user for everything else.

โš ๏ธ Scope control, not a security boundary. This plugin automates the human approval step for a narrow, verifiable class of requests. The DSH sandbox still confines every non-escalated call; an auto-approved call runs with the wider mode for exactly that one call (the same one-shot grant a human click would produce). Do not use it on machines or sessions you would not trust a human operator to run commands on.

What it does

Workspace WriteAuto Approval (this plugin)Full access
Sandbox modeworkspace-writeworkspace-writedanger-full-access
Approval policyaskasknever
Writes inside workspace / tempallowedallowedallowed
Harmless commands (see rule table)askauto-approvednever asks
Targets inside trusted areasaskauto-approvednever asks
Everything elseaskasknever asks

After installing, the new preset appears in both permission surfaces:

  • General settings โ†’ Permission โ€” sets auto-approval as the default for future sessions;
  • /permission picker โ€” switches the current session immediately (/permission auto-approval).

While the plugin is disabled (mode: off), picking the Auto Approval tier behaves exactly like Workspace Write โ€” the tier stays visible in both surfaces, but nothing is auto-approved.

How it works

DSH routes every operation that needs approval through the approval/request waterfall (official docs: approval seam / Chinese). This plugin registers a listener with prepend, so it runs before the web approval prompt:

  1. For each request it looks up the recorded tool/call event by callId in the session log and reads the real tool arguments (command text, file_path, workdir) โ€” it never trusts the model-written justification string.
  2. The pure decision core (lib/decide.js) classifies the request as allow or defer. Path containment is evaluated on real identity: the deepest existing ancestor of every candidate path is resolved through realpath (the same mechanism the DSH filesystem sandbox uses), so symlinks and junctions cannot smuggle an auto-approval to a target outside a trusted area.
  3. allow returns allowed-once โ€” the request never reaches the human UI; the audit pair approval/asked + approval/decided: allowed-once is still written to the session log, and the plugin logs the matched rule.
  4. defer calls next() โ€” the deployment's human answerer decides as usual. The plugin never denies anything.

For the formal architecture and request/decision flow, see docs/architecture.md (ไธญๆ–‡).

Two-layer model: what an agent may do on the Auto Approval tier

The tier's permission scope decomposes into exactly two layers (the architecture doc has the full seven-layer model; this is the user-facing summary):

  • Inner layer: the workspace-write execution boundary โ€” identical to the official Workspace Write tier, not broadened. The DSH sandbox enforces it per tool call: writes inside the session workspace and platform temp areas are allowed, everything else (file/command effects) is denied (reading anywhere stays allowed). Operations inside the workspace are allowed directly by the sandbox โ€” no approval request is ever raised for them.
  • Outer layer: the auto-approval policy (this plugin) โ€” when an operation is denied by the inner layer and the agent explicitly retries with sandbox_permissions, the plugin decides, before the human answerer, on a small class of verifiably safe requests and returns allowed-once; everything else passes through to the human unchanged.
  • There is no third layer of privilege: auto-approval is not Full access with the prompts removed โ€” a granted call runs once with the escalation mode the agent asked for, the session's standing tier is untouched, and the plugin never denies anything.

In one sentence: Auto Approval โ‰  an automatic Full access. It is exactly the Workspace Write sandbox scope + an automatic adjudicator in front of the human confirmation step.

Install

# from the npm registry
dsh plugin --profile <profile> add dsh-auto-approval-plugin
# or from GitHub (pin a commit for reproducibility)
dsh plugin --profile <profile> add github:StyxNether/dsh-auto-approval-plugin#<commit>

The bundle patch restates the complete permission preset table (DSH patches replace a row's whole config), so keep it in sync with @deepseek-ai/dsh-base's table when upgrading DSH โ€” the patch warns and is skipped if the target row is missing.

Configure

Two layers, both live (no restart needed):

  1. Web settings page (easiest): Settings โ†’ Auto Approval (a dedicated page in the settings sidebar). The page has a single master switch "Enable auto-approval"; once enabled it asks for the scope: "Only when the session tier is Auto Approval" (default) or "Globally, regardless of the session tier". You can also edit trusted areas (one absolute path per line), the harmless/dangerous pattern tables, the decision length limit and the log switch there. Changes are written to the auto-approval section of settings.yaml and apply immediately. The page also shows the last few auto-approval decisions.
  2. Composition config (the default base): set in your profile's cordis.patch.yml:
# ~/.dsh/profiles/<profile>/cordis.patch.yml
- id: auto-approval
  config:
    # Master switch: off | global (any tier) | gated (auto-approval tier
    # only). Default after install: gated.
    mode: gated
    # Absolute paths treated as trusted areas. Commands whose workdir lies
    # inside one (and reference it), and fs write/edit targets inside one,
    # are auto-approved. Empty by default: the feature is inert until you
    # add areas.
    trustedAreas:
      - 'D:\data'
      - 'E:\repos'
    # Regex sources matched (case-insensitive) against command text.
    harmlessPatterns: [ ... ]   # defaults: see lib/decide.js
    dangerousPatterns: [ ... ]  # a match defers to the human, never denies
    maxCommandChars: 4000
    logDecisions: true
    # Non-loopback hosts allowed to reach the configuration HTTP API
    # (loopback is always allowed; cross-site requests are rejected).
    trustedHosts: []

Legacy saved configurations (โ‰ค 2.0.0, the enabled / requireTrustedPreset booleans) migrate to mode automatically when read โ€” no manual step needed.

Settings values overlay the composition defaults; the web card marks fields you have overridden and offers a one-click reset back to the defaults.

Every configuration option, and what changing it does

OptionDefaultWhat it doesEffect of changing it
modegatedMaster switch + scopeoff: disables auto-approval entirely (the Auto Approval tier then behaves exactly like Workspace Write); global: auto-approve under any session tier; gated: auto-approve only while the session tier is auto-approval
trustedAreas[]Trusted areas (absolute paths)Empty: the plugin is left with only the harmless-introspection capability. Adding directories grants in-area git read commands, in-area simple commands, and in-area fs writes (see "Trusted area vs outside" below)
harmlessPatternsread-only introspection set (see lib/decide.js)Regexes that classify a command as "harmless"; a match (without shell metacharacters) auto-approvesAdding entries auto-approves more commands; removing restores human confirmation. Add only read-only, side-effect-free commands (the read surface should not exceed the DSH read tool)
dangerousPatternshigh-impact set (system wipes, privilege escalation, ACL, firewall, registry, persistenceโ€ฆ)A match defers to the human โ€” never auto-approved, never deniedMore entries send more high-impact operations to the human; fewer entries widen the auto window (not recommended). The principle is "rather wider than miss": a false positive costs one extra confirmation, a false negative could auto-approve a destructive command
maxCommandChars4000Length cap for commands/targets; longer inputs deferSmaller: longer commands always ask; larger: longer commands may participate in auto-decision (mind the widening)
logDecisionstrueWhether each auto-approval is written to the process log (auto-approval: granted โ€ฆ (rule))false quiets the log but drops rule-level audit lines (the approval/decided audit pair in the session log is still written)
trustedHosts[]Non-loopback hosts allowed to reach the plugin's config HTTP APIUnset: loopback only. Adding entries allows remote management from those hosts (cross-site requests are still rejected)

All of the above apply immediately: settings.yaml is hot-reloaded and the web card saves take effect without a restart.

Config file and log locations

$DSH_HOME defaults to ~/.dsh (override with the DSH_HOME environment variable). The plugin reads/writes these locations:

ContentLocationNotes
Plugin composition defaults<profile>/node_modules/dsh-auto-approval-plugin/cordis.patch.ymlThe bundle patch (default base layer); normally not hand-edited
Composition overlay~/.dsh/profiles/<profile>/cordis.patch.ymlAdd an - id: auto-approval block to override the defaults (example above)
User settings (hot-reloaded)the auto-approval: section of ~/.dsh/settings.yamlWhere the web card saves; manual edits apply immediately, no restart. Back it up first and keep the YAML valid:
Session audit log~/.dsh/sessions/<workspace>/<session-id>/session.jsonl.zstdzstd-compressed JSONL; every auto-approval is the audit pair approval/asked + approval/decided: allowed-once. Decompress with zstd -d to read
Process runtime logThe DSH process's standard error (the terminal/launcher that started the harness)Plugin decision lines are prefixed auto-approval: (e.g. granted pwsh call ... (harmless-command)); warnings/errors appear here too
Recent approvalsmemory only (the settings card shows the last 5)Cleared when DSH restarts

Hand-editing settings.yaml, canonical shape:

auto-approval:
  mode: global                      # off | global | gated
  trustedAreas: ['D:\data', 'E:\repos']
  logDecisions: true

Convention: do not hand-write the legacy enabled / requireTrustedPreset keys โ€” they still migrate on read, but the next card save rewrites the section in mode form.

What is auto-approved (rule table)

For pwsh / bash calls:

RuleConditionExample
harmless-commandPure introspection, no shell metacharacters (; & | < > \ $( newline)ls -la, Get-Process, whoami, echo hello
harmless-repo-commandgit/hub read command and workdir inside a trusted areagit status, git branch in D:\repos\app
trusted-area-commandworkdir inside a trusted area and the command references a trusted pathCopy-Item D:\data\a D:\data\b with workdir D:\data

For write / edit (fs) calls:

RuleConditionExample
trusted-area-targetfile_path (absolute, or relative resolved against the session cwd / workdir) lies inside a trusted areawrite to D:\data\out.txt

Everything else โ€” including git pull/push/fetch/checkout, git diff/log -p (they can run repo-configured textconv/pager programs), commands with redirection or pipes, writes outside trusted areas, and every other tool โ€” defers to the user.

The exact meaning of "trusted area"

A trusted area is not "everything inside runs without a human". Per-request:

  • Auto-approved: a write/edit whose resolved target lies inside the area; or a metacharacter-free command run from a workdir inside the area that matches the harmless table (git/hub read families) or additionally references a trusted path.
  • Not auto-approved: a command run from a trusted workdir that neither matches the harmless table nor references a trusted path (e.g. Invoke-WebRequest) โ†’ defers; any command containing shell metacharacters (; & | < > \ $( newline) โ†’ defers; anything matching dangerousPatterns โ†’ defers, inside trusted areas too.
  • Not even seen by the plugin: writes into the current DSH workspace are allowed directly by the sandbox, so no approval request is ever raised.

Trusted area vs outside: the actual privileges

Example: D:\data configured as a trusted area, C:\work as the session workspace ("inside" = workdir โˆˆ area / target โˆˆ area):

OperationOutsideInside (trusted area)
git status (run inside D:\data\repo)defersโœ… auto-approved
Copy-Item D:\data\a.txt D:\data\b.txtdefersโœ… auto-approved
Remove-Item D:\data\build -Recurse (cleanup)defersโœ… auto-approved
write / edit to D:\data\โ€ฆdefersโœ… auto-approved
Get-Process, Get-Content, โ€ฆ (harmless introspection)โœ… auto-approvedโœ… auto-approved (no difference)
echo hi > D:\data\x.txt (redirection)defersdefers (no metacharacter exemption)
Invoke-WebRequest http://โ€ฆ (trusted workdir, no area path referenced)defersdefers (no arbitrary-command exemption)
reg add / icacls / sudo โ€ฆ (dangerous pattern)defersdefers (no dangerous-operation exemption)

The trusted area grants exactly three privileges: in-area git read commands, in-area "simple single commands" (metacharacter-free and referencing an area path), and fs writes whose target resolves inside the area. It never exempts metacharacters, arbitrary commands, or dangerous patterns.

Deliberately never auto-approved

  • Shell metacharacter commands (redirects, pipes, chaining, substitution) โ€” the "harmless" window accepts only a single simple command; the trusted-area rule is bound by the same restriction.
  • git operations that write, fetch or merge, and git diff/git log -p โ€” untrusted repositories can weaponize git via .git/config (textconv, fsmonitor, pager), so git auto-approval requires a trusted workdir and stays on the read-only family.
  • Anything matching dangerousPatterns โ€” drive/system-root wipes, rm -rf /, format, diskpart, shutdown, fs targets inside Windows / Program Files / ProgramData, and since 2.1.0 also privilege escalation (sudo/gsudo/runas/Start-Process -Verb RunAs/psexec), account & ACL changes (net user/net localgroup/*-LocalUser*/icacls/takeown/Set-Acl), security-control changes (antivirus *-MpPreference, firewall netsh/*-NetFirewall*, Set-ExecutionPolicy), persistence (sc, *-Service, schtasks, *-ScheduledTask), registry & boot (reg add/delete/import/save/restore, regedit /s, bcdedit), dynamic execution (Invoke-Expression/iex/-EncodedCommand/-enc), log tampering (wevtutil cl/Clear-EventLog) and more โ€” these defer to the human even inside trusted areas. The default table errs on the wider side: a false positive costs one extra human confirmation, never a denial.
  • Requests whose tool/call cannot be found in the session log, or whose arguments are missing or oversized โ€” no data, no auto-approval.

Security

  • No secrets. The plugin contains no API keys, no network access beyond its own same-origin config API, and no eval/dynamic code. It never reads configuration outside its own config and settings section.
  • Auditable. Every auto-approval is a one-shot grant recorded in the session log (approval/asked + approval/decided) plus a logger line naming the matched rule; the settings card shows the most recent decisions.
  • Fail-safe direction. Errors in the decision path log a warning and delegate; the plugin cannot deny, block, or lock out a session.
  • Gated config API. GET/PUT /api/dsh-auto-approval-plugin/config accepts only loopback (or configured trustedHosts) same-origin requests; cross-site fetches are rejected. It reads and writes only the plugin's own settings namespace.
  • See SECURITY.md for the threat model and reporting.

Uninstall (no residue)

  1. Remove the plugin: dsh plugin --profile <profile> remove dsh-auto-approval-plugin
  2. Remove the trusted-area override from your profile's cordis.patch.yml (the - id: auto-approval entry, if you added one).
  3. Remove the auto-approval: section from settings.yaml (written by the web card, if you saved there).
  4. Verify no residue: dsh --profile <profile> --dump-config should contain no auto-approval row; grep -n "auto-approval" ~/.dsh/settings.yaml should find nothing.

Nothing else is touched: no other files, no sessions, no credentials.

Development

npm test          # node:test unit tests for the decision core
npm run check     # syntax check + tests
node scripts/verify-composition.js <profile>   # offline composition/schema check

The decision core is dependency-free plain JavaScript; the plugin surface is a standard Cordis plugin (see lib/index.js). Architecture: docs/architecture.md. Relevant official material: extension cookbook, plugin configuration, tool execution pipeline.

Comments

0
Newest first