Back to marketplace

dsh-permission-rules

Security

PerryLink/dsh-permission-rules

Claude Code-style declarative permission rules for DeepSeek Harness with ordered allow/deny/ask rules, tool-name and argument matching, session-log audit, and HMR reload.

  • ai-safety
  • allow-deny-ask
  • approval
  • cordis
  • deepseek
  • deepseek-harness
  • dsh
  • dsh-plugin
  • network
  • network-policy
  • permission
  • policy
  • proxy
  • safety
GitHub Stars
25GitHub
Views
0DSH Plugin Hub
Forks
2GitHub
Open issues
2GitHub Issues
Manifest version
0.5.3dsh-permission-rules
Latest push
Aug 21, 2026GitHub
License
Apache-2.0TypeScript
Plugin type
Host + ClientRuns in both Host and Web Client

README

View source

๐Ÿ›ก๏ธ dsh-permission-rules

Gitee

Claude Code-style declarative permission rules for DeepSeek Harness.

Rules decide what is known. A reviewer model decides what is not.

License DSH plugin Node CI Version npm version npm downloads

English ยท ็ฎ€ไฝ“ไธญๆ–‡ ยท Espaรฑol ยท Portuguรชs ยท เคนเคฟเคจเฅเคฆเฅ€


Compatibility

