Skip to content

Commit d682cda

Browse files
committed
feat: v0.6.0 — MCP security suite, detection benchmark, ReDoS hardening
- MCP tool-poisoning scanner + rug-pull detection + /scan-mcp (stdio + remote HTTP) - customRules config extension point - detection benchmark (bench/, npm run bench) with CI regression gate - ReDoS audit + fixes (splitCommands, zh_mixed_lang_injection) - default injectionThreshold 60->40 (benchmark-calibrated, revertible) - PII false-positive fixes; L5/L7 DLP merge; SECURITY.md accuracy Full granular history on branch feat/mcp-security-suite.
1 parent ee484a2 commit d682cda

31 files changed

Lines changed: 2068 additions & 116 deletions

.github/workflows/ci.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
branches: [main, master]
8+
9+
jobs:
10+
test:
11+
name: Test (Node ${{ matrix.node }})
12+
runs-on: ubuntu-latest
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
node: [18, 20, 22]
17+
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- name: Setup Node ${{ matrix.node }}
22+
uses: actions/setup-node@v4
23+
with:
24+
node-version: ${{ matrix.node }}
25+
26+
# package-lock.json is gitignored, so use `npm install` (not `npm ci`).
27+
- name: Install dependencies
28+
run: npm install
29+
30+
- name: Typecheck
31+
run: npx tsc --noEmit
32+
33+
- name: Build
34+
run: npm run build
35+
36+
- name: Test
37+
run: npm test
38+
39+
- name: Detection benchmark (precision/recall regression gate)
40+
run: npm run bench -- --ci

.github/workflows/release.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: Release
2+
3+
# Publishes to npm with provenance (SLSA build attestation) when a version tag
4+
# is pushed (e.g. `git tag v0.5.12 && git push --tags`).
5+
# Requires an NPM_TOKEN repo secret (automation token, publish scope).
6+
7+
on:
8+
push:
9+
tags:
10+
- 'v*'
11+
12+
permissions:
13+
contents: read
14+
id-token: write # required for npm provenance
15+
16+
jobs:
17+
publish:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- uses: actions/setup-node@v4
23+
with:
24+
node-version: 20
25+
registry-url: 'https://registry.npmjs.org'
26+
27+
- name: Install dependencies
28+
run: npm install
29+
30+
- name: Typecheck
31+
run: npx tsc --noEmit
32+
33+
- name: Test
34+
run: npm test
35+
36+
- name: Detection benchmark gate
37+
run: npm run bench -- --ci
38+
39+
- name: Build
40+
run: npm run build
41+
42+
# Idempotent: skip if this version is already on npm (e.g. published locally).
43+
- name: Check if version already published
44+
id: check
45+
run: |
46+
V=$(node -p "require('./package.json').version")
47+
if npm view "shellward@$V" version >/dev/null 2>&1; then
48+
echo "exists=true" >> "$GITHUB_OUTPUT"
49+
else
50+
echo "exists=false" >> "$GITHUB_OUTPUT"
51+
fi
52+
53+
- name: Publish with provenance
54+
if: steps.check.outputs.exists == 'false'
55+
run: npm publish --provenance --access public
56+
env:
57+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

.github/workflows/test.yml

Lines changed: 0 additions & 13 deletions
This file was deleted.

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ Thumbs.db
2121
.env
2222
.env.*
2323

24+
# Secrets / registry publish tokens (never commit)
25+
.mcpregistry_*
26+
*_token
27+
2428
# npm
2529
.npm/
2630
package-lock.json
@@ -41,3 +45,6 @@ promo/
4145
awesome-llm-security/
4246
*.mp4
4347
docs/
48+
49+
# Agent worktrees
50+
.claude/

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/),
66
and this project adheres to [Semantic Versioning](https://semver.org/).
77

8+
## [0.6.0] - 2026-06-05
9+
10+
### Added
11+
- **MCP tool-poisoning scanner** (`scanToolDefinition` / `scan_mcp_tool` MCP tool): detects hidden instructions, invisible characters, concealment ("don't tell the user"), sensitive-file access and exfiltration hints in an MCP tool's description/parameters
12+
- **MCP rug-pull detection** (`McpBaseline`): fingerprints each tool's description+schema and flags silent changes across runs (`SHELLWARD_BASELINE_PATH` to relocate the store)
13+
- **`/scan-mcp` command + MCP client** (`mcp-client.ts`): discovers configured MCP servers and scans them live — **stdio and remote Streamable-HTTP** (incl. SSE responses + session headers), zero dependencies
14+
- **Custom rules** (`customRules` in `ShellWardConfig`): additive `blockedTools` / `sensitiveTools` / `outboundTools` / `honeypotPaths` / `sensitivePatterns` / `dangerousCommands` / `injectionRules`, plus `allowedTools` that always wins; invalid user regexes are skipped, never throw
15+
- **Detection benchmark** (`bench/`, `npm run bench`): labeled corpus (attacks + hard negatives + documented bypasses) reporting precision/recall/F1; CI regression gate (`--ci`)
16+
- **ReDoS audit** (`test-redos.ts`, in CI): adversarial-input timing budget for every detector
17+
- Unicode tag-character and variation-selector detection in hidden-char scanning
18+
- Startup nudge to run `/scan-mcp` when MCP servers are configured
19+
20+
### Changed
21+
- **Default `injectionThreshold` 60 → 40** — the benchmark showed 60 missed most single-signal attacks (injection recall 37.5% → 100%). More aggressive blocking; revert via config or `SHELLWARD_THRESHOLD`
22+
- Injection rules 32 → 37 (20 ZH + 17 EN); fixed several intervening-word / reversed-order / word-boundary bugs
23+
- Command + injection inputs are normalized before matching (empty-quote de-obfuscation, zero-width stripping)
24+
- L5 Security Gate now delegates outbound DLP to the single L7 path (no divergence)
25+
26+
### Fixed
27+
- **ReDoS**: `splitCommands` (catastrophic backtracking on whitespace floods) and `zh_mixed_lang_injection` (unbounded `.*`)
28+
- **PII false positives**: `phone_cn` restricted to real carrier segments; `bank_card_cn` narrowed to UnionPay (no longer mislabels Visa/Mastercard)
29+
- `SECURITY.md` corrected (no false "no network calls" claim; supported versions; ReDoS claim now CI-verified)
30+
831
## [0.5.16] - 2026-04-15
932

1033
### Added

0 commit comments

Comments
 (0)