mcp-app-lint 0.1.0 → 0.1.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 +18 -6
- package/dist/analyze.d.mts +153 -0
- package/dist/analyze.d.mts.map +1 -0
- package/dist/cli.d.mts +25 -0
- package/dist/cli.d.mts.map +1 -0
- package/dist/domains.d.mts +76 -0
- package/dist/domains.d.mts.map +1 -0
- package/dist/index.d.mts +44 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/lint.d.mts +68 -0
- package/dist/lint.d.mts.map +1 -0
- package/dist/npm.d.mts +152 -0
- package/dist/npm.d.mts.map +1 -0
- package/dist/rules.d.mts +22 -0
- package/dist/rules.d.mts.map +1 -0
- package/dist/sarif.d.mts +80 -0
- package/dist/sarif.d.mts.map +1 -0
- package/package.json +11 -4
- package/src/analyze.mjs +150 -21
- package/src/cli.mjs +43 -6
- package/src/domains.mjs +108 -12
- package/src/index.mjs +22 -2
- package/src/lint.mjs +123 -23
- package/src/npm.mjs +134 -17
- package/src/rules.mjs +18 -7
- package/src/sarif.mjs +18 -2
package/README.md
CHANGED
|
@@ -54,17 +54,17 @@ npx mcp-app-lint --dir . --format sarif --out mcp-app-lint.sarif --fail-on error
|
|
|
54
54
|
|
|
55
55
|
| Id | Name | Level | What it means |
|
|
56
56
|
| --- | --- | --- | --- |
|
|
57
|
-
| MCPAPP001 | csp-missing | warning | no `ui.csp`; a compliant host applies `default-src 'none'`, `connect-src 'none'
|
|
57
|
+
| MCPAPP001 | csp-missing | warning | no `ui.csp`; a compliant host applies `default-src 'none'`, `connect-src 'none'`. For a package: it serves a UI resource and no domain list is found, literal or built at runtime |
|
|
58
58
|
| MCPAPP002 | csp-allows-every-host | error | `*` or a scheme-only entry (`https:`) expands to every host |
|
|
59
59
|
| MCPAPP003 | csp-wildcard-host | warning | `*.example.com` allows every subdomain |
|
|
60
|
-
| MCPAPP004 | csp-insecure-scheme | warning | `http:` origin |
|
|
61
|
-
| MCPAPP005 | csp-sink-host | error | host on the sink denylist
|
|
62
|
-
| MCPAPP006 | csp-development-origin | note | localhost, 127.0.0.1,
|
|
60
|
+
| MCPAPP004 | csp-insecure-scheme | warning | `http:` origin other than loopback |
|
|
61
|
+
| MCPAPP005 | csp-sink-host | error | host on the sink denylist that reaches accounts anyone can create: request catchers, tunnels, form builders, a storage service host (`storage.googleapis.com`), a wildcard over its customers (`*.blob.core.windows.net`) |
|
|
62
|
+
| MCPAPP006 | csp-development-origin | note | localhost, 127.0.0.1, 0.0.0.0 in published metadata |
|
|
63
63
|
| MCPAPP007 | permissions-sensitive | note | camera, microphone or geolocation requested |
|
|
64
64
|
| MCPAPP008 | tool-side-effects-app-visible | warning | tool without `readOnlyHint` callable by the app |
|
|
65
65
|
| MCPAPP009 | tool-visibility-implicit | note | tool with UI metadata relies on the default `["model", "app"]` |
|
|
66
|
-
| MCPAPP010 | html-dynamic-innerhtml | warning | hand-written inline script assigns dynamic content to an HTML sink (with line and column) |
|
|
67
|
-
| MCPAPP011 | html-postmessage-wildcard | warning | hand-written `postMessage(…, '*')` |
|
|
66
|
+
| MCPAPP010 | html-dynamic-innerhtml | warning | hand-written inline script assigns dynamic content to an HTML sink (with line and column); values whose every dynamic part goes through an escaping helper are left out |
|
|
67
|
+
| MCPAPP011 | html-postmessage-wildcard | warning | hand-written `postMessage(…, '*')` outside the app protocol (JSON-RPC or mcp-ui messages to `parent`/`top`), with line |
|
|
68
68
|
| MCPAPP012 | html-inline-handlers | note | `on*` attributes |
|
|
69
69
|
| MCPAPP013 | html-eval | warning | `eval` or `new Function` in hand-written script |
|
|
70
70
|
| MCPAPP014 | html-form-action | note | a form that posts somewhere |
|
|
@@ -72,14 +72,26 @@ npx mcp-app-lint --dir . --format sarif --out mcp-app-lint.sarif --fail-on error
|
|
|
72
72
|
| MCPAPP016 | html-sink-host | error | a URL in the document points at a sink host |
|
|
73
73
|
| MCPAPP017 | meta-list-read-mismatch | warning | `resources/read` declares a different policy than `resources/list` |
|
|
74
74
|
| MCPAPP018 | meta-read-wider-than-list | error | the read-time policy adds domains or wildcards the listing did not declare |
|
|
75
|
+
| MCPAPP019 | csp-sink-tenant-host | note | one bucket, worker or app on a storage or serverless service from the denylist (`acct.blob.core.windows.net`, `pub-<id>.r2.dev`); fine while the owner keeps the name |
|
|
75
76
|
|
|
76
77
|
Hand-written means an inline script with short lines and not too many of them; bundles carry
|
|
77
78
|
framework internals and the SDK bridge (which posts to `'*'` by design) and are not reported
|
|
78
79
|
for MCPAPP010, MCPAPP011 and MCPAPP013.
|
|
79
80
|
|
|
81
|
+
In a package scan, domain lists are read where they are written: as a literal array, or through a
|
|
82
|
+
constant or shorthand defined in the same file (`resourceDomains: DOMAINS`, `{ connectDomains }`).
|
|
83
|
+
Each CSP finding points at that file and line. A list built at runtime counts as declared, so it
|
|
84
|
+
does not raise MCPAPP001, but its entries cannot be checked. Packages that only mention the MCP
|
|
85
|
+
Apps MIME type or `ui://` (hosts, renderers, SDKs) are not treated as serving a UI resource.
|
|
86
|
+
|
|
80
87
|
The rules come out of the [MCP Apps UI census](../../census) in this repository; the analyzer is
|
|
81
88
|
shared. The HTML sink detection is `eslint-plugin-render-policy`'s `no-unsafe-innerhtml`. The sink
|
|
82
89
|
denylist is `@render-policy/core`'s. The package also exports the analysis functions (`analyzeHtml`,
|
|
83
90
|
`analyzeUiMeta`, `compareListRead`, `scanPackageDir`, …) and `categorizeDomain`, the heuristic host
|
|
84
91
|
category (fonts, analytics, maps, storage, media, cdn, api, …) the census report tabulates; it is
|
|
85
92
|
data for aggregates, not a rule.
|
|
93
|
+
|
|
94
|
+
The sources are plain JavaScript with JSDoc types; the package ships TypeScript declarations for
|
|
95
|
+
the functions and the shapes they take and return (`Finding`, `Rule`, `DomainPattern`,
|
|
96
|
+
`HtmlAnalysis`, `PackageScan`, …), so `import { lintResource, type Finding } from 'mcp-app-lint'`
|
|
97
|
+
is typed.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
export type UiMeta = Record<string, any>;
|
|
2
|
+
export type CspKey = 'connectDomains' | 'resourceDomains' | 'frameDomains' | 'baseUriDomains';
|
|
3
|
+
export declare const UI_MIME = "text/html;profile=mcp-app";
|
|
4
|
+
/**
|
|
5
|
+
* `_meta.ui` in the modern shape, or the legacy flat `ui/...` keys folded into one object.
|
|
6
|
+
* @param {unknown} meta a `_meta` object
|
|
7
|
+
* @returns {UiMeta | null}
|
|
8
|
+
*/
|
|
9
|
+
export declare function uiMetaOf(meta: unknown): UiMeta | null;
|
|
10
|
+
/**
|
|
11
|
+
* What one `_meta` object declares: the CSP domain lists classified, permissions, domain.
|
|
12
|
+
* @param {unknown} meta a `_meta` object
|
|
13
|
+
*/
|
|
14
|
+
export declare function analyzeUiMeta(meta: unknown): {
|
|
15
|
+
hasUiMeta: boolean;
|
|
16
|
+
cspDeclared: boolean;
|
|
17
|
+
cspEmpty: boolean;
|
|
18
|
+
domains: Record<CspKey, {
|
|
19
|
+
count: number;
|
|
20
|
+
wildcards: number;
|
|
21
|
+
fullWildcards: number;
|
|
22
|
+
insecure: number;
|
|
23
|
+
sinks: import("@render-policy/core").SinkCategory[];
|
|
24
|
+
tenantSinks: import("@render-policy/core").SinkCategory[];
|
|
25
|
+
hosts: string[];
|
|
26
|
+
categories: ("analytics" | "api" | "cdn" | "development" | "every-host" | "fonts" | "local-scheme" | "maps" | "media" | "other" | "sink" | "storage")[];
|
|
27
|
+
}>;
|
|
28
|
+
anyWildcard: boolean;
|
|
29
|
+
anyFullWildcard: boolean;
|
|
30
|
+
anyInsecure: boolean;
|
|
31
|
+
sinks: import("@render-policy/core").SinkCategory[];
|
|
32
|
+
tenantSinks: import("@render-policy/core").SinkCategory[];
|
|
33
|
+
permissions: string[];
|
|
34
|
+
domain: string | null;
|
|
35
|
+
prefersBorder: boolean;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Does `resources/read` declare a different policy than `resources/list`? The host that checked list is then wrong.
|
|
39
|
+
* @param {unknown} listMeta `_meta` of the listing entry
|
|
40
|
+
* @param {unknown} readMeta `_meta` of the read result's content item
|
|
41
|
+
*/
|
|
42
|
+
export declare function compareListRead(listMeta: unknown, readMeta: unknown): {
|
|
43
|
+
comparable: boolean;
|
|
44
|
+
mismatch: boolean;
|
|
45
|
+
readWider?: undefined;
|
|
46
|
+
listOnly?: undefined;
|
|
47
|
+
readOnly?: undefined;
|
|
48
|
+
} | {
|
|
49
|
+
comparable: boolean;
|
|
50
|
+
mismatch: boolean;
|
|
51
|
+
readWider: boolean;
|
|
52
|
+
listOnly: boolean;
|
|
53
|
+
readOnly: boolean;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Counts over the tools of a tools/list result: with a UI, visible to the app, app-only, side-effecting and app-visible.
|
|
57
|
+
* @param {unknown} tools
|
|
58
|
+
*/
|
|
59
|
+
export declare function analyzeTools(tools: unknown): {
|
|
60
|
+
total: number;
|
|
61
|
+
withUi: number;
|
|
62
|
+
visibleToApp: number;
|
|
63
|
+
appOnly: number;
|
|
64
|
+
sideEffectsVisibleToApp: number;
|
|
65
|
+
};
|
|
66
|
+
/** Escaping helpers whose result the census treats as text: by name, not by implementation. */
|
|
67
|
+
export declare const ESCAPE_FUNCTIONS: string[];
|
|
68
|
+
export type PostMessageCall = {
|
|
69
|
+
line: number;
|
|
70
|
+
column: number;
|
|
71
|
+
protocol: boolean;
|
|
72
|
+
};
|
|
73
|
+
export type HtmlSink = {
|
|
74
|
+
line: number;
|
|
75
|
+
column: number;
|
|
76
|
+
/**
|
|
77
|
+
* the script it is in is hand-written, not a bundle
|
|
78
|
+
*/
|
|
79
|
+
handwritten: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* every dynamic part of the value goes through an escaping helper
|
|
82
|
+
*/
|
|
83
|
+
escaped: boolean;
|
|
84
|
+
message: string;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* A `postMessage(…, '*')` call: 1-based position, and whether it carries the app protocol to the host.
|
|
88
|
+
* @typedef {{ line: number, column: number, protocol: boolean }} PostMessageCall
|
|
89
|
+
*/
|
|
90
|
+
/**
|
|
91
|
+
* A dynamic HTML sink the lint rule reported, positioned in the document.
|
|
92
|
+
* @typedef {object} HtmlSink
|
|
93
|
+
* @property {number} line
|
|
94
|
+
* @property {number} column
|
|
95
|
+
* @property {boolean} handwritten the script it is in is hand-written, not a bundle
|
|
96
|
+
* @property {boolean} escaped every dynamic part of the value goes through an escaping helper
|
|
97
|
+
* @property {string} message
|
|
98
|
+
*/
|
|
99
|
+
/**
|
|
100
|
+
* postMessage calls whose target origin is '*', with position and whether they carry the app protocol to the host.
|
|
101
|
+
* @param {string} code
|
|
102
|
+
* @returns {PostMessageCall[]}
|
|
103
|
+
*/
|
|
104
|
+
export declare function postMessageStarCalls(code: string): PostMessageCall[];
|
|
105
|
+
/**
|
|
106
|
+
* Static signals in a UI resource's HTML. Heuristic by design; the report says so.
|
|
107
|
+
* @param {unknown} html
|
|
108
|
+
*/
|
|
109
|
+
export declare function analyzeHtml(html: unknown): {
|
|
110
|
+
bytes: number;
|
|
111
|
+
inlineScripts: number;
|
|
112
|
+
handwrittenScripts: number;
|
|
113
|
+
externalScripts: string[];
|
|
114
|
+
unsafeInnerHtml: number;
|
|
115
|
+
unsafeInnerHtmlHandwritten: number;
|
|
116
|
+
evalLikeHandwritten: number;
|
|
117
|
+
postMessageStarHandwritten: number;
|
|
118
|
+
postMessageStarHandwrittenNonProtocol: number;
|
|
119
|
+
unsafeInnerHtmlHandwrittenUnescaped: number;
|
|
120
|
+
postMessages: Array<PostMessageCall & {
|
|
121
|
+
handwritten: boolean;
|
|
122
|
+
}>;
|
|
123
|
+
lintErrors: number;
|
|
124
|
+
unparsedScripts: number;
|
|
125
|
+
inlineHandlers: number;
|
|
126
|
+
sinks: HtmlSink[];
|
|
127
|
+
evalLike: number;
|
|
128
|
+
postMessageStar: number;
|
|
129
|
+
forms: number;
|
|
130
|
+
formsWithAction: number;
|
|
131
|
+
metaCsp: boolean;
|
|
132
|
+
externalHosts: string[];
|
|
133
|
+
sinkHosts: string[];
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Short lines and not too many of them: written by a person, not emitted by a bundler.
|
|
137
|
+
* @param {string} code
|
|
138
|
+
*/
|
|
139
|
+
export declare function isHandwritten(code: string): boolean;
|
|
140
|
+
export type HtmlAnalysis = ReturnType<typeof analyzeHtml>;
|
|
141
|
+
/** @typedef {ReturnType<typeof analyzeHtml>} HtmlAnalysis */
|
|
142
|
+
/**
|
|
143
|
+
* Everything the HTML analysis reports about hosts, joined with the sink denylist.
|
|
144
|
+
* @template {{ externalHosts: string[] }} T
|
|
145
|
+
* @param {T} analysis
|
|
146
|
+
* @returns {T & { sinkHosts: string[] }}
|
|
147
|
+
*/
|
|
148
|
+
export declare function withSinkHosts<T extends {
|
|
149
|
+
externalHosts: string[];
|
|
150
|
+
}>(analysis: T): T & {
|
|
151
|
+
sinkHosts: string[];
|
|
152
|
+
};
|
|
153
|
+
//# sourceMappingURL=analyze.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.d.mts","sourceRoot":"","sources":["../src/analyze.mjs"],"names":[],"mappings":"AAgBG,YAA+B,MAAM,GAA3B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAQ;AAEpC,YAAoF,MAAM,GAAhF,gBAAgB,GAAG,iBAAiB,GAAG,cAAc,GAAG,gBAAgB,CAAQ;AAK9F,eAAO,MAAM,OAAO,8BAA8B,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAHlB,OAGkB,GAFhB,MAAM,GAAG,IAAI,CAWzB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAFvB,OAEuB;;;;;;;;;;;;;;;;;;;;;;EAoBjC;AAeD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAH7B,OAG6B,EAAE,QAAQ,EAFvC,OAEuC;;;;;;;;;;;;EAajD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAFvB,OAEuB;;;;;;EAkBjC;AAED,+FAA+F;AAC/F,eAAO,MAAM,gBAAgB,UAA0G,CAAC;AAyDrI,YAA+D,eAAe,GAApE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAiB;AAI9E,YAAkB,QAAQ,GAC1B;IAAmB,IAAI,EAAZ,MAAM,CACjB;IAAmB,MAAM,EAAd,MAAM,CACjB;;;;IAAoB,WAAW,EAApB,OAAO,CAClB;;;;IAAoB,OAAO,EAAhB,OAAO,CAClB;IAAmB,OAAO,EAAf,MAAM,CACnB;CAAA,CAAA;AAZD;;;GAGG;AACH;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAH9B,MAG8B,GAF5B,eAAe,EAAE,CAc7B;AAGD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAFrB,OAEqB;;;;qBAMA,MAAM,EAAE;;;;;;;kBAOX,KAAK,CAAC,eAAe,GAAG;QAAE,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC;;;;WAIxD,QAAQ,EAAE;;;;;;mBAMF,MAAM,EAAE;eACZ,MAAM,EAAE;EA0DjC;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAFvB,MAEuB,WAKjC;AAEG,YAA0C,YAAY,GAA5C,UAAU,CAAC,OAAO,WAAW,CAAC,CAAc;AAA1D,6DAA6D;AAE7D;;;;;GAKG;AACH,wBAAgB,aAAa,CAJc,CAAC,SAA9B;IAAE,aAAa,EAAE,MAAM,EAAE,CAAA;CAAK,EAId,QAAQ,EAH3B,CAG2B,GAFzB,CAAC,GAAG;IAAE,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAIvC"}
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Writable } from 'node:stream';
|
|
2
|
+
export type CliOptions = {
|
|
3
|
+
format: string;
|
|
4
|
+
failOn: string;
|
|
5
|
+
dir?: string;
|
|
6
|
+
package?: string;
|
|
7
|
+
read?: string;
|
|
8
|
+
list?: string;
|
|
9
|
+
tools?: string;
|
|
10
|
+
html?: string;
|
|
11
|
+
out?: string;
|
|
12
|
+
help?: boolean;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* The command line, without the process exit: resolves to the exit code (0 clean, 1 findings at or
|
|
16
|
+
* above --fail-on, 2 usage error). Throws on unreadable input or a failed download.
|
|
17
|
+
* @param {readonly string[]} argv arguments after the program name
|
|
18
|
+
* @param {{ stdout?: Pick<Writable, 'write'>, stderr?: Pick<Writable, 'write'> }} [io]
|
|
19
|
+
* @returns {Promise<number>}
|
|
20
|
+
*/
|
|
21
|
+
export declare function run(argv: readonly string[], { stdout, stderr }?: {
|
|
22
|
+
stdout?: Pick<Writable, 'write'>;
|
|
23
|
+
stderr?: Pick<Writable, 'write'>;
|
|
24
|
+
}): Promise<number>;
|
|
25
|
+
//# sourceMappingURL=cli.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.mts","sourceRoot":"","sources":["../src/cli.mjs"],"names":[],"mappings":"AAmBG,OAAQ,KAAA,EAAE,QAAQ,EAAE,MAAM,aAAa,CACvC;AAGA,YAAkB,UAAU,GAC5B;IAAmB,MAAM,EAAd,MAAM,CACjB;IAAmB,MAAM,EAAd,MAAM,CACjB;IAAoB,GAAG,AAAvB,CACA,EADW,MAAM,CACjB;IAAoB,OAAO,AAA3B,CACA,EADW,MAAM,CACjB;IAAoB,IAAI,AAAxB,CACA,EADW,MAAM,CACjB;IAAoB,IAAI,AAAxB,CACA,EADW,MAAM,CACjB;IAAoB,KAAK,AAAzB,CACA,EADW,MAAM,CACjB;IAAoB,IAAI,AAAxB,CACA,EADW,MAAM,CACjB;IAAoB,GAAG,AAAvB,CACA,EADW,MAAM,CACjB;IAAqB,IAAI,AAAzB,CAA0B,EAAf,OAAO,CAAQ;CAAA,CAAA;AA+C7B;;;;;;GAMG;AACH,wBAAsB,GAAG,CAAC,IAAI,EAJnB,SAAS,MAAM,EAII,EAAE,EAAE,MAAuB,EAAE,MAAuB,EAAE,AAHjF,CACA,EADQ;IAAE,MAAM,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;CAGU,GAF5E,OAAO,CAAC,MAAM,CAAC,CAwD3B"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { SinkCategory, SinkEntry } from '@render-policy/core';
|
|
2
|
+
/**
|
|
3
|
+
* One CSP domain entry, taken apart: scheme, host, wildcard and what the entry widens.
|
|
4
|
+
* @param {unknown} raw
|
|
5
|
+
*/
|
|
6
|
+
export declare function parseDomainPattern(raw: unknown): {
|
|
7
|
+
raw: string;
|
|
8
|
+
scheme: string | null;
|
|
9
|
+
schemeOnly: boolean;
|
|
10
|
+
localScheme: boolean;
|
|
11
|
+
development: boolean;
|
|
12
|
+
host: string;
|
|
13
|
+
wildcard: boolean;
|
|
14
|
+
full: boolean;
|
|
15
|
+
leadingWildcard: boolean;
|
|
16
|
+
bareHost: string;
|
|
17
|
+
insecureScheme: boolean;
|
|
18
|
+
};
|
|
19
|
+
export type DomainPattern = ReturnType<typeof parseDomainPattern>;
|
|
20
|
+
export type SinkScope = {
|
|
21
|
+
entry: SinkEntry;
|
|
22
|
+
scope: 'shared' | 'tenant';
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The sink denylist entry a host pattern falls under, or null.
|
|
26
|
+
* @param {unknown} host
|
|
27
|
+
* @returns {SinkEntry | null}
|
|
28
|
+
*/
|
|
29
|
+
export declare function sinkFor(host: unknown): SinkEntry | null;
|
|
30
|
+
/**
|
|
31
|
+
* Whether a CSP pattern on the sink denylist reaches other people's accounts. 'shared': the
|
|
32
|
+
* service host itself (storage.googleapis.com and s3.amazonaws.com serve any bucket by path), a
|
|
33
|
+
* wildcard over the tenants (*.blob.core.windows.net, *.ngrok-free.dev), a path-style endpoint, or
|
|
34
|
+
* a category where every name is a sink. 'tenant': one account on a multi-tenant storage or
|
|
35
|
+
* serverless service (acct.blob.core.windows.net, pub-<id>.r2.dev); not a channel to a stranger
|
|
36
|
+
* while the owner keeps the name. Null when the host is not on the denylist.
|
|
37
|
+
* @param {string | DomainPattern} pattern
|
|
38
|
+
* @returns {SinkScope | null}
|
|
39
|
+
*/
|
|
40
|
+
export declare function sinkScope(pattern: string | DomainPattern): SinkScope | null;
|
|
41
|
+
export type DomainCategory = typeof DOMAIN_CATEGORIES[number];
|
|
42
|
+
/**
|
|
43
|
+
* Coarse categories for the hosts apps declare or load from. Heuristic, by well-known hosts
|
|
44
|
+
* and naming conventions; a host that fits several falls into the first one listed. Used for
|
|
45
|
+
* aggregates only, so a misfiled host moves a count by one and names nothing.
|
|
46
|
+
*/
|
|
47
|
+
/** @typedef {typeof DOMAIN_CATEGORIES[number]} DomainCategory */
|
|
48
|
+
export declare const DOMAIN_CATEGORIES: readonly ['every-host', 'development', 'local-scheme', 'sink', 'fonts', 'analytics', 'maps', 'storage', 'media', 'cdn', 'api', 'other'];
|
|
49
|
+
/**
|
|
50
|
+
* Category of a parsed domain pattern (see parseDomainPattern).
|
|
51
|
+
* @param {string | DomainPattern} pattern
|
|
52
|
+
* @returns {DomainCategory}
|
|
53
|
+
*/
|
|
54
|
+
export declare function categorizeDomain(pattern: string | DomainPattern): DomainCategory;
|
|
55
|
+
/**
|
|
56
|
+
* Category of a bare host (as found in HTML: script src, img src, …).
|
|
57
|
+
* @param {unknown} host
|
|
58
|
+
*/
|
|
59
|
+
export declare function categorizeHost(host: unknown): "analytics" | "api" | "cdn" | "development" | "every-host" | "fonts" | "local-scheme" | "maps" | "media" | "other" | "sink" | "storage";
|
|
60
|
+
/**
|
|
61
|
+
* Counts and categories over one domain list; anything that is not an array counts as empty.
|
|
62
|
+
* @param {unknown} list
|
|
63
|
+
*/
|
|
64
|
+
export declare function classifyDomains(list: unknown): {
|
|
65
|
+
count: number;
|
|
66
|
+
wildcards: number;
|
|
67
|
+
fullWildcards: number;
|
|
68
|
+
insecure: number;
|
|
69
|
+
sinks: SinkCategory[];
|
|
70
|
+
tenantSinks: SinkCategory[];
|
|
71
|
+
hosts: string[];
|
|
72
|
+
categories: ("analytics" | "api" | "cdn" | "development" | "every-host" | "fonts" | "local-scheme" | "maps" | "media" | "other" | "sink" | "storage")[];
|
|
73
|
+
};
|
|
74
|
+
export type ClassifiedDomains = ReturnType<typeof classifyDomains>;
|
|
75
|
+
/** @typedef {ReturnType<typeof classifyDomains>} ClassifiedDomains */
|
|
76
|
+
//# sourceMappingURL=domains.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domains.d.mts","sourceRoot":"","sources":["../src/domains.mjs"],"names":[],"mappings":"AAQI,OAAQ,KAAA,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAWlE;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAF3B,OAE2B;;;;;;;;;;;;EA2BrC;AAEG,YAAiD,aAAa,GAApD,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAe;AAG/D,YAA4D,SAAS,GAA3D;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,QAAQ,GAAG,QAAQ,CAAA;CAAE,CAAW;AAiCxE;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAHjB,OAGiB,GAFf,SAAS,GAAG,IAAI,CAI5B;AAaD;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAHtB,MAAM,GAAG,aAGa,GAFpB,SAAS,GAAG,IAAI,CAgB5B;AAOG,YAA4C,cAAc,GAAhD,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAgB;AAL9D;;;;GAIG;AACH,iEAAiE;AACjE,eAAO,MAAM,iBAAiB,YAA0B,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAE,CAAC;AAavL;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAH7B,MAAM,GAAG,aAGoB,GAF3B,cAAc,CAU1B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAFxB,OAEwB,2IAGlC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAFzB,OAEyB;IAIhC,KAAK;IACL,SAAS;IACT,aAAa;IACb,QAAQ;IACR,KAAK;IACL,WAAW;IACX,KAAK;IACL,UAAU;EAEb;AAEG,YAA8C,iBAAiB,GAArD,UAAU,CAAC,OAAO,eAAe,CAAC,CAAmB;AAAnE,sEAAsE"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export { analyzeHtml, analyzeTools, analyzeUiMeta, compareListRead, ESCAPE_FUNCTIONS, isHandwritten, postMessageStarCalls, UI_MIME, uiMetaOf, withSinkHosts } from './analyze.mjs';
|
|
2
|
+
export { categorizeDomain, categorizeHost, classifyDomains, DOMAIN_CATEGORIES, parseDomainPattern, sinkFor, sinkScope } from './domains.mjs';
|
|
3
|
+
export { levelOf, lintHtml, lintPackageScan, lintResource, lintTools, lintUiMeta } from './lint.mjs';
|
|
4
|
+
export { downloadTarball, extractTarball, packageMeta, scanPackage, scanPackageDir, searchPackages, UI_SDKS } from './npm.mjs';
|
|
5
|
+
export { RULES, RULE_INDEX, ruleByName } from './rules.mjs';
|
|
6
|
+
export { toSarif, toText, TOOL_VERSION } from './sarif.mjs';
|
|
7
|
+
export { run } from './cli.mjs';
|
|
8
|
+
export type UiMeta = import('./analyze.mjs').UiMeta;
|
|
9
|
+
export type CspKey = import('./analyze.mjs').CspKey;
|
|
10
|
+
export type HtmlAnalysis = import('./analyze.mjs').HtmlAnalysis;
|
|
11
|
+
export type HtmlSink = import('./analyze.mjs').HtmlSink;
|
|
12
|
+
export type PostMessageCall = import('./analyze.mjs').PostMessageCall;
|
|
13
|
+
export type DomainPattern = import('./domains.mjs').DomainPattern;
|
|
14
|
+
export type DomainCategory = import('./domains.mjs').DomainCategory;
|
|
15
|
+
export type ClassifiedDomains = import('./domains.mjs').ClassifiedDomains;
|
|
16
|
+
export type SinkScope = import('./domains.mjs').SinkScope;
|
|
17
|
+
export type Finding = import('./lint.mjs').Finding;
|
|
18
|
+
export type ResourceInput = import('./lint.mjs').ResourceInput;
|
|
19
|
+
export type PackageMeta = import('./npm.mjs').PackageMeta;
|
|
20
|
+
export type PackageScan = import('./npm.mjs').PackageScan;
|
|
21
|
+
export type ScanOptions = import('./npm.mjs').ScanOptions;
|
|
22
|
+
export type DomainSite = import('./npm.mjs').DomainSite;
|
|
23
|
+
export type Rule = import('./rules.mjs').Rule;
|
|
24
|
+
export type Level = import('./rules.mjs').Level;
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {import('./analyze.mjs').UiMeta} UiMeta
|
|
27
|
+
* @typedef {import('./analyze.mjs').CspKey} CspKey
|
|
28
|
+
* @typedef {import('./analyze.mjs').HtmlAnalysis} HtmlAnalysis
|
|
29
|
+
* @typedef {import('./analyze.mjs').HtmlSink} HtmlSink
|
|
30
|
+
* @typedef {import('./analyze.mjs').PostMessageCall} PostMessageCall
|
|
31
|
+
* @typedef {import('./domains.mjs').DomainPattern} DomainPattern
|
|
32
|
+
* @typedef {import('./domains.mjs').DomainCategory} DomainCategory
|
|
33
|
+
* @typedef {import('./domains.mjs').ClassifiedDomains} ClassifiedDomains
|
|
34
|
+
* @typedef {import('./domains.mjs').SinkScope} SinkScope
|
|
35
|
+
* @typedef {import('./lint.mjs').Finding} Finding
|
|
36
|
+
* @typedef {import('./lint.mjs').ResourceInput} ResourceInput
|
|
37
|
+
* @typedef {import('./npm.mjs').PackageMeta} PackageMeta
|
|
38
|
+
* @typedef {import('./npm.mjs').PackageScan} PackageScan
|
|
39
|
+
* @typedef {import('./npm.mjs').ScanOptions} ScanOptions
|
|
40
|
+
* @typedef {import('./npm.mjs').DomainSite} DomainSite
|
|
41
|
+
* @typedef {import('./rules.mjs').Rule} Rule
|
|
42
|
+
* @typedef {import('./rules.mjs').Level} Level
|
|
43
|
+
*/
|
|
44
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mjs"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnL,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC7I,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACrG,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/H,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAG7B,YAA0C,MAAM,GAAtC,OAAO,eAAe,EAAE,MAAM,CAAQ;AAChD,YAA0C,MAAM,GAAtC,OAAO,eAAe,EAAE,MAAM,CAAQ;AAChD,YAAgD,YAAY,GAAlD,OAAO,eAAe,EAAE,YAAY,CAAc;AAC5D,YAA4C,QAAQ,GAA1C,OAAO,eAAe,EAAE,QAAQ,CAAU;AACpD,YAAmD,eAAe,GAAxD,OAAO,eAAe,EAAE,eAAe,CAAiB;AAClE,YAAiD,aAAa,GAApD,OAAO,eAAe,EAAE,aAAa,CAAe;AAC9D,YAAkD,cAAc,GAAtD,OAAO,eAAe,EAAE,cAAc,CAAgB;AAChE,YAAqD,iBAAiB,GAA5D,OAAO,eAAe,EAAE,iBAAiB,CAAmB;AACtE,YAA6C,SAAS,GAA5C,OAAO,eAAe,EAAE,SAAS,CAAW;AACtD,YAAwC,OAAO,GAArC,OAAO,YAAY,EAAE,OAAO,CAAS;AAC/C,YAA8C,aAAa,GAAjD,OAAO,YAAY,EAAE,aAAa,CAAe;AAC3D,YAA2C,WAAW,GAA5C,OAAO,WAAW,EAAE,WAAW,CAAa;AACtD,YAA2C,WAAW,GAA5C,OAAO,WAAW,EAAE,WAAW,CAAa;AACtD,YAA2C,WAAW,GAA5C,OAAO,WAAW,EAAE,WAAW,CAAa;AACtD,YAA0C,UAAU,GAA1C,OAAO,WAAW,EAAE,UAAU,CAAY;AACpD,YAAsC,IAAI,GAAhC,OAAO,aAAa,EAAE,IAAI,CAAM;AAC1C,YAAuC,KAAK,GAAlC,OAAO,aAAa,EAAE,KAAK,CAAO;AAjB/C;;;;;;;;;;;;;;;;;;GAkBG"}
|
package/dist/lint.d.mts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* From analysis to findings (see Finding below).
|
|
3
|
+
*/
|
|
4
|
+
import { analyzeTools } from './analyze.mjs';
|
|
5
|
+
import type { PackageScan } from './npm.mjs';
|
|
6
|
+
import type { Level } from './rules.mjs';
|
|
7
|
+
export type Finding = {
|
|
8
|
+
ruleId: string;
|
|
9
|
+
/**
|
|
10
|
+
* the rule's level when absent
|
|
11
|
+
*/
|
|
12
|
+
level?: Level;
|
|
13
|
+
message: string;
|
|
14
|
+
uri: string;
|
|
15
|
+
region?: {
|
|
16
|
+
startLine: number;
|
|
17
|
+
startColumn?: number;
|
|
18
|
+
};
|
|
19
|
+
logical?: {
|
|
20
|
+
name: string;
|
|
21
|
+
kind: string;
|
|
22
|
+
};
|
|
23
|
+
properties?: Record<string, unknown>;
|
|
24
|
+
};
|
|
25
|
+
export type FindingExtra = Omit<Finding, 'ruleId' | 'message' | 'uri'>;
|
|
26
|
+
export type ResourceInput = {
|
|
27
|
+
uri: string;
|
|
28
|
+
listMeta?: unknown;
|
|
29
|
+
readMeta?: unknown;
|
|
30
|
+
html?: string | null;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Findings about one `_meta` object of a UI resource (the resolved one: read, falling back to list).
|
|
34
|
+
* @param {unknown} meta
|
|
35
|
+
* @param {string} uri
|
|
36
|
+
*/
|
|
37
|
+
export declare function lintUiMeta(meta: unknown, uri: string): Finding[];
|
|
38
|
+
/**
|
|
39
|
+
* Findings about a UI resource as served: listing entry, read result content item, HTML.
|
|
40
|
+
* @param {ResourceInput} resource
|
|
41
|
+
*/
|
|
42
|
+
export declare function lintResource({ uri, listMeta, readMeta, html }: ResourceInput): Finding[];
|
|
43
|
+
/**
|
|
44
|
+
* Findings about the HTML of a UI resource.
|
|
45
|
+
* @param {string} html
|
|
46
|
+
* @param {string} uri
|
|
47
|
+
*/
|
|
48
|
+
export declare function lintHtml(html: string, uri: string): Finding[];
|
|
49
|
+
/**
|
|
50
|
+
* Findings about the tools of a tools/list result.
|
|
51
|
+
* @param {unknown} tools
|
|
52
|
+
* @param {string} [uri]
|
|
53
|
+
*/
|
|
54
|
+
export declare function lintTools(tools: unknown, uri?: string): Finding[];
|
|
55
|
+
/**
|
|
56
|
+
* Findings about a scanned package directory (see scanPackageDir). `root` prefixes file locations.
|
|
57
|
+
* @param {PackageScan} scan
|
|
58
|
+
* @param {string} [root]
|
|
59
|
+
*/
|
|
60
|
+
export declare function lintPackageScan(scan: PackageScan, root?: string): Finding[];
|
|
61
|
+
/**
|
|
62
|
+
* The level a rule reports at; unknown ids count as warnings.
|
|
63
|
+
* @param {string} ruleId
|
|
64
|
+
* @returns {Level}
|
|
65
|
+
*/
|
|
66
|
+
export declare function levelOf(ruleId: string): Level;
|
|
67
|
+
export { analyzeTools };
|
|
68
|
+
//# sourceMappingURL=lint.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.d.mts","sourceRoot":"","sources":["../src/lint.mjs"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAe,YAAY,EAA2D,MAAM,eAAe,CAAC;AAMhH,OAAQ,KAAA,EAAE,WAAW,EAAE,MAAM,WAAW,CACxC;AAAA,OAAQ,KAAA,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAMpC,YAAkB,OAAO,GACzB;IAAmB,MAAM,EAAd,MAAM,CACjB;;;;IAAmB,KAAK,AAAxB,CACA,EADW,KAAK,CAChB;IAAmB,OAAO,EAAf,MAAM,CACjB;IAAmB,GAAG,EAAX,MAAM,CACjB;IAAyD,MAAM,AAA/D,CACA,EADW;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CACtD;IAA4C,OAAO,AAAnD,CACA,EADW;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CACzC;IAAqC,UAAU,AAA/C,CAAgD,EAArC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAc;CAAA,CAAA;AAE/C,YAAuD,YAAY,GAAzD,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC,CAAc;AAIpE,YAAkB,aAAa,GAC/B;IAAmB,GAAG,EAAX,MAAM,CACjB;IAAqB,QAAQ,AAA7B,CACA,EADW,OAAO,CAClB;IAAqB,QAAQ,AAA7B,CACA,EADW,OAAO,CAClB;IAA2B,IAAI,AAA/B,CAAgC,EAArB,MAAM,GAAG,IAAI,CAAQ;CAAA,CAAA;AAsCnC;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAHpB,OAGoB,EAAE,GAAG,EAFzB,MAEyB,aAgBnC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAAE,GAAG,EAAE,QAAe,EAAE,QAAe,EAAE,IAAW,EAAE,EAFxE,aAEwE,aAYlF;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAHlB,MAGkB,EAAE,GAAG,EAFvB,MAEuB,aAEjC;AAiCD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAHpB,OAGoB,EAAE,GAAG,AAFjC,CAAqB,EAAb,MAEwC,aAelD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAHzB,WAGyB,EAAE,IAAI,AAFvC,CAAsB,EAAd,MAEqC,aAqB/C;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAHnB,MAGmB,GAFjB,KAAK,CAIjB;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
package/dist/npm.d.mts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { CspKey, HtmlAnalysis } from './analyze.mjs';
|
|
2
|
+
export type PackageMeta = {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
description: string;
|
|
6
|
+
repository: string | null;
|
|
7
|
+
tarball: string | null;
|
|
8
|
+
unpackedSize: number | null;
|
|
9
|
+
/**
|
|
10
|
+
* the UI SDKs among its dependencies (see UI_SDKS)
|
|
11
|
+
*/
|
|
12
|
+
uiSdks: string[];
|
|
13
|
+
dependsOnMcpSdk: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type ScanOptions = {
|
|
16
|
+
/**
|
|
17
|
+
* tarball size cap, 25 MiB by default
|
|
18
|
+
*/
|
|
19
|
+
maxBytes?: number;
|
|
20
|
+
/**
|
|
21
|
+
* download timeout, 60 s by default
|
|
22
|
+
*/
|
|
23
|
+
timeoutMs?: number;
|
|
24
|
+
/**
|
|
25
|
+
* files walked, 4000 by default
|
|
26
|
+
*/
|
|
27
|
+
maxFiles?: number;
|
|
28
|
+
/**
|
|
29
|
+
* larger files are skipped, 2 MiB by default
|
|
30
|
+
*/
|
|
31
|
+
maxFileBytes?: number;
|
|
32
|
+
};
|
|
33
|
+
export type DomainSite = {
|
|
34
|
+
key: CspKey;
|
|
35
|
+
raw: string;
|
|
36
|
+
file: string;
|
|
37
|
+
line: number;
|
|
38
|
+
};
|
|
39
|
+
export type HtmlDocumentScan = HtmlAnalysis & {
|
|
40
|
+
file: string;
|
|
41
|
+
};
|
|
42
|
+
export declare const UI_SDKS: string[];
|
|
43
|
+
/**
|
|
44
|
+
* All packages a search query returns (the API pages by `from`, 250 per page).
|
|
45
|
+
* @param {string} text
|
|
46
|
+
* @param {{ max?: number }} [options]
|
|
47
|
+
* @returns {Promise<string[]>}
|
|
48
|
+
*/
|
|
49
|
+
export declare function searchPackages(text: string, { max }?: {
|
|
50
|
+
max?: number;
|
|
51
|
+
}): Promise<string[]>;
|
|
52
|
+
/**
|
|
53
|
+
* Latest version manifest (small): dependencies, tarball, size.
|
|
54
|
+
* @param {string} name
|
|
55
|
+
* @returns {Promise<PackageMeta>}
|
|
56
|
+
*/
|
|
57
|
+
export declare function packageMeta(name: string): Promise<PackageMeta>;
|
|
58
|
+
/**
|
|
59
|
+
* Streams a tarball to `dest`, aborting over the size cap or the timeout. Resolves to the bytes received.
|
|
60
|
+
* @param {string} url
|
|
61
|
+
* @param {string} dest
|
|
62
|
+
* @param {ScanOptions} [options]
|
|
63
|
+
*/
|
|
64
|
+
export declare function downloadTarball(url: string, dest: string, { maxBytes, timeoutMs }?: ScanOptions): Promise<number>;
|
|
65
|
+
/**
|
|
66
|
+
* Unpacks a .tgz into `dir` with the system tar.
|
|
67
|
+
* @param {string} file
|
|
68
|
+
* @param {string} dir
|
|
69
|
+
*/
|
|
70
|
+
export declare function extractTarball(file: string, dir: string): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* What an unpacked package says about its UI resources. Regex-based on purpose: a census, not a compiler.
|
|
73
|
+
* @param {string} dir
|
|
74
|
+
* @param {ScanOptions} [options]
|
|
75
|
+
*/
|
|
76
|
+
export declare function scanPackageDir(dir: string, { maxFiles, maxFileBytes }?: ScanOptions): Promise<{
|
|
77
|
+
files: number;
|
|
78
|
+
sourceFiles: number;
|
|
79
|
+
appResourceRegistrations: number;
|
|
80
|
+
uiMimeMentions: number;
|
|
81
|
+
uiUriMentions: number;
|
|
82
|
+
cspArrays: number;
|
|
83
|
+
cspDynamic: number;
|
|
84
|
+
domainSites: DomainSite[];
|
|
85
|
+
/** @type {Record<CspKey, string[]>} */
|
|
86
|
+
domains: Record<CspKey, string[]>;
|
|
87
|
+
toolVisibility: {
|
|
88
|
+
app: number;
|
|
89
|
+
model: number;
|
|
90
|
+
};
|
|
91
|
+
readOnlyHints: number;
|
|
92
|
+
html: HtmlDocumentScan[];
|
|
93
|
+
embeddedHtml: number;
|
|
94
|
+
permissions: string[];
|
|
95
|
+
domainsClassified: Record<CspKey, {
|
|
96
|
+
count: number;
|
|
97
|
+
wildcards: number;
|
|
98
|
+
fullWildcards: number;
|
|
99
|
+
insecure: number;
|
|
100
|
+
sinks: import("@render-policy/core").SinkCategory[];
|
|
101
|
+
tenantSinks: import("@render-policy/core").SinkCategory[];
|
|
102
|
+
hosts: string[];
|
|
103
|
+
categories: ("analytics" | "api" | "cdn" | "development" | "every-host" | "fonts" | "local-scheme" | "maps" | "media" | "other" | "sink" | "storage")[];
|
|
104
|
+
}>;
|
|
105
|
+
declaresUi: boolean;
|
|
106
|
+
cspDeclared: boolean;
|
|
107
|
+
servesUi: boolean;
|
|
108
|
+
}>;
|
|
109
|
+
export type PackageScan = Awaited<ReturnType<typeof scanPackageDir>>;
|
|
110
|
+
/** @typedef {Awaited<ReturnType<typeof scanPackageDir>>} PackageScan */
|
|
111
|
+
/**
|
|
112
|
+
* Download, extract, scan, clean up.
|
|
113
|
+
* @param {Pick<PackageMeta, 'name' | 'tarball'>} meta
|
|
114
|
+
* @param {string} workDir a directory the download and the unpacked files go to; both are removed
|
|
115
|
+
* @param {ScanOptions} [options]
|
|
116
|
+
*/
|
|
117
|
+
export declare function scanPackage(meta: Pick<PackageMeta, 'name' | 'tarball'>, workDir: string, options?: ScanOptions): Promise<{
|
|
118
|
+
files: number;
|
|
119
|
+
sourceFiles: number;
|
|
120
|
+
appResourceRegistrations: number;
|
|
121
|
+
uiMimeMentions: number;
|
|
122
|
+
uiUriMentions: number;
|
|
123
|
+
cspArrays: number;
|
|
124
|
+
cspDynamic: number;
|
|
125
|
+
domainSites: DomainSite[];
|
|
126
|
+
/** @type {Record<CspKey, string[]>} */
|
|
127
|
+
domains: Record<CspKey, string[]>;
|
|
128
|
+
toolVisibility: {
|
|
129
|
+
app: number;
|
|
130
|
+
model: number;
|
|
131
|
+
};
|
|
132
|
+
readOnlyHints: number;
|
|
133
|
+
html: HtmlDocumentScan[];
|
|
134
|
+
embeddedHtml: number;
|
|
135
|
+
permissions: string[];
|
|
136
|
+
domainsClassified: Record<CspKey, {
|
|
137
|
+
count: number;
|
|
138
|
+
wildcards: number;
|
|
139
|
+
fullWildcards: number;
|
|
140
|
+
insecure: number;
|
|
141
|
+
sinks: import("@render-policy/core").SinkCategory[];
|
|
142
|
+
tenantSinks: import("@render-policy/core").SinkCategory[];
|
|
143
|
+
hosts: string[];
|
|
144
|
+
categories: ("analytics" | "api" | "cdn" | "development" | "every-host" | "fonts" | "local-scheme" | "maps" | "media" | "other" | "sink" | "storage")[];
|
|
145
|
+
}>;
|
|
146
|
+
declaresUi: boolean;
|
|
147
|
+
cspDeclared: boolean;
|
|
148
|
+
servesUi: boolean;
|
|
149
|
+
ok: boolean;
|
|
150
|
+
bytes: number;
|
|
151
|
+
}>;
|
|
152
|
+
//# sourceMappingURL=npm.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"npm.d.mts","sourceRoot":"","sources":["../src/npm.mjs"],"names":[],"mappings":"AAgBG,OAAQ,KAAA,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,eAAe,CACrD;AAIA,YAAkB,WAAW,GAC7B;IAAmB,IAAI,EAAZ,MAAM,CACjB;IAAmB,OAAO,EAAf,MAAM,CACjB;IAAmB,WAAW,EAAnB,MAAM,CACjB;IAA0B,UAAU,EAAzB,MAAM,GAAG,IAAI,CACxB;IAA0B,OAAO,EAAtB,MAAM,GAAG,IAAI,CACxB;IAA0B,YAAY,EAA3B,MAAM,GAAG,IAAI,CACxB;;;;IAAqB,MAAM,EAAhB,MAAM,EAAE,CACnB;IAAoB,eAAe,EAAxB,OAAO,CACpB;CAAA,CAAA;AAGE,YAAkB,WAAW,GAC7B;;;;IAAoB,QAAQ,AAA5B,CACA,EADW,MAAM,CACjB;;;;IAAoB,SAAS,AAA7B,CACA,EADW,MAAM,CACjB;;;;IAAoB,QAAQ,AAA5B,CACA,EADW,MAAM,CACjB;;;;IAAoB,YAAY,AAAhC,CACF,EADa,MAAM,CACnB;CAAA,CAAA;AAGE,YAAoE,UAAU,GAApE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAY;AAE7E,YAA2C,gBAAgB,GAAjD,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAkB;AAK/D,eAAO,MAAM,OAAO,UAAyE,CAAC;AAkB9F;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAJ9B,MAI8B,EAAE,EAAE,GAAU,EAAE,AAHtD,CACA,EADQ;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAGqC,GAFjD,OAAO,CAAC,MAAM,EAAE,CAAC,CAY7B;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAH3B,MAG2B,GAFzB,OAAO,CAAC,WAAW,CAAC,CAgBhC;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAJ9B,MAI8B,EAAE,IAAI,EAHpC,MAGoC,EAAE,EAAE,QAA2B,EAAE,SAAiB,EAAE,AAFhG,CAA8B,EAAtB,WAE6F,mBAmBvG;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAH9B,MAG8B,EAAE,GAAG,EAFnC,MAEmC,iBAG7C;AA2CD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAH7B,MAG6B,EAAE,EAAE,QAAe,EAAE,YAA8B,EAAE,AAF1F,CAA8B,EAAtB,WAEuF;;;;;;;;iBAUtE,UAAU,EAAE;IACpC,uCAAuC;aAA5B,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;;QAGjB,GAAG;QAAK,KAAK;;;UAEd,gBAAgB,EAAE;;;;;;;;;;;;;;;;GA2EtC;AAEG,YAAsD,WAAW,GAAvD,OAAO,CAAC,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAa;AAArE,wEAAwE;AAExE;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAJ3B,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAIT,EAAE,OAAO,EAHpC,MAGoC,EAAE,OAAO,AAFrD,CAA8B,EAAtB,WAEkD;;;;;;;;iBA3FjC,UAAU,EAAE;IACpC,uCAAuC;aAA5B,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;;QAGjB,GAAG;QAAK,KAAK;;;UAEd,gBAAgB,EAAE;;;;;;;;;;;;;;;;;;GAmGtC"}
|
package/dist/rules.d.mts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** @typedef {'error' | 'warning' | 'note'} Level */
|
|
2
|
+
/**
|
|
3
|
+
* A rule in SARIF terms: `short` and `full` become its short and full description.
|
|
4
|
+
* @typedef {{ id: string, name: string, level: Level, short: string, full: string }} Rule
|
|
5
|
+
*/
|
|
6
|
+
export type Level = 'error' | 'warning' | 'note';
|
|
7
|
+
export type Rule = {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
level: Level;
|
|
11
|
+
short: string;
|
|
12
|
+
full: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* The rules, in SARIF terms. Ids are stable; names are what a scanner shows.
|
|
16
|
+
* @type {readonly Rule[]}
|
|
17
|
+
*/
|
|
18
|
+
export declare const RULES: readonly Rule[];
|
|
19
|
+
export declare const RULE_INDEX: Map<string, number>;
|
|
20
|
+
/** @param {string} name */
|
|
21
|
+
export declare const ruleByName: (name: string) => Rule | undefined;
|
|
22
|
+
//# sourceMappingURL=rules.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../src/rules.mjs"],"names":[],"mappings":"AAAA,oDAAoD;AACpD;;;GAGG;AAJC,YAAwC,KAAK,GAAnC,OAAO,GAAG,SAAS,GAAG,MAAM,CAAO;AAG9C,YAAmF,IAAI,GAA7E;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAM;AAG1F;;;GAGG;AACH,eAAO,MAAM,KAAK,EAFR,SAAS,IAAI,EAsBtB,CAAC;AAEF,eAAO,MAAM,UAAU,qBAAwD,CAAC;AAChF,2BAA2B;AAC3B,eAAO,MAAM,UAAU,SADX,MAAM,qBACoD,CAAC"}
|