dsh-pwsh-guard
English | 简体中文
A DeepSeek Harness plugin that enforces one rule: read files with read, search with grep,
locate files with glob — not with shell text commands.
It registers a ctx.tools.guard over pwsh, bash and ops_bash. A call whose command line
performs file text I/O is rejected before execution, and the rejection reason names the tool to use
instead. Rejection messages follow the host locale (English or Chinese).
Rules
| id | group | blocks | use instead |
|---|---|---|---|
search.Select-String, search.sls |
textSearch | Select-String, sls |
grep |
search.findstr, search.grep, search.rg |
textSearch | findstr, grep, rg |
grep |
read.Get-Content, read.gc, read.cat, read.type |
textRead | Get-Content and its aliases |
read |
read.more, read.head, read.tail |
textRead | more, head, tail |
read |
bypass.node-eval, bypass.python-c, bypass.php-r, bypass.dotnet-io |
runtimeBypass | node -e, python -c, php -r, [IO.File]::ReadAllText that read files |
read, grep |
wait.Start-Sleep, wait.sleep |
sleep | Start-Sleep, sleep |
job_output wait, schedule_create |
pipeline.stderr-merge |
stderrMerge | 2>&1 followed by a pipe |
$LASTEXITCODE, 2>err.txt |
Two matching rules keep false positives down:
- Short aliases match in command position only (
^,;,|,&,(,{,$(), soGet-ChildItem -Type f,git gcandgit log --grep=xpass. - Quoted spans are ignored, so
git commit -m "stop using Select-String"passes. Rules that inspect a script body (node -e ...) read the raw string.
Configuration
All fields are optional.
- id: pwsh-guard
name: 'dsh-pwsh-guard'
config:
enabled: true
language: auto # zh | en | auto
tools: [pwsh, bash, ops_bash]
disabledGroups: [] # textSearch | textRead | runtimeBypass | sleep | stderrMerge
allowPatterns: [] # regexes matched against the raw command; a match allows the call
rules: [] # extra rules, evaluated before the built-ins
language: auto follows DSH_LOCALE, then the host locale, then LANG / LC_ALL.
rules:
- id: no-force-push
pattern: 'git\s+push\s+--force'
title: 'Force push is blocked'
hint: 'Use --force-with-lease instead'
Installation
From npm:
plugin_manager action: install_bundle target: dsh-pwsh-guard
From GitHub:
plugin_manager action: install_bundle target: https://github.com/Steven-Qiang/dsh-pwsh-guard
From a local checkout (link:, for development):
git clone https://github.com/Steven-Qiang/dsh-pwsh-guard
plugin_manager action: install_bundle target: /path/to/dsh-pwsh-guard
After editing code or config, reload without restarting:
plugin_manager action: set_plugin target: include:pwsh-guard enabled: false
plugin_manager action: set_plugin target: include:pwsh-guard enabled: true
Uninstall: plugin_manager action: remove_bundle target: dsh-pwsh-guard.
If install_bundle reports failedAt: registry, the failure came from another dependency of the
profile; the package link, package.json entry and cordis.yml row are usually already written.
Check for node_modules/dsh-pwsh-guard/index.js and the id: pwsh-guard row before retrying.
Limitations
- Command names inside quotes (
& "Select-String",powershell -Command "Get-Content x") are not detected. -EncodedCommandis not decoded.ops_bash --script_pathis checked by path only, not by script content.- Directory listing (
Get-ChildItem,ls,dir) is out of scope. - Not a sandbox. For destructive commands and secret access use a dedicated guard such as
cc-safety-net; its rule schema cannot express "deny every use of this command", which is why this plugin exists.
Development
npm test
Builds with tsdown, then runs 36 command fixtures and 11 configuration assertions.
Release
Versions are owned by CI — do not edit version. Pushing to main runs
.github/workflows/release.yml:
| commits | bump |
|---|---|
fix: |
patch |
feat: |
minor |
feat!: or BREAKING CHANGE: |
major |
| anything else | no release |
Publishing uses npm Trusted Publishing over OIDC, so no NPM_TOKEN is stored. It requires
permissions.id-token: write (configured), Node ≥ 22.14 with npm CLI ≥ 11.5.1 (the workflow uses
Node 24 plus npm install -g npm@latest), and a repository.url that matches this repository.
The Trusted Publisher is configured on npmjs.com for Steven-Qiang / dsh-pwsh-guard /
release.yml. A new package has to be published manually once before that configuration exists,
and a new Trusted Publisher must complete a successful publish within 2 days or it expires.
No comments yet. Be the first to write one.