vetguard 0.0.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 +202 -0
- package/README.md +153 -0
- package/dist/chunk-XXICU3EZ.js +958 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +108 -0
- package/dist/index.d.ts +263 -0
- package/dist/index.js +46 -0
- package/package.json +72 -0
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
VERSION,
|
|
4
|
+
checkPackage,
|
|
5
|
+
renderJson,
|
|
6
|
+
renderSarif,
|
|
7
|
+
renderTerminal,
|
|
8
|
+
resolveExitCode,
|
|
9
|
+
scanProject
|
|
10
|
+
} from "./chunk-XXICU3EZ.js";
|
|
11
|
+
|
|
12
|
+
// src/cli.ts
|
|
13
|
+
import { parseArgs } from "util";
|
|
14
|
+
import process from "process";
|
|
15
|
+
var HELP = `vetguard ${VERSION} - scan npm dependencies for AI-era supply-chain threats
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
vetguard scan [dir] Scan a project's dependencies (defaults to cwd)
|
|
19
|
+
vetguard check <pkg> Vet a single package before installing
|
|
20
|
+
(e.g. vetguard check react-codeshift, foo@1.2.3)
|
|
21
|
+
vetguard --help Show this help
|
|
22
|
+
vetguard --version Show version
|
|
23
|
+
|
|
24
|
+
Options:
|
|
25
|
+
--offline Do not contact the registry; unverifiable facts
|
|
26
|
+
are reported as "could not verify", never "safe".
|
|
27
|
+
--json Print the report as JSON instead of text.
|
|
28
|
+
--sarif Print SARIF 2.1.0 for GitHub code scanning.
|
|
29
|
+
--fail-on <severity> Exit non-zero only when a finding at or above this
|
|
30
|
+
severity exists (critical|high|medium|low|info).
|
|
31
|
+
Default: any finding exits non-zero.
|
|
32
|
+
|
|
33
|
+
vetguard never executes the code it scans. When it cannot verify something it
|
|
34
|
+
says so rather than calling it safe.`;
|
|
35
|
+
var SEVERITIES = ["critical", "high", "medium", "low", "info"];
|
|
36
|
+
async function main(argv) {
|
|
37
|
+
const { values, positionals } = parseArgs({
|
|
38
|
+
args: argv,
|
|
39
|
+
allowPositionals: true,
|
|
40
|
+
options: {
|
|
41
|
+
help: { type: "boolean", short: "h" },
|
|
42
|
+
version: { type: "boolean", short: "v" },
|
|
43
|
+
offline: { type: "boolean" },
|
|
44
|
+
json: { type: "boolean" },
|
|
45
|
+
sarif: { type: "boolean" },
|
|
46
|
+
"fail-on": { type: "string" }
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
if (values.version) {
|
|
50
|
+
console.log(VERSION);
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
const command = positionals[0];
|
|
54
|
+
if (values.help || command === void 0 || command === "help") {
|
|
55
|
+
console.log(HELP);
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
const failOnRaw = values["fail-on"];
|
|
59
|
+
if (failOnRaw !== void 0 && !SEVERITIES.includes(failOnRaw)) {
|
|
60
|
+
console.error(`Invalid --fail-on value: ${failOnRaw} (expected ${SEVERITIES.join(", ")})`);
|
|
61
|
+
return 2;
|
|
62
|
+
}
|
|
63
|
+
const options = {
|
|
64
|
+
offline: values.offline === true,
|
|
65
|
+
json: values.json === true,
|
|
66
|
+
sarif: values.sarif === true,
|
|
67
|
+
failOn: failOnRaw
|
|
68
|
+
};
|
|
69
|
+
if (command === "scan") {
|
|
70
|
+
return runScan(positionals[1] ?? process.cwd(), options);
|
|
71
|
+
}
|
|
72
|
+
if (command === "check") {
|
|
73
|
+
const spec = positionals[1];
|
|
74
|
+
if (spec === void 0) {
|
|
75
|
+
console.error("check requires a package name, e.g. vetguard check express\n");
|
|
76
|
+
console.error(HELP);
|
|
77
|
+
return 2;
|
|
78
|
+
}
|
|
79
|
+
return runCheck(spec, options);
|
|
80
|
+
}
|
|
81
|
+
console.error(`Unknown command: ${command}
|
|
82
|
+
`);
|
|
83
|
+
console.error(HELP);
|
|
84
|
+
return 2;
|
|
85
|
+
}
|
|
86
|
+
async function runScan(dir, options) {
|
|
87
|
+
let report;
|
|
88
|
+
try {
|
|
89
|
+
report = await scanProject(dir, { offline: options.offline });
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
92
|
+
return 2;
|
|
93
|
+
}
|
|
94
|
+
return emit(report, options);
|
|
95
|
+
}
|
|
96
|
+
async function runCheck(specInput, options) {
|
|
97
|
+
const report = await checkPackage(specInput, { offline: options.offline });
|
|
98
|
+
return emit(report, options);
|
|
99
|
+
}
|
|
100
|
+
function emit(report, options) {
|
|
101
|
+
const output = options.sarif ? renderSarif(report, VERSION) : options.json ? renderJson(report, VERSION) : renderTerminal(report);
|
|
102
|
+
console.log(output);
|
|
103
|
+
return resolveExitCode(report, options.failOn);
|
|
104
|
+
}
|
|
105
|
+
main(process.argv.slice(2)).then((code) => process.exit(code)).catch((err) => {
|
|
106
|
+
console.error(err instanceof Error ? err.stack : String(err));
|
|
107
|
+
process.exit(2);
|
|
108
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core data model. These types are the contract between collectors (which do
|
|
3
|
+
* all IO) and detectors (pure functions that judge). Nothing here does IO.
|
|
4
|
+
*/
|
|
5
|
+
type Severity = "critical" | "high" | "medium" | "low" | "info";
|
|
6
|
+
/** How sure we are the finding is real, distinct from how bad it would be. */
|
|
7
|
+
type Confidence = "high" | "medium" | "low";
|
|
8
|
+
/** How a package entered the dependency graph, so findings can be traced back. */
|
|
9
|
+
type DependencyKind = "prod" | "dev" | "peer" | "optional" | "bundled" | "transitive";
|
|
10
|
+
/** How the version was specified, which decides what we can verify about it. */
|
|
11
|
+
type DependencySource = "registry" | "git" | "file" | "link" | "alias" | "workspace" | "unknown";
|
|
12
|
+
/**
|
|
13
|
+
* The facts a collector gathers about one resolved dependency. Detectors read
|
|
14
|
+
* these; they never reach out to the network or filesystem themselves. Fields
|
|
15
|
+
* are optional because a fact may be genuinely unknowable (offline, private
|
|
16
|
+
* registry, git dependency), and "unknown" must never be treated as "safe".
|
|
17
|
+
*/
|
|
18
|
+
interface PackageFacts {
|
|
19
|
+
name: string;
|
|
20
|
+
/** Resolved version from the lockfile, if any. */
|
|
21
|
+
version?: string;
|
|
22
|
+
requestedRange?: string;
|
|
23
|
+
kind: DependencyKind;
|
|
24
|
+
source: DependencySource;
|
|
25
|
+
/** Whether the registry has any record of this name at all. */
|
|
26
|
+
existsOnRegistry?: boolean;
|
|
27
|
+
/** Whether this exact version is still published. */
|
|
28
|
+
versionPublished?: boolean;
|
|
29
|
+
firstPublishAt?: string;
|
|
30
|
+
latestPublishAt?: string;
|
|
31
|
+
/** Days since the package name was first published, computed at collection time. */
|
|
32
|
+
ageDays?: number;
|
|
33
|
+
/** Number of published versions of this name. */
|
|
34
|
+
versionCount?: number;
|
|
35
|
+
weeklyDownloads?: number;
|
|
36
|
+
hasInstallScript?: boolean;
|
|
37
|
+
repositoryUrl?: string;
|
|
38
|
+
integrity?: string;
|
|
39
|
+
resolvedUrl?: string;
|
|
40
|
+
/** Where this fact set came from, for traceable verdicts. */
|
|
41
|
+
evidencePath?: string;
|
|
42
|
+
}
|
|
43
|
+
/** A single detector result. Evidence is always quoted, never live content. */
|
|
44
|
+
interface Finding {
|
|
45
|
+
ruleId: string;
|
|
46
|
+
severity: Severity;
|
|
47
|
+
confidence: Confidence;
|
|
48
|
+
packageName: string;
|
|
49
|
+
packageVersion?: string;
|
|
50
|
+
title: string;
|
|
51
|
+
/** One line: why this matters. */
|
|
52
|
+
detail: string;
|
|
53
|
+
/** Concrete, escaped, truncated evidence backing the finding. */
|
|
54
|
+
evidence?: string;
|
|
55
|
+
location?: string;
|
|
56
|
+
}
|
|
57
|
+
/** A pure detector: facts in, findings out. No IO, no side effects. */
|
|
58
|
+
interface Detector {
|
|
59
|
+
id: string;
|
|
60
|
+
description: string;
|
|
61
|
+
detect(pkg: PackageFacts): Finding[];
|
|
62
|
+
}
|
|
63
|
+
type ScanVerdict = "clean" | "findings" | "could-not-verify";
|
|
64
|
+
interface Report {
|
|
65
|
+
verdict: ScanVerdict;
|
|
66
|
+
target: string;
|
|
67
|
+
ecosystem: string;
|
|
68
|
+
packagesScanned: number;
|
|
69
|
+
findings: Finding[];
|
|
70
|
+
/** Packages we could not fully verify (offline, unsupported source, etc.). */
|
|
71
|
+
unverified: string[];
|
|
72
|
+
generatedAt: string;
|
|
73
|
+
/** Whether the scan read a resolved lockfile tree or only the manifest. */
|
|
74
|
+
basis?: "lockfile" | "manifest";
|
|
75
|
+
/** Non-fatal notices, e.g. an unsupported lockfile that forced a manifest fallback. */
|
|
76
|
+
warnings?: string[];
|
|
77
|
+
}
|
|
78
|
+
declare const SEVERITY_ORDER: Record<Severity, number>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Runs every detector over every package's facts and aggregates a report.
|
|
82
|
+
* This is deliberately pure: given the same facts and detectors it produces
|
|
83
|
+
* the same report, which is what makes CI runs deterministic and testable.
|
|
84
|
+
*/
|
|
85
|
+
declare function runDetectors(facts: PackageFacts[], detectors: Detector[], context: {
|
|
86
|
+
target: string;
|
|
87
|
+
ecosystem: string;
|
|
88
|
+
unverified: string[];
|
|
89
|
+
generatedAt: string;
|
|
90
|
+
}): Report;
|
|
91
|
+
|
|
92
|
+
/** All built-in detectors. New detectors are registered here. */
|
|
93
|
+
declare const builtinDetectors: Detector[];
|
|
94
|
+
|
|
95
|
+
/** Renders a report as plain text. No color codes yet; kept dependency-free. */
|
|
96
|
+
declare function renderTerminal(report: Report): string;
|
|
97
|
+
|
|
98
|
+
/** Bumped when the JSON shape changes in a way consumers must adapt to. */
|
|
99
|
+
declare const JSON_SCHEMA_VERSION = 1;
|
|
100
|
+
/** Renders a report as stable, machine-readable JSON for CI and tooling. */
|
|
101
|
+
declare function renderJson(report: Report, toolVersion: string): string;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Renders a report as SARIF 2.1.0 for GitHub code scanning. Findings become
|
|
105
|
+
* results anchored to the manifest or lockfile; every built-in detector is
|
|
106
|
+
* advertised as a rule so the tool's coverage is discoverable.
|
|
107
|
+
*/
|
|
108
|
+
declare function renderSarif(report: Report, toolVersion: string, detectors?: Detector[]): string;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The process exit code for a report. No findings is always 0. With no
|
|
112
|
+
* threshold any finding is 1; with a threshold only a finding at or above that
|
|
113
|
+
* severity is 1, so CI can gate on high-severity results alone.
|
|
114
|
+
*/
|
|
115
|
+
declare function resolveExitCode(report: Report, failOn: Severity | undefined): number;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Classifies a version specifier into how we can verify it. A registry range
|
|
119
|
+
* is checkable against the registry; git/file/link/alias/workspace specifiers
|
|
120
|
+
* are not resolvable the same way and must be reported honestly, not assumed
|
|
121
|
+
* safe.
|
|
122
|
+
*/
|
|
123
|
+
declare function classifySource(spec: string): DependencySource;
|
|
124
|
+
/**
|
|
125
|
+
* Reads declared dependencies from a package.json. This is manifest-level only
|
|
126
|
+
* (what was asked for), not the resolved lockfile graph; the lockfile collector
|
|
127
|
+
* (Phase 1) supplies resolved versions and the full transitive tree.
|
|
128
|
+
*/
|
|
129
|
+
declare function readManifestFacts(dir: string): Promise<PackageFacts[]>;
|
|
130
|
+
|
|
131
|
+
/** Names of non-npm lockfiles present in `dir`, so the caller can warn instead of silently skipping. */
|
|
132
|
+
declare function detectOtherLockfiles(dir: string): Promise<string[]>;
|
|
133
|
+
type LockfileOutcome = {
|
|
134
|
+
status: "ok";
|
|
135
|
+
facts: PackageFacts[];
|
|
136
|
+
lockfileVersion: number;
|
|
137
|
+
} | {
|
|
138
|
+
status: "unsupported";
|
|
139
|
+
reason: string;
|
|
140
|
+
} | {
|
|
141
|
+
status: "absent";
|
|
142
|
+
};
|
|
143
|
+
/** Extracts the package name from a lockfile path key, handling nesting and scopes. */
|
|
144
|
+
declare function nameFromLockPath(lockPath: string): string | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* Reads resolved dependency facts from a package-lock.json (v2 or v3). Returns
|
|
147
|
+
* `absent` when there is no lockfile and `unsupported` for a shape we do not
|
|
148
|
+
* parse (v1, or a missing `packages` map), so the caller can fall back to the
|
|
149
|
+
* manifest and say so rather than silently skipping.
|
|
150
|
+
*/
|
|
151
|
+
declare function readLockfile(dir: string): Promise<LockfileOutcome>;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* npm registry client. This is a collector: it does IO and returns facts. It
|
|
155
|
+
* never judges. Every path degrades honestly, a lookup that could not be
|
|
156
|
+
* completed returns status "unverified", never a value that reads as "safe".
|
|
157
|
+
*/
|
|
158
|
+
/** Parsed, flattened facts from a registry packument. */
|
|
159
|
+
interface Packument {
|
|
160
|
+
name: string;
|
|
161
|
+
firstPublishAt?: string;
|
|
162
|
+
latestPublishAt?: string;
|
|
163
|
+
latestVersion?: string;
|
|
164
|
+
versionCount: number;
|
|
165
|
+
/** Whether the resolved version is still published, when a version is asked. */
|
|
166
|
+
requestedVersionPublished?: boolean;
|
|
167
|
+
hasInstallScript: boolean;
|
|
168
|
+
repositoryUrl?: string;
|
|
169
|
+
}
|
|
170
|
+
type RegistryLookup = {
|
|
171
|
+
status: "found";
|
|
172
|
+
packument: Packument;
|
|
173
|
+
} | {
|
|
174
|
+
status: "not-found";
|
|
175
|
+
} | {
|
|
176
|
+
status: "unverified";
|
|
177
|
+
reason: string;
|
|
178
|
+
};
|
|
179
|
+
interface RegistryClientOptions {
|
|
180
|
+
/** Injected for tests; defaults to global fetch. */
|
|
181
|
+
fetchImpl?: typeof fetch;
|
|
182
|
+
registryUrl?: string;
|
|
183
|
+
offline?: boolean;
|
|
184
|
+
timeoutMs?: number;
|
|
185
|
+
}
|
|
186
|
+
interface RegistryClient {
|
|
187
|
+
getPackument(name: string, version?: string): Promise<RegistryLookup>;
|
|
188
|
+
}
|
|
189
|
+
/** Scoped names keep the leading @ but the internal slash must be encoded. */
|
|
190
|
+
declare function encodePackageName(name: string): string;
|
|
191
|
+
declare function createRegistryClient(options?: RegistryClientOptions): RegistryClient;
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* npm downloads API client. A collector: returns the last-week download count
|
|
195
|
+
* for a package, or `undefined` when the count cannot be established (offline,
|
|
196
|
+
* error, or an unsupported name such as a scoped package, which the point API
|
|
197
|
+
* does not serve). `undefined` means "unknown", never "zero" or "safe".
|
|
198
|
+
*/
|
|
199
|
+
interface DownloadsClientOptions {
|
|
200
|
+
fetchImpl?: typeof fetch;
|
|
201
|
+
apiUrl?: string;
|
|
202
|
+
offline?: boolean;
|
|
203
|
+
timeoutMs?: number;
|
|
204
|
+
}
|
|
205
|
+
interface DownloadsClient {
|
|
206
|
+
getWeeklyDownloads(name: string): Promise<number | undefined>;
|
|
207
|
+
}
|
|
208
|
+
declare function createDownloadsClient(options?: DownloadsClientOptions): DownloadsClient;
|
|
209
|
+
|
|
210
|
+
interface EnrichmentResult {
|
|
211
|
+
facts: PackageFacts[];
|
|
212
|
+
/** Names whose registry facts could not be established (offline, error). */
|
|
213
|
+
unverified: string[];
|
|
214
|
+
}
|
|
215
|
+
interface EnrichOptions {
|
|
216
|
+
concurrency?: number;
|
|
217
|
+
downloads?: DownloadsClient;
|
|
218
|
+
/** Injected for a deterministic age computation in tests. */
|
|
219
|
+
now?: () => Date;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Enriches manifest facts with registry and downloads facts. A collector: it
|
|
223
|
+
* performs the IO and folds the results into `PackageFacts` for the pure
|
|
224
|
+
* detectors to judge. Only registry-sourced dependencies are looked up; git,
|
|
225
|
+
* file, link, alias, and workspace specifiers have no registry record and are
|
|
226
|
+
* left as they are.
|
|
227
|
+
*/
|
|
228
|
+
declare function enrichWithRegistry(input: PackageFacts[], client: RegistryClient, options?: EnrichOptions): Promise<EnrichmentResult>;
|
|
229
|
+
|
|
230
|
+
interface PackageSpec {
|
|
231
|
+
name: string;
|
|
232
|
+
version?: string;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Parses a `check` argument like `express`, `express@4.18.2`, `@scope/pkg`, or
|
|
236
|
+
* `@scope/pkg@1.0.0`. The leading @ of a scope is not a version separator, so
|
|
237
|
+
* the split looks for an @ after the first character only.
|
|
238
|
+
*/
|
|
239
|
+
declare function parsePackageSpec(input: string): PackageSpec;
|
|
240
|
+
|
|
241
|
+
interface ScanOptions {
|
|
242
|
+
offline?: boolean;
|
|
243
|
+
/** Injected in tests so the suite never touches the network. */
|
|
244
|
+
client?: RegistryClient;
|
|
245
|
+
downloads?: DownloadsClient;
|
|
246
|
+
detectors?: Detector[];
|
|
247
|
+
/** Injected for a deterministic `generatedAt` and age in tests. */
|
|
248
|
+
now?: () => Date;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Scans a project. Prefers the resolved package-lock.json tree (v2/v3); falls
|
|
252
|
+
* back to the manifest's declared dependencies when the lockfile is absent or
|
|
253
|
+
* an unsupported shape, recording a warning so the fallback is never silent.
|
|
254
|
+
*/
|
|
255
|
+
declare function scanProject(dir: string, options?: ScanOptions): Promise<Report>;
|
|
256
|
+
/** Vets a single package spec (`name`, `name@version`, `@scope/name@version`). */
|
|
257
|
+
declare function checkPackage(specInput: string, options?: ScanOptions): Promise<Report>;
|
|
258
|
+
|
|
259
|
+
/** Public library API. The CLI is a thin consumer of these exports. */
|
|
260
|
+
|
|
261
|
+
declare const VERSION: string;
|
|
262
|
+
|
|
263
|
+
export { type Confidence, type DependencyKind, type DependencySource, type Detector, type DownloadsClient, type DownloadsClientOptions, type EnrichOptions, type EnrichmentResult, type Finding, JSON_SCHEMA_VERSION, type LockfileOutcome, type PackageFacts, type PackageSpec, type Packument, type RegistryClient, type RegistryClientOptions, type RegistryLookup, type Report, SEVERITY_ORDER, type ScanOptions, type ScanVerdict, type Severity, VERSION, builtinDetectors, checkPackage, classifySource, createDownloadsClient, createRegistryClient, detectOtherLockfiles, encodePackageName, enrichWithRegistry, nameFromLockPath, parsePackageSpec, readLockfile, readManifestFacts, renderJson, renderSarif, renderTerminal, resolveExitCode, runDetectors, scanProject };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
JSON_SCHEMA_VERSION,
|
|
3
|
+
SEVERITY_ORDER,
|
|
4
|
+
VERSION,
|
|
5
|
+
builtinDetectors,
|
|
6
|
+
checkPackage,
|
|
7
|
+
classifySource,
|
|
8
|
+
createDownloadsClient,
|
|
9
|
+
createRegistryClient,
|
|
10
|
+
detectOtherLockfiles,
|
|
11
|
+
encodePackageName,
|
|
12
|
+
enrichWithRegistry,
|
|
13
|
+
nameFromLockPath,
|
|
14
|
+
parsePackageSpec,
|
|
15
|
+
readLockfile,
|
|
16
|
+
readManifestFacts,
|
|
17
|
+
renderJson,
|
|
18
|
+
renderSarif,
|
|
19
|
+
renderTerminal,
|
|
20
|
+
resolveExitCode,
|
|
21
|
+
runDetectors,
|
|
22
|
+
scanProject
|
|
23
|
+
} from "./chunk-XXICU3EZ.js";
|
|
24
|
+
export {
|
|
25
|
+
JSON_SCHEMA_VERSION,
|
|
26
|
+
SEVERITY_ORDER,
|
|
27
|
+
VERSION,
|
|
28
|
+
builtinDetectors,
|
|
29
|
+
checkPackage,
|
|
30
|
+
classifySource,
|
|
31
|
+
createDownloadsClient,
|
|
32
|
+
createRegistryClient,
|
|
33
|
+
detectOtherLockfiles,
|
|
34
|
+
encodePackageName,
|
|
35
|
+
enrichWithRegistry,
|
|
36
|
+
nameFromLockPath,
|
|
37
|
+
parsePackageSpec,
|
|
38
|
+
readLockfile,
|
|
39
|
+
readManifestFacts,
|
|
40
|
+
renderJson,
|
|
41
|
+
renderSarif,
|
|
42
|
+
renderTerminal,
|
|
43
|
+
resolveExitCode,
|
|
44
|
+
runDetectors,
|
|
45
|
+
scanProject
|
|
46
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vetguard",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Local-first scanner for AI-era npm supply-chain threats: hallucinated (slopsquatted) dependencies, typosquats, malicious young packages, and prompt injection aimed at coding agents.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"npm",
|
|
7
|
+
"security",
|
|
8
|
+
"supply-chain",
|
|
9
|
+
"slopsquatting",
|
|
10
|
+
"typosquatting",
|
|
11
|
+
"vulnerability",
|
|
12
|
+
"sca",
|
|
13
|
+
"cli"
|
|
14
|
+
],
|
|
15
|
+
"license": "Apache-2.0",
|
|
16
|
+
"homepage": "https://github.com/tallyguard/vetguard#readme",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/tallyguard/vetguard.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/tallyguard/vetguard/issues"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20"
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"vetguard": "./dist/cli.js"
|
|
30
|
+
},
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"LICENSE",
|
|
40
|
+
"README.md"
|
|
41
|
+
],
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"dev": "tsx src/cli.ts",
|
|
47
|
+
"build": "tsup",
|
|
48
|
+
"typecheck": "tsc --noEmit",
|
|
49
|
+
"lint": "eslint .",
|
|
50
|
+
"format": "prettier --write .",
|
|
51
|
+
"format:check": "prettier --check .",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"test:watch": "vitest",
|
|
54
|
+
"scan": "tsx src/cli.ts scan",
|
|
55
|
+
"check": "npm run build && node dist/cli.js",
|
|
56
|
+
"refresh:popular": "node scripts/refresh-popular.mjs",
|
|
57
|
+
"prepack": "npm run build"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@eslint/js": "^9.17.0",
|
|
61
|
+
"@types/node": "^22.10.0",
|
|
62
|
+
"@typescript-eslint/eslint-plugin": "^8.18.0",
|
|
63
|
+
"@typescript-eslint/parser": "^8.18.0",
|
|
64
|
+
"eslint": "^9.17.0",
|
|
65
|
+
"globals": "^17.7.0",
|
|
66
|
+
"prettier": "^3.4.2",
|
|
67
|
+
"tsup": "^8.3.5",
|
|
68
|
+
"tsx": "^4.19.2",
|
|
69
|
+
"typescript": "^5.7.2",
|
|
70
|
+
"vitest": "^2.1.8"
|
|
71
|
+
}
|
|
72
|
+
}
|