api-relay-audit
toby-bridges
Local security audit for AI API relays and LLM proxies: detects prompt injection, model substitution, tool-call rewriting, SSE anomalies, error leakage, and Web3 wallet risks.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:Lstalu/dsh-bundle-dedup-guard
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
A DeepSeek Harness plugin that catches duplicate loader entry ids in profile bundle lists — and audits the whole plugin environment (vendor tree, plugin junctions, known conflict pairs) — on every plugin load.
If a profile's dsh.profile.bundles lists an aggregate bundle (a bundle whose patch inserts all of its sub-plugins, e.g. @linxin666/dsh-web-ui-all) and its sub-plugins individually, the loader receives the same loader-entry id twice. EntryGroup.update throws duplicate loader entry id: <id> before any plugin starts, and the whole profile fails to boot. This plugin exists so that never happens silently again.
Incident, 2026-08-18: a web profile listed @linxin666/dsh-web-ui-all (which aggregates 13 sub-plugins into one patch) and all 13 sub-plugins separately. Every sub-plugin id was inserted twice; the first collision reported was duplicate loader entry id: ui-dsh-aionui-panel. Fixing only the bundles list was not enough — the dsh plugin command's reconcilePlugins re-appends every dependencies entry that declares dsh.bundle to the bundle list after each pnpm operation, so the sub-plugins came back an hour later and crashed the next boot.
Incident, 2026-08-21: a bulk plugin update wiped plugin node_modules (8 plugins failed to boot with Cannot find package); a repair npm install followed an @deepseek-ai junction into the vendor tree and corrupted it; and once dependencies were restored, dsh-better-sidebar ended up mounted twice (/sidebar/api duplicate-route crash). v0.2.0 turns these red lines into automatic site-level checks — see Site-level health checks below.
Full incident records: docs/KNOWN-ISSUE-bundle-duplicate.md (2026-08-18) and the 2026-08-21 self-repair manual surfaced by the site audit.
The loader's failure path is: cordis-plugin-include's applyEntryPatches flattens every bundle's insert entries without deduplicating, then cordis-plugin-loader's EntryGroup.update dedups by id and throws on the first duplicate — before any plugin entry is created. This plugin re-implements exactly that "flatten + dedup by id" semantics in pure Node, and reports the offending ids, their sources (which bundle/patch inserted each), and the fix.
| Trigger | When | Notes |
|---|---|---|
| Apply | every boot | instant health check as the plugin mounts |
| Loader events | loader/entry-init / loader/partial-dispose |
runtime hot loads / plugin additions, debounced 800 ms |
| Manifest watch | fs.watch on the profile dir |
the moment package.json or cordis.patch.yml changes — i.e. dsh plugin add, marketplace installs, or hand edits — warn immediately, before the next restart |
ui-dsh-aionui-panel: @linxin666/dsh-web-ui-all ← @linxin666/dsh-client-ui-aionui-panel).bundles but not resolvable (the loader would loud-fail too).dsh.bundle.patch (a misconfiguration per the loader contract).dependencies entry that declares dsh.bundle but is not in bundles. dsh plugin's reconcile will append it on the next install/update; if it's a sub-plugin covered by an aggregate, that re-creates the crash. The warning names the covered ids. Fix: move such packages to devDependencies (reconcile only reads dependencies).Reports are written to $DSH_HOME/dsh-bundle-dedup-guard/reports/<profile>-<timestamp>.json and <profile>.latest.json.
Beyond the bundle-list checks, every run also audits the whole plugin environment,
turning the 2026-08-21 incident's red lines into automatic checks (lib/site-health.mjs,
zero-dependency, read-only):
| Check | Detects | Incident |
|---|---|---|
| Known conflict pairs | bundles lists @linxin666/dsh-web-ui-all and dsh-better-sidebar together — both entries execute the same lib/index.js and register the same /sidebar/api route (duplicate prefix route crash at apply time) |
2026-08-21 |
| Vendor tree integrity | every @deepseek-ai/* package vs resources/vendor/dsh/node_modules/.package-lock.json: missing / empty dir / package.json name mismatch (wrong content installed) / version mismatch; .name-* temp-dir leftovers (informational) |
2026-08-21 |
| Plugin junction integrity | plugins whose runtime code imports @deepseek-ai/* but whose package/node_modules/@deepseek-ai junction is missing (the exact Cannot find package boot crash), points at the wrong target, or dangles; real-dir copies (works, informational) and .npmbak leftovers (an npm install ran inside a junction dir) |
2026-08-21 |
| Incident manual pointer | surfaces the latest $DSH_HOME/incidents/<date>/README.md self-repair manual |
— |
Every problem is reported with a copy-paste fix command (recreate the junction / run
repair-vendor.ps1 / remove the bundles entry). The guard never modifies anything itself.
The junction check decides "does this plugin need a junction" by source-scanning runtime
import/requireof@deepseek-ai/*(not bypackage.jsondeclarations), and only audits plugins actually listed in some profile'sbundles— dormant plugin dirs are skipped.
The loader deduplicates before creating any plugin entry, so when duplicates already exist at boot, an in-process check cannot run — the tree never mounts. For that case use the standalone CLI below: it is pure disk reads and works even when boot is broken.
As a profile bundle (recommended while in development):
package.json dependencies:"dsh-bundle-dedup-guard": "link:F:/path/to/dsh-bundle-dedup-guard"
"dsh-bundle-dedup-guard" to dsh.profile.bundles (first entry is fine).node_modules (pnpm does this for dsh plugin add).From npm:
dsh plugin --profile web add dsh-bundle-dedup-guard
The plugin checks automatically — no interaction needed. For manual diagnosis (including when boot already crashed):
# check all profiles + site-level health (DSH_HOME defaults to ~/.dsh)
node bin/check.mjs
# a specific profile
node bin/check.mjs --profile web
# a specific manifest file (e.g. a pre-fix backup, for testing)
node bin/check.mjs --manifest <path-to-package.json>
# machine-readable JSON, skip report files
node bin/check.mjs --profile web --json --no-write
# skip the site-level health audit
node bin/check.mjs --no-site
Exit codes: 0 = healthy, 1 = duplicates / unresolved bundles / bundle-less packages /
site-level problems found (useful as a CI gate).
Edit dsh.profile.bundles so each id has exactly one source. The common shape is "aggregate + sub-plugins":
@linxin666/dsh-web-ui-all)dependencies to devDependencies — otherwise dsh plugin reconcile re-appends them on the next install/update (the exact recurrence from 2026-08-18)Then re-run node bin/check.mjs --profile <name> until green, and restart.
npm test # node --test, zero dependencies
npm run check # run the guard against your local profiles
lib/check.mjs — the check core (pure Node, no third-party deps)lib/site-health.mjs — site-level health checks (v0.2.0: conflict pairs, vendor tree, junctions)index.mjs — the Cordis plugin entry (apply + listeners)bin/check.mjs — standalone CLI (works without a booted tree)test/ — unit tests with fixture profilesMIT
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: guard、profile。