sfscanner 0.1.0 → 0.2.1

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.
Files changed (53) hide show
  1. package/README.md +141 -214
  2. package/THIRD_PARTY_NOTICES.md +385 -48
  3. package/dist/_types/contracts/preflight-prerequisite-types.d.ts +73 -0
  4. package/dist/_types/packages/scanner-cli/src/index.d.ts +6 -0
  5. package/dist/_types/packages/scanner-cli/src/v2/arguments.d.ts +102 -0
  6. package/dist/_types/packages/scanner-cli/src/v2/cli-runner.d.ts +22 -0
  7. package/dist/{v2 → _types/packages/scanner-cli/src/v2}/commands.d.ts +10 -5
  8. package/dist/{v2 → _types/packages/scanner-cli/src/v2}/contracts.d.ts +53 -9
  9. package/dist/_types/packages/scanner-cli/src/v2/help.d.ts +64 -0
  10. package/dist/_types/packages/scanner-cli/src/v2/metadata-output.d.ts +37 -0
  11. package/dist/_types/packages/scanner-cli/src/v2/preflight-readiness.d.ts +108 -0
  12. package/dist/_types/packages/scanner-cli/src/v2/privacy-prepare.d.ts +39 -0
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.js +26129 -0
  15. package/dist/index.js.map +7 -0
  16. package/dist/main.js +26874 -12830
  17. package/dist/main.js.map +4 -4
  18. package/dist/mcp.js +19357 -7477
  19. package/dist/mcp.js.map +4 -4
  20. package/dist/scan-worker.js +28702 -0
  21. package/dist/scan-worker.js.map +7 -0
  22. package/dist/v2/arguments.d.ts +1 -86
  23. package/dist/v2/arguments.js +220 -21
  24. package/dist/v2/arguments.js.map +3 -3
  25. package/dist/v2/cli-runner.d.ts +1 -28
  26. package/dist/v2/cli-runner.js +19080 -7387
  27. package/dist/v2/cli-runner.js.map +4 -4
  28. package/dist/v2/help.d.ts +1 -27
  29. package/dist/v2/help.js +77 -9
  30. package/dist/v2/help.js.map +2 -2
  31. package/dist/v2/metadata-output.d.ts +1 -37
  32. package/dist/v2/metadata-output.js +20917 -276
  33. package/dist/v2/metadata-output.js.map +4 -4
  34. package/dist/v2/preflight-readiness.d.ts +1 -0
  35. package/dist/v2/preflight-readiness.js +447 -0
  36. package/dist/v2/preflight-readiness.js.map +7 -0
  37. package/dist/v2/privacy-prepare.d.ts +1 -0
  38. package/dist/v2/privacy-prepare.js +232 -0
  39. package/dist/v2/privacy-prepare.js.map +7 -0
  40. package/package.json +82 -67
  41. package/dist/v2/atomic-output.d.ts +0 -34
  42. package/dist/v2/batch-runner.d.ts +0 -44
  43. package/dist/v2/config.d.ts +0 -16
  44. package/dist/v2/exit-reducer.d.ts +0 -23
  45. package/dist/v2/input-read.d.ts +0 -26
  46. package/dist/v2/input-selection.d.ts +0 -38
  47. package/dist/v2/json-output.d.ts +0 -97
  48. package/dist/v2/metadata-validation.d.ts +0 -4
  49. package/dist/v2/ndjson-output.d.ts +0 -3
  50. package/dist/v2/sarif-helpers.d.ts +0 -69
  51. package/dist/v2/sarif-output.d.ts +0 -40
  52. package/dist/v2/table-output.d.ts +0 -2
  53. package/dist/v2/worker-host.d.ts +0 -24
package/dist/v2/help.d.ts CHANGED
@@ -1,27 +1 @@
1
- export declare const V2_COMMANDS: readonly ["scan", "rules", "explain", "profiles", "validate-config", "version"];
2
- export type V2CommandName = (typeof V2_COMMANDS)[number];
3
- export type V2OptionName = '--help' | '--schema' | '--format' | '--profile' | '--fail-on' | '--require-complete' | '--max-concurrency' | '--concurrency' | '--timeout' | '--output' | '--config' | '--rules' | '--catalogue' | '--limits' | '--target' | '--n8n-version' | '--edition' | '--deployment' | '--role' | '--task-runner' | '--community-package' | '--configuration' | '--include' | '--exclude' | '--allow-unsigned-development-rules' | '--no-color';
4
- export type V2HelpOption = Readonly<{
5
- name: V2OptionName;
6
- value: string;
7
- commands: readonly V2CommandName[];
8
- description: string;
9
- default?: string;
10
- }>;
11
- export declare const V2_OPTIONS: readonly V2HelpOption[];
12
- export type V2LimitName = 'maxBytes' | 'maxNodes' | 'maxEdges' | 'maxConnectionDepth' | 'maxParameterDepth' | 'maxParameterLeaves' | 'workerDeadlineMs' | 'maxSyntaxNodes' | 'maxDocumentDepth' | 'maxMembersAndElements' | 'maxRawEdges' | 'maxDuplicateKeys' | 'maxDiagnostics' | 'maxStoredSourceSpans' | 'maxExpressionTokens' | 'maxCodeSyntaxNodes' | 'maxSourceWork' | 'maxGraphOperations' | 'maxTaintStates' | 'maxTraceStepsPerFinding' | 'maxFindings' | 'maxRedactionPatches' | 'maxRedactedArtifactBytes' | 'maxEvidenceBytes' | 'maxSerializedResultBytes' | 'maxRuleOperations';
13
- export declare const V2_LIMITS: Readonly<Record<V2LimitName, number>>;
14
- export declare const V2_EXIT_CODES: readonly [0, 1, 2, 3, 4, 130];
15
- export type V2ExitCode = (typeof V2_EXIT_CODES)[number];
16
- export declare const V2_EXIT_REASONS: readonly ["CANCELLED", "INTERNAL", "USAGE", "FINDING_THRESHOLD", "REQUIRE_COMPLETE", "ITEM_FAILURE", "UNDER_THRESHOLD"];
17
- export type V2ExitReason = (typeof V2_EXIT_REASONS)[number];
18
- export declare const V2_CANONICAL_CLAIMS: readonly ["v2 is offline and never initiates network requests (HTTP, DNS, sockets, telemetry, or remote imports).", "Labels and output identities never contain absolute paths or workflow source content.", "File output uses a same-directory temporary file and atomic replace; failures and cancellation preserve the prior file.", "beta-default@2.0.0 is admitted; beta-hardened@2.0.0 is defined but not admitted or selectable.", "No shipped path produces a redacted artifact: maxRedactionPatches and maxRedactedArtifactBytes bound redaction-v2, which is development-only and unwired.", "No shipped path admits an external rule pack: --rules and --catalogue accept only builtin, and --allow-unsigned-development-rules is refused because Ed25519 trust-store admission is deferred.", "Published on npm as sfscanner."];
19
- export type V2CommandHelp = Readonly<{
20
- command: V2CommandName;
21
- usage: readonly string[];
22
- text: string;
23
- }>;
24
- export declare const COMMAND_HELP_V2: Readonly<Record<V2CommandName, string>>;
25
- export declare const GENERAL_HELP_V2: string;
26
- export declare const HELP_TEXT_V2: string;
27
- export declare function getHelpV2(command?: V2CommandName): string;
1
+ export * from "../_types/packages/scanner-cli/src/v2/help.js";
package/dist/v2/help.js CHANGED
@@ -117,10 +117,54 @@ var DEFAULT_SCANNER_LIMITS_V2 = Object.freeze({
117
117
  maxSerializedResultBytes: 5e6,
118
118
  maxRuleOperations: 1e6
119
119
  });
120
+ var ADMITTED_LIMIT_STATES = Object.freeze({
121
+ maxBytes: "WIRED",
122
+ maxNodes: "WIRED",
123
+ maxEdges: "WIRED",
124
+ maxConnectionDepth: "WIRED",
125
+ maxParameterDepth: "WIRED",
126
+ maxParameterLeaves: "WIRED",
127
+ workerDeadlineMs: "WIRED",
128
+ maxSyntaxNodes: "WIRED",
129
+ maxDocumentDepth: "WIRED",
130
+ maxMembersAndElements: "WIRED",
131
+ maxRawEdges: "WIRED",
132
+ maxDuplicateKeys: "WIRED",
133
+ maxDiagnostics: "WIRED",
134
+ maxStoredSourceSpans: "WIRED",
135
+ maxSourceWork: "WIRED",
136
+ maxExpressionTokens: "DORMANT",
137
+ maxCodeSyntaxNodes: "DORMANT",
138
+ maxGraphOperations: "WIRED",
139
+ maxTaintStates: "WIRED",
140
+ maxTraceStepsPerFinding: "WIRED",
141
+ maxFindings: "WIRED",
142
+ maxRedactionPatches: "UNWIRED",
143
+ maxRedactedArtifactBytes: "UNWIRED",
144
+ maxEvidenceBytes: "WIRED",
145
+ maxSerializedResultBytes: "WIRED",
146
+ maxRuleOperations: "WIRED"
147
+ });
148
+ var MEASURED_LIMIT_IDS = [
149
+ "maxBytes",
150
+ "maxNodes",
151
+ "maxEdges",
152
+ "maxConnectionDepth",
153
+ "maxParameterDepth",
154
+ "maxParameterLeaves",
155
+ "maxSyntaxNodes",
156
+ "maxRawEdges",
157
+ "maxDuplicateKeys",
158
+ "maxDiagnostics",
159
+ "maxStoredSourceSpans",
160
+ "maxSourceWork",
161
+ "maxFindings"
162
+ ];
120
163
 
121
164
  // src/v2/help.ts
