mcp-app-lint 0.1.1 → 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 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 (request catchers, tunnels, form builders, public buckets) |
62
- | MCPAPP006 | csp-development-origin | note | localhost, 127.0.0.1, `blob:` in published metadata |
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"}
@@ -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"}
@@ -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"}
@@ -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"}