mcp-app-lint 0.1.5 → 0.1.7
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 +105 -5
- package/dist/a2ui.d.mts +28 -0
- package/dist/a2ui.d.mts.map +1 -0
- package/dist/analyze.d.mts +1 -0
- package/dist/analyze.d.mts.map +1 -1
- package/dist/cli.d.mts +13 -0
- package/dist/cli.d.mts.map +1 -1
- package/dist/client.d.mts +178 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/csp.d.mts +52 -0
- package/dist/csp.d.mts.map +1 -0
- package/dist/index.d.mts +15 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/lint.d.mts +26 -1
- package/dist/lint.d.mts.map +1 -1
- package/dist/rules.d.mts +4 -2
- package/dist/rules.d.mts.map +1 -1
- package/dist/sarif.d.mts +1 -1
- package/dist/server.d.mts +33 -0
- package/dist/server.d.mts.map +1 -0
- package/package.json +6 -4
- package/src/a2ui.mjs +360 -0
- package/src/analyze.mjs +3 -0
- package/src/cli.mjs +168 -16
- package/src/client.mjs +454 -0
- package/src/csp.mjs +158 -0
- package/src/index.mjs +10 -1
- package/src/lint.mjs +73 -10
- package/src/rules.mjs +14 -2
- package/src/sarif.mjs +1 -1
- package/src/server.mjs +243 -0
package/README.md
CHANGED
|
@@ -4,15 +4,64 @@ SARIF findings about what an MCP App declares and what its HTML does. Meant to b
|
|
|
4
4
|
scanners and CI, or by hand on a package, a directory, or the JSON a server returned.
|
|
5
5
|
|
|
6
6
|
```
|
|
7
|
+
npx mcp-app-lint --url https://example.com/mcp # a running server over Streamable HTTP
|
|
8
|
+
npx mcp-app-lint --stdio "node dist/server.js" # a local server, started as a process
|
|
7
9
|
npx mcp-app-lint --dir ./my-server # a package directory, built or source
|
|
8
10
|
npx mcp-app-lint --package @scope/name # an npm package: downloaded, scanned, deleted
|
|
9
11
|
npx mcp-app-lint --read read.json --list list.json --tools tools.json
|
|
10
|
-
# results of resources/read, resources/list, tools/list
|
|
12
|
+
# saved results of resources/read, resources/list, tools/list
|
|
11
13
|
npx mcp-app-lint --html app.html # one UI document
|
|
14
|
+
npx mcp-app-lint --a2ui stream.jsonl # A2UI v0.9 messages: one, a JSON array, { messages } or JSONL
|
|
15
|
+
npx mcp-app-lint --html dist/app.html --suggest-csp # the _meta.ui.csp the document needs
|
|
16
|
+
npx mcp-app-lint --mcp # run as an MCP server over stdio (see below)
|
|
12
17
|
```
|
|
13
18
|
|
|
14
19
|
Options: `--out file.sarif`, `--format sarif|text`, `--fail-on error|warning|note|none` (default
|
|
15
|
-
`error`; the exit code is 1 when a finding reaches the threshold, 2 on usage errors
|
|
20
|
+
`error`; the exit code is 1 when a finding reaches the threshold, 2 on usage errors and on a
|
|
21
|
+
server that cannot be reached), `--header "Name: value"` (repeatable, for `--url`), `--timeout ms`
|
|
22
|
+
(per request to a live server, default 20000), `--suggest-csp`.
|
|
23
|
+
|
|
24
|
+
## A running server
|
|
25
|
+
|
|
26
|
+
`--url` and `--stdio` ask the server what it publishes, the same way a host does: `initialize`,
|
|
27
|
+
`resources/list`, `resources/read` for every UI resource (`text/html` or `ui://`), `tools/list`.
|
|
28
|
+
They never call a tool. Findings point at the resource (`ui://…`) or at `tools/list`, and a summary
|
|
29
|
+
of what was read goes to stderr, so stdout stays a clean SARIF log.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
npx mcp-app-lint --url https://example.com/mcp --header "Authorization: Bearer $TOKEN" --format text
|
|
33
|
+
npx mcp-app-lint --stdio "node dist/index.js --stdio" --format text
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`--url` speaks Streamable HTTP (JSON or SSE answers, session header, a `DELETE` to end the session).
|
|
37
|
+
A 401 or 403 means the server wants credentials: pass them with `--header`. `--stdio` runs the
|
|
38
|
+
command through the shell in the current directory, keeps the server's stderr out of the output
|
|
39
|
+
(its last lines go into the error if it exits), answers its `ping`, and closes stdin when done. The
|
|
40
|
+
SARIF log records the command line with header values, URL credentials and query values, and
|
|
41
|
+
`NAME=value` assignments in the `--stdio` command masked, since code scanning stores it.
|
|
42
|
+
|
|
43
|
+
## Suggested CSP
|
|
44
|
+
|
|
45
|
+
`--suggest-csp` prints, instead of findings, the `_meta.ui.csp` each UI document needs, as JSON
|
|
46
|
+
keyed by document (or one block per document with `--format text`):
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"ui://weather/view": {
|
|
51
|
+
"connectDomains": ["https://api.weather.example", "wss://live.weather.example"],
|
|
52
|
+
"resourceDomains": ["https://fonts.googleapis.com", "https://fonts.gstatic.com", "https://img.weather.example"]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The keys follow the host formula in the specification: `connectDomains` for `fetch`, `WebSocket`,
|
|
58
|
+
`EventSource`, XHR and `sendBeacon`; `resourceDomains` for scripts (including module imports from a
|
|
59
|
+
URL), stylesheets and CSS `url()`/`@import`, images (`src`, `srcset`, `poster`) and media;
|
|
60
|
+
`frameDomains` for iframes. Links (`<a href>`, `rel="canonical"`, `preconnect`) need nothing. A
|
|
61
|
+
Google Fonts stylesheet brings `fonts.gstatic.com` with it. It is static: a URL the script assembles
|
|
62
|
+
at run time is not seen, so run it on the built document and add such hosts by hand. `null` means
|
|
63
|
+
the document references no external origin and the restrictive default fits it. MCPAPP001 carries
|
|
64
|
+
the same suggestion in its message and in `properties.suggestedCsp`.
|
|
16
65
|
|
|
17
66
|
## Use in CI (GitHub code scanning)
|
|
18
67
|
|
|
@@ -41,7 +90,8 @@ Inputs: `directory` (default `.`), `fail-on` (default `error`), `upload-sarif` (
|
|
|
41
90
|
`sarif-file` (default `mcp-app-lint.sarif`), `version` (npm dist-tag or version, default `latest`),
|
|
42
91
|
`args` (passed through), `node-version` (default `22`, empty to use the runner's Node). The SARIF
|
|
43
92
|
is written even when findings fail the job, so it still uploads; paths are repo-relative so code
|
|
44
|
-
scanning maps them to the source.
|
|
93
|
+
scanning maps them to the source. Recorded A2UI streams go through `args`, for example
|
|
94
|
+
`args: --a2ui test/fixtures/surface.jsonl`.
|
|
45
95
|
|
|
46
96
|
Any CI can run the CLI directly and read the exit code (`0` clean, `1` findings at or above
|
|
47
97
|
`--fail-on`, `2` usage error):
|
|
@@ -73,6 +123,31 @@ npx mcp-app-lint --dir . --format sarif --out mcp-app-lint.sarif --fail-on error
|
|
|
73
123
|
| MCPAPP017 | meta-list-read-mismatch | warning | `resources/read` and `resources/list` both declare a field (`csp`, `permissions`, `domain`) with different values; a listing with no CSP is not a mismatch, since the SDK puts the CSP in the read result |
|
|
74
124
|
| MCPAPP018 | meta-read-wider-than-list | error | both declare a CSP and the read-time one adds an entry (domain or wildcard) the listing did not declare under the same key |
|
|
75
125
|
| 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 |
|
|
126
|
+
| MCPAPP020 | csp-unused-domain | note | a declared entry whose host the document never mentions: the policy is wider than the UI needs. Only where the exact document is known (a read result, a live server); a host the script assembles from parts at run time is reported too |
|
|
127
|
+
| MCPAPP021 | csp-host-undeclared | warning | the UI declares a CSP, but the document loads from or calls an origin under a key that does not allow it (see Suggested CSP); the host blocks the request and the UI breaks |
|
|
128
|
+
|
|
129
|
+
### A2UI rules
|
|
130
|
+
|
|
131
|
+
`--a2ui` reads A2UI v0.9 server-to-client messages (`createSurface`, `updateComponents`,
|
|
132
|
+
`updateDataModel`, `deleteSurface`). A2UI sends no HTML; what matters is where the agent's data may
|
|
133
|
+
flow. Absolute `{ "path": … }` bindings and `formatString` interpolations are resolved against the
|
|
134
|
+
`updateDataModel` messages of the same stream after every message, so a value swapped after the
|
|
135
|
+
first render is checked too, at the line of the update that brought it. Relative paths in list
|
|
136
|
+
templates and client function calls are left to the runtime check
|
|
137
|
+
([`@render-policy/a2ui`](../a2ui)); this scan is a CI aid, not a substitute for it.
|
|
138
|
+
|
|
139
|
+
| Id | Name | Level | What it means |
|
|
140
|
+
| --- | --- | --- | --- |
|
|
141
|
+
| A2UI001 | a2ui-openurl-scheme | error | `openUrl` target that does not resolve to http or https (`javascript:`, `data:`, `mailto:`), as the basic catalog requires |
|
|
142
|
+
| A2UI002 | a2ui-sink-host | error | `Image`, `Video` or `AudioPlayer` url, `theme.iconUrl` or an `openUrl` target on a sink host anyone can use |
|
|
143
|
+
| A2UI003 | a2ui-url-encoded-payload | warning | media or icon URL, fetched without a click, that is too long or carries a long high-entropy token |
|
|
144
|
+
| A2UI004 | a2ui-url-template-host | warning | `formatString` takes the scheme or host of a media URL or `openUrl` target from data |
|
|
145
|
+
| A2UI005 | a2ui-url-template-data | note | `formatString` puts data into the path or query of a media URL |
|
|
146
|
+
| A2UI006 | a2ui-text-markup | warning | `Text` contains raw HTML, a Markdown image or a link (bare URLs count; code spans and fences do not), which the basic catalog excludes |
|
|
147
|
+
| A2UI007 | a2ui-remote-media-hosts | note | the hosts media and icons load from, for the client's allowlist |
|
|
148
|
+
|
|
149
|
+
On the 44 examples of the v0.9 basic catalog the scan reports A2UI007 notes and one A2UI006: the
|
|
150
|
+
Markdown example puts a link in `Text`.
|
|
76
151
|
|
|
77
152
|
Hand-written means an inline script with short lines and not too many of them; bundles carry
|
|
78
153
|
framework internals and the SDK bridge (which posts to `'*'` by design) and are not reported
|
|
@@ -86,10 +161,35 @@ Apps MIME type or `ui://` (hosts, renderers, SDKs) are not treated as serving a
|
|
|
86
161
|
looked for in the package's HTML documents only: a `fetch` in server code is not bound by the CSP,
|
|
87
162
|
so it does not count.
|
|
88
163
|
|
|
89
|
-
|
|
164
|
+
## As an MCP server
|
|
165
|
+
|
|
166
|
+
`mcp-app-lint --mcp` serves the checks over stdio, so an agent that builds an MCP App can run them
|
|
167
|
+
while it works. Install the package, then register it with the client, for example in Claude Code:
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
npm install --global mcp-app-lint
|
|
171
|
+
claude mcp add mcp-app-lint -- mcp-app-lint --mcp
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
| Tool | Input | Output |
|
|
175
|
+
| --- | --- | --- |
|
|
176
|
+
| `lint_ui_resource` | `html`, `meta` (the read result's `_meta`), `list_meta`, `uri` | findings for the document and its `_meta.ui` |
|
|
177
|
+
| `suggest_csp` | `html` | the `_meta.ui.csp` the document needs, or `null` |
|
|
178
|
+
| `lint_tools` | `tools` (a `tools/list` array) | findings |
|
|
179
|
+
| `lint_a2ui` | `messages` (array, JSON or JSONL text) | findings |
|
|
180
|
+
| `lint_directory` | `path` of a package directory | findings, with file and line |
|
|
181
|
+
| `lint_server` | `url` of a Streamable HTTP endpoint, `headers` | findings for what the server publishes |
|
|
182
|
+
|
|
183
|
+
Every tool is marked read-only. Findings come as text (one line each) and as `structuredContent`
|
|
184
|
+
(`counts` by level and the findings). No tool starts a process: a local stdio server is linted from
|
|
185
|
+
the command line with `--stdio`, where the user sees the command.
|
|
186
|
+
|
|
187
|
+
The MCP Apps rules come out of the [MCP Apps UI census](../../census) in this repository; the analyzer is
|
|
90
188
|
shared. The HTML sink detection is `eslint-plugin-render-policy`'s `no-unsafe-innerhtml`. The sink
|
|
91
189
|
denylist is `@render-policy/core`'s. The package also exports the analysis functions (`analyzeHtml`,
|
|
92
|
-
`analyzeUiMeta`, `compareListRead`, `scanPackageDir`,
|
|
190
|
+
`analyzeUiMeta`, `compareListRead`, `scanPackageDir`, `lintA2uiMessages`, `parseA2uiMessages`,
|
|
191
|
+
`cspNeeds`, `suggestCsp`, `lintSnapshot`, …), the read-only clients (`McpHttpClient`,
|
|
192
|
+
`McpStdioClient`, `snapshotServer`), the MCP server (`serveMcp`, `handleMessage`) and `categorizeDomain`, the heuristic host
|
|
93
193
|
category (fonts, analytics, maps, storage, media, cdn, api, …) the census report tabulates; it is
|
|
94
194
|
data for aggregates, not a rule.
|
|
95
195
|
|
package/dist/a2ui.d.mts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Finding } from './lint.mjs';
|
|
2
|
+
export type A2uiEntry = {
|
|
3
|
+
message: unknown;
|
|
4
|
+
line?: number;
|
|
5
|
+
};
|
|
6
|
+
export type UrlUse = 'fetched' | 'openUrl';
|
|
7
|
+
export type Place = {
|
|
8
|
+
surfaceId: string;
|
|
9
|
+
componentId?: string;
|
|
10
|
+
property: string;
|
|
11
|
+
dataPath?: string;
|
|
12
|
+
line?: number;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Messages from the text of a file: one message, an array of them, an object with `messages` (the
|
|
16
|
+
* list wrapper and the specification's samples), or JSONL. Throws on a line that does not parse.
|
|
17
|
+
* @param {string} text
|
|
18
|
+
* @returns {A2uiEntry[]}
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseA2uiMessages(text: string): A2uiEntry[];
|
|
21
|
+
/**
|
|
22
|
+
* Findings over a stream of A2UI v0.9 messages. `uri` is the file the messages came from.
|
|
23
|
+
* @param {readonly (A2uiEntry | unknown)[]} input messages, or entries with their line
|
|
24
|
+
* @param {string} uri
|
|
25
|
+
* @returns {Finding[]}
|
|
26
|
+
*/
|
|
27
|
+
export declare function lintA2uiMessages(input: readonly (A2uiEntry | unknown)[], uri: string): Finding[];
|
|
28
|
+
//# sourceMappingURL=a2ui.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a2ui.d.mts","sourceRoot":"","sources":["../src/a2ui.mjs"],"names":[],"mappings":"AAiBG,OAAQ,KAAA,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAIrC,YAA+C,SAAS,GAA9C;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAW;AAEvD,YAAiC,MAAM,GAA7B,SAAS,GAAG,SAAS,CAAQ;AAGxC,YAA2G,KAAK,GAAtG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAO;AAmBnH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAH3B,MAG2B,GAFzB,SAAS,EAAE,CA4BvB;AA4GD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAJ3B,SAAS,CAAC,SAAS,GAAG,OAAO,CAAC,EAIH,EAAE,GAAG,EAHhC,MAGgC,GAF9B,OAAO,EAAE,CA0KrB"}
|
package/dist/analyze.d.mts
CHANGED
|
@@ -137,6 +137,7 @@ export declare function analyzeHtml(html: unknown): {
|
|
|
137
137
|
externalHosts: string[];
|
|
138
138
|
networkHosts: string[];
|
|
139
139
|
sinkHosts: string[];
|
|
140
|
+
cspNeeds: import("./csp.mjs").CspNeeds;
|
|
140
141
|
};
|
|
141
142
|
/**
|
|
142
143
|
* Short lines and not too many of them: written by a person, not emitted by a bundler.
|
package/dist/analyze.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze.d.mts","sourceRoot":"","sources":["../src/analyze.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"analyze.d.mts","sourceRoot":"","sources":["../src/analyze.mjs"],"names":[],"mappings":"AAiBG,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;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAH7B,OAG6B,EAAE,QAAQ,EAFvC,OAEuC;;;;;;;;;;;;;;EAWjD;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;AAqBD;;;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;kBAET,MAAM,EAAE;eACX,MAAM,EAAE;;EAmDjC;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
CHANGED
|
@@ -8,9 +8,22 @@ export type CliOptions = {
|
|
|
8
8
|
list?: string;
|
|
9
9
|
tools?: string;
|
|
10
10
|
html?: string;
|
|
11
|
+
a2ui?: string;
|
|
12
|
+
url?: string;
|
|
13
|
+
stdio?: string;
|
|
14
|
+
headers: Record<string, string>;
|
|
15
|
+
timeout?: number;
|
|
11
16
|
out?: string;
|
|
17
|
+
suggestCsp?: boolean;
|
|
18
|
+
mcp?: boolean;
|
|
12
19
|
help?: boolean;
|
|
13
20
|
};
|
|
21
|
+
/**
|
|
22
|
+
* The command line as the SARIF log records it, which code scanning stores and shows: header values,
|
|
23
|
+
* credentials and query values in a URL, and environment assignments in a --stdio command are masked.
|
|
24
|
+
* @param {readonly string[]} argv
|
|
25
|
+
*/
|
|
26
|
+
export declare function redactArgv(argv: readonly string[]): string[];
|
|
14
27
|
/**
|
|
15
28
|
* The command line, without the process exit: resolves to the exit code (0 clean, 1 findings at or
|
|
16
29
|
* above --fail-on, 2 usage error). Throws on unreadable input or a failed download.
|
package/dist/cli.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.mts","sourceRoot":"","sources":["../src/cli.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.mts","sourceRoot":"","sources":["../src/cli.mjs"],"names":[],"mappings":"AA4BG,OAAQ,KAAA,EAAE,QAAQ,EAAE,MAAM,aAAa,CACvC;AAMA,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,IAAI,AAAxB,CACA,EADW,MAAM,CACjB;IAAoB,GAAG,AAAvB,CACA,EADW,MAAM,CACjB;IAAoB,KAAK,AAAzB,CACA,EADW,MAAM,CACjB;IAAmC,OAAO,EAA/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CACjC;IAAoB,OAAO,AAA3B,CACA,EADW,MAAM,CACjB;IAAoB,GAAG,AAAvB,CACA,EADW,MAAM,CACjB;IAAqB,UAAU,AAA/B,CACA,EADW,OAAO,CAClB;IAAqB,GAAG,AAAxB,CACA,EADW,OAAO,CAClB;IAAqB,IAAI,AAAzB,CAA0B,EAAf,OAAO,CAAQ;CAAA,CAAA;AA+G7B;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAFpB,SAAS,MAAM,EAEK,YAoB9B;AAED;;;;;;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,CAkG3B"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { ChildProcessWithoutNullStreams } from 'node:child_process';
|
|
2
|
+
export type ClientOptions = {
|
|
3
|
+
/**
|
|
4
|
+
* per request
|
|
5
|
+
*/
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
/**
|
|
8
|
+
* `clientInfo.name` sent at initialize
|
|
9
|
+
*/
|
|
10
|
+
clientName?: string;
|
|
11
|
+
clientVersion?: string;
|
|
12
|
+
};
|
|
13
|
+
export type ServerSnapshot = {
|
|
14
|
+
serverInfo: Record<string, any> | null;
|
|
15
|
+
/**
|
|
16
|
+
* the resources/list entries
|
|
17
|
+
*/
|
|
18
|
+
resources: any[];
|
|
19
|
+
/**
|
|
20
|
+
* the resources/read content items of the UI resources
|
|
21
|
+
*/
|
|
22
|
+
contents: any[];
|
|
23
|
+
/**
|
|
24
|
+
* the tools/list entries ([] when the server has no tools capability)
|
|
25
|
+
*/
|
|
26
|
+
tools: any[];
|
|
27
|
+
/**
|
|
28
|
+
* reads that failed; the rest is still linted
|
|
29
|
+
*/
|
|
30
|
+
errors: {
|
|
31
|
+
uri: string;
|
|
32
|
+
message: string;
|
|
33
|
+
}[];
|
|
34
|
+
};
|
|
35
|
+
export declare const UI_MIME_PREFIX = "text/html";
|
|
36
|
+
/** @type {(r: any) => boolean} */
|
|
37
|
+
export declare const isUiResource: (r: any) => boolean;
|
|
38
|
+
/**
|
|
39
|
+
* initialize, then the requests every client shares. Subclasses provide `request` and `notify`.
|
|
40
|
+
*/
|
|
41
|
+
declare class BaseClient {
|
|
42
|
+
timeoutMs: number;
|
|
43
|
+
clientName: string;
|
|
44
|
+
clientVersion: string;
|
|
45
|
+
nextId: number;
|
|
46
|
+
/** @type {Record<string, any> | null} */
|
|
47
|
+
serverInfo: Record<string, any> | null;
|
|
48
|
+
/** @type {Record<string, any>} */
|
|
49
|
+
capabilities: Record<string, any>;
|
|
50
|
+
/** @param {ClientOptions} options */
|
|
51
|
+
constructor({ timeoutMs, clientName, clientVersion }?: ClientOptions);
|
|
52
|
+
/**
|
|
53
|
+
* @param {string} method
|
|
54
|
+
* @param {Record<string, unknown>} [params]
|
|
55
|
+
* @returns {Promise<any>}
|
|
56
|
+
*/
|
|
57
|
+
request(method: string, params?: Record<string, unknown>): Promise<any>;
|
|
58
|
+
/**
|
|
59
|
+
* @param {string} method
|
|
60
|
+
* @param {Record<string, unknown>} [params]
|
|
61
|
+
* @returns {Promise<void>}
|
|
62
|
+
*/
|
|
63
|
+
notify(method: string, params?: Record<string, unknown>): Promise<void>;
|
|
64
|
+
initialize(): Promise<any>;
|
|
65
|
+
/**
|
|
66
|
+
* Every page of a paginated list (at most 50 pages).
|
|
67
|
+
* @param {string} method
|
|
68
|
+
* @param {string} key
|
|
69
|
+
*/
|
|
70
|
+
listAll(method: string, key: string): Promise<any[]>;
|
|
71
|
+
listResources(): Promise<any[]>;
|
|
72
|
+
listTools(): Promise<any[]>;
|
|
73
|
+
/** @param {string} uri */
|
|
74
|
+
readResource(uri: string): Promise<any>;
|
|
75
|
+
close(): Promise<void>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Streamable HTTP. `headers` go on every request (for example an Authorization header).
|
|
79
|
+
*/
|
|
80
|
+
export declare class McpHttpClient extends BaseClient {
|
|
81
|
+
url: string;
|
|
82
|
+
fetch: typeof fetch;
|
|
83
|
+
userAgent: string;
|
|
84
|
+
headers: Record<string, string>;
|
|
85
|
+
/** @type {string | null} */
|
|
86
|
+
sessionId: string | null;
|
|
87
|
+
/**
|
|
88
|
+
* @param {string} url
|
|
89
|
+
* @param {ClientOptions & { userAgent?: string, headers?: Record<string, string>, fetch?: typeof globalThis.fetch }} [options]
|
|
90
|
+
*/
|
|
91
|
+
constructor(url: string, { userAgent, headers, fetch, ...options }?: ClientOptions & {
|
|
92
|
+
userAgent?: string;
|
|
93
|
+
headers?: Record<string, string>;
|
|
94
|
+
fetch?: typeof globalThis.fetch;
|
|
95
|
+
});
|
|
96
|
+
/**
|
|
97
|
+
* @param {{ jsonrpc: string, id?: number, method: string, params: unknown }} body
|
|
98
|
+
* @param {{ expectBody?: boolean }} [options]
|
|
99
|
+
* @returns {Promise<any>}
|
|
100
|
+
*/
|
|
101
|
+
post(body: {
|
|
102
|
+
jsonrpc: string;
|
|
103
|
+
id?: number;
|
|
104
|
+
method: string;
|
|
105
|
+
params: unknown;
|
|
106
|
+
}, { expectBody }?: {
|
|
107
|
+
expectBody?: boolean;
|
|
108
|
+
}): Promise<any>;
|
|
109
|
+
/**
|
|
110
|
+
* Ends the session on the server, if it gave one; a server that does not support it is fine.
|
|
111
|
+
* @override
|
|
112
|
+
*/
|
|
113
|
+
close(): Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The JSON-RPC message with the given id (or the first one) out of an SSE body.
|
|
117
|
+
* @param {string} text
|
|
118
|
+
* @param {number} [id]
|
|
119
|
+
* @returns {any}
|
|
120
|
+
*/
|
|
121
|
+
export declare function firstJsonRpcFromSse(text: string, id?: number): any;
|
|
122
|
+
/**
|
|
123
|
+
* A local server started as a child process: `command` runs through the shell, so it is written
|
|
124
|
+
* the way the user types it (`node dist/server.js`, `uv run server.py`). The server's stderr is
|
|
125
|
+
* kept (the last lines go into a startup error) and not printed.
|
|
126
|
+
*/
|
|
127
|
+
export declare class McpStdioClient extends BaseClient {
|
|
128
|
+
command: string;
|
|
129
|
+
/** @type {ChildProcessWithoutNullStreams} */
|
|
130
|
+
child: ChildProcessWithoutNullStreams;
|
|
131
|
+
/** @type {Map<number, { resolve: (v: any) => void, reject: (e: Error) => void, method: string, timer: NodeJS.Timeout }>} */
|
|
132
|
+
pending: Map<number, {
|
|
133
|
+
resolve: (v: any) => void;
|
|
134
|
+
reject: (e: Error) => void;
|
|
135
|
+
method: string;
|
|
136
|
+
timer: NodeJS.Timeout;
|
|
137
|
+
}>;
|
|
138
|
+
stderrTail: string;
|
|
139
|
+
buffer: string;
|
|
140
|
+
/** @type {Error | null} */
|
|
141
|
+
exited: Error | null;
|
|
142
|
+
/**
|
|
143
|
+
* @param {string} command
|
|
144
|
+
* @param {ClientOptions & { cwd?: string, env?: NodeJS.ProcessEnv }} [options]
|
|
145
|
+
*/
|
|
146
|
+
constructor(command: string, { cwd, env, ...options }?: ClientOptions & {
|
|
147
|
+
cwd?: string;
|
|
148
|
+
env?: NodeJS.ProcessEnv;
|
|
149
|
+
});
|
|
150
|
+
/** @param {Error} error */
|
|
151
|
+
fail(error: Error): void;
|
|
152
|
+
/** @param {string} chunk */
|
|
153
|
+
onData(chunk: string): void;
|
|
154
|
+
/** @param {any} message */
|
|
155
|
+
onMessage(message: any): void;
|
|
156
|
+
/** @param {unknown} message */
|
|
157
|
+
write(message: unknown): void;
|
|
158
|
+
/**
|
|
159
|
+
* Closes stdin, the shutdown the stdio transport defines, then terminates a server that lingers.
|
|
160
|
+
* @override
|
|
161
|
+
*/
|
|
162
|
+
close(): Promise<void>;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Classify an error thrown by a client: auth, egress-blocked, not-mcp, http, timeout, network, spawn, exit, protocol.
|
|
166
|
+
* @param {any} error
|
|
167
|
+
* @returns {string}
|
|
168
|
+
*/
|
|
169
|
+
export declare function failureKind(error: any): string;
|
|
170
|
+
/**
|
|
171
|
+
* Everything the linter reads from a live server: listing, the read result of every UI resource,
|
|
172
|
+
* tools. A failed read is recorded and the rest goes on; a failed initialize or listing throws.
|
|
173
|
+
* @param {BaseClient} client
|
|
174
|
+
* @returns {Promise<ServerSnapshot>}
|
|
175
|
+
*/
|
|
176
|
+
export declare function snapshotServer(client: BaseClient): Promise<ServerSnapshot>;
|
|
177
|
+
export { BaseClient };
|
|
178
|
+
//# sourceMappingURL=client.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.mts","sourceRoot":"","sources":["../src/client.mjs"],"names":[],"mappings":"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"}
|
package/dist/csp.d.mts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type CspNeeds = {
|
|
2
|
+
connectDomains: string[];
|
|
3
|
+
resourceDomains: string[];
|
|
4
|
+
frameDomains: string[];
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* The origins a document needs, by CSP key, sorted and without duplicates.
|
|
8
|
+
* @param {unknown} html
|
|
9
|
+
* @returns {CspNeeds}
|
|
10
|
+
*/
|
|
11
|
+
export declare function cspNeeds(html: unknown): CspNeeds;
|
|
12
|
+
/**
|
|
13
|
+
* A suggestion in the `_meta.ui.csp` shape: only the keys with origins, or null when the document
|
|
14
|
+
* needs none (the restrictive default fits it).
|
|
15
|
+
* @param {CspNeeds} needs
|
|
16
|
+
* @returns {Partial<CspNeeds> | null}
|
|
17
|
+
*/
|
|
18
|
+
export declare function suggestCsp(needs: CspNeeds): Partial<CspNeeds> | null;
|
|
19
|
+
/**
|
|
20
|
+
* The needs of several documents (a package with more than one), merged.
|
|
21
|
+
* @param {Iterable<Partial<CspNeeds> | undefined>} all
|
|
22
|
+
* @returns {CspNeeds}
|
|
23
|
+
*/
|
|
24
|
+
export declare function mergeNeeds(all: Iterable<Partial<CspNeeds> | undefined>): CspNeeds;
|
|
25
|
+
/**
|
|
26
|
+
* Does one declared entry allow the origin? Hosts are compared, not schemes or ports: a host that
|
|
27
|
+
* builds the CSP may widen schemes (https to wss), and a false "undeclared" is worse than a missed one.
|
|
28
|
+
* @param {string} entry
|
|
29
|
+
* @param {string} origin
|
|
30
|
+
*/
|
|
31
|
+
export declare function entryCovers(entry: string, origin: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Needed origins the declared CSP does not allow, by key. The host blocks them: a broken UI.
|
|
34
|
+
* @param {Record<string, unknown> | null | undefined} csp the declared `_meta.ui.csp`
|
|
35
|
+
* @param {CspNeeds} needs
|
|
36
|
+
* @returns {Partial<CspNeeds>}
|
|
37
|
+
*/
|
|
38
|
+
export declare function undeclaredOrigins(csp: Record<string, unknown> | null | undefined, needs: CspNeeds): Partial<CspNeeds>;
|
|
39
|
+
/**
|
|
40
|
+
* Declared entries whose host the document never mentions: wider than the UI needs. Wildcards,
|
|
41
|
+
* scheme-only and local-scheme entries are left to the other rules; `baseUriDomains` is not
|
|
42
|
+
* checked. The test is a substring search over the whole document, so a host that appears only
|
|
43
|
+
* in a URL assembled at run time from parts is reported; the rule says so.
|
|
44
|
+
* @param {Record<string, unknown> | null | undefined} csp
|
|
45
|
+
* @param {unknown} html
|
|
46
|
+
* @returns {{ key: keyof CspNeeds, entry: string }[]}
|
|
47
|
+
*/
|
|
48
|
+
export declare function unusedEntries(csp: Record<string, unknown> | null | undefined, html: unknown): {
|
|
49
|
+
key: keyof CspNeeds;
|
|
50
|
+
entry: string;
|
|
51
|
+
}[];
|
|
52
|
+
//# sourceMappingURL=csp.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csp.d.mts","sourceRoot":"","sources":["../src/csp.mjs"],"names":[],"mappings":"AAUG,YAA2F,QAAQ,GAAzF;IAAE,cAAc,EAAE,MAAM,EAAE,CAAC;IAAC,eAAe,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,CAAU;AA6BtG;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAHlB,OAGkB,GAFhB,QAAQ,CAmCpB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAHrB,QAGqB,GAFnB,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAKpC;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAHnB,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,SAAS,CAGnB,GAFjB,QAAQ,CASpB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAHtB,MAGsB,EAAE,MAAM,EAF9B,MAE8B,WAOxC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAJ1B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAIP,EAAE,KAAK,EAHjC,QAGiC,GAF/B,OAAO,CAAC,QAAQ,CAAC,CAW7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAJtB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAIX,EAAE,IAAI,EAH5B,OAG4B,GAF1B;IAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAcpD"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
|
+
export { lintA2uiMessages, parseA2uiMessages } from './a2ui.mjs';
|
|
2
|
+
export { failureKind, firstJsonRpcFromSse, isUiResource, McpHttpClient, McpStdioClient, snapshotServer } from './client.mjs';
|
|
3
|
+
export { cspNeeds, entryCovers, mergeNeeds, suggestCsp, undeclaredOrigins, unusedEntries } from './csp.mjs';
|
|
1
4
|
export { analyzeHtml, analyzeTools, analyzeUiMeta, compareListRead, ESCAPE_FUNCTIONS, isHandwritten, postMessageStarCalls, UI_MIME, uiMetaOf, withSinkHosts } from './analyze.mjs';
|
|
2
5
|
export { categorizeDomain, categorizeHost, classifyDomains, DOMAIN_CATEGORIES, parseDomainPattern, sinkFor, sinkScope } from './domains.mjs';
|
|
3
|
-
export { levelOf, lintHtml, lintPackageScan, lintResource, lintTools, lintUiMeta } from './lint.mjs';
|
|
6
|
+
export { levelOf, lintHtml, lintPackageScan, lintResource, lintSnapshot, lintTools, lintUiMeta } from './lint.mjs';
|
|
4
7
|
export { downloadTarball, extractTarball, packageMeta, scanPackage, scanPackageDir, searchPackages, UI_SDKS } from './npm.mjs';
|
|
5
8
|
export { RULES, RULE_INDEX, ruleByName } from './rules.mjs';
|
|
6
9
|
export { toSarif, toText, TOOL_VERSION } from './sarif.mjs';
|
|
7
10
|
export { run } from './cli.mjs';
|
|
11
|
+
export { handleMessage, serveMcp, TOOLS as MCP_TOOLS } from './server.mjs';
|
|
12
|
+
export type A2uiEntry = import('./a2ui.mjs').A2uiEntry;
|
|
8
13
|
export type UiMeta = import('./analyze.mjs').UiMeta;
|
|
9
14
|
export type CspKey = import('./analyze.mjs').CspKey;
|
|
10
15
|
export type HtmlAnalysis = import('./analyze.mjs').HtmlAnalysis;
|
|
11
16
|
export type HtmlSink = import('./analyze.mjs').HtmlSink;
|
|
12
17
|
export type PostMessageCall = import('./analyze.mjs').PostMessageCall;
|
|
18
|
+
export type ClientOptions = import('./client.mjs').ClientOptions;
|
|
19
|
+
export type ServerSnapshot = import('./client.mjs').ServerSnapshot;
|
|
20
|
+
export type CspNeeds = import('./csp.mjs').CspNeeds;
|
|
13
21
|
export type DomainPattern = import('./domains.mjs').DomainPattern;
|
|
14
22
|
export type DomainCategory = import('./domains.mjs').DomainCategory;
|
|
15
23
|
export type ClassifiedDomains = import('./domains.mjs').ClassifiedDomains;
|
|
16
24
|
export type SinkScope = import('./domains.mjs').SinkScope;
|
|
17
25
|
export type Finding = import('./lint.mjs').Finding;
|
|
18
26
|
export type ResourceInput = import('./lint.mjs').ResourceInput;
|
|
27
|
+
export type Snapshot = import('./lint.mjs').Snapshot;
|
|
19
28
|
export type PackageMeta = import('./npm.mjs').PackageMeta;
|
|
20
29
|
export type PackageScan = import('./npm.mjs').PackageScan;
|
|
21
30
|
export type ScanOptions = import('./npm.mjs').ScanOptions;
|
|
@@ -23,17 +32,22 @@ export type DomainSite = import('./npm.mjs').DomainSite;
|
|
|
23
32
|
export type Rule = import('./rules.mjs').Rule;
|
|
24
33
|
export type Level = import('./rules.mjs').Level;
|
|
25
34
|
/**
|
|
35
|
+
* @typedef {import('./a2ui.mjs').A2uiEntry} A2uiEntry
|
|
26
36
|
* @typedef {import('./analyze.mjs').UiMeta} UiMeta
|
|
27
37
|
* @typedef {import('./analyze.mjs').CspKey} CspKey
|
|
28
38
|
* @typedef {import('./analyze.mjs').HtmlAnalysis} HtmlAnalysis
|
|
29
39
|
* @typedef {import('./analyze.mjs').HtmlSink} HtmlSink
|
|
30
40
|
* @typedef {import('./analyze.mjs').PostMessageCall} PostMessageCall
|
|
41
|
+
* @typedef {import('./client.mjs').ClientOptions} ClientOptions
|
|
42
|
+
* @typedef {import('./client.mjs').ServerSnapshot} ServerSnapshot
|
|
43
|
+
* @typedef {import('./csp.mjs').CspNeeds} CspNeeds
|
|
31
44
|
* @typedef {import('./domains.mjs').DomainPattern} DomainPattern
|
|
32
45
|
* @typedef {import('./domains.mjs').DomainCategory} DomainCategory
|
|
33
46
|
* @typedef {import('./domains.mjs').ClassifiedDomains} ClassifiedDomains
|
|
34
47
|
* @typedef {import('./domains.mjs').SinkScope} SinkScope
|
|
35
48
|
* @typedef {import('./lint.mjs').Finding} Finding
|
|
36
49
|
* @typedef {import('./lint.mjs').ResourceInput} ResourceInput
|
|
50
|
+
* @typedef {import('./lint.mjs').Snapshot} Snapshot
|
|
37
51
|
* @typedef {import('./npm.mjs').PackageMeta} PackageMeta
|
|
38
52
|
* @typedef {import('./npm.mjs').PackageScan} PackageScan
|
|
39
53
|
* @typedef {import('./npm.mjs').ScanOptions} ScanOptions
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mjs"],"names":[],"mappings":"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"}
|
package/dist/lint.d.mts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* From analysis to findings (see Finding below).
|
|
3
3
|
*/
|
|
4
4
|
import { analyzeTools } from './analyze.mjs';
|
|
5
|
+
import type { CspNeeds } from './csp.mjs';
|
|
5
6
|
import type { PackageScan } from './npm.mjs';
|
|
6
7
|
import type { Level } from './rules.mjs';
|
|
7
8
|
export type Finding = {
|
|
@@ -36,8 +37,9 @@ export type ResourceInput = {
|
|
|
36
37
|
* @param {unknown} meta
|
|
37
38
|
* @param {string} uri
|
|
38
39
|
* @param {readonly string[]} [hosts]
|
|
40
|
+
* @param {Partial<CspNeeds>} [needs] the origins the UI needs, for the suggestion MCPAPP001 carries
|
|
39
41
|
*/
|
|
40
|
-
export declare function lintUiMeta(meta: unknown, uri: string, hosts?: readonly string[]): Finding[];
|
|
42
|
+
export declare function lintUiMeta(meta: unknown, uri: string, hosts?: readonly string[], needs?: Partial<CspNeeds>): Finding[];
|
|
41
43
|
/**
|
|
42
44
|
* Findings about a UI resource as served: listing entry, read result content item, HTML.
|
|
43
45
|
* @param {ResourceInput} resource
|
|
@@ -55,6 +57,29 @@ export declare function lintHtml(html: string, uri: string): Finding[];
|
|
|
55
57
|
* @param {string} [uri]
|
|
56
58
|
*/
|
|
57
59
|
export declare function lintTools(tools: unknown, uri?: string): Finding[];
|
|
60
|
+
export type Snapshot = {
|
|
61
|
+
/**
|
|
62
|
+
* resources/read content items (each with its `uri`)
|
|
63
|
+
*/
|
|
64
|
+
contents: readonly any[];
|
|
65
|
+
/**
|
|
66
|
+
* resources/list entries, for the list/read comparison
|
|
67
|
+
*/
|
|
68
|
+
resources?: readonly any[];
|
|
69
|
+
/**
|
|
70
|
+
* tools/list entries
|
|
71
|
+
*/
|
|
72
|
+
tools?: readonly any[];
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Findings about everything a server publishes: each UI resource as listed and read, and its tools.
|
|
76
|
+
* @param {Snapshot} snapshot
|
|
77
|
+
* @param {{ uri?: string, toolsUri?: string }} [where] fallback uri of a content item without one; location of tool findings
|
|
78
|
+
*/
|
|
79
|
+
export declare function lintSnapshot({ contents, resources, tools }: Snapshot, { uri, toolsUri }?: {
|
|
80
|
+
uri?: string;
|
|
81
|
+
toolsUri?: string;
|
|
82
|
+
}): Finding[];
|
|
58
83
|
/**
|
|
59
84
|
* Findings about a scanned package directory (see scanPackageDir). `root` prefixes file locations.
|
|
60
85
|
* @param {PackageScan} scan
|
package/dist/lint.d.mts.map
CHANGED
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"lint.d.mts","sourceRoot":"","sources":["../src/lint.mjs"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAe,YAAY,EAA2D,MAAM,eAAe,CAAC;AAOhH,OAAQ,KAAA,EAAE,QAAQ,EAAE,MAAM,WAAW,CACrC;AAAA,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;AAgDnC;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EALpB,OAKoB,EAAE,GAAG,EAJzB,MAIyB,EAAE,KAAK,AAHxC,CACA,EADQ,SAAS,MAAM,EAGsB,EAAE,KAAK,AAFpD,CACF,EADU,OAAO,CAAC,QAAQ,CAEwC,aAgBlE;AAmCD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAAE,GAAG,EAAE,QAAe,EAAE,QAAe,EAAE,IAAW,EAAE,EAFxE,aAEwE,aAclF;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;AAIE,YAAkB,QAAQ,GAC1B;;;;IAA2B,QAAQ,EAAxB,SAAS,GAAG,EAAE,CACzB;;;;IAA4B,SAAS,AAArC,CACA,EADW,SAAS,GAAG,EAAE,CACzB;;;;IAA4B,KAAK,AAAjC,CACF,EADa,SAAS,GAAG,EAAE,CAC3B;CAAA,CAAA;AAKD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAc,EAAE,KAAU,EAAE,EAH1D,QAG0D,EAAE,EAAE,GAAsB,EAAE,QAAuB,EAAE,AAFvH,CACF,EADU;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAEmF,aAU9H;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAHzB,WAGyB,EAAE,IAAI,AAFvC,CAAsB,EAAd,MAEqC,aA2B/C;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAHnB,MAGmB,GAFjB,KAAK,CAIjB;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
package/dist/rules.d.mts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** @typedef {'error' | 'warning' | 'note'} Level */
|
|
2
2
|
/**
|
|
3
|
-
* A rule in SARIF terms: `short` and `full` become its short and full description
|
|
4
|
-
*
|
|
3
|
+
* A rule in SARIF terms: `short` and `full` become its short and full description; `tags` default to
|
|
4
|
+
* security and mcp-apps.
|
|
5
|
+
* @typedef {{ id: string, name: string, level: Level, short: string, full: string, tags?: readonly string[] }} Rule
|
|
5
6
|
*/
|
|
6
7
|
export type Level = 'error' | 'warning' | 'note';
|
|
7
8
|
export type Rule = {
|
|
@@ -10,6 +11,7 @@ export type Rule = {
|
|
|
10
11
|
level: Level;
|
|
11
12
|
short: string;
|
|
12
13
|
full: string;
|
|
14
|
+
tags?: readonly string[];
|
|
13
15
|
};
|
|
14
16
|
/**
|
|
15
17
|
* The rules, in SARIF terms. Ids are stable; names are what a scanner shows.
|
package/dist/rules.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../src/rules.mjs"],"names":[],"mappings":"AAAA,oDAAoD;AACpD
|
|
1
|
+
{"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../src/rules.mjs"],"names":[],"mappings":"AAAA,oDAAoD;AACpD;;;;GAIG;AALC,YAAwC,KAAK,GAAnC,OAAO,GAAG,SAAS,GAAG,MAAM,CAAO;AAI9C,YAA6G,IAAI,GAAvG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAM;AAKpH;;;GAGG;AACH,eAAO,MAAM,KAAK,EAFR,SAAS,IAAI,EA+BtB,CAAC;AAEF,eAAO,MAAM,UAAU,qBAAwD,CAAC;AAChF,2BAA2B;AAC3B,eAAO,MAAM,UAAU,SADX,MAAM,qBACoD,CAAC"}
|