122
165
  var V2_COMMANDS = Object.freeze([
123
166
  "scan",
167
+ "fix",
124
168
  "rules",
125
169
  "explain",
126
170
  "profiles",
@@ -128,14 +172,18 @@ var V2_COMMANDS = Object.freeze([
128
172
  "version"
129
173
  ]);
130
174
  var SCAN = Object.freeze(["scan"]);
131
- var ALL = V2_COMMANDS;
175
+ var ALL = Object.freeze(["scan", "rules", "explain", "profiles", "validate-config", "version"]);
176
+ var FIX = Object.freeze(["fix"]);
177
+ var EVERY = V2_COMMANDS;
132
178
  var V2_OPTIONS = Object.freeze([
133
179
  { name: "--help", value: "", commands: ALL, description: "Show deterministic help for the selected v2 command." },
134
- { name: "--schema", value: "<v2>", commands: ALL, description: "Select the versioned v2 grammar." },
180
+ { name: "--schema", value: "<v2>", commands: EVERY, description: "Select the versioned v2 grammar." },
135
181
  { name: "--format", value: "<json|ndjson|sarif|table>", commands: ALL, description: "Select the output format.", default: "json for scan; json for metadata" },
136
182
  { name: "--profile", value: "<beta-default>", commands: SCAN, description: "Select the admitted analysis profile.", default: "beta-default" },
137
183
  { name: "--fail-on", value: "<CRITICAL|HIGH|MEDIUM|LOW|INFO>", commands: SCAN, description: "Fail when a finding meets or exceeds this severity.", default: "INFO" },
138
184
  { name: "--require-complete", value: "", commands: SCAN, description: "Treat DEGRADED analysis as a failure.", default: "false" },
185
+ { name: "--audit-supply-chain", value: "", commands: SCAN, description: "Emit canonical advisory status and request metadata; no admitted snapshot is available and no network is used.", default: "false" },
186
+ { name: "--include-readiness", value: "", commands: SCAN, description: "Include preflight readiness assessment in json, ndjson, or table output.", default: "false" },
139
187
  { name: "--max-concurrency", value: "<1..16>", commands: SCAN, description: "Bound concurrent inputs; --concurrency is an equivalent spelling.", default: "1" },
140
188
  { name: "--concurrency", value: "<1..16>", commands: SCAN, description: "Bound concurrent inputs; --max-concurrency is an equivalent spelling.", default: "1" },
141
189
  { name: "--timeout", value: "<1..2147483647>", commands: SCAN, description: "Set the per-input timeout in milliseconds.", default: "30000 (workerDeadlineMs)" },
@@ -152,10 +200,17 @@ var V2_OPTIONS = Object.freeze([
152
200
  { name: "--task-runner", value: "<LEGACY|NATIVE|UNKNOWN>", commands: SCAN, description: "Set the task-runner fact.", default: "UNKNOWN" },
153
201
  { name: "--community-package", value: "<package>", commands: SCAN, description: "Record a community package in target context; repeatable." },
154
202
  { name: "--configuration", value: "<name>", commands: SCAN, description: "Record a configuration fact; repeatable." },
203
+ { name: "--approved-domain", value: "<host>", commands: SCAN, description: "Treat this hostname as approved for UNAPPROVED_DOMAIN; repeatable." },
155
204
  { name: "--include", value: "<posix-glob>", commands: SCAN, description: "Replace config includes with a case-sensitive POSIX glob; repeatable.", default: "**/*.json" },
156
205
  { name: "--exclude", value: "<posix-glob>", commands: SCAN, description: "Replace config excludes with a case-sensitive POSIX glob; repeatable.", default: ".git/**, node_modules/**" },
157
206
  { name: "--allow-unsigned-development-rules", value: "", commands: SCAN, description: "Refused: external rule packs are not admitted, so no pack can be selected.", default: "false" },
158
- { name: "--no-color", value: "", commands: SCAN, description: "Disable color in human output.", default: "false (also disabled for non-TTY)" }
207
+ { name: "--no-color", value: "", commands: SCAN, description: "Disable color in human output.", default: "false (also disabled for non-TTY)" },
208
+ { name: "--root", value: "<absolute-local-dir>", commands: FIX, description: "Absolute local directory holding the workflow and receipts." },
209
+ { name: "--input", value: "<relative-file>", commands: FIX, description: "Root-relative workflow file; standard input cannot be re-read." },
210
+ { name: "--fix", value: "<fix-id>", commands: FIX, description: "Select one reviewed fix; apply requires exactly one." },
211
+ { name: "--mapping", value: "<relative-file>", commands: FIX, description: "Root-relative credential-mapping receipt." },
212
+ { name: "--endpoint-receipt", value: "<relative-file>", commands: FIX, description: "Root-relative endpoint-compatibility receipt." },
213
+ { name: "--confirm", value: "", commands: FIX, description: "Explicit confirmation; required for apply and refused for suggest.", default: "false" }
159
214
  ]);
160
215
  var V2_LIMITS = Object.freeze({
161
216
  maxBytes: DEFAULT_SCANNER_LIMITS_V2.maxBytes,
@@ -185,10 +240,13 @@ var V2_LIMITS = Object.freeze({
185
240
  maxSerializedResultBytes: DEFAULT_SCANNER_LIMITS_V2.maxSerializedResultBytes,
186
241
  maxRuleOperations: DEFAULT_SCANNER_LIMITS_V2.maxRuleOperations
187
242
  });
188
- var UNWIRED_LIMITS = Object.freeze({
189
- maxRedactionPatches: true,
190
- maxRedactedArtifactBytes: true
191
- });
243
+ function limitStateMarker(name) {
244
+ const state = ADMITTED_LIMIT_STATES[name];
245
+ if (state === "WIRED") {
246
+ return MEASURED_LIMIT_IDS.some((id) => id === name) ? " (enforced; usage row)" : " (enforced; no usage row)";
247
+ }
248
+ return state === "DORMANT" ? " (dormant in beta-default; hardened-only)" : " (development-only, unwired)";
249
+ }
192
250
  var V2_EXIT_CODES = Object.freeze([0, 1, 2, 3, 4, 130]);
193
251
  var V2_EXIT_REASONS = Object.freeze([
194
252
  "CANCELLED",
@@ -215,12 +273,21 @@ function optionLine(option, command) {
215
273
  return ` ${option.name}${syntax} ${option.description}${defaultText}`;
216
274
  }
217
275
  function renderCommand(command) {
218
- const usage = command === "scan" ? [`sfscanner --schema v2 scan <file|directory|-> [options]`] : command === "explain" ? [`sfscanner --schema v2 explain <rule-id> [--format json|table]`] : command === "validate-config" ? [`sfscanner --schema v2 validate-config <local-file> [--format json|table]`] : [`sfscanner --schema v2 ${command} [--format json|table]`];
276
+ const usage = command === "scan" ? [`sfscanner --schema v2 scan <file|directory|-> [options]`] : command === "explain" ? [`sfscanner --schema v2 explain <rule-id> [--format json|table]`] : command === "validate-config" ? [`sfscanner --schema v2 validate-config <local-file> [--format json|table]`] : command === "fix" ? [
277
+ "sfscanner --schema v2 fix suggest --root <absolute-local-dir> --input <relative-file> [options]",
278
+ "sfscanner --schema v2 fix apply --root <absolute-local-dir> --input <relative-file> --fix <fix-id> --confirm [options]"
279
+ ] : [`sfscanner --schema v2 ${command} [--format json|table]`];
219
280
  const options = V2_OPTIONS.filter((option) => option.commands.includes(command));
220
281
  const lines = [`sfscanner v2 ${command}`, "", "Usage:", ...usage, "", "Options:", ...options.map((option) => optionLine(option, command))];
221
282
  if (command === "scan") lines.push("", "Inputs are sorted by normalized relative label; stdin (-) cannot join a batch.");
222
283
  if (command === "profiles") lines.push("", "beta-default is admitted. beta-hardened is defined but not admitted or selectable.");
223
284
  if (command === "validate-config") lines.push("", "Only the explicitly supplied local config file is read.");
285
+ if (command === "fix") lines.push(
286
+ "",
287
+ "Output is one JSON n8n-scan-fix-v2 document; --format and --help are not admitted for fix.",
288
+ "Without a conditional-replace host, apply returns HOST_UNSUPPORTED (NO_CONDITIONAL_REPLACE) before any write; the READY plan remains in the document.",
289
+ "Exit codes: 0 READY or WRITTEN; 2 USAGE; 3 FAILED, REFUSED, NO_SUGGESTION or HOST_UNSUPPORTED; 130 CANCELLED."
290
+ );
224
291
  return Object.freeze({ command, usage: Object.freeze(usage), text: `${lines.join("\n")}
225
292
  ` });
226
293
  }
@@ -232,6 +299,7 @@ var generalLines = [
232
299
  "",
233
300
  "Usage:",
234
301
  " sfscanner --schema v2 scan <file|directory|-> [options]",
302
+ " sfscanner --schema v2 fix suggest|apply [options]",
235
303
  " sfscanner --schema v2 rules [--format json|table]",
236
304
  " sfscanner --schema v2 explain <rule-id> [--format json|table]",
237
305
  " sfscanner --schema v2 profiles [--format json|table]",
@@ -249,7 +317,7 @@ var generalLines = [
249
317
  " beta-hardened@2.0.0: defined but not admitted or selectable.",
250
318
  "",
251
319
  "Limits:",
252
- ...Object.entries(V2_LIMITS).map(([name, value]) => ` ${name}=${value}${UNWIRED_LIMITS[name] ? " (development-only, unwired)" : ""}`),
320
+ ...Object.entries(V2_LIMITS).map(([name, value]) => ` ${name}=${value}${limitStateMarker(name)}`),
253
321
  "",
254
322
  "Exit codes and precedence:",
255
323
  " 130 CANCELLED; 4 INTERNAL; 2 USAGE; 1 FINDING_THRESHOLD or REQUIRE_COMPLETE; 3 ITEM_FAILURE; 0 UNDER_THRESHOLD.",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../contracts/exclusion-registry.ts", "../../../../contracts/limits.ts", "../../../../contracts/scanner-limits.ts", "../../../../contracts/scanner-limits-v2.ts", "../../src/v2/help.ts"],
4
- "sourcesContent": ["/**\n * The exclusion registry: an immutable, append-only list of workflow\n * identities withheld from every downstream artifact, keyed by reason and\n * date.\n *\n * **Identity.** A record is keyed by the same canonical id every `Entry`/\n * `Candidate` already carries \u2014 the 16-hex-character output of\n * `pipeline/acquire/entry-id.ts`'s `entryId(kind, ref, path)`. No second\n * identity scheme is introduced: a title or slug can change on a re-scrape or\n * a re-describe, but `entryId` is a hash of the source coordinates\n * (kind + ref + path) that never changes for the same workflow, which is\n * exactly the stability an exclusion needs \u2014 an exclusion recorded against a\n * workflow must still match it after its title, tags, or description change.\n *\n * **Registry vs. rights status.** This module answers a different question\n * than `rights.ts`'s `excluded` status does. The registry is the *cause* and\n * the *authority*: it is consulted at the earliest possible pipeline point\n * (immediately after acquisition, before rights are even decided \u2014 see\n * `pipeline/rights/exclusion-filter.ts`), so an excluded workflow is dropped\n * before it becomes a `Candidate` that anything downstream can see. A record\n * never becomes a full `Entry` at all in the ordinary path, so\n * `EntryRights.status === 'excluded'` is not how exclusion normally\n * propagates \u2014 it exists in the rights union as a second, independent gate:\n * if a record ever reaches rights decision or entry construction carrying an\n * id this registry recognizes, that code path can name it `excluded` instead\n * of guessing, rather than treating an already-answered question as still\n * open. Two gates, not one, because the registry's contents can change\n * (append-only growth) independently of any single pipeline run, and a\n * resumed or partially-cached run is exactly the case where the earlier gate\n * could be bypassed by stale state.\n *\n * **Immutable, append-only.** A registry value is frozen and exposes no\n * mutation method. Growing it means calling `appendExclusion`, which returns\n * a *new* registry with strictly one more record \u2014 it never edits or removes\n * an existing one. There is deliberately no `remove`/`update` API: a\n * retraction is a policy decision ([B]-blocked per\n * `openspec/changes/legal-rights-and-release-integrity/tasks.md`), not a data\n * operation this module should make easy to do quietly.\n */\n\nconst ENTRY_ID_PATTERN = /^[0-9a-f]{16}$/;\n\nexport type ExclusionRecord = {\n /** Canonical `entryId(kind, ref, path)` output \u2014 see module doc comment. */\n id: string;\n /** Human-readable, non-empty. Evidence, not a legal conclusion. */\n reason: string;\n /** ISO 8601 timestamp the exclusion was recorded. */\n excludedAt: string;\n};\n\nexport type ExclusionRegistry = {\n readonly records: readonly ExclusionRecord[];\n isExcluded(id: string): boolean;\n recordFor(id: string): ExclusionRecord | undefined;\n};\n\nfunction validateRecord(record: ExclusionRecord): void {\n if (!ENTRY_ID_PATTERN.test(record.id)) {\n throw new Error(\n `Exclusion registry record has a malformed id \"${record.id}\": expected the 16-hex-character ` +\n 'output of entryId(kind, ref, path), not a title, slug, or free-text identifier.',\n );\n }\n if (!record.reason.trim()) {\n throw new Error(`Exclusion registry record for id \"${record.id}\" has no reason recorded.`);\n }\n if (!Number.isFinite(Date.parse(record.excludedAt))) {\n throw new Error(\n `Exclusion registry record for id \"${record.id}\" has an invalid excludedAt timestamp: \"${record.excludedAt}\".`,\n );\n }\n}\n\n/**\n * Builds a frozen registry from a list of records, validating every one and\n * rejecting a duplicate id outright \u2014 the registry has exactly one record per\n * excluded identity, so two different reasons for the same id is a data bug,\n * not a merge to resolve silently.\n */\nexport function createExclusionRegistry(records: readonly ExclusionRecord[]): ExclusionRegistry {\n const seen = new Set<string>();\n for (const record of records) {\n validateRecord(record);\n if (seen.has(record.id)) {\n throw new Error(`Exclusion registry has more than one record for id \"${record.id}\".`);\n }\n seen.add(record.id);\n }\n const frozenRecords = Object.freeze(records.map((record) => Object.freeze({ ...record })));\n const byId = new Map(frozenRecords.map((record) => [record.id, record]));\n\n return Object.freeze({\n records: frozenRecords,\n isExcluded: (id: string) => byId.has(id),\n recordFor: (id: string) => byId.get(id),\n });\n}\n\n/** The starting registry: no exclusions recorded yet (owner decision [B]-blocked). */\nexport const EMPTY_EXCLUSION_REGISTRY: ExclusionRegistry = createExclusionRegistry([]);\n\n/**\n * Returns a *new* registry with `record` appended. Never mutates `registry`.\n * Throws if `record.id` is already present \u2014 append-only means every id is\n * recorded once, with one reason and one date; changing either requires a\n * conscious new record kept separately, not an overwrite of this one.\n */\nexport function appendExclusion(registry: ExclusionRegistry, record: ExclusionRecord): ExclusionRegistry {\n if (registry.isExcluded(record.id)) {\n throw new Error(\n `Id \"${record.id}\" is already recorded in the exclusion registry (reason: ` +\n `\"${registry.recordFor(record.id)?.reason}\"). Append-only: it cannot be re-added or overwritten.`,\n );\n }\n return createExclusionRegistry([...registry.records, record]);\n}\n", "/** SPDX ids we treat as redistributable. Static allowlist, deliberately short. */\nexport const REDISTRIBUTABLE_LICENCES: Record<string, true> = {\n 'MIT': true,\n 'Apache-2.0': true,\n 'BSD-2-Clause': true,\n 'BSD-3-Clause': true,\n 'ISC': true,\n 'CC0-1.0': true,\n 'Unlicense': true,\n};\n\n/** Hard limits. Shared by acquisition, the worker, and the browser scanner. */\nexport const LIMITS = {\n maxWorkflowBytes: 1_536_000,\n maxNodes: 5_000,\n scanTimeoutMs: 30_000,\n packSize: 250,\n shardSize: 2_000,\n maxPublicFindings: 20,\n} as const;\n", "import { LIMITS } from './limits';\n\n/**\n * The one versioned scanner-limits contract browser, worker, and pipeline\n * callers share. `LIMITS` (limits.ts) stays the source for the three numbers\n * it already owned (maxWorkflowBytes/maxNodes/scanTimeoutMs are read from it\n * below, not re-declared) so this cannot silently drift from it.\n */\nexport const SCANNER_LIMITS_SCHEMA_VERSION = 'scanner-limits-v1' as const;\n\nexport type ScannerLimitsV1 = {\n schemaVersion: typeof SCANNER_LIMITS_SCHEMA_VERSION;\n maxBytes: number;\n maxNodes: number;\n maxEdges: number;\n maxConnectionDepth: number;\n maxParameterDepth: number;\n maxParameterLeaves: number;\n workerDeadlineMs: number;\n};\n\nconst SCANNER_LIMITS_FIELDS = [\n 'schemaVersion',\n 'maxBytes',\n 'maxNodes',\n 'maxEdges',\n 'maxConnectionDepth',\n 'maxParameterDepth',\n 'maxParameterLeaves',\n 'workerDeadlineMs',\n] as const satisfies readonly (keyof ScannerLimitsV1)[];\n\nexport const DEFAULT_SCANNER_LIMITS: ScannerLimitsV1 = Object.freeze({\n schemaVersion: SCANNER_LIMITS_SCHEMA_VERSION,\n maxBytes: LIMITS.maxWorkflowBytes,\n maxNodes: LIMITS.maxNodes,\n maxEdges: 10_000,\n // Previously declared as a `workflow.ts` option but never wired to any\n // production caller (see scanner-contract-and-calibration tasks.md P1.1) \u2014\n // this is the first real default for it.\n maxConnectionDepth: 64,\n maxParameterDepth: 64,\n maxParameterLeaves: 10_000,\n workerDeadlineMs: LIMITS.scanTimeoutMs,\n});\n\nexport type ScannerLimitsValidationResult =\n | { ok: true; limits: ScannerLimitsV1 }\n | { ok: false; errors: string[] };\n\n/** Fails closed and reports every violation, not just the first, so a bad config is fully diagnosable. */\nexport function validateScannerLimits(candidate: unknown): ScannerLimitsValidationResult {\n if (typeof candidate !== 'object' || candidate === null || Array.isArray(candidate)) {\n return { ok: false, errors: ['limits must be an object'] };\n }\n\n const value = candidate as Record<string, unknown>;\n const errors: string[] = [];\n const knownFields = new Set<string>(SCANNER_LIMITS_FIELDS);\n\n for (const field of Object.keys(value)) {\n if (!knownFields.has(field)) errors.push(`unknown field: ${field}`);\n }\n\n if (value.schemaVersion !== SCANNER_LIMITS_SCHEMA_VERSION) {\n errors.push(`unsupported schemaVersion: ${String(value.schemaVersion)}`);\n }\n\n for (const field of SCANNER_LIMITS_FIELDS) {\n if (field === 'schemaVersion') continue;\n if (!(field in value)) {\n errors.push(`missing field: ${field}`);\n continue;\n }\n const raw = value[field];\n if (typeof raw !== 'number' || !Number.isInteger(raw) || raw < 0) {\n errors.push(`${field} must be a non-negative integer`);\n }\n }\n\n if (errors.length > 0) return { ok: false, errors };\n\n return {\n ok: true,\n limits: {\n schemaVersion: SCANNER_LIMITS_SCHEMA_VERSION,\n maxBytes: value.maxBytes as number,\n maxNodes: value.maxNodes as number,\n maxEdges: value.maxEdges as number,\n maxConnectionDepth: value.maxConnectionDepth as number,\n maxParameterDepth: value.maxParameterDepth as number,\n maxParameterLeaves: value.maxParameterLeaves as number,\n workerDeadlineMs: value.workerDeadlineMs as number,\n },\n };\n}\n\n/** Stable key order regardless of the input object's own construction order. */\nexport function serializeScannerLimits(limits: ScannerLimitsV1): string {\n return JSON.stringify(limits, SCANNER_LIMITS_FIELDS as unknown as string[]);\n}\n\n/** The exact option shape `parseWorkflowJson` accepts \u2014 one mapping, not one per caller. */\nexport function toWorkflowParseOptions(limits: ScannerLimitsV1): {\n maxBytes: number;\n maxNodes: number;\n maxEdges: number;\n maxConnectionDepth: number;\n maxParameterDepth: number;\n maxParameterLeaves: number;\n} {\n return {\n maxBytes: limits.maxBytes,\n maxNodes: limits.maxNodes,\n maxEdges: limits.maxEdges,\n maxConnectionDepth: limits.maxConnectionDepth,\n maxParameterDepth: limits.maxParameterDepth,\n maxParameterLeaves: limits.maxParameterLeaves,\n };\n}\n", "import {\n DEFAULT_SCANNER_LIMITS,\n type ScannerLimitsV1,\n} from './scanner-limits';\n\nexport const SCANNER_LIMITS_V2_SCHEMA_VERSION = 'scanner-limits-v2' as const;\n\nconst SCANNER_LIMITS_V2_NUMERIC_FIELDS = [\n 'maxBytes',\n 'maxNodes',\n 'maxEdges',\n 'maxConnectionDepth',\n 'maxParameterDepth',\n 'maxParameterLeaves',\n 'workerDeadlineMs',\n 'maxSyntaxNodes',\n 'maxDocumentDepth',\n 'maxMembersAndElements',\n 'maxRawEdges',\n 'maxDuplicateKeys',\n 'maxDiagnostics',\n 'maxStoredSourceSpans',\n 'maxSourceWork',\n 'maxExpressionTokens',\n 'maxCodeSyntaxNodes',\n 'maxGraphOperations',\n 'maxTaintStates',\n 'maxTraceStepsPerFinding',\n 'maxFindings',\n 'maxRedactionPatches',\n 'maxRedactedArtifactBytes',\n 'maxEvidenceBytes',\n 'maxSerializedResultBytes',\n 'maxRuleOperations',\n] as const;\n\nconst SCANNER_LIMITS_V2_FIELDS = [\n 'schemaVersion',\n ...SCANNER_LIMITS_V2_NUMERIC_FIELDS,\n] as const;\n\nexport type ScannerLimitsV2 = Omit<ScannerLimitsV1, 'schemaVersion'> & {\n schemaVersion: typeof SCANNER_LIMITS_V2_SCHEMA_VERSION;\n maxSyntaxNodes: number;\n maxDocumentDepth: number;\n maxMembersAndElements: number;\n maxRawEdges: number;\n maxDuplicateKeys: number;\n maxDiagnostics: number;\n maxStoredSourceSpans: number;\n maxSourceWork: number;\n maxExpressionTokens: number;\n maxCodeSyntaxNodes: number;\n maxGraphOperations: number;\n maxTaintStates: number;\n maxTraceStepsPerFinding: number;\n maxFindings: number;\n maxRedactionPatches: number;\n maxRedactedArtifactBytes: number;\n maxEvidenceBytes: number;\n maxSerializedResultBytes: number;\n maxRuleOperations: number;\n};\n\nexport const DEFAULT_SCANNER_LIMITS_V2: ScannerLimitsV2 = Object.freeze({\n ...DEFAULT_SCANNER_LIMITS,\n schemaVersion: SCANNER_LIMITS_V2_SCHEMA_VERSION,\n maxSyntaxNodes: 250_000,\n maxDocumentDepth: 256,\n maxMembersAndElements: 250_000,\n maxRawEdges: 50_000,\n maxDuplicateKeys: 1_000,\n maxDiagnostics: 1_000,\n maxStoredSourceSpans: 25_000,\n maxSourceWork: 1_000_000,\n maxExpressionTokens: 100_000,\n maxCodeSyntaxNodes: 250_000,\n maxGraphOperations: 1_000_000,\n maxTaintStates: 250_000,\n maxTraceStepsPerFinding: 100,\n maxFindings: 5_000,\n maxRedactionPatches: 10_000,\n maxRedactedArtifactBytes: 1_536_000,\n maxEvidenceBytes: 1_536_000,\n maxSerializedResultBytes: 5_000_000,\n maxRuleOperations: 1_000_000,\n});\n\nexport type ScannerLimitsV2ValidationResult =\n | { ok: true; limits: ScannerLimitsV2 }\n | { ok: false; errors: string[] };\n\nexport function validateScannerLimitsV2(candidate: unknown): ScannerLimitsV2ValidationResult {\n if (typeof candidate !== 'object' || candidate === null || Array.isArray(candidate)) {\n return { ok: false, errors: ['limits must be an object'] };\n }\n\n const value = candidate as Record<string, unknown>;\n const knownFields = new Set<string>(SCANNER_LIMITS_V2_FIELDS);\n const errors = Object.keys(value)\n .filter((field) => !knownFields.has(field))\n .sort()\n .map((field) => `unknown field: ${field}`);\n\n if (!Object.prototype.hasOwnProperty.call(value, 'schemaVersion')) {\n errors.push('missing field: schemaVersion');\n } else if (value.schemaVersion !== SCANNER_LIMITS_V2_SCHEMA_VERSION) {\n errors.push(`unsupported schemaVersion: ${String(value.schemaVersion)}`);\n }\n\n for (const field of SCANNER_LIMITS_V2_NUMERIC_FIELDS) {\n if (!Object.prototype.hasOwnProperty.call(value, field)) {\n errors.push(`missing field: ${field}`);\n continue;\n }\n const raw = value[field];\n if (!Number.isSafeInteger(raw) || (raw as number) < 0) {\n errors.push(`${field} must be a non-negative safe integer`);\n } else if (field === 'maxDiagnostics' && (raw as number) < 1) {\n errors.push('maxDiagnostics must be at least 1');\n }\n }\n\n if (errors.length > 0) return { ok: false, errors };\n return {\n ok: true,\n limits: {\n schemaVersion: SCANNER_LIMITS_V2_SCHEMA_VERSION,\n ...Object.fromEntries(SCANNER_LIMITS_V2_NUMERIC_FIELDS.map((field) => [field, value[field]])),\n } as ScannerLimitsV2,\n };\n}\n\nexport function toWorkflowParseOptionsV2(limits: ScannerLimitsV2): {\n maxBytes: number;\n maxNodes: number;\n maxEdges: number;\n maxConnectionDepth: number;\n maxParameterDepth: number;\n maxParameterLeaves: number;\n} {\n return {\n maxBytes: limits.maxBytes,\n maxNodes: limits.maxNodes,\n maxEdges: limits.maxEdges,\n maxConnectionDepth: limits.maxConnectionDepth,\n maxParameterDepth: limits.maxParameterDepth,\n maxParameterLeaves: limits.maxParameterLeaves,\n };\n}\n", "import { DEFAULT_SCANNER_LIMITS_V2 } from '@hub/contracts';\n\nexport const V2_COMMANDS = Object.freeze([\n 'scan', 'rules', 'explain', 'profiles', 'validate-config', 'version',\n] as const);\nexport type V2CommandName = (typeof V2_COMMANDS)[number];\n\nexport type V2OptionName =\n | '--help' | '--schema' | '--format' | '--profile' | '--fail-on' | '--require-complete'\n | '--max-concurrency' | '--concurrency' | '--timeout' | '--output' | '--config'\n | '--rules' | '--catalogue' | '--limits' | '--target' | '--n8n-version' | '--edition'\n | '--deployment' | '--role' | '--task-runner' | '--community-package' | '--configuration'\n | '--include' | '--exclude' | '--allow-unsigned-development-rules' | '--no-color';\n\nexport type V2HelpOption = Readonly<{\n name: V2OptionName;\n value: string;\n commands: readonly V2CommandName[];\n description: string;\n default?: string;\n}>;\n\nconst SCAN = Object.freeze(['scan'] as const);\nconst ALL = V2_COMMANDS;\n\nexport const V2_OPTIONS: readonly V2HelpOption[] = Object.freeze([\n { name: '--help', value: '', commands: ALL, description: 'Show deterministic help for the selected v2 command.' },\n { name: '--schema', value: '<v2>', commands: ALL, description: 'Select the versioned v2 grammar.' },\n { name: '--format', value: '<json|ndjson|sarif|table>', commands: ALL, description: 'Select the output format.', default: 'json for scan; json for metadata' },\n { name: '--profile', value: '<beta-default>', commands: SCAN, description: 'Select the admitted analysis profile.', default: 'beta-default' },\n { name: '--fail-on', value: '<CRITICAL|HIGH|MEDIUM|LOW|INFO>', commands: SCAN, description: 'Fail when a finding meets or exceeds this severity.', default: 'INFO' },\n { name: '--require-complete', value: '', commands: SCAN, description: 'Treat DEGRADED analysis as a failure.', default: 'false' },\n { name: '--max-concurrency', value: '<1..16>', commands: SCAN, description: 'Bound concurrent inputs; --concurrency is an equivalent spelling.', default: '1' },\n { name: '--concurrency', value: '<1..16>', commands: SCAN, description: 'Bound concurrent inputs; --max-concurrency is an equivalent spelling.', default: '1' },\n { name: '--timeout', value: '<1..2147483647>', commands: SCAN, description: 'Set the per-input timeout in milliseconds.', default: '30000 (workerDeadlineMs)' },\n { name: '--output', value: '<local-file|->', commands: SCAN, description: 'Write to a local file atomically, or use - for stdout.', default: 'stdout' },\n { name: '--config', value: '<local-file>', commands: SCAN, description: 'Load one explicit versioned config file.' },\n { name: '--rules', value: '<builtin>', commands: SCAN, description: 'Select the embedded built-in ruleset; the only admitted authority.', default: 'builtin' },\n { name: '--catalogue', value: '<builtin>', commands: SCAN, description: 'Select the embedded semantic catalogue.', default: 'builtin' },\n { name: '--limits', value: '<local-file|builtin>', commands: SCAN, description: 'Select embedded or local scanner limits.', default: 'builtin' },\n { name: '--target', value: '<local-file>', commands: SCAN, description: 'Load a local target context.' },\n { name: '--n8n-version', value: '<semver>', commands: SCAN, description: 'Set the n8n version fact.' },\n { name: '--edition', value: '<COMMUNITY|ENTERPRISE|CLOUD|UNKNOWN>', commands: SCAN, description: 'Set the deployment edition fact.', default: 'UNKNOWN' },\n { name: '--deployment', value: '<SELF_HOSTED|CLOUD|UNKNOWN>', commands: SCAN, description: 'Set the deployment fact.', default: 'UNKNOWN' },\n { name: '--role', value: '<OWNER|ADMIN|MEMBER|UNKNOWN>', commands: SCAN, description: 'Set the operator role fact.', default: 'UNKNOWN' },\n { name: '--task-runner', value: '<LEGACY|NATIVE|UNKNOWN>', commands: SCAN, description: 'Set the task-runner fact.', default: 'UNKNOWN' },\n { name: '--community-package', value: '<package>', commands: SCAN, description: 'Record a community package in target context; repeatable.' },\n { name: '--configuration', value: '<name>', commands: SCAN, description: 'Record a configuration fact; repeatable.' },\n { name: '--include', value: '<posix-glob>', commands: SCAN, description: 'Replace config includes with a case-sensitive POSIX glob; repeatable.', default: '**/*.json' },\n { name: '--exclude', value: '<posix-glob>', commands: SCAN, description: 'Replace config excludes with a case-sensitive POSIX glob; repeatable.', default: '.git/**, node_modules/**' },\n { name: '--allow-unsigned-development-rules', value: '', commands: SCAN, description: 'Refused: external rule packs are not admitted, so no pack can be selected.', default: 'false' },\n { name: '--no-color', value: '', commands: SCAN, description: 'Disable color in human output.', default: 'false (also disabled for non-TTY)' },\n]);\n\nexport type V2LimitName =\n | 'maxBytes' | 'maxNodes' | 'maxEdges' | 'maxConnectionDepth' | 'maxParameterDepth' | 'maxParameterLeaves'\n | 'workerDeadlineMs' | 'maxSyntaxNodes' | 'maxDocumentDepth' | 'maxMembersAndElements' | 'maxRawEdges'\n | 'maxDuplicateKeys' | 'maxDiagnostics' | 'maxStoredSourceSpans' | 'maxExpressionTokens' | 'maxCodeSyntaxNodes'\n | 'maxSourceWork'\n | 'maxGraphOperations' | 'maxTaintStates' | 'maxTraceStepsPerFinding' | 'maxFindings' | 'maxRedactionPatches'\n | 'maxRedactedArtifactBytes' | 'maxEvidenceBytes' | 'maxSerializedResultBytes' | 'maxRuleOperations';\n\nexport const V2_LIMITS: Readonly<Record<V2LimitName, number>> = Object.freeze({\n maxBytes: DEFAULT_SCANNER_LIMITS_V2.maxBytes,\n maxNodes: DEFAULT_SCANNER_LIMITS_V2.maxNodes,\n maxEdges: DEFAULT_SCANNER_LIMITS_V2.maxEdges,\n maxConnectionDepth: DEFAULT_SCANNER_LIMITS_V2.maxConnectionDepth,\n maxParameterDepth: DEFAULT_SCANNER_LIMITS_V2.maxParameterDepth,\n maxParameterLeaves: DEFAULT_SCANNER_LIMITS_V2.maxParameterLeaves,\n workerDeadlineMs: DEFAULT_SCANNER_LIMITS_V2.workerDeadlineMs,\n maxSyntaxNodes: DEFAULT_SCANNER_LIMITS_V2.maxSyntaxNodes,\n maxDocumentDepth: DEFAULT_SCANNER_LIMITS_V2.maxDocumentDepth,\n maxMembersAndElements: DEFAULT_SCANNER_LIMITS_V2.maxMembersAndElements,\n maxRawEdges: DEFAULT_SCANNER_LIMITS_V2.maxRawEdges,\n maxDuplicateKeys: DEFAULT_SCANNER_LIMITS_V2.maxDuplicateKeys,\n maxDiagnostics: DEFAULT_SCANNER_LIMITS_V2.maxDiagnostics,\n maxStoredSourceSpans: DEFAULT_SCANNER_LIMITS_V2.maxStoredSourceSpans,\n maxSourceWork: DEFAULT_SCANNER_LIMITS_V2.maxSourceWork,\n maxExpressionTokens: DEFAULT_SCANNER_LIMITS_V2.maxExpressionTokens,\n maxCodeSyntaxNodes: DEFAULT_SCANNER_LIMITS_V2.maxCodeSyntaxNodes,\n maxGraphOperations: DEFAULT_SCANNER_LIMITS_V2.maxGraphOperations,\n maxTaintStates: DEFAULT_SCANNER_LIMITS_V2.maxTaintStates,\n maxTraceStepsPerFinding: DEFAULT_SCANNER_LIMITS_V2.maxTraceStepsPerFinding,\n maxFindings: DEFAULT_SCANNER_LIMITS_V2.maxFindings,\n maxRedactionPatches: DEFAULT_SCANNER_LIMITS_V2.maxRedactionPatches,\n maxRedactedArtifactBytes: DEFAULT_SCANNER_LIMITS_V2.maxRedactedArtifactBytes,\n maxEvidenceBytes: DEFAULT_SCANNER_LIMITS_V2.maxEvidenceBytes,\n maxSerializedResultBytes: DEFAULT_SCANNER_LIMITS_V2.maxSerializedResultBytes,\n maxRuleOperations: DEFAULT_SCANNER_LIMITS_V2.maxRuleOperations,\n});\n\n/** Limits whose operation is development-only and unwired; see V2_CANONICAL_CLAIMS below. */\nconst UNWIRED_LIMITS: Readonly<Record<string, true>> = Object.freeze({\n maxRedactionPatches: true,\n maxRedactedArtifactBytes: true,\n});\n\nexport const V2_EXIT_CODES = Object.freeze([0, 1, 2, 3, 4, 130] as const);\nexport type V2ExitCode = (typeof V2_EXIT_CODES)[number];\nexport const V2_EXIT_REASONS = Object.freeze([\n 'CANCELLED', 'INTERNAL', 'USAGE', 'FINDING_THRESHOLD', 'REQUIRE_COMPLETE', 'ITEM_FAILURE', 'UNDER_THRESHOLD',\n] as const);\nexport type V2ExitReason = (typeof V2_EXIT_REASONS)[number];\n\nexport const V2_CANONICAL_CLAIMS = Object.freeze([\n 'v2 is offline and never initiates network requests (HTTP, DNS, sockets, telemetry, or remote imports).',\n 'Labels and output identities never contain absolute paths or workflow source content.',\n 'File output uses a same-directory temporary file and atomic replace; failures and cancellation preserve the prior file.',\n 'beta-default@2.0.0 is admitted; beta-hardened@2.0.0 is defined but not admitted or selectable.',\n 'No shipped path produces a redacted artifact: maxRedactionPatches and maxRedactedArtifactBytes bound redaction-v2, which is development-only and unwired.',\n 'No shipped path admits an external rule pack: --rules and --catalogue accept only builtin, and --allow-unsigned-development-rules is refused because Ed25519 trust-store admission is deferred.',\n 'Published on npm as sfscanner.',\n] as const);\n\nexport type V2CommandHelp = Readonly<{ command: V2CommandName; usage: readonly string[]; text: string }>;\n\nfunction optionLine(option: V2HelpOption, command?: V2CommandName): string {\n const value = option.name === '--format' && command !== undefined && command !== 'scan' ? '<json|table>' : option.value;\n const syntax = value ? ` ${value}` : '';\n const defaultText = option.default ? ` Default: ${option.default}.` : '';\n return ` ${option.name}${syntax} ${option.description}${defaultText}`;\n}\nfunction renderCommand(command: V2CommandName): V2CommandHelp {\n const usage = command === 'scan'\n ? [`sfscanner --schema v2 scan <file|directory|-> [options]`]\n : command === 'explain'\n ? [`sfscanner --schema v2 explain <rule-id> [--format json|table]`]\n : command === 'validate-config'\n ? [`sfscanner --schema v2 validate-config <local-file> [--format json|table]`]\n : [`sfscanner --schema v2 ${command} [--format json|table]`];\n const options = V2_OPTIONS.filter((option) => option.commands.includes(command));\n const lines = [`sfscanner v2 ${command}`, '', 'Usage:', ...usage, '', 'Options:', ...options.map((option) => optionLine(option, command))];\n if (command === 'scan') lines.push('', 'Inputs are sorted by normalized relative label; stdin (-) cannot join a batch.');\n if (command === 'profiles') lines.push('', 'beta-default is admitted. beta-hardened is defined but not admitted or selectable.');\n if (command === 'validate-config') lines.push('', 'Only the explicitly supplied local config file is read.');\n return Object.freeze({ command, usage: Object.freeze(usage), text: `${lines.join('\\n')}\\n` });\n}\n\nexport const COMMAND_HELP_V2: Readonly<Record<V2CommandName, string>> = Object.freeze(\n Object.fromEntries(V2_COMMANDS.map((command) => [command, renderCommand(command).text])) as Record<V2CommandName, string>,\n);\n\nconst generalLines = [\n 'sfscanner \u2014 deterministic offline n8n workflow scanner (v2)', '', 'Usage:',\n ' sfscanner --schema v2 scan <file|directory|-> [options]',\n ' sfscanner --schema v2 rules [--format json|table]',\n ' sfscanner --schema v2 explain <rule-id> [--format json|table]',\n ' sfscanner --schema v2 profiles [--format json|table]',\n ' sfscanner --schema v2 validate-config <local-file> [--format json|table]',\n ' sfscanner --schema v2 version [--format json|table]', '', 'Commands:',\n 'Defaults:', ' format: json; profile: beta-default; fail-on: INFO; require-complete: false; concurrency: 1 (maximum 16); timeout: 30000ms.',\n ' include: **/*.json; exclude: .git/**, node_modules/**; rules/catalogue/limits: builtin.',\n ' target facts: edition UNKNOWN; deployment UNKNOWN; role UNKNOWN; task-runner UNKNOWN.',\n '', 'Profiles:', ' beta-default@2.0.0: admitted and selectable.',\n ' beta-hardened@2.0.0: defined but not admitted or selectable.', '', 'Limits:',\n ...Object.entries(V2_LIMITS).map(([name, value]) => ` ${name}=${value}${UNWIRED_LIMITS[name] ? ' (development-only, unwired)' : ''}`), '',\n 'Exit codes and precedence:', ' 130 CANCELLED; 4 INTERNAL; 2 USAGE; 1 FINDING_THRESHOLD or REQUIRE_COMPLETE; 3 ITEM_FAILURE; 0 UNDER_THRESHOLD.',\n ' Pre-scan usage/configuration/admission failures emit no machine envelope and exit 2; stdout cancellation emits a valid envelope and exits 130.',\n ' When finding threshold and require-complete both fire, FINDING_THRESHOLD wins. Indeterminate never exits 0.', '',\n ...V2_CANONICAL_CLAIMS,\n];\nexport const GENERAL_HELP_V2 = `${generalLines.join('\\n')}\\n`;\nexport const HELP_TEXT_V2 = GENERAL_HELP_V2;\n\nexport function getHelpV2(command?: V2CommandName): string {\n return command === undefined ? GENERAL_HELP_V2 : COMMAND_HELP_V2[command];\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * The exclusion registry: an immutable, append-only list of workflow\n * identities withheld from every downstream artifact, keyed by reason and\n * date.\n *\n * **Identity.** A record is keyed by the same canonical id every `Entry`/\n * `Candidate` already carries \u2014 the 16-hex-character output of\n * `pipeline/acquire/entry-id.ts`'s `entryId(kind, ref, path)`. No second\n * identity scheme is introduced: a title or slug can change on a re-scrape or\n * a re-describe, but `entryId` is a hash of the source coordinates\n * (kind + ref + path) that never changes for the same workflow, which is\n * exactly the stability an exclusion needs \u2014 an exclusion recorded against a\n * workflow must still match it after its title, tags, or description change.\n *\n * **Registry vs. rights status.** This module answers a different question\n * than `rights.ts`'s `excluded` status does. The registry is the *cause* and\n * the *authority*: it is consulted at the earliest possible pipeline point\n * (immediately after acquisition, before rights are even decided \u2014 see\n * `pipeline/rights/exclusion-filter.ts`), so an excluded workflow is dropped\n * before it becomes a `Candidate` that anything downstream can see. A record\n * never becomes a full `Entry` at all in the ordinary path, so\n * `EntryRights.status === 'excluded'` is not how exclusion normally\n * propagates \u2014 it exists in the rights union as a second, independent gate:\n * if a record ever reaches rights decision or entry construction carrying an\n * id this registry recognizes, that code path can name it `excluded` instead\n * of guessing, rather than treating an already-answered question as still\n * open. Two gates, not one, because the registry's contents can change\n * (append-only growth) independently of any single pipeline run, and a\n * resumed or partially-cached run is exactly the case where the earlier gate\n * could be bypassed by stale state.\n *\n * **Immutable, append-only.** A registry value is frozen and exposes no\n * mutation method. Growing it means calling `appendExclusion`, which returns\n * a *new* registry with strictly one more record \u2014 it never edits or removes\n * an existing one. There is deliberately no `remove`/`update` API: a\n * retraction is a policy decision ([B]-blocked per\n * `openspec/changes/legal-rights-and-release-integrity/tasks.md`), not a data\n * operation this module should make easy to do quietly.\n */\n\nconst ENTRY_ID_PATTERN = /^[0-9a-f]{16}$/;\n\nexport type ExclusionRecord = {\n /** Canonical `entryId(kind, ref, path)` output \u2014 see module doc comment. */\n id: string;\n /** Human-readable, non-empty. Evidence, not a legal conclusion. */\n reason: string;\n /** ISO 8601 timestamp the exclusion was recorded. */\n excludedAt: string;\n};\n\nexport type ExclusionRegistry = {\n readonly records: readonly ExclusionRecord[];\n isExcluded(id: string): boolean;\n recordFor(id: string): ExclusionRecord | undefined;\n};\n\nfunction validateRecord(record: ExclusionRecord): void {\n if (!ENTRY_ID_PATTERN.test(record.id)) {\n throw new Error(\n `Exclusion registry record has a malformed id \"${record.id}\": expected the 16-hex-character ` +\n 'output of entryId(kind, ref, path), not a title, slug, or free-text identifier.',\n );\n }\n if (!record.reason.trim()) {\n throw new Error(`Exclusion registry record for id \"${record.id}\" has no reason recorded.`);\n }\n if (!Number.isFinite(Date.parse(record.excludedAt))) {\n throw new Error(\n `Exclusion registry record for id \"${record.id}\" has an invalid excludedAt timestamp: \"${record.excludedAt}\".`,\n );\n }\n}\n\n/**\n * Builds a frozen registry from a list of records, validating every one and\n * rejecting a duplicate id outright \u2014 the registry has exactly one record per\n * excluded identity, so two different reasons for the same id is a data bug,\n * not a merge to resolve silently.\n */\nexport function createExclusionRegistry(records: readonly ExclusionRecord[]): ExclusionRegistry {\n const seen = new Set<string>();\n for (const record of records) {\n validateRecord(record);\n if (seen.has(record.id)) {\n throw new Error(`Exclusion registry has more than one record for id \"${record.id}\".`);\n }\n seen.add(record.id);\n }\n const frozenRecords = Object.freeze(records.map((record) => Object.freeze({ ...record })));\n const byId = new Map(frozenRecords.map((record) => [record.id, record]));\n\n return Object.freeze({\n records: frozenRecords,\n isExcluded: (id: string) => byId.has(id),\n recordFor: (id: string) => byId.get(id),\n });\n}\n\n/** The starting registry: no exclusions recorded yet (owner decision [B]-blocked). */\nexport const EMPTY_EXCLUSION_REGISTRY: ExclusionRegistry = createExclusionRegistry([]);\n\n/**\n * Returns a *new* registry with `record` appended. Never mutates `registry`.\n * Throws if `record.id` is already present \u2014 append-only means every id is\n * recorded once, with one reason and one date; changing either requires a\n * conscious new record kept separately, not an overwrite of this one.\n */\nexport function appendExclusion(registry: ExclusionRegistry, record: ExclusionRecord): ExclusionRegistry {\n if (registry.isExcluded(record.id)) {\n throw new Error(\n `Id \"${record.id}\" is already recorded in the exclusion registry (reason: ` +\n `\"${registry.recordFor(record.id)?.reason}\"). Append-only: it cannot be re-added or overwritten.`,\n );\n }\n return createExclusionRegistry([...registry.records, record]);\n}\n", "/** SPDX ids we treat as redistributable. Static allowlist, deliberately short. */\nexport const REDISTRIBUTABLE_LICENCES: Record<string, true> = {\n 'MIT': true,\n 'Apache-2.0': true,\n 'BSD-2-Clause': true,\n 'BSD-3-Clause': true,\n 'ISC': true,\n 'CC0-1.0': true,\n 'Unlicense': true,\n};\n\n/** Hard limits. Shared by acquisition, the worker, and the browser scanner. */\nexport const LIMITS = {\n maxWorkflowBytes: 1_536_000,\n maxNodes: 5_000,\n scanTimeoutMs: 30_000,\n packSize: 250,\n shardSize: 2_000,\n maxPublicFindings: 20,\n} as const;\n", "import { LIMITS } from './limits';\n\n/**\n * The one versioned scanner-limits contract browser, worker, and pipeline\n * callers share. `LIMITS` (limits.ts) stays the source for the three numbers\n * it already owned (maxWorkflowBytes/maxNodes/scanTimeoutMs are read from it\n * below, not re-declared) so this cannot silently drift from it.\n */\nexport const SCANNER_LIMITS_SCHEMA_VERSION = 'scanner-limits-v1' as const;\n\nexport type ScannerLimitsV1 = {\n schemaVersion: typeof SCANNER_LIMITS_SCHEMA_VERSION;\n maxBytes: number;\n maxNodes: number;\n maxEdges: number;\n maxConnectionDepth: number;\n maxParameterDepth: number;\n maxParameterLeaves: number;\n workerDeadlineMs: number;\n};\n\nconst SCANNER_LIMITS_FIELDS = [\n 'schemaVersion',\n 'maxBytes',\n 'maxNodes',\n 'maxEdges',\n 'maxConnectionDepth',\n 'maxParameterDepth',\n 'maxParameterLeaves',\n 'workerDeadlineMs',\n] as const satisfies readonly (keyof ScannerLimitsV1)[];\n\nexport const DEFAULT_SCANNER_LIMITS: ScannerLimitsV1 = Object.freeze({\n schemaVersion: SCANNER_LIMITS_SCHEMA_VERSION,\n maxBytes: LIMITS.maxWorkflowBytes,\n maxNodes: LIMITS.maxNodes,\n maxEdges: 10_000,\n // Previously declared as a `workflow.ts` option but never wired to any\n // production caller (see scanner-contract-and-calibration tasks.md P1.1) \u2014\n // this is the first real default for it.\n maxConnectionDepth: 64,\n maxParameterDepth: 64,\n maxParameterLeaves: 10_000,\n workerDeadlineMs: LIMITS.scanTimeoutMs,\n});\n\nexport type ScannerLimitsValidationResult =\n | { ok: true; limits: ScannerLimitsV1 }\n | { ok: false; errors: string[] };\n\n/** Fails closed and reports every violation, not just the first, so a bad config is fully diagnosable. */\nexport function validateScannerLimits(candidate: unknown): ScannerLimitsValidationResult {\n if (typeof candidate !== 'object' || candidate === null || Array.isArray(candidate)) {\n return { ok: false, errors: ['limits must be an object'] };\n }\n\n const value = candidate as Record<string, unknown>;\n const errors: string[] = [];\n const knownFields = new Set<string>(SCANNER_LIMITS_FIELDS);\n\n for (const field of Object.keys(value)) {\n if (!knownFields.has(field)) errors.push(`unknown field: ${field}`);\n }\n\n if (value.schemaVersion !== SCANNER_LIMITS_SCHEMA_VERSION) {\n errors.push(`unsupported schemaVersion: ${String(value.schemaVersion)}`);\n }\n\n for (const field of SCANNER_LIMITS_FIELDS) {\n if (field === 'schemaVersion') continue;\n if (!(field in value)) {\n errors.push(`missing field: ${field}`);\n continue;\n }\n const raw = value[field];\n if (typeof raw !== 'number' || !Number.isInteger(raw) || raw < 0) {\n errors.push(`${field} must be a non-negative integer`);\n }\n }\n\n if (errors.length > 0) return { ok: false, errors };\n\n return {\n ok: true,\n limits: {\n schemaVersion: SCANNER_LIMITS_SCHEMA_VERSION,\n maxBytes: value.maxBytes as number,\n maxNodes: value.maxNodes as number,\n maxEdges: value.maxEdges as number,\n maxConnectionDepth: value.maxConnectionDepth as number,\n maxParameterDepth: value.maxParameterDepth as number,\n maxParameterLeaves: value.maxParameterLeaves as number,\n workerDeadlineMs: value.workerDeadlineMs as number,\n },\n };\n}\n\n/** Stable key order regardless of the input object's own construction order. */\nexport function serializeScannerLimits(limits: ScannerLimitsV1): string {\n return JSON.stringify(limits, SCANNER_LIMITS_FIELDS as unknown as string[]);\n}\n\n/** The exact option shape `parseWorkflowJson` accepts \u2014 one mapping, not one per caller. */\nexport function toWorkflowParseOptions(limits: ScannerLimitsV1): {\n maxBytes: number;\n maxNodes: number;\n maxEdges: number;\n maxConnectionDepth: number;\n maxParameterDepth: number;\n maxParameterLeaves: number;\n} {\n return {\n maxBytes: limits.maxBytes,\n maxNodes: limits.maxNodes,\n maxEdges: limits.maxEdges,\n maxConnectionDepth: limits.maxConnectionDepth,\n maxParameterDepth: limits.maxParameterDepth,\n maxParameterLeaves: limits.maxParameterLeaves,\n };\n}\n", "import {\n DEFAULT_SCANNER_LIMITS,\n type ScannerLimitsV1,\n} from './scanner-limits';\n\nexport const SCANNER_LIMITS_V2_SCHEMA_VERSION = 'scanner-limits-v2' as const;\n\nconst SCANNER_LIMITS_V2_NUMERIC_FIELDS = [\n 'maxBytes',\n 'maxNodes',\n 'maxEdges',\n 'maxConnectionDepth',\n 'maxParameterDepth',\n 'maxParameterLeaves',\n 'workerDeadlineMs',\n 'maxSyntaxNodes',\n 'maxDocumentDepth',\n 'maxMembersAndElements',\n 'maxRawEdges',\n 'maxDuplicateKeys',\n 'maxDiagnostics',\n 'maxStoredSourceSpans',\n 'maxSourceWork',\n 'maxExpressionTokens',\n 'maxCodeSyntaxNodes',\n 'maxGraphOperations',\n 'maxTaintStates',\n 'maxTraceStepsPerFinding',\n 'maxFindings',\n 'maxRedactionPatches',\n 'maxRedactedArtifactBytes',\n 'maxEvidenceBytes',\n 'maxSerializedResultBytes',\n 'maxRuleOperations',\n] as const;\n\nconst SCANNER_LIMITS_V2_FIELDS = [\n 'schemaVersion',\n ...SCANNER_LIMITS_V2_NUMERIC_FIELDS,\n] as const;\n\nexport type ScannerLimitsV2 = Omit<ScannerLimitsV1, 'schemaVersion'> & {\n schemaVersion: typeof SCANNER_LIMITS_V2_SCHEMA_VERSION;\n maxSyntaxNodes: number;\n maxDocumentDepth: number;\n maxMembersAndElements: number;\n maxRawEdges: number;\n maxDuplicateKeys: number;\n maxDiagnostics: number;\n maxStoredSourceSpans: number;\n maxSourceWork: number;\n maxExpressionTokens: number;\n maxCodeSyntaxNodes: number;\n maxGraphOperations: number;\n maxTaintStates: number;\n maxTraceStepsPerFinding: number;\n maxFindings: number;\n maxRedactionPatches: number;\n maxRedactedArtifactBytes: number;\n maxEvidenceBytes: number;\n maxSerializedResultBytes: number;\n maxRuleOperations: number;\n};\n\nexport const DEFAULT_SCANNER_LIMITS_V2: ScannerLimitsV2 = Object.freeze({\n ...DEFAULT_SCANNER_LIMITS,\n schemaVersion: SCANNER_LIMITS_V2_SCHEMA_VERSION,\n maxSyntaxNodes: 250_000,\n maxDocumentDepth: 256,\n maxMembersAndElements: 250_000,\n maxRawEdges: 50_000,\n maxDuplicateKeys: 1_000,\n maxDiagnostics: 1_000,\n maxStoredSourceSpans: 25_000,\n maxSourceWork: 1_000_000,\n maxExpressionTokens: 100_000,\n maxCodeSyntaxNodes: 250_000,\n maxGraphOperations: 1_000_000,\n maxTaintStates: 250_000,\n maxTraceStepsPerFinding: 100,\n maxFindings: 5_000,\n maxRedactionPatches: 10_000,\n maxRedactedArtifactBytes: 1_536_000,\n maxEvidenceBytes: 1_536_000,\n maxSerializedResultBytes: 5_000_000,\n maxRuleOperations: 1_000_000,\n});\n\nexport type ScannerLimitsV2ValidationResult =\n | { ok: true; limits: ScannerLimitsV2 }\n | { ok: false; errors: string[] };\n\nexport type AdmittedLimitStateV2 = 'WIRED' | 'DORMANT' | 'UNWIRED';\nexport type ScannerLimitNameV2 = (typeof SCANNER_LIMITS_V2_NUMERIC_FIELDS)[number];\n\n/**\n * The one authority every help surface and usage row reads. `WIRED` is consumed by the admitted\n * beta-default path, `DORMANT` only by an unadmitted profile (the hardened code, value-flow, taint\n * and graph analyzers), and `UNWIRED` by no shipped path (redaction-v2 is development-only).\n * `workerDeadlineMs` is wired because the CLI worker host enforces it.\n */\nexport const ADMITTED_LIMIT_STATES: Readonly<Record<ScannerLimitNameV2, AdmittedLimitStateV2>> = Object.freeze({\n maxBytes: 'WIRED',\n maxNodes: 'WIRED',\n maxEdges: 'WIRED',\n maxConnectionDepth: 'WIRED',\n maxParameterDepth: 'WIRED',\n maxParameterLeaves: 'WIRED',\n workerDeadlineMs: 'WIRED',\n maxSyntaxNodes: 'WIRED',\n maxDocumentDepth: 'WIRED',\n maxMembersAndElements: 'WIRED',\n maxRawEdges: 'WIRED',\n maxDuplicateKeys: 'WIRED',\n maxDiagnostics: 'WIRED',\n maxStoredSourceSpans: 'WIRED',\n maxSourceWork: 'WIRED',\n maxExpressionTokens: 'DORMANT',\n maxCodeSyntaxNodes: 'DORMANT',\n maxGraphOperations: 'WIRED',\n maxTaintStates: 'WIRED',\n maxTraceStepsPerFinding: 'WIRED',\n maxFindings: 'WIRED',\n maxRedactionPatches: 'UNWIRED',\n maxRedactedArtifactBytes: 'UNWIRED',\n maxEvidenceBytes: 'WIRED',\n maxSerializedResultBytes: 'WIRED',\n maxRuleOperations: 'WIRED',\n});\n\n/**\n * The subset with a real per-scan counter. The narrow tuple is deliberate: a wide annotation would\n * make every one of the 26 names assignable, so the 13-key counter object below would not typecheck.\n * A budget without a counter is enforced where it is consumed but has no honest `usage` row.\n */\nexport const MEASURED_LIMIT_IDS = [\n 'maxBytes', 'maxNodes', 'maxEdges', 'maxConnectionDepth', 'maxParameterDepth',\n 'maxParameterLeaves', 'maxSyntaxNodes', 'maxRawEdges', 'maxDuplicateKeys',\n 'maxDiagnostics', 'maxStoredSourceSpans', 'maxSourceWork', 'maxFindings',\n] as const satisfies readonly ScannerLimitNameV2[];\n\nexport function validateScannerLimitsV2(candidate: unknown): ScannerLimitsV2ValidationResult {\n if (typeof candidate !== 'object' || candidate === null || Array.isArray(candidate)) {\n return { ok: false, errors: ['limits must be an object'] };\n }\n\n const value = candidate as Record<string, unknown>;\n const knownFields = new Set<string>(SCANNER_LIMITS_V2_FIELDS);\n const errors = Object.keys(value)\n .filter((field) => !knownFields.has(field))\n .sort()\n .map((field) => `unknown field: ${field}`);\n\n if (!Object.prototype.hasOwnProperty.call(value, 'schemaVersion')) {\n errors.push('missing field: schemaVersion');\n } else if (value.schemaVersion !== SCANNER_LIMITS_V2_SCHEMA_VERSION) {\n errors.push(`unsupported schemaVersion: ${String(value.schemaVersion)}`);\n }\n\n for (const field of SCANNER_LIMITS_V2_NUMERIC_FIELDS) {\n if (!Object.prototype.hasOwnProperty.call(value, field)) {\n errors.push(`missing field: ${field}`);\n continue;\n }\n const raw = value[field];\n if (!Number.isSafeInteger(raw) || (raw as number) < 0) {\n errors.push(`${field} must be a non-negative safe integer`);\n } else if (field === 'maxDiagnostics' && (raw as number) < 1) {\n errors.push('maxDiagnostics must be at least 1');\n }\n }\n\n if (errors.length > 0) return { ok: false, errors };\n return {\n ok: true,\n limits: {\n schemaVersion: SCANNER_LIMITS_V2_SCHEMA_VERSION,\n ...Object.fromEntries(SCANNER_LIMITS_V2_NUMERIC_FIELDS.map((field) => [field, value[field]])),\n } as ScannerLimitsV2,\n };\n}\n\nexport function toWorkflowParseOptionsV2(limits: ScannerLimitsV2): {\n maxBytes: number;\n maxNodes: number;\n maxEdges: number;\n maxConnectionDepth: number;\n maxParameterDepth: number;\n maxParameterLeaves: number;\n} {\n return {\n maxBytes: limits.maxBytes,\n maxNodes: limits.maxNodes,\n maxEdges: limits.maxEdges,\n maxConnectionDepth: limits.maxConnectionDepth,\n maxParameterDepth: limits.maxParameterDepth,\n maxParameterLeaves: limits.maxParameterLeaves,\n };\n}\n", "import { ADMITTED_LIMIT_STATES, DEFAULT_SCANNER_LIMITS_V2, MEASURED_LIMIT_IDS } from '@hub/contracts';\r\n\r\nexport const V2_COMMANDS = Object.freeze([\r\n 'scan', 'fix', 'rules', 'explain', 'profiles', 'validate-config', 'version',\r\n] as const);\r\nexport type V2CommandName = (typeof V2_COMMANDS)[number];\r\n\r\nexport type V2OptionName =\r\n | '--help' | '--schema' | '--format' | '--profile' | '--fail-on' | '--require-complete' | '--audit-supply-chain' | '--include-readiness'\r\n | '--max-concurrency' | '--concurrency' | '--timeout' | '--output' | '--config'\r\n | '--rules' | '--catalogue' | '--limits' | '--target' | '--n8n-version' | '--edition'\r\n | '--deployment' | '--role' | '--task-runner' | '--community-package' | '--configuration'\r\n | '--approved-domain' | '--include' | '--exclude' | '--allow-unsigned-development-rules' | '--no-color'\r\n | '--root' | '--input' | '--fix' | '--mapping' | '--endpoint-receipt' | '--confirm';\r\n\r\nexport type V2HelpOption = Readonly<{\r\n name: V2OptionName;\r\n value: string;\r\n commands: readonly V2CommandName[];\r\n description: string;\r\n default?: string;\r\n}>;\r\n\r\nconst SCAN = Object.freeze(['scan'] as const);\r\nconst ALL = Object.freeze(['scan', 'rules', 'explain', 'profiles', 'validate-config', 'version'] as const);\r\nconst FIX = Object.freeze(['fix'] as const);\r\nconst EVERY = V2_COMMANDS;\r\n\r\nexport const V2_OPTIONS: readonly V2HelpOption[] = Object.freeze([\r\n { name: '--help', value: '', commands: ALL, description: 'Show deterministic help for the selected v2 command.' },\r\n { name: '--schema', value: '<v2>', commands: EVERY, description: 'Select the versioned v2 grammar.' },\r\n { name: '--format', value: '<json|ndjson|sarif|table>', commands: ALL, description: 'Select the output format.', default: 'json for scan; json for metadata' },\r\n { name: '--profile', value: '<beta-default>', commands: SCAN, description: 'Select the admitted analysis profile.', default: 'beta-default' },\r\n { name: '--fail-on', value: '<CRITICAL|HIGH|MEDIUM|LOW|INFO>', commands: SCAN, description: 'Fail when a finding meets or exceeds this severity.', default: 'INFO' },\r\n { name: '--require-complete', value: '', commands: SCAN, description: 'Treat DEGRADED analysis as a failure.', default: 'false' },\r\n { name: '--audit-supply-chain', value: '', commands: SCAN, description: 'Emit canonical advisory status and request metadata; no admitted snapshot is available and no network is used.', default: 'false' },\r\n { name: '--include-readiness', value: '', commands: SCAN, description: 'Include preflight readiness assessment in json, ndjson, or table output.', default: 'false' },\r\n { name: '--max-concurrency', value: '<1..16>', commands: SCAN, description: 'Bound concurrent inputs; --concurrency is an equivalent spelling.', default: '1' },\r\n { name: '--concurrency', value: '<1..16>', commands: SCAN, description: 'Bound concurrent inputs; --max-concurrency is an equivalent spelling.', default: '1' },\r\n { name: '--timeout', value: '<1..2147483647>', commands: SCAN, description: 'Set the per-input timeout in milliseconds.', default: '30000 (workerDeadlineMs)' },\r\n { name: '--output', value: '<local-file|->', commands: SCAN, description: 'Write to a local file atomically, or use - for stdout.', default: 'stdout' },\r\n { name: '--config', value: '<local-file>', commands: SCAN, description: 'Load one explicit versioned config file.' },\r\n { name: '--rules', value: '<builtin>', commands: SCAN, description: 'Select the embedded built-in ruleset; the only admitted authority.', default: 'builtin' },\r\n { name: '--catalogue', value: '<builtin>', commands: SCAN, description: 'Select the embedded semantic catalogue.', default: 'builtin' },\r\n { name: '--limits', value: '<local-file|builtin>', commands: SCAN, description: 'Select embedded or local scanner limits.', default: 'builtin' },\r\n { name: '--target', value: '<local-file>', commands: SCAN, description: 'Load a local target context.' },\r\n { name: '--n8n-version', value: '<semver>', commands: SCAN, description: 'Set the n8n version fact.' },\r\n { name: '--edition', value: '<COMMUNITY|ENTERPRISE|CLOUD|UNKNOWN>', commands: SCAN, description: 'Set the deployment edition fact.', default: 'UNKNOWN' },\r\n { name: '--deployment', value: '<SELF_HOSTED|CLOUD|UNKNOWN>', commands: SCAN, description: 'Set the deployment fact.', default: 'UNKNOWN' },\r\n { name: '--role', value: '<OWNER|ADMIN|MEMBER|UNKNOWN>', commands: SCAN, description: 'Set the operator role fact.', default: 'UNKNOWN' },\r\n { name: '--task-runner', value: '<LEGACY|NATIVE|UNKNOWN>', commands: SCAN, description: 'Set the task-runner fact.', default: 'UNKNOWN' },\r\n { name: '--community-package', value: '<package>', commands: SCAN, description: 'Record a community package in target context; repeatable.' },\r\n { name: '--configuration', value: '<name>', commands: SCAN, description: 'Record a configuration fact; repeatable.' },\r\n { name: '--approved-domain', value: '<host>', commands: SCAN, description: 'Treat this hostname as approved for UNAPPROVED_DOMAIN; repeatable.' },\r\n { name: '--include', value: '<posix-glob>', commands: SCAN, description: 'Replace config includes with a case-sensitive POSIX glob; repeatable.', default: '**/*.json' },\r\n { name: '--exclude', value: '<posix-glob>', commands: SCAN, description: 'Replace config excludes with a case-sensitive POSIX glob; repeatable.', default: '.git/**, node_modules/**' },\r\n { name: '--allow-unsigned-development-rules', value: '', commands: SCAN, description: 'Refused: external rule packs are not admitted, so no pack can be selected.', default: 'false' },\r\n { name: '--no-color', value: '', commands: SCAN, description: 'Disable color in human output.', default: 'false (also disabled for non-TTY)' },\r\n { name: '--root', value: '<absolute-local-dir>', commands: FIX, description: 'Absolute local directory holding the workflow and receipts.' },\r\n { name: '--input', value: '<relative-file>', commands: FIX, description: 'Root-relative workflow file; standard input cannot be re-read.' },\r\n { name: '--fix', value: '<fix-id>', commands: FIX, description: 'Select one reviewed fix; apply requires exactly one.' },\r\n { name: '--mapping', value: '<relative-file>', commands: FIX, description: 'Root-relative credential-mapping receipt.' },\r\n { name: '--endpoint-receipt', value: '<relative-file>', commands: FIX, description: 'Root-relative endpoint-compatibility receipt.' },\r\n { name: '--confirm', value: '', commands: FIX, description: 'Explicit confirmation; required for apply and refused for suggest.', default: 'false' },\r\n]);\r\n\r\n/**\r\n * The published limit vocabulary, restated because a shipped declaration must not name the private,\r\n * unpublished contracts workspace: a strict NodeNext consumer of the packed package fails `TS2307`\r\n * on that import. The values below still come from the contract, and the two vocabularies stay\r\n * exhaustive against each other at compile time.\r\n */\r\nexport type V2LimitName =\r\n | 'maxBytes' | 'maxNodes' | 'maxEdges' | 'maxConnectionDepth' | 'maxParameterDepth' | 'maxParameterLeaves'\r\n | 'workerDeadlineMs' | 'maxSyntaxNodes' | 'maxDocumentDepth' | 'maxMembersAndElements' | 'maxRawEdges'\r\n | 'maxDuplicateKeys' | 'maxDiagnostics' | 'maxStoredSourceSpans' | 'maxSourceWork' | 'maxExpressionTokens'\r\n | 'maxCodeSyntaxNodes' | 'maxGraphOperations' | 'maxTaintStates' | 'maxTraceStepsPerFinding' | 'maxFindings'\r\n | 'maxRedactionPatches' | 'maxRedactedArtifactBytes' | 'maxEvidenceBytes' | 'maxSerializedResultBytes'\r\n | 'maxRuleOperations';\r\n\r\nexport const V2_LIMITS: Readonly<Record<V2LimitName, number>> = Object.freeze({\r\n maxBytes: DEFAULT_SCANNER_LIMITS_V2.maxBytes,\r\n maxNodes: DEFAULT_SCANNER_LIMITS_V2.maxNodes,\r\n maxEdges: DEFAULT_SCANNER_LIMITS_V2.maxEdges,\r\n maxConnectionDepth: DEFAULT_SCANNER_LIMITS_V2.maxConnectionDepth,\r\n maxParameterDepth: DEFAULT_SCANNER_LIMITS_V2.maxParameterDepth,\r\n maxParameterLeaves: DEFAULT_SCANNER_LIMITS_V2.maxParameterLeaves,\r\n workerDeadlineMs: DEFAULT_SCANNER_LIMITS_V2.workerDeadlineMs,\r\n maxSyntaxNodes: DEFAULT_SCANNER_LIMITS_V2.maxSyntaxNodes,\r\n maxDocumentDepth: DEFAULT_SCANNER_LIMITS_V2.maxDocumentDepth,\r\n maxMembersAndElements: DEFAULT_SCANNER_LIMITS_V2.maxMembersAndElements,\r\n maxRawEdges: DEFAULT_SCANNER_LIMITS_V2.maxRawEdges,\r\n maxDuplicateKeys: DEFAULT_SCANNER_LIMITS_V2.maxDuplicateKeys,\r\n maxDiagnostics: DEFAULT_SCANNER_LIMITS_V2.maxDiagnostics,\r\n maxStoredSourceSpans: DEFAULT_SCANNER_LIMITS_V2.maxStoredSourceSpans,\r\n maxSourceWork: DEFAULT_SCANNER_LIMITS_V2.maxSourceWork,\r\n maxExpressionTokens: DEFAULT_SCANNER_LIMITS_V2.maxExpressionTokens,\r\n maxCodeSyntaxNodes: DEFAULT_SCANNER_LIMITS_V2.maxCodeSyntaxNodes,\r\n maxGraphOperations: DEFAULT_SCANNER_LIMITS_V2.maxGraphOperations,\r\n maxTaintStates: DEFAULT_SCANNER_LIMITS_V2.maxTaintStates,\r\n maxTraceStepsPerFinding: DEFAULT_SCANNER_LIMITS_V2.maxTraceStepsPerFinding,\r\n maxFindings: DEFAULT_SCANNER_LIMITS_V2.maxFindings,\r\n maxRedactionPatches: DEFAULT_SCANNER_LIMITS_V2.maxRedactionPatches,\r\n maxRedactedArtifactBytes: DEFAULT_SCANNER_LIMITS_V2.maxRedactedArtifactBytes,\r\n maxEvidenceBytes: DEFAULT_SCANNER_LIMITS_V2.maxEvidenceBytes,\r\n maxSerializedResultBytes: DEFAULT_SCANNER_LIMITS_V2.maxSerializedResultBytes,\r\n maxRuleOperations: DEFAULT_SCANNER_LIMITS_V2.maxRuleOperations,\r\n});\r\n\r\n/**\r\n * Type-only parity guard: every limit the contract gains must be published here. `Exclude` is\r\n * `never` while the two vocabularies agree, and the type-parameter default stops satisfying its\r\n * constraint the moment they diverge. Erased at emit, so it costs no runtime lookup and never\r\n * reaches `help.d.ts`; the reverse direction is enforced by indexing `ADMITTED_LIMIT_STATES`.\r\n */\r\ntype AssertEveryContractLimitIsPublished<Gap extends never = Exclude<keyof typeof ADMITTED_LIMIT_STATES, V2LimitName>> = Gap;\r\n\r\n/** The help marker for one budget, derived from the contract authority rather than a copy. */\r\nfunction limitStateMarker(name: V2LimitName): string {\r\n const state = ADMITTED_LIMIT_STATES[name];\r\n if (state === 'WIRED') {\r\n return MEASURED_LIMIT_IDS.some((id) => id === name) ? ' (enforced; usage row)' : ' (enforced; no usage row)';\r\n }\r\n return state === 'DORMANT' ? ' (dormant in beta-default; hardened-only)' : ' (development-only, unwired)';\r\n}\r\n\r\nexport const V2_EXIT_CODES = Object.freeze([0, 1, 2, 3, 4, 130] as const);\r\nexport type V2ExitCode = (typeof V2_EXIT_CODES)[number];\r\nexport const V2_EXIT_REASONS = Object.freeze([\r\n 'CANCELLED', 'INTERNAL', 'USAGE', 'FINDING_THRESHOLD', 'REQUIRE_COMPLETE', 'ITEM_FAILURE', 'UNDER_THRESHOLD',\r\n] as const);\r\nexport type V2ExitReason = (typeof V2_EXIT_REASONS)[number];\r\n\r\nexport const V2_CANONICAL_CLAIMS = Object.freeze([\r\n 'v2 is offline and never initiates network requests (HTTP, DNS, sockets, telemetry, or remote imports).',\r\n 'Labels and output identities never contain absolute paths or workflow source content.',\r\n 'File output uses a same-directory temporary file and atomic replace; failures and cancellation preserve the prior file.',\r\n 'beta-default@2.0.0 is admitted; beta-hardened@2.0.0 is defined but not admitted or selectable.',\r\n 'No shipped path produces a redacted artifact: maxRedactionPatches and maxRedactedArtifactBytes bound redaction-v2, which is development-only and unwired.',\r\n 'No shipped path admits an external rule pack: --rules and --catalogue accept only builtin, and --allow-unsigned-development-rules is refused because Ed25519 trust-store admission is deferred.',\r\n 'Published on npm as sfscanner.',\r\n] as const);\r\n\r\nexport type V2CommandHelp = Readonly<{ command: V2CommandName; usage: readonly string[]; text: string }>;\r\n\r\nfunction optionLine(option: V2HelpOption, command?: V2CommandName): string {\r\n const value = option.name === '--format' && command !== undefined && command !== 'scan' ? '<json|table>' : option.value;\r\n const syntax = value ? ` ${value}` : '';\r\n const defaultText = option.default ? ` Default: ${option.default}.` : '';\r\n return ` ${option.name}${syntax} ${option.description}${defaultText}`;\r\n}\r\nfunction renderCommand(command: V2CommandName): V2CommandHelp {\r\n const usage = command === 'scan'\r\n ? [`sfscanner --schema v2 scan <file|directory|-> [options]`]\r\n : command === 'explain'\r\n ? [`sfscanner --schema v2 explain <rule-id> [--format json|table]`]\r\n : command === 'validate-config'\r\n ? [`sfscanner --schema v2 validate-config <local-file> [--format json|table]`]\r\n : command === 'fix'\r\n ? [\r\n 'sfscanner --schema v2 fix suggest --root <absolute-local-dir> --input <relative-file> [options]',\r\n 'sfscanner --schema v2 fix apply --root <absolute-local-dir> --input <relative-file> --fix <fix-id> --confirm [options]',\r\n ]\r\n : [`sfscanner --schema v2 ${command} [--format json|table]`];\r\n const options = V2_OPTIONS.filter((option) => option.commands.includes(command));\r\n const lines = [`sfscanner v2 ${command}`, '', 'Usage:', ...usage, '', 'Options:', ...options.map((option) => optionLine(option, command))];\r\n if (command === 'scan') lines.push('', 'Inputs are sorted by normalized relative label; stdin (-) cannot join a batch.');\r\n if (command === 'profiles') lines.push('', 'beta-default is admitted. beta-hardened is defined but not admitted or selectable.');\r\n if (command === 'validate-config') lines.push('', 'Only the explicitly supplied local config file is read.');\r\n if (command === 'fix') lines.push('',\r\n 'Output is one JSON n8n-scan-fix-v2 document; --format and --help are not admitted for fix.',\r\n 'Without a conditional-replace host, apply returns HOST_UNSUPPORTED (NO_CONDITIONAL_REPLACE) before any write; the READY plan remains in the document.',\r\n 'Exit codes: 0 READY or WRITTEN; 2 USAGE; 3 FAILED, REFUSED, NO_SUGGESTION or HOST_UNSUPPORTED; 130 CANCELLED.');\r\n return Object.freeze({ command, usage: Object.freeze(usage), text: `${lines.join('\\n')}\\n` });\r\n}\r\n\r\nexport const COMMAND_HELP_V2: Readonly<Record<V2CommandName, string>> = Object.freeze(\r\n Object.fromEntries(V2_COMMANDS.map((command) => [command, renderCommand(command).text])) as Record<V2CommandName, string>,\r\n);\r\n\r\nconst generalLines = [\r\n 'sfscanner \u2014 deterministic offline n8n workflow scanner (v2)', '', 'Usage:',\r\n ' sfscanner --schema v2 scan <file|directory|-> [options]',\r\n ' sfscanner --schema v2 fix suggest|apply [options]',\r\n ' sfscanner --schema v2 rules [--format json|table]',\r\n ' sfscanner --schema v2 explain <rule-id> [--format json|table]',\r\n ' sfscanner --schema v2 profiles [--format json|table]',\r\n ' sfscanner --schema v2 validate-config <local-file> [--format json|table]',\r\n ' sfscanner --schema v2 version [--format json|table]', '', 'Commands:',\r\n 'Defaults:', ' format: json; profile: beta-default; fail-on: INFO; require-complete: false; concurrency: 1 (maximum 16); timeout: 30000ms.',\r\n ' include: **/*.json; exclude: .git/**, node_modules/**; rules/catalogue/limits: builtin.',\r\n ' target facts: edition UNKNOWN; deployment UNKNOWN; role UNKNOWN; task-runner UNKNOWN.',\r\n '', 'Profiles:', ' beta-default@2.0.0: admitted and selectable.',\r\n ' beta-hardened@2.0.0: defined but not admitted or selectable.', '', 'Limits:',\r\n ...Object.entries(V2_LIMITS).map(([name, value]) => ` ${name}=${value}${limitStateMarker(name as V2LimitName)}`), '',\r\n 'Exit codes and precedence:', ' 130 CANCELLED; 4 INTERNAL; 2 USAGE; 1 FINDING_THRESHOLD or REQUIRE_COMPLETE; 3 ITEM_FAILURE; 0 UNDER_THRESHOLD.',\r\n ' Pre-scan usage/configuration/admission failures emit no machine envelope and exit 2; stdout cancellation emits a valid envelope and exits 130.',\r\n ' When finding threshold and require-complete both fire, FINDING_THRESHOLD wins. Indeterminate never exits 0.', '',\r\n ...V2_CANONICAL_CLAIMS,\r\n];\r\nexport const GENERAL_HELP_V2 = `${generalLines.join('\\n')}\\n`;\r\nexport const HELP_TEXT_V2 = GENERAL_HELP_V2;\r\n\r\nexport function getHelpV2(command?: V2CommandName): string {\r\n return command === undefined ? GENERAL_HELP_V2 : COMMAND_HELP_V2[command];\r\n}\r\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,37 +1 @@
1
- import type { CliMetadataDocumentV2 } from './commands';
2
- import type { CliCommandV2 } from './arguments';
3
- export type MetadataFormatV2 = 'json' | 'table';
4
- export type MetadataOutputOptionsV2 = Readonly<{
5
- color?: boolean;
6
- configDirectory?: string;
7
- }>;
8
- export declare function renderMetadataTableV2(document: CliMetadataDocumentV2, _options?: MetadataOutputOptionsV2): string;
9
- export declare function serializeMetadataJsonV2(document: CliMetadataDocumentV2): string;
10
- export declare function serializeMetadataV2(document: CliMetadataDocumentV2, format?: MetadataFormatV2, options?: MetadataOutputOptionsV2): string;
11
- export declare function emitRulesDocumentV2(format?: MetadataFormatV2, options?: MetadataOutputOptionsV2): string;
12
- export declare function emitExplainDocumentV2(ruleId: string, format?: MetadataFormatV2, options?: MetadataOutputOptionsV2): Readonly<{
13
- ok: true;
14
- output: string;
15
- } | {
16
- ok: false;
17
- code: 'USAGE';
18
- message: string;
19
- }>;
20
- export declare function emitProfilesDocumentV2(format?: MetadataFormatV2, options?: MetadataOutputOptionsV2): string;
21
- export declare function emitVersionDocumentV2(format?: MetadataFormatV2, options?: MetadataOutputOptionsV2): string;
22
- export declare function emitConfigValidationDocumentV2(input: unknown, format?: MetadataFormatV2, options?: MetadataOutputOptionsV2): Readonly<{
23
- ok: true;
24
- output: string;
25
- } | {
26
- ok: false;
27
- code: 'USAGE';
28
- message: string;
29
- }>;
30
- export declare function emitMetadataCommandResultV2(command: CliCommandV2, configInput?: unknown, options?: MetadataOutputOptionsV2): Readonly<{
31
- ok: true;
32
- output: string;
33
- } | {
34
- ok: false;
35
- code: 'USAGE';
36
- message: string;
37
- }>;
1
+ export * from "../_types/packages/scanner-cli/src/v2/metadata-output.js";