dsh-plugin-inspector 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 +271 -0
- package/lib/checks/input.js +10 -0
- package/lib/checks/tier-a.js +632 -0
- package/lib/checks/tier-b.js +411 -0
- package/lib/checks/tier-c.js +288 -0
- package/lib/cli.js +154 -0
- package/lib/cordis-yaml.js +393 -0
- package/lib/files.js +131 -0
- package/lib/index.js +22 -0
- package/lib/injection.js +90 -0
- package/lib/inspect.js +168 -0
- package/lib/knowledge.js +321 -0
- package/lib/manifest.js +143 -0
- package/lib/model.js +55 -0
- package/lib/publish.js +208 -0
- package/lib/report.js +182 -0
- package/lib/source.js +410 -0
- package/lib/types/checks/input.d.ts +42 -0
- package/lib/types/checks/tier-a.d.ts +24 -0
- package/lib/types/checks/tier-b.d.ts +23 -0
- package/lib/types/checks/tier-c.d.ts +37 -0
- package/lib/types/cli.d.ts +56 -0
- package/lib/types/cordis-yaml.d.ts +133 -0
- package/lib/types/files.d.ts +71 -0
- package/lib/types/index.d.ts +23 -0
- package/lib/types/injection.d.ts +41 -0
- package/lib/types/inspect.d.ts +31 -0
- package/lib/types/knowledge.d.ts +137 -0
- package/lib/types/manifest.d.ts +62 -0
- package/lib/types/model.d.ts +160 -0
- package/lib/types/publish.d.ts +55 -0
- package/lib/types/report.d.ts +27 -0
- package/lib/types/source.d.ts +71 -0
- package/package.json +61 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which files of a working tree npm would actually publish.
|
|
3
|
+
*
|
|
4
|
+
* A directory target is a repository checkout, and a repository holds far more
|
|
5
|
+
* than the package: tests, fixtures, CI config, build scratch. None of that is
|
|
6
|
+
* installed, none of it is mounted, and none of it can act on a user. Reporting
|
|
7
|
+
* on it produces findings nobody can act on and buries the ones they can, so
|
|
8
|
+
* the directory reader is scoped to the set npm would put in the tarball.
|
|
9
|
+
*
|
|
10
|
+
* The rules are npm's, transcribed from `npm-packlist`: the `files` allowlist
|
|
11
|
+
* when the manifest declares one, otherwise `.npmignore` — or `.gitignore` when
|
|
12
|
+
* there is no `.npmignore` — over everything else. A handful of files are
|
|
13
|
+
* always published whatever the manifest says, and a handful are never
|
|
14
|
+
* published whatever it says.
|
|
15
|
+
* @module dsh-plugin-inspector/publish
|
|
16
|
+
*/
|
|
17
|
+
/** How the publish set was decided, reported so the user knows what was read. */
|
|
18
|
+
export type PublishBasis = 'files-allowlist' | 'ignore-rules' | 'tarball';
|
|
19
|
+
/** A path is published, or it is not. */
|
|
20
|
+
export interface PublishSet {
|
|
21
|
+
readonly basis: PublishBasis;
|
|
22
|
+
/**
|
|
23
|
+
* Whether npm would put this path in the tarball.
|
|
24
|
+
* @param path - package-relative POSIX path.
|
|
25
|
+
* @returns true when the file is published.
|
|
26
|
+
*/
|
|
27
|
+
includes: (path: string) => boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Whether a glob matches a path.
|
|
31
|
+
* @param pattern - the glob, package-relative and POSIX.
|
|
32
|
+
* @param path - the path, package-relative and POSIX.
|
|
33
|
+
* @returns true when the glob matches.
|
|
34
|
+
*/
|
|
35
|
+
export declare function globMatch(pattern: string, path: string): boolean;
|
|
36
|
+
/** Everything the publish set needs from the working tree. */
|
|
37
|
+
export interface PublishInputs {
|
|
38
|
+
/** The manifest's `files` array, or `null` when it declares none. */
|
|
39
|
+
readonly files: readonly string[] | null;
|
|
40
|
+
/** `.npmignore` content, or `null` when the package has none. */
|
|
41
|
+
readonly npmignore: string | null;
|
|
42
|
+
/** `.gitignore` content, or `null` when the package has none. */
|
|
43
|
+
readonly gitignore: string | null;
|
|
44
|
+
/** The manifest's `main`, which npm publishes even outside the allowlist. */
|
|
45
|
+
readonly main: string | null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build the publish set for a working tree.
|
|
49
|
+
* @param inputs - the manifest fields and ignore files the decision needs.
|
|
50
|
+
* @returns the membership test and the basis it used.
|
|
51
|
+
*/
|
|
52
|
+
export declare function publishSet(inputs: PublishInputs): PublishSet;
|
|
53
|
+
/** Every file is published: a tarball is already the publish set. */
|
|
54
|
+
export declare const TARBALL_PUBLISH_SET: PublishSet;
|
|
55
|
+
//# sourceMappingURL=publish.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering a report for a person and for a machine.
|
|
3
|
+
*
|
|
4
|
+
* The human renderer has one rule it is not allowed to break: when
|
|
5
|
+
* `analysis.negativesReliable` is false it must not print a clean bill. Saying
|
|
6
|
+
* "no findings" about a package the tool could not read would be worse than
|
|
7
|
+
* printing nothing at all, so the degraded banner replaces that line rather
|
|
8
|
+
* than accompanying it.
|
|
9
|
+
* @module dsh-plugin-inspector/report
|
|
10
|
+
*/
|
|
11
|
+
import { type Report } from './model.ts';
|
|
12
|
+
/**
|
|
13
|
+
* Serialise the report as stable JSON. Key order is fixed by the object
|
|
14
|
+
* literals in `inspect.ts` and findings are pre-sorted, so two runs over the
|
|
15
|
+
* same input produce byte-identical output.
|
|
16
|
+
* @param report - the report.
|
|
17
|
+
* @returns pretty-printed JSON with a trailing newline.
|
|
18
|
+
*/
|
|
19
|
+
export declare function renderJson(report: Report): string;
|
|
20
|
+
/**
|
|
21
|
+
* Render the human report.
|
|
22
|
+
* @param report - the report.
|
|
23
|
+
* @param color - whether to emit ANSI colour.
|
|
24
|
+
* @returns the rendered text, ending in a newline.
|
|
25
|
+
*/
|
|
26
|
+
export declare function renderHuman(report: Report, color: boolean): string;
|
|
27
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the package under analysis without installing, building, or running
|
|
3
|
+
* any part of it.
|
|
4
|
+
*
|
|
5
|
+
* A directory is walked directly. A tarball is decoded **entirely in memory** —
|
|
6
|
+
* nothing is ever written to disk. That is a safety property, not an
|
|
7
|
+
* optimisation: it makes tar path traversal (`../../.ssh/authorized_keys`)
|
|
8
|
+
* structurally impossible rather than something a filter has to catch, and it
|
|
9
|
+
* lets a test assert that analysing a hostile tarball touched no file.
|
|
10
|
+
*
|
|
11
|
+
* Every cap is enforced *while* bytes arrive, not after. A tar entry is
|
|
12
|
+
* abandoned the moment its running total passes {@link MAX_FILE_BYTES}, so a
|
|
13
|
+
* 27 MB tarball holding a 6 GB member costs 27 MB of decompression and no
|
|
14
|
+
* memory at all. Enforcing a cap on an already-materialised buffer is not a
|
|
15
|
+
* cap.
|
|
16
|
+
*
|
|
17
|
+
* Symbolic links are recorded and never followed, for the same reason: a link
|
|
18
|
+
* pointing outside the package is not part of the package.
|
|
19
|
+
* @module dsh-plugin-inspector/source
|
|
20
|
+
*/
|
|
21
|
+
import type { SkippedFile } from './model.ts';
|
|
22
|
+
import { type PublishBasis } from './publish.ts';
|
|
23
|
+
/** Largest single file the analyzer will hold in memory. */
|
|
24
|
+
export declare const MAX_FILE_BYTES: number;
|
|
25
|
+
/** Largest total payload the analyzer will hold in memory. */
|
|
26
|
+
export declare const MAX_TOTAL_BYTES: number;
|
|
27
|
+
/** Largest number of files the analyzer will consider. */
|
|
28
|
+
export declare const MAX_ENTRIES = 10000;
|
|
29
|
+
/** The resource ceilings one read runs under. */
|
|
30
|
+
export interface ReadLimits {
|
|
31
|
+
readonly maxFileBytes: number;
|
|
32
|
+
readonly maxTotalBytes: number;
|
|
33
|
+
readonly maxEntries: number;
|
|
34
|
+
}
|
|
35
|
+
/** The shipping ceilings. Tests substitute smaller ones to exercise each cap. */
|
|
36
|
+
export declare const DEFAULT_LIMITS: ReadLimits;
|
|
37
|
+
/** Thrown when the target cannot be read at all, which is exit code 2, not a finding. */
|
|
38
|
+
export declare class SourceError extends Error {
|
|
39
|
+
}
|
|
40
|
+
/** The analysed package, decoded into memory. */
|
|
41
|
+
export interface PluginSource {
|
|
42
|
+
readonly kind: 'directory' | 'tarball';
|
|
43
|
+
/** The target as the user gave it, resolved to an absolute path. */
|
|
44
|
+
readonly path: string;
|
|
45
|
+
/** Package-relative POSIX path to UTF-8 text, for every readable text file. */
|
|
46
|
+
readonly files: ReadonlyMap<string, string>;
|
|
47
|
+
/** Files deliberately not read, each with the reason. Feeds Tier C. */
|
|
48
|
+
readonly skipped: readonly SkippedFile[];
|
|
49
|
+
readonly bytesRead: number;
|
|
50
|
+
/** How the set of analysed files was decided. */
|
|
51
|
+
readonly publishBasis: PublishBasis;
|
|
52
|
+
/** Working-tree files npm would not publish, and which were therefore not read. */
|
|
53
|
+
readonly unpublishedFiles: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Decompressed tar bytes one tarball may produce before the read is abandoned.
|
|
57
|
+
*
|
|
58
|
+
* Eight times the in-memory ceiling. A plugin tarball is never this large, and
|
|
59
|
+
* one that is has already answered the only question worth asking about it.
|
|
60
|
+
*/
|
|
61
|
+
export declare const MAX_STREAM_BYTES: number;
|
|
62
|
+
/**
|
|
63
|
+
* Read the package under analysis.
|
|
64
|
+
* @param target - a plugin directory, or a `.tgz` / `.tar.gz` npm tarball.
|
|
65
|
+
* @param limits - resource ceilings; defaults to {@link DEFAULT_LIMITS}.
|
|
66
|
+
* @returns the decoded package.
|
|
67
|
+
* @throws SourceError when the target does not exist, is not a readable
|
|
68
|
+
* tarball, or holds no `package.json`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function loadSource(target: string, limits?: ReadLimits): Promise<PluginSource>;
|
|
71
|
+
//# sourceMappingURL=source.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-plugin-inspector",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Know what a DeepSeek Harness plugin does before you install it — static pre-install analysis of a plugin directory or tarball",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Ivan Tyshchenko",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/CharlotteN7/dsh-plugin-inspector.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/CharlotteN7/dsh-plugin-inspector/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/CharlotteN7/dsh-plugin-inspector#readme",
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": "^22.19.0 || >=24"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"main": "lib/index.js",
|
|
20
|
+
"types": "lib/types/index.d.ts",
|
|
21
|
+
"bin": {
|
|
22
|
+
"dsh-inspect": "lib/cli.js"
|
|
23
|
+
},
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./lib/types/index.d.ts",
|
|
27
|
+
"default": "./lib/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"lib/**/*.js",
|
|
33
|
+
"lib/types/**/*.d.ts"
|
|
34
|
+
],
|
|
35
|
+
"keywords": [
|
|
36
|
+
"dsh-plugin",
|
|
37
|
+
"deepseek-harness",
|
|
38
|
+
"static-analysis",
|
|
39
|
+
"supply-chain"
|
|
40
|
+
],
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"js-yaml": "^4.2.0",
|
|
43
|
+
"tar": "^7.5.7",
|
|
44
|
+
"typescript": "^5.9.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/js-yaml": "^4.0.9",
|
|
48
|
+
"@types/node": "^22.20.0",
|
|
49
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
50
|
+
"tsx": "^4.22.4",
|
|
51
|
+
"vitest": "^4.1.8"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsc -p tsconfig.build.json",
|
|
55
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
56
|
+
"test": "vitest run --config vitest.config.ts",
|
|
57
|
+
"test:coverage": "vitest run --config vitest.config.ts --coverage",
|
|
58
|
+
"inspect": "node --experimental-strip-types src/cli.ts",
|
|
59
|
+
"test:e2e": "pnpm run build && vitest run --config vitest.e2e.config.ts"
|
|
60
|
+
}
|
|
61
|
+
}
|