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 +21 -0
- package/README.md +136 -0
- package/bin/dsh-vet.mjs +7 -0
- package/docs/calibration-v0.1.md +58 -0
- package/docs/dsh-vet-v1.md +159 -0
- package/docs/rules/dep.floating-range.md +26 -0
- package/docs/rules/dep.install-scripts.md +26 -0
- package/docs/rules/dep.typosquat-proximity.md +30 -0
- package/docs/rules/egress.outbound-endpoints.md +29 -0
- package/docs/rules/egress.secret-adjacent.md +31 -0
- package/docs/rules/obf.charcode-chain.md +24 -0
- package/docs/rules/obf.dynamic-require.md +27 -0
- package/docs/rules/obf.encoded-payload.md +29 -0
- package/docs/rules/obf.eval-detect.md +26 -0
- package/docs/rules/obf.unparseable.md +25 -0
- package/docs/rules/perm.network-client.md +27 -0
- package/docs/rules/perm.seam-mismatch.md +34 -0
- package/docs/rules/perm.subprocess-spawn.md +28 -0
- package/docs/rules/perm.undeclared-fs-write.md +32 -0
- package/docs/rules/perm.unreachable-files.md +26 -0
- package/lib/index.d.mts +297 -0
- package/lib/index.mjs +1741 -0
- package/package.json +64 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# perm.network-client
|
|
2
|
+
|
|
3
|
+
Opens network connections.
|
|
4
|
+
|
|
5
|
+
## What it looks for
|
|
6
|
+
|
|
7
|
+
Client-side network calls: `http(s).request/get`, `net.connect`,
|
|
8
|
+
`net.createConnection`, `tls.connect`, `dns.lookup/resolve`,
|
|
9
|
+
`dgram.createSocket`, global `fetch`, `new WebSocket()`.
|
|
10
|
+
|
|
11
|
+
## Severity / confidence policy
|
|
12
|
+
|
|
13
|
+
- Client calls present, no `"web"` seam declared: **medium / high.**
|
|
14
|
+
- `"web"` declared in `dsh.seams`: **info / high** — expected capability,
|
|
15
|
+
reported for inventory (the endpoints themselves are listed by
|
|
16
|
+
`egress.outbound-endpoints`).
|
|
17
|
+
- Network modules imported with no observable client call: **info / high.**
|
|
18
|
+
|
|
19
|
+
## False positives
|
|
20
|
+
|
|
21
|
+
Server-only listeners (`http.createServer`) are intentionally not counted.
|
|
22
|
+
Transitive network use inside dependencies is not attributed to the plugin
|
|
23
|
+
here — dependency scanning is the `dep.*` family's business.
|
|
24
|
+
|
|
25
|
+
## Remediation
|
|
26
|
+
|
|
27
|
+
Declare the web seam in `dsh.seams`, or drop the network access.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# perm.seam-mismatch
|
|
2
|
+
|
|
3
|
+
Capability used by the plugin's code but not declared in `dsh.seams`.
|
|
4
|
+
|
|
5
|
+
## What it looks for
|
|
6
|
+
|
|
7
|
+
Seams are declared in package.json:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{ "dsh": { "seams": ["fs", "web"] } }
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Detected capabilities map to seams: `fs`/`fs-write` → `fs`, `child_process` →
|
|
14
|
+
`shell`, net/http/fetch → `web`, `worker_threads` → `workers`. When a
|
|
15
|
+
declaration exists and a detected capability's seam is missing from it, this
|
|
16
|
+
rule reports the first use site per missing seam.
|
|
17
|
+
|
|
18
|
+
## Severity / confidence policy
|
|
19
|
+
|
|
20
|
+
- **medium / medium.** The capability demonstrably exists; the declaration
|
|
21
|
+
demonstrably omits it. Both are facts; the mismatch is the interpretation.
|
|
22
|
+
- When **no** `dsh.seams` declaration exists at all, this rule stays silent:
|
|
23
|
+
an absent declaration is not a claim, and per-capability rules
|
|
24
|
+
(`perm.subprocess-spawn`, `perm.network-client`) speak instead.
|
|
25
|
+
|
|
26
|
+
## False positives
|
|
27
|
+
|
|
28
|
+
A plugin that computes its seam set at runtime or documents capabilities in
|
|
29
|
+
README prose only. Dispute with evidence of the declaration and the rule will
|
|
30
|
+
be taught that form.
|
|
31
|
+
|
|
32
|
+
## Remediation
|
|
33
|
+
|
|
34
|
+
Add the seam to `dsh.seams`, or drop the capability if it is not needed.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# perm.subprocess-spawn
|
|
2
|
+
|
|
3
|
+
Spawns subprocesses.
|
|
4
|
+
|
|
5
|
+
## What it looks for
|
|
6
|
+
|
|
7
|
+
`child_process` call sites: `spawn`, `exec`, `execFile`, `fork` and their
|
|
8
|
+
`*Sync` variants, reached through any import style (ESM named/default, CJS
|
|
9
|
+
`require`, destructuring).
|
|
10
|
+
|
|
11
|
+
## Severity / confidence policy
|
|
12
|
+
|
|
13
|
+
- Spawn call sites present, no `"shell"` seam declared: **medium / high.**
|
|
14
|
+
The call is a fact; whether it is justified is the reader's call.
|
|
15
|
+
- `"shell"` declared in `dsh.seams`: **info / high** — expected capability,
|
|
16
|
+
reported for inventory.
|
|
17
|
+
- `child_process` imported but never called: **info / high** (dead import,
|
|
18
|
+
not an exercised capability).
|
|
19
|
+
|
|
20
|
+
## False positives
|
|
21
|
+
|
|
22
|
+
Plugins whose whole purpose is running commands (a shell helper, a runner).
|
|
23
|
+
Declaring the `shell` seam turns the finding into inventory; the honest fix
|
|
24
|
+
is the declaration, not the report's silence.
|
|
25
|
+
|
|
26
|
+
## Remediation
|
|
27
|
+
|
|
28
|
+
Declare the shell seam in `dsh.seams`, or avoid spawning processes.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# perm.undeclared-fs-write
|
|
2
|
+
|
|
3
|
+
Writes or deletes files outside any plausible plugin scope.
|
|
4
|
+
|
|
5
|
+
## What it looks for
|
|
6
|
+
|
|
7
|
+
Calls to write/delete filesystem APIs (`writeFile`, `rm`, `unlink`,
|
|
8
|
+
`truncate`, `rename`, …) in shipped JS. Literal absolute paths **outside** the
|
|
9
|
+
plausible scope — the temp directory and the DSH home subtree
|
|
10
|
+
(`~/.dsh`, `~/.config/dsh`, `~/.cache/dsh`) — are flagged. Relative paths and
|
|
11
|
+
in-scope paths are not.
|
|
12
|
+
|
|
13
|
+
## Severity / confidence policy
|
|
14
|
+
|
|
15
|
+
- Destructive APIs (`rm`, `unlink`, `rmdir`, `truncate`) with a literal
|
|
16
|
+
out-of-scope absolute path: **critical / high** — the spec reserves critical
|
|
17
|
+
for destructive operations outside any declared scope.
|
|
18
|
+
- Non-destructive writes with a literal out-of-scope absolute path:
|
|
19
|
+
**high / high.**
|
|
20
|
+
- Writes whose target is a runtime value: **medium / low** (ROADMAP D2 —
|
|
21
|
+
runtime-dependent findings never lower a grade on their own).
|
|
22
|
+
|
|
23
|
+
## False positives
|
|
24
|
+
|
|
25
|
+
A plugin that legitimately manages files elsewhere on disk (a backup tool,
|
|
26
|
+
say). The scope list is deliberately tiny; dispute with the use case and the
|
|
27
|
+
scope list gets a documented entry, not a silent exception.
|
|
28
|
+
|
|
29
|
+
## Remediation
|
|
30
|
+
|
|
31
|
+
Keep writes inside the workspace or the DSH home directory; derive paths from
|
|
32
|
+
configuration instead of literals.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# perm.unreachable-files
|
|
2
|
+
|
|
3
|
+
Shipped files not reachable from any declared entry point.
|
|
4
|
+
|
|
5
|
+
## What it looks for
|
|
6
|
+
|
|
7
|
+
The module graph starts from `main`/`module`/`exports`/`bin` entry points and
|
|
8
|
+
follows static imports and literal `require`/`import()`. Files the graph never
|
|
9
|
+
reaches are listed.
|
|
10
|
+
|
|
11
|
+
## Severity / confidence policy
|
|
12
|
+
|
|
13
|
+
**info / high** — never affects a grade. Unreachable is not malicious: dead
|
|
14
|
+
code, fixtures, and dynamic-graph plugins all produce it. It is reported
|
|
15
|
+
because shipped-but-unreachable files can still be `require`d at runtime by
|
|
16
|
+
design or by an attacker, and a reviewer should know they exist.
|
|
17
|
+
|
|
18
|
+
## False positives
|
|
19
|
+
|
|
20
|
+
Plugins that build their module graph from config strings at runtime. This is
|
|
21
|
+
the same population as `obf.dynamic-require`; the fix there (plain
|
|
22
|
+
specifiers) fixes the noise here.
|
|
23
|
+
|
|
24
|
+
## Remediation
|
|
25
|
+
|
|
26
|
+
Delete dead files or wire them into the import graph.
|
package/lib/index.d.mts
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { Node } from "acorn";
|
|
3
|
+
//#region src/contract.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The `dsh-vet/v1` report contract: a machine-readable, implementation-
|
|
6
|
+
* agnostic audit report for DeepSeek Harness (DSH) plugins. Any scanner may
|
|
7
|
+
* emit it; any marketplace, CI job, or UI may consume it. See
|
|
8
|
+
* `docs/dsh-vet-v1.md` for the normative spec.
|
|
9
|
+
*
|
|
10
|
+
* Two design rules are encoded here, not just documented:
|
|
11
|
+
* - Findings are signals, not verdicts: a finding with `low` confidence never
|
|
12
|
+
* lowers a grade, and `info` severity never does either.
|
|
13
|
+
* - Reports are deterministic: findings are sorted, and the summary is always
|
|
14
|
+
* derived, never asserted by the emitter.
|
|
15
|
+
*
|
|
16
|
+
* @module dsh-vet/contract
|
|
17
|
+
*/
|
|
18
|
+
/** Literal `schema` value every dsh-vet/v1 report carries. */
|
|
19
|
+
declare const SCHEMA_ID: "dsh-vet/v1";
|
|
20
|
+
/** How bad a finding is. `info` never affects a grade. */
|
|
21
|
+
type VetSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
22
|
+
/** How sure the scanner is that the finding is real. `low` never affects a grade. */
|
|
23
|
+
type VetConfidence = 'high' | 'medium' | 'low';
|
|
24
|
+
/** Grade derived from graded findings. `X` marks an incomplete or errored scan. */
|
|
25
|
+
type VetGrade = 'A' | 'B' | 'C' | 'D' | 'F' | 'X';
|
|
26
|
+
interface VetEvidence {
|
|
27
|
+
/** Path within the audited package, e.g. `host-half.js`. */
|
|
28
|
+
file: string;
|
|
29
|
+
/** 1-based line of the evidence, when known. */
|
|
30
|
+
line?: number;
|
|
31
|
+
/** Minimal verbatim snippet; never include secrets. */
|
|
32
|
+
snippet?: string;
|
|
33
|
+
/** What the evidence shows, one sentence. */
|
|
34
|
+
note?: string;
|
|
35
|
+
}
|
|
36
|
+
interface VetFinding {
|
|
37
|
+
/**
|
|
38
|
+
* Namespaced rule id, e.g. `perm.broad-fs-write` or a vendor-prefixed
|
|
39
|
+
* `acme.eval-detect`. Must match {@link RULE_ID_PATTERN}; two or more
|
|
40
|
+
* dot-separated segments so third-party rule sets never collide.
|
|
41
|
+
*/
|
|
42
|
+
id: string;
|
|
43
|
+
/** One-line human title, e.g. `Writes outside the declared workspace`. */
|
|
44
|
+
title: string;
|
|
45
|
+
severity: VetSeverity;
|
|
46
|
+
confidence: VetConfidence;
|
|
47
|
+
evidence: VetEvidence[];
|
|
48
|
+
/** How to make the finding go away, when actionable. */
|
|
49
|
+
remediation?: string;
|
|
50
|
+
/** URLs to rule documentation. */
|
|
51
|
+
references?: string[];
|
|
52
|
+
}
|
|
53
|
+
type VetTargetKind = 'npm-package' | 'git-repo' | 'local-path';
|
|
54
|
+
interface VetTarget {
|
|
55
|
+
kind: VetTargetKind;
|
|
56
|
+
/** What was audited, e.g. `dsh-vault@1.10.6`, a git URL, or a path. */
|
|
57
|
+
specifier: string;
|
|
58
|
+
/** What the specifier resolved to, when applicable. */
|
|
59
|
+
resolved?: {
|
|
60
|
+
version?: string;
|
|
61
|
+
commit?: string;
|
|
62
|
+
/** Subresource integrity of the exact artifact that was scanned. */
|
|
63
|
+
integrity?: string;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
interface VetScanner {
|
|
67
|
+
/** Emitting implementation, e.g. `dsh-vet`. */
|
|
68
|
+
name: string;
|
|
69
|
+
version: string;
|
|
70
|
+
/** RFC 3339 timestamp of the run. */
|
|
71
|
+
ranAt: string;
|
|
72
|
+
}
|
|
73
|
+
interface VetSummaryCounts {
|
|
74
|
+
critical: number;
|
|
75
|
+
high: number;
|
|
76
|
+
medium: number;
|
|
77
|
+
low: number;
|
|
78
|
+
info: number;
|
|
79
|
+
}
|
|
80
|
+
interface VetSummary {
|
|
81
|
+
grade: VetGrade;
|
|
82
|
+
counts: VetSummaryCounts;
|
|
83
|
+
}
|
|
84
|
+
interface VetReport {
|
|
85
|
+
readonly schema: typeof SCHEMA_ID;
|
|
86
|
+
readonly target: VetTarget;
|
|
87
|
+
readonly scanner: VetScanner;
|
|
88
|
+
readonly summary: VetSummary;
|
|
89
|
+
readonly findings: readonly VetFinding[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Rule ids are two or more dot-separated lowercase segments
|
|
93
|
+
* (`perm.broad-fs-write`, `acme.eval-detect`). Deliberately open-ended about
|
|
94
|
+
* segment count so vendor-prefixed rule sets work — a closed single-segment
|
|
95
|
+
* pattern is how the dsh-doctor contract initially broke third-party ids.
|
|
96
|
+
*/
|
|
97
|
+
declare const RULE_ID_PATTERN: RegExp;
|
|
98
|
+
/** True when a finding participates in grading: not `info`, not low-confidence. */
|
|
99
|
+
declare function isGraded(finding: VetFinding): boolean;
|
|
100
|
+
/** Per-severity finding totals. */
|
|
101
|
+
declare function countFindings(findings: readonly VetFinding[]): VetSummaryCounts;
|
|
102
|
+
/**
|
|
103
|
+
* Derive the grade from graded findings only: worst severity decides.
|
|
104
|
+
* `low`-confidence and `info` findings are reported but never lower a grade.
|
|
105
|
+
*/
|
|
106
|
+
declare function gradeFor(findings: readonly VetFinding[]): Exclude<VetGrade, 'X'>;
|
|
107
|
+
interface CreateReportInput {
|
|
108
|
+
target: VetTarget;
|
|
109
|
+
scanner: VetScanner;
|
|
110
|
+
findings: readonly VetFinding[];
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Build a normalized report: validates rule ids, sorts findings
|
|
114
|
+
* deterministically (worst severity first, then id ascending), and derives
|
|
115
|
+
* the summary. Emitters must go through this instead of assembling a report
|
|
116
|
+
* by hand — it is what keeps two runs over the same artifact identical.
|
|
117
|
+
*/
|
|
118
|
+
declare function createReport(input: CreateReportInput): VetReport;
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/analyze.d.ts
|
|
121
|
+
type Capability = 'fs' | 'fs-write' | 'shell' | 'net' | 'workers' | 'crypto' | 'env' | 'homedir' | 'secret-read';
|
|
122
|
+
interface CapabilityUse {
|
|
123
|
+
cap: Capability;
|
|
124
|
+
file: string;
|
|
125
|
+
line: number;
|
|
126
|
+
api: string;
|
|
127
|
+
snippet: string;
|
|
128
|
+
/** Literal argument strings at the call site, when statically known. */
|
|
129
|
+
literals: string[];
|
|
130
|
+
}
|
|
131
|
+
interface NetUse {
|
|
132
|
+
file: string;
|
|
133
|
+
line: number;
|
|
134
|
+
api: string;
|
|
135
|
+
literals: string[];
|
|
136
|
+
snippet: string;
|
|
137
|
+
}
|
|
138
|
+
interface EvalUse {
|
|
139
|
+
file: string;
|
|
140
|
+
line: number;
|
|
141
|
+
kind: 'eval' | 'Function';
|
|
142
|
+
literal: boolean;
|
|
143
|
+
snippet: string;
|
|
144
|
+
}
|
|
145
|
+
interface DynamicImportUse {
|
|
146
|
+
file: string;
|
|
147
|
+
line: number;
|
|
148
|
+
kind: 'require' | 'import';
|
|
149
|
+
/** Statically concatenated argument value, when recoverable. */
|
|
150
|
+
literals: string[] | null;
|
|
151
|
+
snippet: string;
|
|
152
|
+
}
|
|
153
|
+
interface EncodedLiteral {
|
|
154
|
+
file: string;
|
|
155
|
+
line: number;
|
|
156
|
+
value: string;
|
|
157
|
+
charset: 'base64' | 'hex';
|
|
158
|
+
}
|
|
159
|
+
interface CharcodeCall {
|
|
160
|
+
file: string;
|
|
161
|
+
line: number;
|
|
162
|
+
chars: string;
|
|
163
|
+
}
|
|
164
|
+
interface ImportRef {
|
|
165
|
+
specifier: string;
|
|
166
|
+
line: number;
|
|
167
|
+
}
|
|
168
|
+
interface SourceFile {
|
|
169
|
+
path: string;
|
|
170
|
+
code: string;
|
|
171
|
+
sourceType: 'module' | 'script' | null;
|
|
172
|
+
parseError: string | null;
|
|
173
|
+
ast: Node | null;
|
|
174
|
+
imports: ImportRef[];
|
|
175
|
+
/** Bare (non-relative) module specifiers imported by this file. */
|
|
176
|
+
externals: string[];
|
|
177
|
+
}
|
|
178
|
+
interface PkgJson {
|
|
179
|
+
raw: Record<string, unknown>;
|
|
180
|
+
name: string;
|
|
181
|
+
version: string;
|
|
182
|
+
scripts: Record<string, string>;
|
|
183
|
+
dependencies: Record<string, string>;
|
|
184
|
+
devDependencies: Record<string, string>;
|
|
185
|
+
/** Declared Cordis seams, read from `dsh.seams` in package.json. */
|
|
186
|
+
seams: string[] | null;
|
|
187
|
+
entryHints: string[];
|
|
188
|
+
}
|
|
189
|
+
interface Analysis {
|
|
190
|
+
rootDir: string;
|
|
191
|
+
pkg: PkgJson | null;
|
|
192
|
+
files: SourceFile[];
|
|
193
|
+
fileByPath: Map<string, SourceFile>;
|
|
194
|
+
entries: string[];
|
|
195
|
+
reachable: Set<string>;
|
|
196
|
+
unreachable: string[];
|
|
197
|
+
/** Static relative import edges: file → files it imports. */
|
|
198
|
+
edges: Map<string, string[]>;
|
|
199
|
+
capUses: CapabilityUse[];
|
|
200
|
+
netUses: NetUse[];
|
|
201
|
+
evalUses: EvalUse[];
|
|
202
|
+
dynamicImports: DynamicImportUse[];
|
|
203
|
+
encodedLiterals: EncodedLiteral[];
|
|
204
|
+
charcodeCalls: CharcodeCall[];
|
|
205
|
+
}
|
|
206
|
+
/** Analyze a package directory: parse, walk, and build the module graph. */
|
|
207
|
+
declare function analyze(rootDir: string): Analysis;
|
|
208
|
+
/** All files statically reachable from `path` (excluding the path itself). */
|
|
209
|
+
declare function reachableFrom(analysis: Analysis, path: string): Set<string>;
|
|
210
|
+
//#endregion
|
|
211
|
+
//#region src/resolve.d.ts
|
|
212
|
+
type SpecifierKind = VetTarget['kind'];
|
|
213
|
+
interface ResolveOptions {
|
|
214
|
+
/** Injectable fetch for tests; defaults to the global fetch. */
|
|
215
|
+
fetchImpl?: (url: string) => Promise<Response>;
|
|
216
|
+
/** Registry base, default `https://registry.npmjs.org`. */
|
|
217
|
+
registry?: string;
|
|
218
|
+
}
|
|
219
|
+
interface ResolvedTarget {
|
|
220
|
+
target: VetTarget;
|
|
221
|
+
/** Local directory holding the artifact to scan. */
|
|
222
|
+
rootDir: string;
|
|
223
|
+
/** Package-relative paths of files the tarball contained, when known. */
|
|
224
|
+
files?: string[];
|
|
225
|
+
/** Links found in the tarball that were deliberately not materialized. */
|
|
226
|
+
skippedLinks?: Array<{
|
|
227
|
+
path: string;
|
|
228
|
+
type: string;
|
|
229
|
+
target: string;
|
|
230
|
+
}>;
|
|
231
|
+
cleanup: () => void;
|
|
232
|
+
}
|
|
233
|
+
declare class VetError extends Error {}
|
|
234
|
+
/** Classify a specifier without any network access. */
|
|
235
|
+
declare function classifySpecifier(specifier: string, exists?: typeof existsSync): SpecifierKind;
|
|
236
|
+
/** Split `name`, `name@version`, `@scope/name`, `@scope/name@tag|version`. */
|
|
237
|
+
declare function parseNpmSpecifier(specifier: string): {
|
|
238
|
+
name: string;
|
|
239
|
+
spec?: string;
|
|
240
|
+
};
|
|
241
|
+
/** Resolve any accepted specifier into a scannable local directory. */
|
|
242
|
+
declare function resolveTarget(specifier: string, opts?: ResolveOptions): Promise<ResolvedTarget>;
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region src/rule.d.ts
|
|
245
|
+
interface RuleContext {
|
|
246
|
+
analysis: Analysis;
|
|
247
|
+
}
|
|
248
|
+
interface Rule {
|
|
249
|
+
/** Namespaced id matching RULE_ID_PATTERN, e.g. `perm.subprocess-spawn`. */
|
|
250
|
+
id: string;
|
|
251
|
+
/** One-line human title. */
|
|
252
|
+
title: string;
|
|
253
|
+
defaultSeverity: VetSeverity;
|
|
254
|
+
defaultConfidence: VetConfidence;
|
|
255
|
+
check(ctx: RuleContext): VetFinding[];
|
|
256
|
+
}
|
|
257
|
+
interface FindingInit {
|
|
258
|
+
title?: string;
|
|
259
|
+
severity?: VetSeverity;
|
|
260
|
+
confidence?: VetConfidence;
|
|
261
|
+
evidence: VetEvidence[];
|
|
262
|
+
remediation?: string;
|
|
263
|
+
references?: string[];
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/rules/index.d.ts
|
|
267
|
+
declare const RULES: Rule[];
|
|
268
|
+
declare function ruleIds(): string[];
|
|
269
|
+
/** Run all rules, or the subset named in `only` (unknown ids throw). */
|
|
270
|
+
declare function runRules(analysis: Analysis, only?: string[]): VetFinding[];
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/scanner.d.ts
|
|
273
|
+
/** Kept in lockstep with package.json; a test asserts they match. */
|
|
274
|
+
declare const SCANNER_VERSION = "0.1.0";
|
|
275
|
+
interface ScanOptions extends ResolveOptions {
|
|
276
|
+
/** Injectable clock for deterministic tests/reports. */
|
|
277
|
+
now?: () => string;
|
|
278
|
+
/** Restrict the scan to these rule ids (CLI `--rules`). */
|
|
279
|
+
rules?: string[];
|
|
280
|
+
}
|
|
281
|
+
declare function scanDirectory(dir: string, options?: ScanOptions): Promise<VetReport>;
|
|
282
|
+
declare function scan(specifier: string, options?: ScanOptions): Promise<VetReport>;
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region src/cli.d.ts
|
|
285
|
+
/**
|
|
286
|
+
* CLI (ROADMAP T4). Exit semantics per the dsh-vet/v1 spec: `0` for any
|
|
287
|
+
* completed report (even a graded-F one), non-zero for scanner failure;
|
|
288
|
+
* `--strict`/`--fail-on` turn threshold breaches into exit code 1.
|
|
289
|
+
*/
|
|
290
|
+
interface CliIo {
|
|
291
|
+
stdout: (line: string) => void;
|
|
292
|
+
stderr: (line: string) => void;
|
|
293
|
+
}
|
|
294
|
+
/** Parse and run; returns the process exit code. */
|
|
295
|
+
declare function runCli(argv: string[], io: CliIo): Promise<number>;
|
|
296
|
+
//#endregion
|
|
297
|
+
export { type Analysis, type Capability, type CapabilityUse, type CharcodeCall, type CliIo, type CreateReportInput, type DynamicImportUse, type EncodedLiteral, type EvalUse, type FindingInit, type ImportRef, type NetUse, type PkgJson, RULES, RULE_ID_PATTERN, type ResolveOptions, type ResolvedTarget, type Rule, type RuleContext, SCANNER_VERSION, SCHEMA_ID, type ScanOptions, type SourceFile, type SpecifierKind, type VetConfidence, VetError, type VetEvidence, type VetFinding, type VetGrade, type VetReport, type VetScanner, type VetSeverity, type VetSummary, type VetSummaryCounts, type VetTarget, type VetTargetKind, analyze, classifySpecifier, countFindings, createReport, gradeFor, isGraded, parseNpmSpecifier, reachableFrom, resolveTarget, ruleIds, runCli, runRules, scan, scanDirectory };
|