SurfaceStatus
HarnessDeepSeek Harness 0.1.1-rc.2
Node`^22.19.0
PlatformsAll (host + web settings client)
ModelAny (deny/ask reasons surface through tool results)

What you get

dsh-permission-rules puts an ordered allow / deny / ask rule list in front of every tool call on the tools/pre-execute waterfall โ€” deterministic, instant, auditable, and written by you in plain YAML:

  • deny blocks the call; the rule's reason becomes the model-visible error.
  • ask rides the official approval seam (mount dsh-auto-review for a second-model answerer, or a human answers; with neither, the harness fails closed).
  • allow (and no-match) strictly delegates via next() โ€” downstream listeners are never short-circuited.

Every hit and every passthrough is audit-logged as a permissionRules/decision session event (log-only โ€” nothing extra is injected into the model context).

  • Rich matching โ€” tool-name globs (including mcp__*), agent-identity selectors (main / subagent / preset:*), argument key/value globs or regexes (with !pattern negation and an absent key dimension), workspace-relative path globs at any nesting depth, and when host conditions (env vars, platform).
  • Hierarchical rule files โ€” optional searchUp merges every .dsh/rules.yaml from the session cwd to the filesystem root, nearest first.
  • Dry-run rollout โ€” enforce: false audits what the policy would do while passing every call through.
  • Hot reload โ€” Chokidar watch with debounce; a broken edit keeps the previous rules, never crashes.
  • Fail loud โ€” invalid YAML, unknown actions/fields, bad globs/regexes, backtracking-prone patterns, or more than maxRules rules fail the load.

Rule syntax

# <project>/.dsh/rules.yaml
rules:
  - match: { tools: [bash, pwsh], params: { command: "git push*" }, paths: ["**/secrets/**"] }
    action: deny
    reason: "No pushes from protected paths"

  - match: { tools: [edit, write] }
    action: ask
    reason: "File writes need confirmation"
  • Match dimensions โ€” tools (globs, incl. mcp__*), agents (main / subagent / preset:<name>; unknown identity never matches โ€” fail closed), params (key/value globs or regexes, !pattern negation, absent key dimension), paths (workspace-relative globs extracted at any nesting depth), when (env var globs/regexes + a closed platform list), and network (domains / ips / ports / schemes โ€” globs, wildcards, CIDRs, port ranges).
  • Actions โ€” allow / deny / ask, evaluated in file order, first match wins.
  • Rule metadata โ€” enabled: false (visible but inert), description, tags; unknown fields fail the load.
  • Schema โ€” a JSON Schema ships at docs/rules-format.schema.json (editor completion via # yaml-language-server: $schema=...); the full vocabulary and a 5-rule security baseline live in docs/rules-format.en.md.

Network policy

A Codex-style process-level network policy: shell subprocess traffic flows through a built-in local HTTP/CONNECT proxy, and every connection is decided by ordered network rules or by three modes mapped onto the official sandbox presets:

  • deny-all โ€” the read-only sandbox preset: block all outbound.

  • whitelist โ€” the workspace-write preset: allow listed targets, unlisted: ask (or deny) for the rest.

  • allow-all โ€” the danger-full-access preset: allow everything.

  • auto (default) โ€” follows the sandbox preset; on hosts without the sandbox-policy service it resolves to autoFallback (allow-all).

  • Matching โ€” match.network with domains / ips / ports / schemes (globs, wildcards, CIDRs, port ranges; numeric YAML ports are accepted). URL-candidate extraction on the tools/pre-execute hot path fires on web-tool arguments and URLs embedded in bash/pwsh command text; loopback targets can short-circuit rules per loopback policy.

  • Audit โ€” denied connections append permissionRules/network to the owning session (same adaptive ignorable gate), with block counters and recent interceptions in /rules network and the settings page.

Quick start

# 1. install the bundle into your profile
dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"

# or from npm (published releases)
dsh plugin --profile web add dsh-permission-rules

# 2. restart and verify the row
dsh --profile web --dump-config | grep -A4 'id: permission-rules'

Install & uninstall

  • git channel (latest main): dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main" โ€” the prepare script builds with production dependencies only.
  • npm channel (published releases): dsh plugin --profile web add dsh-permission-rules.
  • tarball channel: pnpm pack in this repo, then dsh plugin --profile web add ./dsh-permission-rules-<version>.tgz.
  • uninstall: dsh plugin --profile web remove dsh-permission-rules.

Configuration

All tunables are Schemastery Config fields (changeable from cordis.yml). An id-targeted override replaces the whole row โ€” restate every key you need.

KeyDefaultMeaning
rulesFile.dsh/rules.yamlRule file location; relative = resolved against the calling session's cwd, absolute = global and validated at mount
fallbackPath(none)Rule file used when per-cwd discovery finds nothing; validated at mount
badFilePolicyfailBad rule file: fail errors the pending tool call loudly; ignore-with-warning warns and continues empty
maxRules256Hard cap on rule count across the effective source chain
maxCachedWorkspaces512Hard cap on cached per-workspace rule loads (LRU eviction)
patternModeglobparams/paths/when.env pattern flavor: glob or regex (tool names are always globs)
watchtrueChokidar watch + reload on change
watchStabilityThresholdMs200Reload debounce window (ms)
languageen/rules output language: en, zh, es, pt, hi
caseInsensitivePaths(win32)paths patterns and workspace-root comparison ignore ASCII case; true on Windows
auditallAudit granularity: all logs every hit AND passthrough; hits skips passthrough events
searchUpfalseWalk parent directories from the session cwd and merge every found rule file, nearest first
maxGlobStars2Hard cap on unbounded */** quantifiers per glob pattern
enforcetruefalse = dry-run mode: deny/ask hits are audit-logged with a dryRun marker and every call passes through
allowUnmarkedAuditfalsePre-marker hosts drop the ignorable marker; the plugin disables session-log audit with a warning. Set true to opt back in
network.enabledtrueMaster switch for the proxy, env injection, and web-tool mode defaults
network.modeautoPolicy mode: auto follows the sandbox preset, or deny-all / whitelist / allow-all
network.autoFallbackallow-allMode used when auto has no sandbox-policy service
network.unlistedaskWhitelist-mode handling of targets no rule matched: ask or deny
network.proxyBind127.0.0.1Local proxy bind address (loopback only)
network.proxyPort0Local proxy port; 0 picks a free ephemeral port
network.proxyMaxRecent100Cap on recent-block records kept for the settings page
network.loopbackallowLoopback targets: allow (Codex parity) or policy
network.injectEnvtrueWhether proxy environment variables are injected for subprocesses
network.noProxyclearSubprocess NO_PROXY handling: clear enforces the policy or preserve

Tools & surfaces

SurfaceKindNotes
tools/pre-executelistenerFirst-match allow/deny/ask rules + network URL-candidate extraction
/rulescommandlist ยท reload ยท decisions [n] ยท test <tool> <json>
permissionRules/decisioneventLog-only audit for every hit and passthrough
permissionRules/networkeventProxy-layer audit for blocked connections
HTTP/CONNECT proxyserviceBuilt-in local proxy governing shell subprocess traffic
settings pageclientNetwork-mode editor, rule editor, block counters, recent interceptions
/rules                        list the active rules, their source files, and any last-reload error
/rules list                   explicit alias for the bare listing
/rules reload                 re-read the rule-file chain for this workspace
/rules decisions [n]          show the last n permission decisions of this session (default 10)
/rules test <tool> <json>     dry-evaluate the rules against a hypothetical call

/rules test also accepts leading flags: --cwd <dir>, --env KEY=VALUE (repeatable), --agent <selector> (repeatable), and --platform <name>. In multi-file chains (e.g. searchUp), every listed rule line is attributed to its own source file.

Permissions & data

  • Permissions: declares files:read, files:watch, files:write, session:append, and network:outbound in its workshop manifest. ask decisions ride the official approval seam โ€” nothing is re-implemented or bypassed.
  • Data: rule files are read from disk; no rule data is written. No model calls, no reviewer subagents.
  • Session log: permissionRules/decision is never injected into the model context and is appended with the envelope's ignorable: true marker so any harness build loads the log.

Security boundaries

  • Policy, not a kernel. paths candidates come only from a documented set of argument keys (at any nesting depth, depth-capped), and only workspace-relative paths match.
  • No reviewer here. The plugin never spawns subagents or calls models โ€” producing an ask decision is the end of its work.
  • No sandbox changes. OS-level sandbox policy belongs to the sandbox seam, not this plugin.
  • Loud misconfiguration. Unknown YAML fields, unknown actions, and bad patterns are rejected at load.
  • Backtracking bounds. Glob patterns are capped at maxGlobStars unbounded star expansions; regex-mode patterns reject nested unbounded quantifiers and quantified overlapping literal alternations.

Known limitations

  • Audit marker on pre-marker hosts. permissionRules/decision is appended with ignorable: true; hosts whose Session.append predates the marker (the 0.1.0-rc.6 line) silently drop it, so the runtime disables session-log audit with a one-time warning. Set allowUnmarkedAudit: true to opt back in; repair already-written logs with scripts/repair-session-logs.mjs.
  • Path candidates are heuristic. Only the documented argument keys feed path matching, and workspace-relative matching is ASCII-case-insensitive only when caseInsensitivePaths is on.
  • Globs are a conservative subset. No brace expansion โ€” write two patterns, or use regex mode.
  • The regex backtracking guard is structural, not exhaustive. Prefer glob mode for untrusted files.

Collaborating with dsh-auto-review

  • dsh-permission-rules produces ask; dsh-auto-review answers on the approval/request waterfall with a read-only second-model verdict (or delegates to humans). Mount both for the full closed loop.
  • Integration-tested: permissionRules/decision โ†’ approval/asked โ†’ autoReview/verdict โ†’ approval/decided, with the reviewer replaced by a scripted mock.
  • The never approval policy and every fail-closed guarantee of the official harness stay untouched.

Session log repair

Session logs written before the ignorable marker existed can be refused by newer harness builds (SessionFormatUnsupportedError). The shipped scripts/repair-session-logs.mjs rewrites only the targeted audit rows to carry ignorable: true, frame-preserving, with backups:

node scripts/repair-session-logs.mjs scan [--home DIR]      # report foreign rows, change nothing
node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]

--home defaults to $DSH_HOME/sessions (or ~/.dsh/sessions).

Development

pnpm install            # node ^22.19 || >=24
pnpm run typecheck      # tsc, src + tests
pnpm run lint           # eslint, src + tests + scripts
pnpm test               # vitest: 236 tests, 20 files
pnpm run test:coverage  # coverage gate (90/80/90/90)
pnpm run build          # tsc declarations + tsdown bundles (lib/)
pnpm run pack:check     # build + pack (the published artifact)
node scripts/check-readme-sync.mjs   # five-language README sync gate (also in CI)

See VERIFICATION.md for the headless end-to-end verification record.

Topics

dsh, dsh-plugin, deepseek-harness, permission, policy, allow-deny-ask, approval, safety, network, network-policy, proxy

Contributors

  • @PerryLink โ€” creator and maintainer: rule vocabulary and evaluation, runtime, HMR watch, session-log audit, network policy + proxy, and the five-language docs.
  • @22xuan โ€” the detailed report on rc.6 hosts silently dropping the audit event's ignorable marker (#2) and the upstream harness discussion; the v0.4.1 runtime host-capability detection and the documentation correction drew directly from that analysis.
  • @sjh9714 โ€” proposed the shared rule-syntax test-vector corpus (#4, #5), shipped in v0.5.1 as docs/rule-test-vectors/, and supplied the AST-decomposition boundary cases on the design discussion.
  • @weipeng1999 โ€” the AST-based command-decomposition feature proposal (#8) behind the design discussion.
  • @alexchenzl โ€” the DSH Directory listing request (#7).
  • @zl190 โ€” reported and verified the 0.1.0-rc.7 harness compatibility gap (PR #9).

PerryLink DSH Plugin Family

This project is one of the 15 DeepSeek Harness plugins maintained by PerryLink. If this one helps you, the others likely will too:

PluginOne-liner
dsh-mcp-panelRead-only MCP runtime panel: /mcp command + Settings tab with status, tools and errors
dsh-doublecheckEngineering-discipline guard: requirements grill, test gates, adversary review
dsh-background-agentsDurable background child agents with a Web UI sidebar, messaging and interrupt
dsh-lsp-actionsLSP diagnostics, formatting, completion, code actions and rename over language servers
dsh-output-stylesClaude Code outputStyles-equivalent runtime style switching
dsh-checkpoint-rewindClaude Code /rewind-equivalent: snapshots, session forks, one-shot restore
dsh-permission-rulesClaude Code-style declarative allow/deny/ask permission rules with audit
dsh-auto-reviewSecond-model auto-review on the approval chain, fail-closed by default
dsh-mementoApproval-gated cross-session memory: ctx.memory seam + SQLite + memory tool
dsh-skill-pack-securitySecurity-audit skill pack: secret scan, dependency and supply-chain review
dsh-session-pinPin sessions in the Web sidebar with durable ordering
dsh-composer-historyTerminal-style input history for the web composer: arrows, Ctrl+R search
dsh-githubGitHub PR/issues integration for DSH, every write gated by approval
dsh-plugin-guidePlugin-development knowledge base as an on-demand agent skill
dsh-claude-moveMigrate Claude Code sessions, memory, skills and CLAUDE.md into DSH

License

Apache License 2.0 ยฉ 2026 dsh-permission-rules contributors

Comments

0
Newest first