dsh-vet 0.2.0 → 0.2.2
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/README.md +2 -0
- package/docs/rules/perm.undeclared-fs-write.md +13 -2
- package/docs/rules/scan.empty-audit.md +27 -0
- package/lib/index.d.mts +1 -1
- package/lib/index.mjs +32 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# dsh-vet
|
|
2
2
|
|
|
3
|
+
[](https://github.com/rogerdigital/dsh-vet/blob/main/.dsh-vet/report.json)
|
|
4
|
+
|
|
3
5
|
Security vetting for DeepSeek Harness (DSH) plugins: permission & supply-chain
|
|
4
6
|
audits before install, graded via the open [`dsh-vet/v1`](docs/dsh-vet-v1.md)
|
|
5
7
|
report standard.
|
|
@@ -17,8 +17,19 @@ in-scope paths are not.
|
|
|
17
17
|
for destructive operations outside any declared scope.
|
|
18
18
|
- Non-destructive writes with a literal out-of-scope absolute path:
|
|
19
19
|
**high / high.**
|
|
20
|
-
- Writes whose target is a runtime value: **
|
|
21
|
-
|
|
20
|
+
- Writes whose target is a runtime value: **low / low** — reported as
|
|
21
|
+
"scope not statically verifiable", never as an out-of-scope claim the
|
|
22
|
+
scanner cannot make, and never grade-affecting (ROADMAP D2). Normal
|
|
23
|
+
plugins write dynamic paths constantly; this tier is a review prompt,
|
|
24
|
+
not an accusation.
|
|
25
|
+
|
|
26
|
+
The runtime-value tier deliberately does **not** attempt the path-derivation
|
|
27
|
+
dataflow some disputes have asked for: constraint chains are usually
|
|
28
|
+
interprocedural (derive-and-validate in one function, delete in another),
|
|
29
|
+
beyond best-effort static analysis. The honest position is "unresolvable —
|
|
30
|
+
review the derivation", which is what the finding now says. Clarified via
|
|
31
|
+
[dispute #10](https://github.com/rogerdigital/dsh-vet/issues/10), whose
|
|
32
|
+
guarded managed-directory removal is the canonical well-reviewed case.
|
|
22
33
|
|
|
23
34
|
## False positives
|
|
24
35
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# scan.empty-audit
|
|
2
|
+
|
|
3
|
+
No analyzable JavaScript found in the target.
|
|
4
|
+
|
|
5
|
+
## What it looks for
|
|
6
|
+
|
|
7
|
+
The scanner found zero `.js`/`.mjs`/`.cjs` files (or Node-shebang bin
|
|
8
|
+
scripts) in the audited tree. This rule fires on behalf of the report's
|
|
9
|
+
readers: a scan that audited nothing must not read as a clean pass.
|
|
10
|
+
|
|
11
|
+
## Severity / confidence policy
|
|
12
|
+
|
|
13
|
+
**medium / high.** Nothing was analyzed — that is a fact, and a grade of A
|
|
14
|
+
here would be vacuous. Found live during v0.2 activation: a
|
|
15
|
+
TypeScript-source repository scanned as a local path ships no `.js` in its
|
|
16
|
+
working tree, so `.` audits nothing while the badge reads green.
|
|
17
|
+
|
|
18
|
+
## False positives
|
|
19
|
+
|
|
20
|
+
Targets that genuinely contain no JavaScript at all (a data package, a
|
|
21
|
+
documentation bundle). If that is you, the finding is still correct — you
|
|
22
|
+
are advertising an audit that cannot say anything.
|
|
23
|
+
|
|
24
|
+
## Remediation
|
|
25
|
+
|
|
26
|
+
Audit what actually ships: pass the npm package specifier (or a directory
|
|
27
|
+
containing the built output) instead of the source tree.
|
package/lib/index.d.mts
CHANGED
|
@@ -271,7 +271,7 @@ declare function runRules(analysis: Analysis, only?: string[]): VetFinding[];
|
|
|
271
271
|
//#endregion
|
|
272
272
|
//#region src/scanner.d.ts
|
|
273
273
|
/** Kept in lockstep with package.json; a test asserts they match. */
|
|
274
|
-
declare const SCANNER_VERSION = "0.2.
|
|
274
|
+
declare const SCANNER_VERSION = "0.2.2";
|
|
275
275
|
interface ScanOptions extends ResolveOptions {
|
|
276
276
|
/** Injectable clock for deterministic tests/reports. */
|
|
277
277
|
now?: () => string;
|
package/lib/index.mjs
CHANGED
|
@@ -1463,14 +1463,16 @@ const undeclaredFsWrite = {
|
|
|
1463
1463
|
}));
|
|
1464
1464
|
}
|
|
1465
1465
|
if (dynamic.length > 0) findings.push(finding(this, {
|
|
1466
|
-
|
|
1466
|
+
title: "Write/delete target is a runtime value — scope not statically verifiable",
|
|
1467
|
+
severity: "low",
|
|
1467
1468
|
confidence: "low",
|
|
1468
1469
|
evidence: capEvidence(dynamic.map((use) => ({
|
|
1469
1470
|
file: use.file,
|
|
1470
1471
|
line: use.line,
|
|
1471
|
-
snippet: use.snippet
|
|
1472
|
+
snippet: use.snippet,
|
|
1473
|
+
note: "review how the target is derived; the scanner cannot resolve it statically"
|
|
1472
1474
|
})), 5),
|
|
1473
|
-
remediation: "
|
|
1475
|
+
remediation: "Keep the derivation next to the call, test the invariant (fixed child of a validated root), and document the managed scope so reviewers can verify it.",
|
|
1474
1476
|
references: ["https://github.com/rogerdigital/dsh-vet/blob/main/docs/rules/perm.undeclared-fs-write.md"]
|
|
1475
1477
|
}));
|
|
1476
1478
|
return findings;
|
|
@@ -1601,9 +1603,31 @@ function runRules(analysis, only) {
|
|
|
1601
1603
|
* over the same artifact with the same version are identical.
|
|
1602
1604
|
*/
|
|
1603
1605
|
/** Kept in lockstep with package.json; a test asserts they match. */
|
|
1604
|
-
const SCANNER_VERSION = "0.2.
|
|
1606
|
+
const SCANNER_VERSION = "0.2.2";
|
|
1607
|
+
/**
|
|
1608
|
+
* A scan that audited zero JavaScript files must not read as a clean pass —
|
|
1609
|
+
* a TypeScript source tree scanned as a local path has no `.js` to analyze,
|
|
1610
|
+
* and silence there would be an A-by-vacuity. (Found live during v0.2
|
|
1611
|
+
* activation; docs/rules/scan.empty-audit.md.)
|
|
1612
|
+
*/
|
|
1613
|
+
function emptyAuditFinding(targetSpecifier) {
|
|
1614
|
+
return {
|
|
1615
|
+
id: "scan.empty-audit",
|
|
1616
|
+
title: "No analyzable JavaScript found in the target",
|
|
1617
|
+
severity: "medium",
|
|
1618
|
+
confidence: "high",
|
|
1619
|
+
evidence: [{
|
|
1620
|
+
file: ".",
|
|
1621
|
+
note: `zero .js/.mjs/.cjs files (or Node-shebang bin scripts) found under ${targetSpecifier}`
|
|
1622
|
+
}],
|
|
1623
|
+
remediation: "Audit what actually ships: pass the npm package specifier (or a directory containing the built output) instead of the source tree.",
|
|
1624
|
+
references: ["https://github.com/rogerdigital/dsh-vet/blob/main/docs/rules/scan.empty-audit.md"]
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1605
1627
|
async function scanDirectory(dir, options = {}) {
|
|
1606
1628
|
const analysis = analyze(dir);
|
|
1629
|
+
const findings = options.rules ? runRules(analysis, options.rules) : runRules(analysis);
|
|
1630
|
+
if (analysis.files.length === 0) findings.push(emptyAuditFinding(dir));
|
|
1607
1631
|
return createReport({
|
|
1608
1632
|
target: {
|
|
1609
1633
|
kind: "local-path",
|
|
@@ -1614,13 +1638,15 @@ async function scanDirectory(dir, options = {}) {
|
|
|
1614
1638
|
version: SCANNER_VERSION,
|
|
1615
1639
|
ranAt: options.now?.() ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1616
1640
|
},
|
|
1617
|
-
findings
|
|
1641
|
+
findings
|
|
1618
1642
|
});
|
|
1619
1643
|
}
|
|
1620
1644
|
async function scan(specifier, options = {}) {
|
|
1621
1645
|
const resolved = await resolveTarget(specifier, options);
|
|
1622
1646
|
try {
|
|
1623
1647
|
const analysis = analyze(resolved.rootDir);
|
|
1648
|
+
const findings = options.rules ? runRules(analysis, options.rules) : runRules(analysis);
|
|
1649
|
+
if (analysis.files.length === 0) findings.push(emptyAuditFinding(specifier));
|
|
1624
1650
|
return createReport({
|
|
1625
1651
|
target: resolved.target,
|
|
1626
1652
|
scanner: {
|
|
@@ -1628,7 +1654,7 @@ async function scan(specifier, options = {}) {
|
|
|
1628
1654
|
version: SCANNER_VERSION,
|
|
1629
1655
|
ranAt: options.now?.() ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1630
1656
|
},
|
|
1631
|
-
findings
|
|
1657
|
+
findings
|
|
1632
1658
|
});
|
|
1633
1659
|
} finally {
|
|
1634
1660
|
resolved.cleanup();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vet",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Security vetting for DeepSeek Harness (DSH) plugins: permission & supply-chain audits before install, graded via the open dsh-vet/v1 report standard.",
|
|
5
5
|
"dsh": {
|
|
6
6
|
"seams": ["fs", "shell", "web"]
|