@extenshi/cli 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +47 -1
- package/dist/cli.js.map +1 -1
- package/dist/icon-preview.js +1 -1
- package/dist/icon.d.ts +4 -3
- package/dist/icon.d.ts.map +1 -1
- package/dist/icon.js +4 -18
- package/dist/icon.js.map +1 -1
- package/dist/risk.d.ts +76 -0
- package/dist/risk.d.ts.map +1 -0
- package/dist/risk.js +214 -0
- package/dist/risk.js.map +1 -0
- package/dist/scan.d.ts +1 -1
- package/dist/scan.js +1 -1
- package/dist/svg-scrub.d.ts +36 -0
- package/dist/svg-scrub.d.ts.map +1 -0
- package/dist/svg-scrub.js +82 -0
- package/dist/svg-scrub.js.map +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* before the process exits. See telemetry.ts — disabled by default until a
|
|
22
22
|
* PostHog key is configured, and a no-op under DO_NOT_TRACK / EXTENSHI_TELEMETRY=0.
|
|
23
23
|
*/
|
|
24
|
+
import { readFile } from 'node:fs/promises';
|
|
24
25
|
import { createRequire } from 'node:module';
|
|
25
26
|
import * as readline from 'node:readline/promises';
|
|
26
27
|
import { program } from 'commander';
|
|
@@ -29,12 +30,13 @@ import { loadConfig, saveConfig } from './config.js';
|
|
|
29
30
|
import { runIconPreview } from './icon.js';
|
|
30
31
|
import { runPublish } from './publish.js';
|
|
31
32
|
import { runReviewRisk } from './review-risk.js';
|
|
33
|
+
import { parseRefs, parseStore, runRisk } from './risk.js';
|
|
32
34
|
import { runScan } from './scan.js';
|
|
33
35
|
import { captureError, captureEvent, classifyError, flagsFromArgv, flushTelemetry, initTelemetry, } from './telemetry.js';
|
|
34
36
|
const MISSING_KEY_HELP = "Looks like you don't have an Extenshi API key set up yet — let's fix that.\n" +
|
|
35
37
|
'\n' +
|
|
36
38
|
'Getting started is free: no credit card required. Every account includes a\n' +
|
|
37
|
-
'free
|
|
39
|
+
'one-time free allowance (10 catalog reads + 3 scans), so you can try things out\n' +
|
|
38
40
|
'before paying for anything.\n' +
|
|
39
41
|
'\n' +
|
|
40
42
|
'1. Create a free account at https://auth.extenshi.io/signup\n' +
|
|
@@ -234,6 +236,50 @@ program
|
|
|
234
236
|
});
|
|
235
237
|
});
|
|
236
238
|
});
|
|
239
|
+
program
|
|
240
|
+
.command('risk [store-ids...]')
|
|
241
|
+
.description('Look up the safety score for extensions you already have, by their STORE ids (the id in the ' +
|
|
242
|
+
'store URL). Built for inventories: up to 40 extensions per request, and the whole request ' +
|
|
243
|
+
'costs 1 read credit — not 1 per extension. Scores are cluster-resolved server-side, so they ' +
|
|
244
|
+
'match what the extension page shows.')
|
|
245
|
+
.option('--store <store>', 'Store the ids belong to: chrome | firefox | edge (default: chrome). Per-id "firefox:some-addon" prefixes override it.')
|
|
246
|
+
.option('--file <path>', 'Read ids from a file instead of (or in addition to) the arguments: one per line, bare id or "store:id". Blank lines and # comments are ignored.')
|
|
247
|
+
.option('--catalog-url <url>', 'Override the catalog BFF base URL (default: https://bff.extenshi.io)')
|
|
248
|
+
.option('--json', 'Output raw JSON')
|
|
249
|
+
.action(async (storeIds, opts) => {
|
|
250
|
+
await runCommand('risk', async () => {
|
|
251
|
+
const defaultStore = parseStore(opts.store ?? 'chrome');
|
|
252
|
+
if (!defaultStore) {
|
|
253
|
+
throw new Error('--store must be one of: chrome, firefox, edge');
|
|
254
|
+
}
|
|
255
|
+
const entries = [...(storeIds ?? [])];
|
|
256
|
+
if (opts.file) {
|
|
257
|
+
let contents;
|
|
258
|
+
try {
|
|
259
|
+
contents = await readFile(opts.file, 'utf8');
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
throw new Error(`could not read --file "${opts.file}"`);
|
|
263
|
+
}
|
|
264
|
+
entries.push(...contents.split(/\r?\n/));
|
|
265
|
+
}
|
|
266
|
+
const { refs, invalid } = parseRefs(entries, defaultStore);
|
|
267
|
+
if (invalid.length > 0) {
|
|
268
|
+
console.error(`Skipped ${invalid.length} unusable entr(y/ies): ${invalid.slice(0, 5).join(', ')}`);
|
|
269
|
+
}
|
|
270
|
+
const config = await loadConfig();
|
|
271
|
+
if (!config.apiKey) {
|
|
272
|
+
throw new Error(MISSING_KEY_HELP);
|
|
273
|
+
}
|
|
274
|
+
await checkAutoUpdate();
|
|
275
|
+
await runRisk({
|
|
276
|
+
refs,
|
|
277
|
+
bffUrl: (opts.catalogUrl ?? config.bffUrl ?? 'https://bff.extenshi.io').replace(/\/$/, ''),
|
|
278
|
+
apiKey: config.apiKey,
|
|
279
|
+
outputJson: opts.json ?? false,
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
});
|
|
237
283
|
const icon = program
|
|
238
284
|
.command('icon')
|
|
239
285
|
.description('Icon utilities for browser extensions (free, fully offline)');
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,QAAQ,MAAM,wBAAwB,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EACN,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,aAAa,EACb,cAAc,EACd,aAAa,GACb,MAAM,gBAAgB,CAAA;AAEvB,MAAM,gBAAgB,GACrB,8EAA8E;IAC9E,IAAI;IACJ,8EAA8E;IAC9E,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/icon-preview.js
CHANGED
|
@@ -1112,7 +1112,7 @@ export function renderIconPreviewHtml(input) {
|
|
|
1112
1112
|
<span class="next-ico">${SCAN_ICON}</span>
|
|
1113
1113
|
<div class="next-body">
|
|
1114
1114
|
<code>npx @extenshi/cli scan dist.zip</code>
|
|
1115
|
-
<p>Pre-publish security scan of the built artifact, with a free
|
|
1115
|
+
<p>Pre-publish security scan of the built artifact, with a free allowance.</p>
|
|
1116
1116
|
</div>
|
|
1117
1117
|
</div>
|
|
1118
1118
|
<div class="next-item">
|
package/dist/icon.d.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* command only renders the verification page. The paid, server-side
|
|
12
12
|
* generation path lives in dojo and is metered separately.
|
|
13
13
|
*/
|
|
14
|
+
import { scrubSvgMarkup } from './svg-scrub.js';
|
|
14
15
|
export interface IconPreviewCmdOptions {
|
|
15
16
|
/** Extension display name shown in the mockups (default: derived from the file name). */
|
|
16
17
|
name?: string;
|
|
@@ -23,10 +24,10 @@ export interface IconPreviewCmdOptions {
|
|
|
23
24
|
* Strip scripting constructs from untrusted SVG source. The preview page only
|
|
24
25
|
* ever embeds the icon through <img src="data:..."> (where scripts never run),
|
|
25
26
|
* but the raw source is also shipped inside the export ZIP — so it must not
|
|
26
|
-
* carry executable payloads either.
|
|
27
|
-
* (
|
|
27
|
+
* carry executable payloads either. The scrubber itself lives in svg-scrub.ts
|
|
28
|
+
* (shared byte-identical with dojo); re-exported here under its historical name.
|
|
28
29
|
*/
|
|
29
|
-
export declare
|
|
30
|
+
export declare const sanitizeSvgSource: typeof scrubSvgMarkup;
|
|
30
31
|
/** Human-ish display name from a file name: "my-cool_icon.svg" → "My Cool Icon". */
|
|
31
32
|
export declare function displayNameFromFile(filePath: string): string;
|
|
32
33
|
/** Resolve the HTML destination: explicit --output wins, else auto-named in cwd. */
|
package/dist/icon.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icon.d.ts","sourceRoot":"","sources":["../src/icon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"icon.d.ts","sourceRoot":"","sources":["../src/icon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAM/C,MAAM,WAAW,qBAAqB;IACrC,yFAAyF;IACzF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oFAAoF;IACpF,IAAI,CAAC,EAAE,OAAO,CAAA;CACd;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,uBAAiB,CAAA;AAE/C,oFAAoF;AACpF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAK5D;AAED,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIvF;AAmBD;;;GAGG;AACH,wBAAsB,cAAc,CACnC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,qBAAqB,EAC3B,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAkDf"}
|
package/dist/icon.js
CHANGED
|
@@ -15,6 +15,7 @@ import { spawn } from 'node:child_process';
|
|
|
15
15
|
import * as fs from 'node:fs';
|
|
16
16
|
import * as path from 'node:path';
|
|
17
17
|
import { renderIconPreviewHtml } from './icon-preview.js';
|
|
18
|
+
import { scrubSvgMarkup } from './svg-scrub.js';
|
|
18
19
|
const MAX_SVG_BYTES = 512 * 1024;
|
|
19
20
|
const MAX_PNG_BYTES = 2 * 1024 * 1024;
|
|
20
21
|
const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
@@ -22,25 +23,10 @@ const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0
|
|
|
22
23
|
* Strip scripting constructs from untrusted SVG source. The preview page only
|
|
23
24
|
* ever embeds the icon through <img src="data:..."> (where scripts never run),
|
|
24
25
|
* but the raw source is also shipped inside the export ZIP — so it must not
|
|
25
|
-
* carry executable payloads either.
|
|
26
|
-
* (
|
|
26
|
+
* carry executable payloads either. The scrubber itself lives in svg-scrub.ts
|
|
27
|
+
* (shared byte-identical with dojo); re-exported here under its historical name.
|
|
27
28
|
*/
|
|
28
|
-
export
|
|
29
|
-
return (svg
|
|
30
|
-
// XML prolog junk that has no place in an icon
|
|
31
|
-
.replace(/<!DOCTYPE[\s\S]*?>/gi, '')
|
|
32
|
-
.replace(/<!ENTITY[\s\S]*?>/gi, '')
|
|
33
|
-
// executable containers, paired or self-closing
|
|
34
|
-
.replace(/<\s*(script|foreignObject|iframe|object|embed)\b[\s\S]*?<\s*\/\s*\1\s*>/gi, '')
|
|
35
|
-
.replace(/<\s*(script|foreignObject|iframe|object|embed)\b[^>]*\/?>/gi, '')
|
|
36
|
-
// inline event handlers: onload="..." / onclick='...' / onerror=x
|
|
37
|
-
.replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, '')
|
|
38
|
-
.replace(/\son[a-z]+\s*=\s*'[^']*'/gi, '')
|
|
39
|
-
.replace(/\son[a-z]+\s*=\s*[^\s>]+/gi, '')
|
|
40
|
-
// javascript:/data: link targets
|
|
41
|
-
.replace(/\s(href|xlink:href)\s*=\s*"(\s*(javascript|data):)[^"]*"/gi, '')
|
|
42
|
-
.replace(/\s(href|xlink:href)\s*=\s*'(\s*(javascript|data):)[^']*'/gi, ''));
|
|
43
|
-
}
|
|
29
|
+
export const sanitizeSvgSource = scrubSvgMarkup;
|
|
44
30
|
/** Human-ish display name from a file name: "my-cool_icon.svg" → "My Cool Icon". */
|
|
45
31
|
export function displayNameFromFile(filePath) {
|
|
46
32
|
const base = path.basename(filePath).replace(/\.(svg|png)$/i, '');
|
package/dist/icon.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icon.js","sourceRoot":"","sources":["../src/icon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"icon.js","sourceRoot":"","sources":["../src/icon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAE/C,MAAM,aAAa,GAAG,GAAG,GAAG,IAAI,CAAA;AAChC,MAAM,aAAa,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAA;AACrC,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;AAWnF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAA;AAE/C,oFAAoF;AACpF,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IACnD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAA;IACjE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACpD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,cAAc,CAAA;IAC7C,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAC1E,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,kBAAkB,CAAC,MAA0B,EAAE,QAAgB;IAC9E,IAAI,MAAM;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAA;IACtD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAA;IACjE,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,oBAAoB,CAAC,CAAA;AAChE,CAAC;AAED,iEAAiE;AACjE,SAAS,aAAa,CAAC,MAAc;IACpC,IAAI,CAAC;QACJ,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GACpB,OAAO,CAAC,QAAQ,KAAK,QAAQ;YAC5B,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;YACpB,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;gBAC7B,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;gBACtC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;QAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAiB,EAAE,IAAgB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;QAC7F,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QAC3B,KAAK,CAAC,KAAK,EAAE,CAAA;IACd,CAAC;IAAC,MAAM,CAAC;QACR,8DAA8D;IAC/D,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,QAAgB,EAChB,IAA2B,EAC3B,UAAkB;IAElB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAA;IACtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,wBAAwB,QAAQ,EAAE,CAAC,CAAA;IACpD,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAA;IAChD,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,IAAI,QAAQ,+BAA+B,CAAC,CAAA;IAC5F,CAAC;IAED,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAA;IACrC,IAAI,OAAe,CAAA;IACnB,IAAI,SAA6B,CAAA;IACjC,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;QACpB,IAAI,GAAG,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAA;QAC9E,CAAC;QACD,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;QACjC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;QAC1D,CAAC;QACD,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAA;QACnC,OAAO,GAAG,6BAA6B,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAA;IAC3F,CAAC;SAAM,CAAC;QACP,IAAI,GAAG,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;QAC5E,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;YAC/C,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;QACvE,CAAC;QACD,OAAO,GAAG,yBAAyB,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAA;IAC5D,CAAC;IAED,MAAM,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,mBAAmB,CAAC,QAAQ,CAAC;QACxD,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACjC,OAAO;QACP,KAAK,EAAE,GAAG,KAAK,MAAM;QACrB,SAAS;QACT,UAAU;QACV,WAAW,EAAE,IAAI,IAAI,EAAE;KACvB,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IACzD,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;IACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,OAAO,IAAI,CAAC,CAAA;IAC5D,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAA;QAC1D,aAAa,CAAC,OAAO,CAAC,CAAA;IACvB,CAAC;AACF,CAAC"}
|
package/dist/risk.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `extenshi risk` — bulk safety lookup for extensions you already have,
|
|
3
|
+
* addressed by their STORE ids.
|
|
4
|
+
*
|
|
5
|
+
* This is the inventory case: you hold a list of installed extensions (from a
|
|
6
|
+
* fleet report, an MDM export, `chrome://extensions`) and want to know which of
|
|
7
|
+
* them are risky, without knowing their catalog ids.
|
|
8
|
+
*
|
|
9
|
+
* Why it exists as its own command
|
|
10
|
+
* --------------------------------
|
|
11
|
+
* Doing this one extension at a time cost three metered catalog reads each
|
|
12
|
+
* (resolve the store id → follow the cluster canonical → fetch the risk
|
|
13
|
+
* summary). This command hits `security.getSecuritySummaryBatch`, which answers
|
|
14
|
+
* up to {@link MAX_EXTENSIONS_PER_REQUEST} extensions in ONE metered read — an
|
|
15
|
+
* 87-extension inventory drops from 261 credits to 3.
|
|
16
|
+
*
|
|
17
|
+
* Cluster correctness is handled server-side: each store id is resolved through
|
|
18
|
+
* its cross-store cluster to the same listing the extension page renders, so a
|
|
19
|
+
* score printed here matches the page a user can open. When the score comes
|
|
20
|
+
* from a DIFFERENT store's listing of the same extension (cluster members are
|
|
21
|
+
* scanned independently and genuinely disagree), the row says so rather than
|
|
22
|
+
* presenting a sibling's number as this listing's own.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Max extensions per request.
|
|
26
|
+
*
|
|
27
|
+
* CROSS-PACKAGE SYNC POINT — this is a published npm package and cannot import
|
|
28
|
+
* from the BFF. The authority is `METERED_BATCH_MAX_EXTENSIONS` in
|
|
29
|
+
* `catalog/catalog-bff/src/lib/metered-batch-cap.ts`, which rejects an over-cap
|
|
30
|
+
* request; this constant only lets the CLI fail fast with a useful message
|
|
31
|
+
* instead of round-tripping. Pinned by `risk.test.ts`.
|
|
32
|
+
*/
|
|
33
|
+
export declare const MAX_EXTENSIONS_PER_REQUEST = 40;
|
|
34
|
+
export type Store = 'CHROME' | 'FIREFOX' | 'EDGE';
|
|
35
|
+
export interface StoreRef {
|
|
36
|
+
storeId: string;
|
|
37
|
+
store: Store;
|
|
38
|
+
}
|
|
39
|
+
/** Normalise a user-supplied store name, or undefined when unrecognised. */
|
|
40
|
+
export declare function parseStore(value: string | undefined): Store | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Parse the id list a caller supplied (argv and/or a `--file`) into store refs.
|
|
43
|
+
*
|
|
44
|
+
* Accepted per entry: a bare id (uses `defaultStore`) or `store:id`, so one
|
|
45
|
+
* file can mix browsers — which is the normal shape of a real inventory.
|
|
46
|
+
* Blank lines and `#` comments are skipped. Duplicates collapse: a user with
|
|
47
|
+
* the same extension in two profiles should not pay for it twice.
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseRefs(entries: string[], defaultStore: Store): {
|
|
50
|
+
refs: StoreRef[];
|
|
51
|
+
invalid: string[];
|
|
52
|
+
};
|
|
53
|
+
/** One row as returned by the BFF batch (fields beyond these are ignored). */
|
|
54
|
+
interface RiskRow {
|
|
55
|
+
storeId?: string;
|
|
56
|
+
store?: string;
|
|
57
|
+
extensionId?: number | null;
|
|
58
|
+
canonicalExtensionId?: number | null;
|
|
59
|
+
extensionName?: string | null;
|
|
60
|
+
riskCategory?: string | null;
|
|
61
|
+
riskScore?: number | string | null;
|
|
62
|
+
scoredExtensionId?: number | null;
|
|
63
|
+
scoredStore?: string | null;
|
|
64
|
+
totalFindings?: number | null;
|
|
65
|
+
lastScanDate?: string | null;
|
|
66
|
+
}
|
|
67
|
+
export declare function rankRow(row: RiskRow): number;
|
|
68
|
+
export interface RunRiskOptions {
|
|
69
|
+
refs: StoreRef[];
|
|
70
|
+
bffUrl: string;
|
|
71
|
+
apiKey?: string;
|
|
72
|
+
outputJson: boolean;
|
|
73
|
+
}
|
|
74
|
+
export declare function runRisk(opts: RunRiskOptions): Promise<void>;
|
|
75
|
+
export {};
|
|
76
|
+
//# sourceMappingURL=risk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"risk.d.ts","sourceRoot":"","sources":["../src/risk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAOH;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAA;AAE5C,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAA;AAEjD,MAAM,WAAW,QAAQ;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,KAAK,CAAA;CACZ;AAQD,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,SAAS,CAGvE;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,KAAK,GAAG;IAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CA8BzG;AAED,8EAA8E;AAC9E,UAAU,OAAO;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAClC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AA0CD,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAE5C;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,QAAQ,EAAE,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,OAAO,CAAA;CACnB;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmDjE"}
|
package/dist/risk.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `extenshi risk` — bulk safety lookup for extensions you already have,
|
|
3
|
+
* addressed by their STORE ids.
|
|
4
|
+
*
|
|
5
|
+
* This is the inventory case: you hold a list of installed extensions (from a
|
|
6
|
+
* fleet report, an MDM export, `chrome://extensions`) and want to know which of
|
|
7
|
+
* them are risky, without knowing their catalog ids.
|
|
8
|
+
*
|
|
9
|
+
* Why it exists as its own command
|
|
10
|
+
* --------------------------------
|
|
11
|
+
* Doing this one extension at a time cost three metered catalog reads each
|
|
12
|
+
* (resolve the store id → follow the cluster canonical → fetch the risk
|
|
13
|
+
* summary). This command hits `security.getSecuritySummaryBatch`, which answers
|
|
14
|
+
* up to {@link MAX_EXTENSIONS_PER_REQUEST} extensions in ONE metered read — an
|
|
15
|
+
* 87-extension inventory drops from 261 credits to 3.
|
|
16
|
+
*
|
|
17
|
+
* Cluster correctness is handled server-side: each store id is resolved through
|
|
18
|
+
* its cross-store cluster to the same listing the extension page renders, so a
|
|
19
|
+
* score printed here matches the page a user can open. When the score comes
|
|
20
|
+
* from a DIFFERENT store's listing of the same extension (cluster members are
|
|
21
|
+
* scanned independently and genuinely disagree), the row says so rather than
|
|
22
|
+
* presenting a sibling's number as this listing's own.
|
|
23
|
+
*/
|
|
24
|
+
import chalk from 'chalk';
|
|
25
|
+
import { fetch } from 'undici';
|
|
26
|
+
const FETCH_TIMEOUT_MS = 20_000;
|
|
27
|
+
/**
|
|
28
|
+
* Max extensions per request.
|
|
29
|
+
*
|
|
30
|
+
* CROSS-PACKAGE SYNC POINT — this is a published npm package and cannot import
|
|
31
|
+
* from the BFF. The authority is `METERED_BATCH_MAX_EXTENSIONS` in
|
|
32
|
+
* `catalog/catalog-bff/src/lib/metered-batch-cap.ts`, which rejects an over-cap
|
|
33
|
+
* request; this constant only lets the CLI fail fast with a useful message
|
|
34
|
+
* instead of round-tripping. Pinned by `risk.test.ts`.
|
|
35
|
+
*/
|
|
36
|
+
export const MAX_EXTENSIONS_PER_REQUEST = 40;
|
|
37
|
+
const STORE_ALIASES = {
|
|
38
|
+
chrome: 'CHROME',
|
|
39
|
+
firefox: 'FIREFOX',
|
|
40
|
+
edge: 'EDGE',
|
|
41
|
+
};
|
|
42
|
+
/** Normalise a user-supplied store name, or undefined when unrecognised. */
|
|
43
|
+
export function parseStore(value) {
|
|
44
|
+
if (!value)
|
|
45
|
+
return undefined;
|
|
46
|
+
return STORE_ALIASES[value.trim().toLowerCase()];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Parse the id list a caller supplied (argv and/or a `--file`) into store refs.
|
|
50
|
+
*
|
|
51
|
+
* Accepted per entry: a bare id (uses `defaultStore`) or `store:id`, so one
|
|
52
|
+
* file can mix browsers — which is the normal shape of a real inventory.
|
|
53
|
+
* Blank lines and `#` comments are skipped. Duplicates collapse: a user with
|
|
54
|
+
* the same extension in two profiles should not pay for it twice.
|
|
55
|
+
*/
|
|
56
|
+
export function parseRefs(entries, defaultStore) {
|
|
57
|
+
const refs = [];
|
|
58
|
+
const invalid = [];
|
|
59
|
+
const seen = new Set();
|
|
60
|
+
for (const raw of entries) {
|
|
61
|
+
const line = raw.trim();
|
|
62
|
+
if (!line || line.startsWith('#'))
|
|
63
|
+
continue;
|
|
64
|
+
let store = defaultStore;
|
|
65
|
+
let storeId = line;
|
|
66
|
+
const sep = line.indexOf(':');
|
|
67
|
+
if (sep > 0) {
|
|
68
|
+
const named = parseStore(line.slice(0, sep));
|
|
69
|
+
if (named) {
|
|
70
|
+
store = named;
|
|
71
|
+
storeId = line.slice(sep + 1).trim();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!storeId || storeId.length > 100) {
|
|
75
|
+
invalid.push(line);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const key = `${store}:${storeId}`;
|
|
79
|
+
if (seen.has(key))
|
|
80
|
+
continue;
|
|
81
|
+
seen.add(key);
|
|
82
|
+
refs.push({ storeId, store });
|
|
83
|
+
}
|
|
84
|
+
return { refs, invalid };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The website's coefficient: 100 = safest. The backend stores RISK (0 = safest),
|
|
88
|
+
* so every user-facing surface inverts it — printing the raw number here would
|
|
89
|
+
* make the CLI contradict the extension page by construction.
|
|
90
|
+
*/
|
|
91
|
+
function toSafetyScore(risk) {
|
|
92
|
+
if (risk === undefined || risk === null)
|
|
93
|
+
return null;
|
|
94
|
+
const value = Number(risk);
|
|
95
|
+
if (!Number.isFinite(value))
|
|
96
|
+
return null;
|
|
97
|
+
return Math.max(0, Math.min(100, Math.round((100 - value) * 100) / 100));
|
|
98
|
+
}
|
|
99
|
+
function colorForCategory(category) {
|
|
100
|
+
switch (category) {
|
|
101
|
+
case 'CRITICAL':
|
|
102
|
+
return chalk.bgRed.white;
|
|
103
|
+
case 'HIGH':
|
|
104
|
+
return chalk.red;
|
|
105
|
+
case 'MEDIUM':
|
|
106
|
+
return chalk.yellow;
|
|
107
|
+
case 'LOW':
|
|
108
|
+
return chalk.cyan;
|
|
109
|
+
case 'NONE':
|
|
110
|
+
return chalk.green;
|
|
111
|
+
default:
|
|
112
|
+
return chalk.gray;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Severity order for the printed table: worst first, unscanned last. */
|
|
116
|
+
const CATEGORY_RANK = {
|
|
117
|
+
CRITICAL: 0,
|
|
118
|
+
HIGH: 1,
|
|
119
|
+
MEDIUM: 2,
|
|
120
|
+
REVIEW_REQUIRED: 3,
|
|
121
|
+
LOW: 4,
|
|
122
|
+
NONE: 5,
|
|
123
|
+
UNKNOWN: 6,
|
|
124
|
+
};
|
|
125
|
+
export function rankRow(row) {
|
|
126
|
+
return CATEGORY_RANK[row.riskCategory ?? ''] ?? 7;
|
|
127
|
+
}
|
|
128
|
+
export async function runRisk(opts) {
|
|
129
|
+
if (opts.refs.length === 0) {
|
|
130
|
+
throw new Error('no extension ids given. Pass ids as arguments or use --file <path>.');
|
|
131
|
+
}
|
|
132
|
+
if (opts.refs.length > MAX_EXTENSIONS_PER_REQUEST) {
|
|
133
|
+
throw new Error(`${opts.refs.length} extensions given; the limit is ${MAX_EXTENSIONS_PER_REQUEST} per request.\n` +
|
|
134
|
+
`Split the list into batches of ${MAX_EXTENSIONS_PER_REQUEST} or fewer — each request costs ` +
|
|
135
|
+
'1 read credit, whatever its size.');
|
|
136
|
+
}
|
|
137
|
+
// tRPC queries travel as a GET query string, so the batch size bounds the URL
|
|
138
|
+
// length. Worst case the schema allows — 40 entries of 100-char Firefox ids —
|
|
139
|
+
// measured at 7,060 bytes and verified against the live BFF (HTTP 200), which
|
|
140
|
+
// leaves ~1 KB under nginx's default 8 KB header buffer. That margin is the
|
|
141
|
+
// real constraint on raising MAX_EXTENSIONS_PER_REQUEST: a larger cap needs
|
|
142
|
+
// the header buffer raised (or a POST transport) first, not just a new number.
|
|
143
|
+
const input = encodeURIComponent(JSON.stringify({ extensions: opts.refs }));
|
|
144
|
+
const url = `${opts.bffUrl}/api/trpc/security.getSecuritySummaryBatch?input=${input}`;
|
|
145
|
+
let res;
|
|
146
|
+
try {
|
|
147
|
+
res = await fetch(url, {
|
|
148
|
+
headers: opts.apiKey ? { authorization: `Bearer ${opts.apiKey}` } : {},
|
|
149
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
const e = err;
|
|
154
|
+
const why = e.name === 'TimeoutError' ? `timed out after ${FETCH_TIMEOUT_MS / 1000}s` : e.message;
|
|
155
|
+
throw new Error(`could not reach the catalog (${why})`);
|
|
156
|
+
}
|
|
157
|
+
const body = (await res.json().catch(() => null));
|
|
158
|
+
if (body?.error?.message)
|
|
159
|
+
throw new Error(body.error.message);
|
|
160
|
+
if (!res.ok)
|
|
161
|
+
throw new Error(`catalog returned HTTP ${res.status}`);
|
|
162
|
+
const rows = (Array.isArray(body?.result?.data) ? body.result.data : []);
|
|
163
|
+
if (opts.outputJson) {
|
|
164
|
+
console.log(JSON.stringify({ requested: opts.refs.length, matched: rows.length, extensions: rows }, null, 2));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
printTable(opts.refs, rows);
|
|
168
|
+
}
|
|
169
|
+
function printTable(refs, rows) {
|
|
170
|
+
const byKey = new Map(rows.map((r) => [`${r.store}:${r.storeId}`, r]));
|
|
171
|
+
const sorted = [...rows].sort((a, b) => rankRow(a) - rankRow(b));
|
|
172
|
+
console.log('');
|
|
173
|
+
console.log(chalk.bold(`Safety for ${refs.length} extension${refs.length === 1 ? '' : 's'} (100 = safest)`));
|
|
174
|
+
console.log('');
|
|
175
|
+
if (sorted.length === 0) {
|
|
176
|
+
console.log(chalk.gray(' None of these ids matched a catalog listing.'));
|
|
177
|
+
}
|
|
178
|
+
for (const row of sorted) {
|
|
179
|
+
const color = colorForCategory(row.riskCategory);
|
|
180
|
+
const score = toSafetyScore(row.riskScore);
|
|
181
|
+
const name = row.extensionName ?? row.storeId ?? '(unnamed)';
|
|
182
|
+
const scoreText = score === null ? chalk.gray(' —') : color(String(score).padStart(3));
|
|
183
|
+
const category = row.riskCategory ? color(row.riskCategory) : chalk.gray('not scanned');
|
|
184
|
+
console.log(` ${scoreText} ${chalk.bold(name)} ${category}`);
|
|
185
|
+
const details = [];
|
|
186
|
+
const canonical = row.canonicalExtensionId ?? row.extensionId;
|
|
187
|
+
if (canonical != null)
|
|
188
|
+
details.push(`https://catalog.extenshi.io/extensions/${canonical}`);
|
|
189
|
+
if (row.totalFindings != null)
|
|
190
|
+
details.push(`${row.totalFindings} findings`);
|
|
191
|
+
if (details.length > 0)
|
|
192
|
+
console.log(chalk.gray(` ${details.join(' · ')}`));
|
|
193
|
+
// Cluster members are scanned independently and their scores disagree,
|
|
194
|
+
// so a borrowed number is labelled rather than presented as this
|
|
195
|
+
// listing's own.
|
|
196
|
+
if (row.scoredExtensionId != null && row.scoredExtensionId !== row.extensionId && row.scoredStore) {
|
|
197
|
+
console.log(chalk.gray(` score measured on this extension's ${row.scoredStore} listing`));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const missing = refs.filter((ref) => !byKey.has(`${ref.store}:${ref.storeId}`));
|
|
201
|
+
if (missing.length > 0) {
|
|
202
|
+
console.log('');
|
|
203
|
+
console.log(chalk.gray(` ${missing.length} id(s) have no catalog listing yet (not a safety verdict):`));
|
|
204
|
+
for (const ref of missing)
|
|
205
|
+
console.log(chalk.gray(` ${ref.store.toLowerCase()}:${ref.storeId}`));
|
|
206
|
+
}
|
|
207
|
+
const unscanned = rows.filter((r) => r.riskCategory == null).length;
|
|
208
|
+
if (unscanned > 0) {
|
|
209
|
+
console.log('');
|
|
210
|
+
console.log(chalk.gray(` ${unscanned} listed but never scanned — no score is not a clean bill of health.`));
|
|
211
|
+
}
|
|
212
|
+
console.log('');
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=risk.js.map
|
package/dist/risk.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"risk.js","sourceRoot":"","sources":["../src/risk.ts"],"names":[],"mappings":"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"}
|
package/dist/scan.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ import { type ComplianceReviewPayload } from './compliance-review.js';
|
|
|
31
31
|
* ownership you have verified, and ONLY when you name one with
|
|
32
32
|
* `--extension-id <id>` (the numeric catalog ID of a verified extension). A
|
|
33
33
|
* bare `extenshi scan <artifact>` running on free credits therefore 403s —
|
|
34
|
-
* which is confusing when dojo just says "
|
|
34
|
+
* which is confusing when dojo just says "3 free scans left".
|
|
35
35
|
*
|
|
36
36
|
* The BFF signals this case via a stable `errorCode`
|
|
37
37
|
* (`FREE_REQUIRES_CLAIM` / `FREE_REQUIRES_VERIFIED_OWNERSHIP`). The CLI ships
|
package/dist/scan.js
CHANGED
|
@@ -44,7 +44,7 @@ const DOJO_URL = 'https://dojo.extenshi.io';
|
|
|
44
44
|
* ownership you have verified, and ONLY when you name one with
|
|
45
45
|
* `--extension-id <id>` (the numeric catalog ID of a verified extension). A
|
|
46
46
|
* bare `extenshi scan <artifact>` running on free credits therefore 403s —
|
|
47
|
-
* which is confusing when dojo just says "
|
|
47
|
+
* which is confusing when dojo just says "3 free scans left".
|
|
48
48
|
*
|
|
49
49
|
* The BFF signals this case via a stable `errorCode`
|
|
50
50
|
* (`FREE_REQUIRES_CLAIM` / `FREE_REQUIRES_VERIFIED_OWNERSHIP`). The CLI ships
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical regex-based SVG scrubber, shared by the offline CLI preview, the
|
|
3
|
+
* hosted dojo generator and the catalog-bff persistence path so all three strip
|
|
4
|
+
* the exact same constructs.
|
|
5
|
+
*
|
|
6
|
+
* Regex-based (not DOM-based) on purpose: it must run in Node with no DOMParser
|
|
7
|
+
* — the CLI runs offline in a terminal, and the dojo API route runs server-side.
|
|
8
|
+
* Patterns intentionally over-remove rather than under-remove: an icon has no
|
|
9
|
+
* legitimate use for scripts, styles, animation, or external references.
|
|
10
|
+
*
|
|
11
|
+
* SINGLE SOURCE, THREE COPIES: this file exists byte-identical at
|
|
12
|
+
* tools/extenshi-cli/src/svg-scrub.ts (canonical — edit here)
|
|
13
|
+
* dojo/src/lib/tools/svg-scrub.ts (synced copy for the dojo build)
|
|
14
|
+
* catalog/catalog-bff/src/lib/svg-scrub.ts (synced copy for the BFF build)
|
|
15
|
+
* because the published @extenshi/cli npm package cannot depend on unpublished
|
|
16
|
+
* workspace packages, and neither dojo's nor the BFF's Docker build builds the
|
|
17
|
+
* CLI package. The BFF copy is the load-bearing one for welcome pages: it
|
|
18
|
+
* sanitizes agent-authored SVG *before it is persisted*, so a hostile block can
|
|
19
|
+
* never reach the database, let alone a rendered page.
|
|
20
|
+
* A test in tools/extenshi-cli/src/icon.test.ts fails CI when the copies drift;
|
|
21
|
+
* after editing, run:
|
|
22
|
+
* cp tools/extenshi-cli/src/svg-scrub.ts dojo/src/lib/tools/svg-scrub.ts
|
|
23
|
+
* cp tools/extenshi-cli/src/svg-scrub.ts catalog/catalog-bff/src/lib/svg-scrub.ts
|
|
24
|
+
*
|
|
25
|
+
* This is the same rationale as icon-preview.ts (see the note there).
|
|
26
|
+
*/
|
|
27
|
+
export declare const SVG_DANGEROUS_TAGS: readonly ["script", "style", "foreignObject", "iframe", "object", "embed", "applet", "form", "input", "textarea", "select", "button", "animate", "animateTransform", "animateMotion", "set"];
|
|
28
|
+
/**
|
|
29
|
+
* Strip scripting / active constructs from untrusted SVG source. Safe to run on
|
|
30
|
+
* anything: LLM output (dojo), a developer's own file (CLI). The result is only
|
|
31
|
+
* ever consumed as image data (rendered through `<img src="data:…">`, where
|
|
32
|
+
* scripts never execute, and rasterized on a canvas), but we scrub anyway so the
|
|
33
|
+
* raw string is also safe if written to a file or opened directly.
|
|
34
|
+
*/
|
|
35
|
+
export declare function scrubSvgMarkup(svg: string): string;
|
|
36
|
+
//# sourceMappingURL=svg-scrub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svg-scrub.d.ts","sourceRoot":"","sources":["../src/svg-scrub.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,eAAO,MAAM,kBAAkB,8LAiBrB,CAAA;AAIV;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA0BlD"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical regex-based SVG scrubber, shared by the offline CLI preview, the
|
|
3
|
+
* hosted dojo generator and the catalog-bff persistence path so all three strip
|
|
4
|
+
* the exact same constructs.
|
|
5
|
+
*
|
|
6
|
+
* Regex-based (not DOM-based) on purpose: it must run in Node with no DOMParser
|
|
7
|
+
* — the CLI runs offline in a terminal, and the dojo API route runs server-side.
|
|
8
|
+
* Patterns intentionally over-remove rather than under-remove: an icon has no
|
|
9
|
+
* legitimate use for scripts, styles, animation, or external references.
|
|
10
|
+
*
|
|
11
|
+
* SINGLE SOURCE, THREE COPIES: this file exists byte-identical at
|
|
12
|
+
* tools/extenshi-cli/src/svg-scrub.ts (canonical — edit here)
|
|
13
|
+
* dojo/src/lib/tools/svg-scrub.ts (synced copy for the dojo build)
|
|
14
|
+
* catalog/catalog-bff/src/lib/svg-scrub.ts (synced copy for the BFF build)
|
|
15
|
+
* because the published @extenshi/cli npm package cannot depend on unpublished
|
|
16
|
+
* workspace packages, and neither dojo's nor the BFF's Docker build builds the
|
|
17
|
+
* CLI package. The BFF copy is the load-bearing one for welcome pages: it
|
|
18
|
+
* sanitizes agent-authored SVG *before it is persisted*, so a hostile block can
|
|
19
|
+
* never reach the database, let alone a rendered page.
|
|
20
|
+
* A test in tools/extenshi-cli/src/icon.test.ts fails CI when the copies drift;
|
|
21
|
+
* after editing, run:
|
|
22
|
+
* cp tools/extenshi-cli/src/svg-scrub.ts dojo/src/lib/tools/svg-scrub.ts
|
|
23
|
+
* cp tools/extenshi-cli/src/svg-scrub.ts catalog/catalog-bff/src/lib/svg-scrub.ts
|
|
24
|
+
*
|
|
25
|
+
* This is the same rationale as icon-preview.ts (see the note there).
|
|
26
|
+
*/
|
|
27
|
+
// Executable / interactive / animated containers that have no place in a flat
|
|
28
|
+
// icon. The DOM-based sanitizer in dojo (svg-sanitize.ts → DANGEROUS_ELEMENTS)
|
|
29
|
+
// derives its element set from this list, so the two sanitizers can never
|
|
30
|
+
// disagree on which elements are dangerous.
|
|
31
|
+
export const SVG_DANGEROUS_TAGS = [
|
|
32
|
+
'script',
|
|
33
|
+
'style',
|
|
34
|
+
'foreignObject',
|
|
35
|
+
'iframe',
|
|
36
|
+
'object',
|
|
37
|
+
'embed',
|
|
38
|
+
'applet',
|
|
39
|
+
'form',
|
|
40
|
+
'input',
|
|
41
|
+
'textarea',
|
|
42
|
+
'select',
|
|
43
|
+
'button',
|
|
44
|
+
'animate',
|
|
45
|
+
'animateTransform',
|
|
46
|
+
'animateMotion',
|
|
47
|
+
'set',
|
|
48
|
+
];
|
|
49
|
+
const TAGS_ALT = SVG_DANGEROUS_TAGS.join('|');
|
|
50
|
+
/**
|
|
51
|
+
* Strip scripting / active constructs from untrusted SVG source. Safe to run on
|
|
52
|
+
* anything: LLM output (dojo), a developer's own file (CLI). The result is only
|
|
53
|
+
* ever consumed as image data (rendered through `<img src="data:…">`, where
|
|
54
|
+
* scripts never execute, and rasterized on a canvas), but we scrub anyway so the
|
|
55
|
+
* raw string is also safe if written to a file or opened directly.
|
|
56
|
+
*/
|
|
57
|
+
export function scrubSvgMarkup(svg) {
|
|
58
|
+
return (svg
|
|
59
|
+
// XML prolog junk (DOCTYPE/ENTITY → billion-laughs / XXE vectors).
|
|
60
|
+
// Handle the internal-subset form `<!DOCTYPE x [ … ]>` first (a
|
|
61
|
+
// non-greedy `<!DOCTYPE …?>` would stop at the first `>` inside the
|
|
62
|
+
// brackets and leave `]>` behind), then the simple form and entities.
|
|
63
|
+
.replace(/<!DOCTYPE[^>[]*\[[\s\S]*?\]\s*>/gi, '')
|
|
64
|
+
.replace(/<!DOCTYPE[^>]*>/gi, '')
|
|
65
|
+
.replace(/<!ENTITY[\s\S]*?>/gi, '')
|
|
66
|
+
// executable / interactive / animation containers, paired or self-closing.
|
|
67
|
+
// One pass, non-greedy: multiply-nested tags of the SAME type (e.g.
|
|
68
|
+
// `<script>a<script>b</script>c</script>`) are not fully unwrapped, but
|
|
69
|
+
// the leftover text is inert and the render context (<img src="data:…">)
|
|
70
|
+
// never executes it — the self-closing sweep below catches stray opens.
|
|
71
|
+
.replace(new RegExp(`<\\s*(${TAGS_ALT})\\b[\\s\\S]*?<\\s*/\\s*\\1\\s*>`, 'gi'), '')
|
|
72
|
+
.replace(new RegExp(`<\\s*(${TAGS_ALT})\\b[^>]*/?>`, 'gi'), '')
|
|
73
|
+
// inline event handlers: onload="…" / onclick='…' / onerror=x
|
|
74
|
+
.replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, '')
|
|
75
|
+
.replace(/\son[a-z]+\s*=\s*'[^']*'/gi, '')
|
|
76
|
+
.replace(/\son[a-z]+\s*=\s*[^\s>]+/gi, '')
|
|
77
|
+
// javascript:/data: link targets on href / xlink:href
|
|
78
|
+
.replace(/\s(href|xlink:href)\s*=\s*"(\s*(javascript|data):)[^"]*"/gi, '')
|
|
79
|
+
.replace(/\s(href|xlink:href)\s*=\s*'(\s*(javascript|data):)[^']*'/gi, '')
|
|
80
|
+
.trim());
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=svg-scrub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svg-scrub.js","sourceRoot":"","sources":["../src/svg-scrub.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,8EAA8E;AAC9E,+EAA+E;AAC/E,0EAA0E;AAC1E,4CAA4C;AAC5C,MAAM,CAAC,MAAM,kBAAkB,GAAG;IACjC,QAAQ;IACR,OAAO;IACP,eAAe;IACf,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,MAAM;IACN,OAAO;IACP,UAAU;IACV,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,kBAAkB;IAClB,eAAe;IACf,KAAK;CACI,CAAA;AAEV,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAE7C;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACzC,OAAO,CACN,GAAG;QACF,mEAAmE;QACnE,gEAAgE;QAChE,oEAAoE;QACpE,sEAAsE;SACrE,OAAO,CAAC,mCAAmC,EAAE,EAAE,CAAC;SAChD,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC;SAChC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC;QACnC,2EAA2E;QAC3E,oEAAoE;QACpE,wEAAwE;QACxE,yEAAyE;QACzE,wEAAwE;SACvE,OAAO,CAAC,IAAI,MAAM,CAAC,SAAS,QAAQ,kCAAkC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;SAClF,OAAO,CAAC,IAAI,MAAM,CAAC,SAAS,QAAQ,cAAc,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;QAC/D,8DAA8D;SAC7D,OAAO,CAAC,4BAA4B,EAAE,EAAE,CAAC;SACzC,OAAO,CAAC,4BAA4B,EAAE,EAAE,CAAC;SACzC,OAAO,CAAC,4BAA4B,EAAE,EAAE,CAAC;QAC1C,sDAAsD;SACrD,OAAO,CAAC,4DAA4D,EAAE,EAAE,CAAC;SACzE,OAAO,CAAC,4DAA4D,EAAE,EAAE,CAAC;SACzE,IAAI,EAAE,CACR,CAAA;AACF,CAAC"}
|