dsh-vet 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Roger Deng
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,136 @@
1
+ # dsh-vet
2
+
3
+ Security vetting for DeepSeek Harness (DSH) plugins: permission & supply-chain
4
+ audits before install, graded via the open [`dsh-vet/v1`](docs/dsh-vet-v1.md)
5
+ report standard.
6
+
7
+ > **Status: v0.1 complete.** Contract draft, reference scanner + CLI, rule
8
+ > set with public rationales, and an 11-package calibration sweep are in;
9
+ > the `0.1.0` npm release is the remaining step.
10
+
11
+ ## Install
12
+
13
+ Requires Node ≥ 20.
14
+
15
+ ```sh
16
+ npm install -g dsh-vet # or: pnpm add -g dsh-vet / bun add -g dsh-vet
17
+ dsh-vet --help
18
+ ```
19
+
20
+ Prefer not installing? `npx dsh-vet <specifier>` runs the same scanner with
21
+ zero footprint. The scanner is the only thing that runs — dsh-vet never
22
+ installs the plugin it audits.
23
+
24
+ ## Usage
25
+
26
+ ```sh
27
+ npx dsh-vet <specifier> # npm package, git URL, or local path
28
+ npx dsh-vet --json <specifier> # dsh-vet/v1 report on stdout
29
+ npx dsh-vet --strict <specifier> # exit 1 on findings >= high (confidence >= medium)
30
+ npx dsh-vet --rules dep.install-scripts <specifier>
31
+ ```
32
+
33
+ Any completed report exits `0` — grades describe findings, they do not gate.
34
+ Scanner failures exit non-zero. The scanner runs locally, reads the npm
35
+ registry for dependency metadata only, and never transmits audited code.
36
+
37
+ Shipped rules (each with a public rationale under
38
+ [`docs/rules/`](docs/rules)):
39
+
40
+ | Family | Rules |
41
+ |---|---|
42
+ | `perm.*` | seam-mismatch, undeclared-fs-write, subprocess-spawn, network-client, unreachable-files |
43
+ | `dep.*` | install-scripts, floating-range, typosquat-proximity |
44
+ | `obf.*` | eval-detect, dynamic-require, encoded-payload, charcode-chain, unparseable |
45
+ | `egress.*` | outbound-endpoints, secret-adjacent |
46
+
47
+ dsh-vet audits itself with the same scanner:
48
+ [`examples/dsh-vet.report.json`](examples/dsh-vet.report.json) is generated
49
+ from the exact tarball that ships (`npm pack` → scan), seams declared in
50
+ package.json. It is not an A-by-cheating report — every signal the scanner
51
+ finds in itself is in there.
52
+
53
+ ## Why
54
+
55
+ DSH's everything-is-a-plugin architecture is its greatest strength and its
56
+ largest attack surface: a plugin you install can register tools, touch the
57
+ filesystem, and open network connections. The community's single most-upvoted
58
+ feature request asks for marketplace standards and review mechanisms
59
+ ([deepseek-harness#1115](https://github.com/deepseek-ai/deepseek-harness/discussions/1115)),
60
+ and incidents like a Full Access session deleting a user's home directory
61
+ ([#461](https://github.com/deepseek-ai/deepseek-harness/discussions/461)) show
62
+ the stakes. The official marketplace will take time; trust tooling cannot.
63
+
64
+ `dsh-vet` exists so that "should I install this plugin?" has a shared,
65
+ machine-readable answer instead of vibes.
66
+
67
+ ## What it checks (v0.1 scope)
68
+
69
+ - **Capability surface** — which Cordis seams a plugin injects (`fs`, `shell`,
70
+ `web`, …) versus what its manifest and README claim
71
+ - **Supply chain** — dependency tree, install scripts, `postinstall` hooks,
72
+ typosquat-adjacent package names
73
+ - **Obfuscated behavior** — `eval` / `new Function` / dynamic `require` /
74
+ encoded payloads
75
+ - **Data egress** — outbound endpoints reachable from code that can read
76
+ secrets, session data, or the DSH home directory
77
+
78
+ ## The `dsh-vet/v1` report standard
79
+
80
+ The differentiating piece is not another scanner — it is
81
+ [`docs/dsh-vet-v1.md`](docs/dsh-vet-v1.md): an implementation-agnostic,
82
+ deterministic JSON report contract (findings with severity **and confidence**,
83
+ derived A–F grades) that any scanner may emit and any marketplace, CI job, or
84
+ UI may consume, in the spirit of the community's `dsh-doctor/v1` contract.
85
+ The TypeScript reference types ship from this package; third-party emitters
86
+ are welcome and listed here once verified.
87
+
88
+ ## How it differs
89
+
90
+ | Tool | Form | Focus |
91
+ |---|---|---|
92
+ | `dsh-plugin-vetting` | dsh plugin | install-time static heuristics |
93
+ | `dsh-audit` | CLI | ecosystem-wide catalog scoring (maintenance / docs / npm, security veto) |
94
+ | `dsh-plugin-audit` | dsh plugin | per-plugin permission profiling + runtime sentinel |
95
+ | `plugin_vet` skill pack | agent skills | audit methodology + gate |
96
+ | **`dsh-vet`** | library + CLI + CI action | the open `dsh-vet/v1` report contract, a reference scanner, and author-side badges — the shared trust layer others can emit and consume |
97
+
98
+ ## Findings are signals, not verdicts
99
+
100
+ A report describes what code does, not what its author intended. Low-confidence
101
+ findings never lower a grade, and every finding carries evidence and a
102
+ remediation. If you believe a finding about your plugin is wrong, open a
103
+ [false-positive dispute](.github/ISSUE_TEMPLATE/false-positive.md) — disputed
104
+ rules get re-examined and the rule set gets corrected in public.
105
+
106
+ ## Roadmap
107
+
108
+ - **v0.1** — contract frozen; reference CLI (`dsh vet <pkg>`) with the four
109
+ check families above
110
+ - **v0.2** — GitHub Action + badge so plugin authors self-audit and publish
111
+ their grade
112
+ - **v0.3** — marketplace integrations render `dsh-vet/v1` reports; contract
113
+ adopted by at least one third-party emitter
114
+
115
+ The detailed, trackable plan — task breakdowns, recorded decisions,
116
+ definitions of done, risks, and kill criteria — lives in
117
+ [ROADMAP.md](ROADMAP.md).
118
+
119
+ ## Develop
120
+
121
+ ```sh
122
+ pnpm install
123
+ pnpm test # vitest
124
+ pnpm build # tsdown → lib/
125
+ pnpm verify # typecheck + test + build + pack check
126
+ ```
127
+
128
+ ## Security
129
+
130
+ Reporting a vulnerability in dsh-vet itself: see [SECURITY.md](SECURITY.md).
131
+ dsh-vet runs locally, reads the npm registry for dependency metadata, and
132
+ never transmits audited code or results anywhere.
133
+
134
+ ## License
135
+
136
+ [MIT](LICENSE)
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from '../lib/index.mjs'
3
+
4
+ process.exit(await runCli(process.argv.slice(2), {
5
+ stdout: (line) => process.stdout.write(line + '\n'),
6
+ stderr: (line) => process.stderr.write(line + '\n'),
7
+ }))
@@ -0,0 +1,58 @@
1
+ # v0.1 calibration sweep
2
+
3
+ ROADMAP T6 requires a sweep of ~10 real ecosystem plugins with tuning until
4
+ zero **obvious** false positives. Swept on 2026-08-30 with scanner 0.1.0;
5
+ reports are committed under [`examples/sweep/`](../examples/sweep).
6
+
7
+ ## Packages
8
+
9
+ | Package | Grade | Graded findings |
10
+ |---|---|---|
11
+ | `dsh-searxng@0.1.1` | C | network-client (no `web` seam declared) |
12
+ | `@linxin666/dsh-doctor@0.3.6` | C | secret-adjacent¹, network-client, subprocess-spawn, undeclared-fs-write¹ |
13
+ | `dsh-zcf@0.5.3` | C | secret-adjacent¹, network-client, subprocess-spawn, undeclared-fs-write¹ |
14
+ | `@mlgbnb/dsh-archive-manager@1.0.7` | C | network-client, undeclared-fs-write¹ |
15
+ | `@michengai/dsh-skills-manager@0.1.30` | C | network-client, undeclared-fs-write¹ |
16
+ | `dsh-wechat@0.7.2` | C | network-client, undeclared-fs-write¹ |
17
+ | `dsh-find-plugin@0.3.7` | C | network-client |
18
+ | `dsh-better-sidebar@0.17.1` | C | secret-adjacent¹, encoded-payload¹, network-client, subprocess-spawn, undeclared-fs-write¹ |
19
+ | `@linxin666/dsh-live-stats@0.1.20` | A | — |
20
+ | `@aiwayds/dsh-tui-pi@1.1.0` | C | secret-adjacent¹, network-client, subprocess-spawn, undeclared-fs-write¹ |
21
+ | `dsh-fabric-host@0.1.11` | A | — |
22
+
23
+ ¹ Emitted at `low` confidence (or includes only low-confidence parts) —
24
+ reported, never grade-affecting, per the contract.
25
+
26
+ Every capability finding was hand-checked against the package's code; every
27
+ one describes real code shape. No scanner failures, no criticals.
28
+
29
+ ## What the sweep says about the ecosystem
30
+
31
+ - No swept package declares `dsh.seams` yet (the vocabulary ships with
32
+ dsh-vet), so honest plugins that legitimately use the network or spawn
33
+ processes read as C until they declare. That is the designed adoption
34
+ path, not a defect: the remediation in each finding is "declare the seam
35
+ or drop the capability".
36
+ - Bundled web UIs put `process.env` reads and `fetch` in one file; the
37
+ `egress.secret-adjacent` finding fires at `low` confidence there — signal,
38
+ not verdict, exactly as documented.
39
+
40
+ ## Tuning performed
41
+
42
+ 1. **`obf.encoded-payload`: hex literals now require ≥ 8 distinct
43
+ characters.** `dsh-better-sidebar`'s repeated-digit palette strings
44
+ (`8888…`) are tables, not encodings; encoded payloads use the hex
45
+ alphabet broadly. This removed 2 of 3 findings on that package.
46
+ 2. **`dep.typosquat-proximity`: popular-name list verified against npm.**
47
+ `dsh-ankh-guard` was removed (not installable — a name that cannot be
48
+ installed cannot be typosquatted); verified-active names were added
49
+ (`dsh-zcf`, `dsh-wechat`, `dsh-better-sidebar`, `dsh-find-plugin`). The
50
+ list is now all-real by construction and re-checked each sweep.
51
+
52
+ ## Accepted residuals
53
+
54
+ - One 60-char high-diversity hex literal in `dsh-better-sidebar`
55
+ (`4e79a7…` — concatenated d3 category colors). Indistinguishable from a
56
+ real encoding by charset statistics; stays as a medium/**low** finding.
57
+ - Typosquat distance-2 findings remain possible on short names; they emit
58
+ at `low` severity and cannot lower a grade.
@@ -0,0 +1,159 @@
1
+ # The `dsh-vet/v1` report contract
2
+
3
+ Status: **draft** — open for community input before v0.1 freezes it.
4
+ Reference TypeScript types: `src/contract.ts` (shipped from this package).
5
+
6
+ `dsh-vet/v1` defines a machine-readable audit report for a DeepSeek Harness
7
+ (DSH) plugin. It is implementation-agnostic: any scanner may emit it, and any
8
+ marketplace, CI job, or UI may consume it. It follows the community pattern
9
+ proven by `dsh-doctor/v1`: freeze a small, boring contract first, compete on
10
+ implementations.
11
+
12
+ ## Design rules
13
+
14
+ 1. **Findings are signals, not verdicts.** A report describes what code does.
15
+ It does not assert maliciousness, and it never names intent.
16
+ 2. **Confidence gates severity.** Only findings with confidence `medium` or
17
+ `high` may lower a grade. A `low`-confidence finding is reported but cannot
18
+ cost the audited plugin a grade — false positives must not damage authors.
19
+ 3. **Reports are deterministic.** `findings` are sorted (worst severity
20
+ first, then rule id ascending); `summary` is always derived from
21
+ `findings`, never asserted by the emitter. Two runs over the same artifact
22
+ with the same scanner version produce byte-identical reports (timestamps
23
+ aside).
24
+ 4. **Consumers must ignore unknown fields.** Emitters may attach extra fields
25
+ (encouraged to prefix them `x-`); consumers must tolerate them. Emitters
26
+ must not change the meaning of fields defined here.
27
+
28
+ ## Report envelope
29
+
30
+ ```jsonc
31
+ {
32
+ "schema": "dsh-vet/v1", // literal; required
33
+ "target": { /* what was audited */ },
34
+ "scanner": { /* who audited, when */ },
35
+ "summary": { /* derived grade + counts */ },
36
+ "findings": [ /* zero or more findings, sorted */ }
37
+ }
38
+ ```
39
+
40
+ ### `target`
41
+
42
+ | Field | Type | Notes |
43
+ |---|---|---|
44
+ | `kind` | `'npm-package' \| 'git-repo' \| 'local-path'` | |
45
+ | `specifier` | `string` | e.g. `dsh-vault@1.10.6`, a git URL, or a path |
46
+ | `resolved.version` | `string?` | version actually audited |
47
+ | `resolved.commit` | `string?` | for `git-repo` targets |
48
+ | `resolved.integrity` | `string?` | subresource integrity of the exact artifact scanned, when available |
49
+
50
+ ### `scanner`
51
+
52
+ | Field | Type | Notes |
53
+ |---|---|---|
54
+ | `name` | `string` | emitting implementation, e.g. `dsh-vet` |
55
+ | `version` | `string` | its version |
56
+ | `ranAt` | `string` | RFC 3339 timestamp of the run |
57
+
58
+ ### `finding`
59
+
60
+ | Field | Type | Notes |
61
+ |---|---|---|
62
+ | `id` | `string` | rule id; see below |
63
+ | `title` | `string` | one line, human |
64
+ | `severity` | `'critical' \| 'high' \| 'medium' \| 'low' \| 'info'` | |
65
+ | `confidence` | `'high' \| 'medium' \| 'low'` | |
66
+ | `evidence` | `VetEvidence[]` | at least one item |
67
+ | `remediation` | `string?` | how to make the finding go away |
68
+ | `references` | `string[]?` | URLs to rule documentation |
69
+
70
+ `VetEvidence` = `{ file, line?, snippet?, note? }`. Snippets must be minimal
71
+ and must never include secrets.
72
+
73
+ #### Severity definitions
74
+
75
+ - **critical** — direct credential/secret exfiltration, code execution during
76
+ install, or destructive filesystem operations outside any declared scope
77
+ - **high** — capability that contradicts the plugin's declared purpose
78
+ (undeclared broad fs/shell/net), obfuscated payloads, obvious typosquat
79
+ - **medium** — suspicious-but-explainable egress, `eval` of non-literal
80
+ input, install scripts without evident need
81
+ - **low** — hygiene: unpinned dependencies, missing license, unscoped
82
+ permissions in the manifest
83
+ - **info** — purely informational; never affects the grade
84
+
85
+ ### Rule ids
86
+
87
+ Pattern: `^[a-z0-9-]+(\.[a-z0-9-]+)+$` — two or more dot-separated lowercase
88
+ segments. Reference rules use two segments (`perm.broad-fs-write`,
89
+ `dep.postinstall-script`); third-party rule sets prefix their own vendor
90
+ segment (`acme.eval-detect`). The pattern is deliberately open-ended about
91
+ segment count: a closed single-segment pattern is exactly how `dsh-doctor/v1`
92
+ initially broke vendor-prefixed check ids — do not repeat that.
93
+
94
+ ### `summary`
95
+
96
+ `counts` = per-severity finding totals. `grade` is derived:
97
+
98
+ | Grade | Condition (over findings with confidence ≥ `medium`) |
99
+ |---|---|
100
+ | `A` | none, or only `info`/`low`-severity findings |
101
+ | `B` | worst graded finding is `low` |
102
+ | `C` | worst graded finding is `medium` |
103
+ | `D` | worst graded finding is `high` |
104
+ | `F` | at least one `critical` |
105
+ | `X` | scan incomplete or errored — never presented as the plugin's grade |
106
+
107
+ ## CLI recommendations (non-normative)
108
+
109
+ Emitters that ship a CLI should exit `0` whenever a report was produced —
110
+ findings are data, not failures — and non-zero only on scanner failure.
111
+ A `--strict` flag may exit `1` when findings of severity ≥ `high` with
112
+ confidence ≥ `medium` exist, for CI gating.
113
+
114
+ ## Example
115
+
116
+ ```json
117
+ {
118
+ "schema": "dsh-vet/v1",
119
+ "target": {
120
+ "kind": "npm-package",
121
+ "specifier": "fixture-plugin@1.2.3",
122
+ "resolved": { "version": "1.2.3", "integrity": "sha512-…" }
123
+ },
124
+ "scanner": { "name": "dsh-vet", "version": "0.1.0", "ranAt": "2026-09-01T00:00:00Z" },
125
+ "summary": { "grade": "C", "counts": { "critical": 0, "high": 0, "medium": 1, "low": 1, "info": 1 } },
126
+ "findings": [
127
+ {
128
+ "id": "egress.endpoint-reach",
129
+ "title": "Sends data to an endpoint from code that can read the DSH home directory",
130
+ "severity": "medium",
131
+ "confidence": "high",
132
+ "evidence": [{ "file": "host-half.js", "line": 42, "note": "fetch to https://example.example/collect" }],
133
+ "remediation": "Document the endpoint and what is sent, or remove the call."
134
+ },
135
+ {
136
+ "id": "dep.unpinned",
137
+ "title": "Dependency ranges are not pinned",
138
+ "severity": "low",
139
+ "confidence": "medium",
140
+ "evidence": [{ "file": "package.json" }]
141
+ },
142
+ {
143
+ "id": "meta.no-license",
144
+ "title": "No license field",
145
+ "severity": "info",
146
+ "confidence": "high",
147
+ "evidence": [{ "file": "package.json" }]
148
+ }
149
+ ]
150
+ }
151
+ ```
152
+
153
+ ## Versioning
154
+
155
+ `/v1` freezes at the v0.1 release of this package. Backward-compatible
156
+ additions (new optional fields, new rule ids) stay in `/v1`; semantic changes
157
+ get `/v2` with a migration note. Discussion happens in
158
+ [GitHub Discussions](https://github.com/rogerdigital/dsh-vet/discussions)
159
+ and the [dsh-plugin topic](https://github.com/topics/dsh-plugin).
@@ -0,0 +1,26 @@
1
+ # dep.floating-range
2
+
3
+ Runtime dependencies that resolve to whatever the registry serves.
4
+
5
+ ## What it looks for
6
+
7
+ `dependencies` entries whose spec is `*`, `latest`, `x`, or empty — anything
8
+ the registry can satisfy with a version that did not exist when the plugin
9
+ was last reviewed. `^` and `~` ranges are **not** flagged: they are normal
10
+ npm practice and stay within a reviewed major/minor.
11
+
12
+ Only `dependencies` are checked; `devDependencies` do not execute on the
13
+ installing user's machine.
14
+
15
+ ## Severity / confidence policy
16
+
17
+ **medium / high** — the spec's supply-chain tier.
18
+
19
+ ## False positives
20
+
21
+ Rare; the floating set is deliberately minimal. If an ecosystem convention
22
+ emerges around another spec form, it gets listed here.
23
+
24
+ ## Remediation
25
+
26
+ Pin to an exact version or a narrow range.
@@ -0,0 +1,26 @@
1
+ # dep.install-scripts
2
+
3
+ Runs code at install time.
4
+
5
+ ## What it looks for
6
+
7
+ `preinstall`, `install`, and `postinstall` entries in package.json
8
+ `scripts`. These execute on the **installing** user's machine, before the
9
+ plugin has been audited or sandboxed by anything.
10
+
11
+ ## Severity / confidence policy
12
+
13
+ **medium / high.** Presence is a fact from the manifest. The spec calls
14
+ install scripts without evident need *medium*; static analysis cannot see
15
+ "need", so the finding carries a remediation instead of a guess.
16
+
17
+ ## False positives
18
+
19
+ Genuine build-from-source cases (native modules). The remediation documents
20
+ the expectation: explain the script in the README, or move the work to
21
+ publish time.
22
+
23
+ ## Remediation
24
+
25
+ Remove the lifecycle script, or move the work to a prepublish/build step and
26
+ document why install-time execution is required.
@@ -0,0 +1,30 @@
1
+ # dep.typosquat-proximity
2
+
3
+ Dependency name is one or two edits from a popular `dsh-*` package.
4
+
5
+ ## What it looks for
6
+
7
+ Each runtime dependency is compared (bounded Levenshtein distance) against a
8
+ curated list of popular `dsh-*` package names. Every list entry is verified
9
+ to exist on npm — a name that cannot be installed cannot be typosquatted —
10
+ and the list is re-checked during calibration sweeps. Exact matches to a
11
+ popular name are skipped.
12
+
13
+ ## Severity / confidence policy
14
+
15
+ - Distance 1 (one insert/delete/substitute away): **high / medium.**
16
+ - Distance 2: **low / medium** — reported, cannot lower a grade.
17
+
18
+ Confidence stays medium because proximity is a heuristic, not proof: the
19
+ dependency's repository, publisher, and download history decide.
20
+
21
+ ## False positives
22
+
23
+ Legitimate adjacent names (a monorepo's `-cli`/`-core` companions, or a name
24
+ that happens to differ by one edit). Dispute with the package's provenance
25
+ and, if the name is established, it joins the popular list.
26
+
27
+ ## Remediation
28
+
29
+ Verify the dependency is the package you mean — exact spelling, real
30
+ repository, real publisher.
@@ -0,0 +1,29 @@
1
+ # egress.outbound-endpoints
2
+
3
+ Outbound endpoints the code can contact.
4
+
5
+ ## What it looks for
6
+
7
+ Literal hosts and URLs passed to the network-client calls tracked by
8
+ `perm.network-client`. One evidence entry per unique endpoint (URLs reduce
9
+ to their host), sorted, with the first call site.
10
+
11
+ ## Severity / confidence policy
12
+
13
+ **info / high.** This rule is inventory, never a judgment: contacting a host
14
+ is not wrong, but every host is a data-flow decision the author owns, and
15
+ consumers of the report deserve the list. Grading is left to
16
+ `egress.secret-adjacent` when endpoints meet secret reads.
17
+
18
+ Endpoints with non-literal (runtime-computed) targets cannot be listed —
19
+ that gap shows up as `obf.dynamic-require`-style opacity elsewhere.
20
+
21
+ ## False positives
22
+
23
+ Endpoints that are never contacted in practice (dead code paths) still
24
+ appear; reachability from an entry point is visible in the report via
25
+ `perm.unreachable-files`.
26
+
27
+ ## Remediation
28
+
29
+ Document each endpoint in the README.
@@ -0,0 +1,31 @@
1
+ # egress.secret-adjacent
2
+
3
+ Network calls reachable from code that reads secrets.
4
+
5
+ ## What it looks for
6
+
7
+ Files that read secrets — `process.env`, `os.homedir()`, or credential-like
8
+ file paths (`.env*`, `credentials`, `auth.json`, `token`, `.dsh/`, `.ssh`,
9
+ `.npmrc`, `.netrc`) — and any network-client call in that file or in files
10
+ statically reachable **from** it.
11
+
12
+ ## Severity / confidence policy
13
+
14
+ - **high** severity. Secrets plus egress in one static graph is the shape of
15
+ exfiltration; it is also the shape of every API client that authenticates,
16
+ so severity stays at high, not critical — critical requires proof of data
17
+ flow, which static analysis does not have.
18
+ - Confidence **medium** when the same file reads a secret and calls a
19
+ literal endpoint (the common, explainable API-client case); **low** when
20
+ the connection only exists through imports.
21
+
22
+ ## False positives
23
+
24
+ Any legitimate authenticated client. This finding is the report's most
25
+ important *signal, not verdict* case: read the endpoints, decide if the data
26
+ flow matches the plugin's documented purpose.
27
+
28
+ ## Remediation
29
+
30
+ Keep secret reads and network clients in separate modules, or document the
31
+ intended data flow for each endpoint.
@@ -0,0 +1,24 @@
1
+ # obf.charcode-chain
2
+
3
+ Builds strings from character codes.
4
+
5
+ ## What it looks for
6
+
7
+ `String.fromCharCode(...)` calls with 8+ numeric literal arguments. The
8
+ finding's note shows the decoded preview, so the reviewer sees what is being
9
+ hidden from grep.
10
+
11
+ ## Severity / confidence policy
12
+
13
+ **medium / medium.** Building a readable string through charcodes is not a
14
+ normal authoring pattern; there is a credible benign population (obfuscating
15
+ game spoilers, license checks) small enough to justify the confidence.
16
+
17
+ ## False positives
18
+
19
+ Table-driven encoders (font subsets, protocol codecs) that feed arrays, not
20
+ inline literals — the literal-argument requirement excludes most of them.
21
+
22
+ ## Remediation
23
+
24
+ Write the string literal directly.
@@ -0,0 +1,27 @@
1
+ # obf.dynamic-require
2
+
3
+ Loads modules through a computed specifier.
4
+
5
+ ## What it looks for
6
+
7
+ `require(x)` and `import(x)` where `x` is not a plain string literal —
8
+ runtime variables, or concatenations that static evaluation can recover
9
+ (`'./mod-' + 'ule.js'`).
10
+
11
+ ## Severity / confidence policy
12
+
13
+ - Concatenation that statically resolves to one value: **medium / medium.**
14
+ The loaded module is knowable, but the graph hides from naive review.
15
+ - Fully runtime-dependent specifier: **medium / low** (ROADMAP D2 — depends
16
+ on runtime values, so it never lowers a grade by itself).
17
+
18
+ ## False positives
19
+
20
+ Optional-dependency loaders (`require(name)` in a try/catch) and plugin
21
+ systems that resolve names from config. Those are exactly the patterns the
22
+ finding asks you to make visible — the specifier is recoverable, say it in
23
+ the source.
24
+
25
+ ## Remediation
26
+
27
+ Use plain specifiers so the module graph stays auditable.
@@ -0,0 +1,29 @@
1
+ # obf.encoded-payload
2
+
3
+ Long base64/hex string literals in shipped code.
4
+
5
+ ## What it looks for
6
+
7
+ String literals of 48+ characters with no whitespace whose entire charset is
8
+ base64 (letters + digits + `/` `=`, actually mixing cases with digits) or hex
9
+ — and for hex, using at least 8 distinct characters. Palette and table data
10
+ (think `8888…`, d3 category colors) reuse a handful of digits; encoded
11
+ payloads use the alphabet broadly. The diversity floor was calibrated on the
12
+ v0.1 ecosystem sweep ([calibration record](../calibration-v0.1.md)).
13
+
14
+ ## Severity / confidence policy
15
+
16
+ **medium / low.** An encoded blob is often legitimate (fonts, hashes,
17
+ fixtures) — hence low confidence: reported, never grade-affecting on its own.
18
+ It matters when it sits next to `eval` or egress, which is exactly what a
19
+ reviewer checks next.
20
+
21
+ ## False positives
22
+
23
+ Sourcemaps-in-strings, embedded assets, test vectors. The finding is
24
+ inventory for reviewers; a blob with a visible decoder gets disputed in one
25
+ message.
26
+
27
+ ## Remediation
28
+
29
+ Decode payloads to plain assets, or ship them as files a reviewer can open.
@@ -0,0 +1,26 @@
1
+ # obf.eval-detect
2
+
3
+ Evaluates dynamically built code.
4
+
5
+ ## What it looks for
6
+
7
+ `eval(...)` calls and `new Function(...)` constructions in shipped JS.
8
+
9
+ ## Severity / confidence policy
10
+
11
+ - Argument is not a string literal (variable, concatenation, decoding result):
12
+ **medium / medium** — the spec's "eval of non-literal input" tier.
13
+ - Argument is a constant string: **info / high** — inert (the code is visible
14
+ in the literal), still reported because eval-shaped code is where payload
15
+ injection starts.
16
+
17
+ ## False positives
18
+
19
+ DSLs and template engines that genuinely need runtime compilation. Declare
20
+ the case in your README; if the pattern is common enough, the rule learns a
21
+ documented exception.
22
+
23
+ ## Remediation
24
+
25
+ Replace `eval`/`new Function` with direct code; dynamic evaluation defeats
26
+ static audit.
@@ -0,0 +1,25 @@
1
+ # obf.unparseable
2
+
3
+ Shipped JS files that no standard parser accepts.
4
+
5
+ ## What it looks for
6
+
7
+ Files with a `.js`/`.mjs`/`.cjs` extension (or Node-shebang bin scripts) that
8
+ fail to parse both as an ECMAScript module and as a script under acorn.
9
+
10
+ ## Severity / confidence policy
11
+
12
+ **medium / high.** That the file does not parse is certain; what it actually
13
+ runs is not. Shipped-unparseable JS usually means generated or deliberately
14
+ mangled code that plain review tooling cannot see into — the finding asks
15
+ for source that reviewers can read.
16
+
17
+ ## False positives
18
+
19
+ New syntax acorn has not learned yet (bleeding-edge proposals), or files
20
+ that are data with a misleading extension. Dispute with the syntax; the
21
+ finder tracks acorn's version.
22
+
23
+ ## Remediation
24
+
25
+ Ship parseable source, or source maps that let tooling see the real code.