sfscanner 0.2.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.
- package/README.md +13 -3
- package/THIRD_PARTY_NOTICES.md +385 -48
- package/dist/_types/contracts/preflight-prerequisite-types.d.ts +73 -0
- package/dist/_types/packages/scanner-cli/src/index.d.ts +6 -0
- package/dist/_types/packages/scanner-cli/src/v2/arguments.d.ts +102 -0
- package/dist/_types/packages/scanner-cli/src/v2/cli-runner.d.ts +22 -0
- package/dist/{v2 → _types/packages/scanner-cli/src/v2}/commands.d.ts +6 -2
- package/dist/{v2 → _types/packages/scanner-cli/src/v2}/contracts.d.ts +34 -7
- package/dist/_types/packages/scanner-cli/src/v2/help.d.ts +64 -0
- package/dist/_types/packages/scanner-cli/src/v2/metadata-output.d.ts +37 -0
- package/dist/_types/packages/scanner-cli/src/v2/preflight-readiness.d.ts +108 -0
- package/dist/_types/packages/scanner-cli/src/v2/privacy-prepare.d.ts +39 -0
- package/dist/index.d.ts +1 -4
- package/dist/index.js +17285 -1922
- package/dist/index.js.map +4 -4
- package/dist/main.js +25308 -8016
- package/dist/main.js.map +4 -4
- package/dist/mcp.js +16987 -1870
- package/dist/mcp.js.map +4 -4
- package/dist/scan-worker.js +20064 -3162
- package/dist/scan-worker.js.map +4 -4
- package/dist/v2/arguments.d.ts +1 -87
- package/dist/v2/arguments.js +96 -6
- package/dist/v2/arguments.js.map +2 -2
- package/dist/v2/cli-runner.d.ts +1 -22
- package/dist/v2/cli-runner.js +16813 -1772
- package/dist/v2/cli-runner.js.map +4 -4
- package/dist/v2/help.d.ts +1 -33
- package/dist/v2/help.js +28 -7
- package/dist/v2/help.js.map +2 -2
- package/dist/v2/metadata-output.d.ts +1 -37
- package/dist/v2/metadata-output.js +15405 -1469
- package/dist/v2/metadata-output.js.map +4 -4
- package/dist/v2/preflight-readiness.d.ts +1 -0
- package/dist/v2/preflight-readiness.js +447 -0
- package/dist/v2/preflight-readiness.js.map +7 -0
- package/dist/v2/privacy-prepare.d.ts +1 -0
- package/dist/v2/privacy-prepare.js +232 -0
- package/dist/v2/privacy-prepare.js.map +7 -0
- package/package.json +12 -4
- package/dist/v2/atomic-output.d.ts +0 -34
- package/dist/v2/batch-runner.d.ts +0 -50
- package/dist/v2/cli-runner-internal.d.ts +0 -28
- package/dist/v2/config.d.ts +0 -16
- package/dist/v2/exit-reducer.d.ts +0 -23
- package/dist/v2/input-read.d.ts +0 -26
- package/dist/v2/input-selection.d.ts +0 -38
- package/dist/v2/json-output.d.ts +0 -97
- package/dist/v2/metadata-validation.d.ts +0 -4
- package/dist/v2/ndjson-output.d.ts +0 -3
- package/dist/v2/path-admission.d.ts +0 -8
- package/dist/v2/scan-config-load.d.ts +0 -21
- package/dist/v2/scan-worker-host.d.ts +0 -29
- package/dist/v2/table-output.d.ts +0 -2
- package/dist/v2/worker-host.d.ts +0 -24
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// src/v2/privacy-prepare.ts
|
|
2
|
+
var PRIVACY_PREPARE_SCHEMA_V1 = "n8n-workflow-privacy-prepare-v1";
|
|
3
|
+
var PRIVACY_PREPARE_DISCLAIMER_V1 = "Heuristic and rule-bounded privacy preparation. Does NOT guarantee complete secret or PII removal. The raw diff and change excerpts contain the original removed values and are sensitive local review material; never share or upload them.";
|
|
4
|
+
var CREDENTIAL_ID_PLACEHOLDER = "[REDACTED_CREDENTIAL_ID]";
|
|
5
|
+
var CREDENTIAL_NAME_PLACEHOLDER = "[REDACTED_CREDENTIAL_NAME]";
|
|
6
|
+
var HEADER_VALUE_PLACEHOLDER = "[REDACTED_HEADER_VALUE]";
|
|
7
|
+
var INTERNAL_URL_PLACEHOLDER = "https://internal.example.invalid/...";
|
|
8
|
+
var TOKEN_PLACEHOLDER = "[REDACTED_TOKEN]";
|
|
9
|
+
var WEBHOOK_PATH_PLACEHOLDER = "[REDACTED_WEBHOOK_PATH]";
|
|
10
|
+
var WEBHOOK_ID_PLACEHOLDER = "[REDACTED_WEBHOOK_ID]";
|
|
11
|
+
var DEFAULT_INTERNAL_SUFFIXES = Object.freeze([".local", ".internal", ".localdomain", ".lan", ".home.arpa", ".corp", ".intranet"]);
|
|
12
|
+
var WEBHOOK_TYPE = "n8n-nodes-base.webhook";
|
|
13
|
+
var EXCERPT_LIMIT = 80;
|
|
14
|
+
var SENSITIVE_HEADER_NAMES = Object.freeze({
|
|
15
|
+
authorization: true,
|
|
16
|
+
"proxy-authorization": true,
|
|
17
|
+
cookie: true,
|
|
18
|
+
"set-cookie": true,
|
|
19
|
+
"x-api-key": true,
|
|
20
|
+
"x-auth-token": true,
|
|
21
|
+
"x-access-token": true,
|
|
22
|
+
"x-secret": true,
|
|
23
|
+
"x-api-token": true,
|
|
24
|
+
"api-key": true,
|
|
25
|
+
apikey: true
|
|
26
|
+
});
|
|
27
|
+
var SECRET_PARAMETER_KEYS = Object.freeze({
|
|
28
|
+
password: true,
|
|
29
|
+
passwd: true,
|
|
30
|
+
apikey: true,
|
|
31
|
+
api_key: true,
|
|
32
|
+
token: true,
|
|
33
|
+
secret: true,
|
|
34
|
+
clientsecret: true,
|
|
35
|
+
client_secret: true,
|
|
36
|
+
privatekey: true,
|
|
37
|
+
private_key: true,
|
|
38
|
+
accesstoken: true,
|
|
39
|
+
access_token: true,
|
|
40
|
+
refreshtoken: true,
|
|
41
|
+
refresh_token: true,
|
|
42
|
+
authorization: true
|
|
43
|
+
});
|
|
44
|
+
var URL_HOST_RE = /^[a-z][a-z0-9+.-]*:\/\/(?:[^/?#@]*@)?(\[[^\]]+\]|[^:/?#]+)/i;
|
|
45
|
+
var EMBEDDED_TOKEN_RULES = Object.freeze([
|
|
46
|
+
[/([?&](?:access_token|refresh_token|id_token|token|api_key|apikey|key|secret|sig|signature|auth|password|code)=)[^&#\s]+/gi, `$1${TOKEN_PLACEHOLDER}`],
|
|
47
|
+
[/\bBearer\s+[A-Za-z0-9._~+/=-]+/g, `Bearer ${TOKEN_PLACEHOLDER}`],
|
|
48
|
+
[/\bBasic\s+[A-Za-z0-9+/=]+/g, `Basic ${TOKEN_PLACEHOLDER}`]
|
|
49
|
+
]);
|
|
50
|
+
var isDict = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
51
|
+
var defineOwn = (target, key, value) => {
|
|
52
|
+
Object.defineProperty(target, key, { value, enumerable: true, configurable: true, writable: true });
|
|
53
|
+
};
|
|
54
|
+
var escapePointer = (key) => key.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
55
|
+
var excerpt = (value) => value.length <= EXCERPT_LIMIT ? value : `${value.slice(0, EXCERPT_LIMIT - 3)}...`;
|
|
56
|
+
var cmp = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
57
|
+
function recordChange(changes, path, kind, original, replacement) {
|
|
58
|
+
if (original !== replacement) changes.push(Object.freeze({ path, kind, originalExcerpt: excerpt(original), replacement }));
|
|
59
|
+
return replacement;
|
|
60
|
+
}
|
|
61
|
+
function isPrivateIpv4(host) {
|
|
62
|
+
const parts = host.split(".");
|
|
63
|
+
if (parts.length !== 4 || parts.some((part) => !/^[0-9]{1,3}$/.test(part))) return false;
|
|
64
|
+
const octets = parts.map(Number);
|
|
65
|
+
if (octets.some((octet) => octet > 255)) return false;
|
|
66
|
+
const first = octets[0] ?? -1;
|
|
67
|
+
const second = octets[1] ?? -1;
|
|
68
|
+
return first === 0 || first === 10 || first === 127 || first === 192 && second === 168 || first === 172 && second >= 16 && second <= 31 || first === 169 && second === 254 || first === 100 && second >= 64 && second <= 127;
|
|
69
|
+
}
|
|
70
|
+
var IPV4_MAPPED_RE = /^::ffff:(\d{1,3}(?:\.\d{1,3}){3}|[0-9a-f]{1,4}:[0-9a-f]{1,4})$/i;
|
|
71
|
+
function mappedIpv4(host) {
|
|
72
|
+
const text = IPV4_MAPPED_RE.exec(host)?.[1];
|
|
73
|
+
if (text === void 0) return null;
|
|
74
|
+
if (text.includes(".")) return text;
|
|
75
|
+
const [high = 0, low = 0] = text.split(":").map((group) => Number.parseInt(group, 16));
|
|
76
|
+
return [high >> 8, high & 255, low >> 8, low & 255].join(".");
|
|
77
|
+
}
|
|
78
|
+
function isInternalIpv6(host) {
|
|
79
|
+
const mapped = mappedIpv4(host);
|
|
80
|
+
if (mapped !== null) return isPrivateIpv4(mapped);
|
|
81
|
+
if (host === "::" || host === "::1") return true;
|
|
82
|
+
return /^f[cd][0-9a-f]{2}:/.test(host) || /^fe[89ab][0-9a-f]:/.test(host);
|
|
83
|
+
}
|
|
84
|
+
function isInternalHost(host, suffixes) {
|
|
85
|
+
const normalized = host.toLowerCase().replace(/^\[|\]$/g, "").replace(/\.+$/, "");
|
|
86
|
+
if (normalized === "localhost" || normalized === "0.0.0.0") return true;
|
|
87
|
+
if (normalized.includes(":")) return isInternalIpv6(normalized);
|
|
88
|
+
if (isPrivateIpv4(normalized)) return true;
|
|
89
|
+
if (suffixes.some((suffix) => normalized.endsWith(suffix))) return true;
|
|
90
|
+
return !normalized.includes(".");
|
|
91
|
+
}
|
|
92
|
+
function resolveUrlHost(value) {
|
|
93
|
+
const match = URL_HOST_RE.exec(value);
|
|
94
|
+
if (match === null) return "";
|
|
95
|
+
const fallback = match[1] ?? "";
|
|
96
|
+
try {
|
|
97
|
+
return new URL(value).hostname || fallback;
|
|
98
|
+
} catch {
|
|
99
|
+
return fallback;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function isInternalUrl(value, suffixes) {
|
|
103
|
+
const host = resolveUrlHost(value);
|
|
104
|
+
return host.length > 0 && isInternalHost(host, suffixes);
|
|
105
|
+
}
|
|
106
|
+
function sanitizeString(value, path, ctx) {
|
|
107
|
+
if (value.length === 0) return value;
|
|
108
|
+
if (isInternalUrl(value, ctx.suffixes)) return recordChange(ctx.changes, path, "INTERNAL_URL", value, ctx.urlPlaceholder);
|
|
109
|
+
if (!path.includes("/parameters/")) return value;
|
|
110
|
+
if (SECRET_PARAMETER_KEYS[path.slice(path.lastIndexOf("/") + 1).toLowerCase()] === true) return recordChange(ctx.changes, path, "TOKEN", value, TOKEN_PLACEHOLDER);
|
|
111
|
+
const scrubbed = EMBEDDED_TOKEN_RULES.reduce((current, [pattern, replacement]) => current.replace(pattern, replacement), value);
|
|
112
|
+
return scrubbed === value ? value : recordChange(ctx.changes, path, "TOKEN", value, scrubbed);
|
|
113
|
+
}
|
|
114
|
+
function sanitizeValue(value, path, ctx) {
|
|
115
|
+
if (Array.isArray(value)) return value.map((item, index) => sanitizeValue(item, `${path}/${index}`, ctx));
|
|
116
|
+
if (isDict(value)) return sanitizeObject(value, path, ctx);
|
|
117
|
+
return typeof value === "string" ? sanitizeString(value, path, ctx) : value;
|
|
118
|
+
}
|
|
119
|
+
function sanitizeCredentials(input, path, ctx) {
|
|
120
|
+
const out = {};
|
|
121
|
+
for (const [credentialType, details] of Object.entries(input)) {
|
|
122
|
+
const typePath = `${path}/${escapePointer(credentialType)}`;
|
|
123
|
+
if (!isDict(details)) {
|
|
124
|
+
defineOwn(out, credentialType, sanitizeValue(details, typePath, ctx));
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const cleaned = {};
|
|
128
|
+
for (const [key, raw] of Object.entries(details)) {
|
|
129
|
+
const valuePath = `${typePath}/${escapePointer(key)}`;
|
|
130
|
+
if (key === "id" && (typeof raw === "string" || typeof raw === "number") && String(raw).length > 0) {
|
|
131
|
+
defineOwn(cleaned, key, recordChange(ctx.changes, valuePath, "CREDENTIAL", String(raw), CREDENTIAL_ID_PLACEHOLDER));
|
|
132
|
+
} else if (key === "name" && typeof raw === "string" && raw.length > 0) {
|
|
133
|
+
defineOwn(cleaned, key, recordChange(ctx.changes, valuePath, "CREDENTIAL", raw, CREDENTIAL_NAME_PLACEHOLDER));
|
|
134
|
+
} else {
|
|
135
|
+
defineOwn(cleaned, key, sanitizeValue(raw, valuePath, ctx));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
defineOwn(out, credentialType, cleaned);
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
function sanitizeObject(input, path, ctx) {
|
|
143
|
+
const nodeType = typeof input.type === "string" ? input.type : ctx.nodeType;
|
|
144
|
+
const scoped = { ...ctx, nodeType };
|
|
145
|
+
const headerName = typeof input.name === "string" ? input.name.trim().toLowerCase() : null;
|
|
146
|
+
const isHeaderEntry = headerName !== null && path.includes("/headerParameters/parameters/") && SENSITIVE_HEADER_NAMES[headerName] === true;
|
|
147
|
+
const out = {};
|
|
148
|
+
for (const [key, raw] of Object.entries(input)) {
|
|
149
|
+
const childPath = `${path}/${escapePointer(key)}`;
|
|
150
|
+
if (key === "credentials" && isDict(raw)) {
|
|
151
|
+
defineOwn(out, key, sanitizeCredentials(raw, childPath, scoped));
|
|
152
|
+
} else if (isHeaderEntry && key === "value" && typeof raw === "string" && raw.length > 0) {
|
|
153
|
+
defineOwn(out, key, recordChange(scoped.changes, childPath, "HEADER", raw, HEADER_VALUE_PLACEHOLDER));
|
|
154
|
+
} else if (key === "webhookId" && typeof raw === "string" && raw.length > 0) {
|
|
155
|
+
defineOwn(out, key, recordChange(scoped.changes, childPath, "TOKEN", raw, WEBHOOK_ID_PLACEHOLDER));
|
|
156
|
+
} else if (key === "path" && nodeType === WEBHOOK_TYPE && typeof raw === "string" && raw.length > 0) {
|
|
157
|
+
defineOwn(out, key, recordChange(scoped.changes, childPath, "TOKEN", raw, WEBHOOK_PATH_PLACEHOLDER));
|
|
158
|
+
} else {
|
|
159
|
+
defineOwn(out, key, sanitizeValue(raw, childPath, scoped));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
function buildDiff(before, after) {
|
|
165
|
+
const beforeLines = JSON.stringify(before, null, 2).split("\n");
|
|
166
|
+
const afterLines = JSON.stringify(after, null, 2).split("\n");
|
|
167
|
+
const total = Math.max(beforeLines.length, afterLines.length);
|
|
168
|
+
const hunks = [];
|
|
169
|
+
let index = 0;
|
|
170
|
+
while (index < total) {
|
|
171
|
+
if (beforeLines[index] === afterLines[index]) {
|
|
172
|
+
index += 1;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const start = index;
|
|
176
|
+
const removed = [];
|
|
177
|
+
const added = [];
|
|
178
|
+
while (index < total && beforeLines[index] !== afterLines[index]) {
|
|
179
|
+
const beforeLine = beforeLines[index];
|
|
180
|
+
const afterLine = afterLines[index];
|
|
181
|
+
if (beforeLine !== void 0) removed.push(beforeLine);
|
|
182
|
+
if (afterLine !== void 0) added.push(afterLine);
|
|
183
|
+
index += 1;
|
|
184
|
+
}
|
|
185
|
+
hunks.push(`@@ -${start + 1},${removed.length} +${start + 1},${added.length} @@`, ...removed.map((line) => `-${line}`), ...added.map((line) => `+${line}`));
|
|
186
|
+
}
|
|
187
|
+
return hunks.length === 0 ? "" : ["--- workflow.before.json", "+++ workflow.after.json", ...hunks].join("\n");
|
|
188
|
+
}
|
|
189
|
+
function readOptions(options) {
|
|
190
|
+
if (!isDict(options)) throw new TypeError("options must be an object");
|
|
191
|
+
const unknownKey = Object.keys(options).find((key) => key !== "internalUrlPlaceholder" && key !== "internalHostSuffixes");
|
|
192
|
+
if (unknownKey !== void 0) throw new TypeError(`unknown option: ${unknownKey}`);
|
|
193
|
+
const placeholder = options.internalUrlPlaceholder;
|
|
194
|
+
if (placeholder !== void 0 && (typeof placeholder !== "string" || placeholder.trim().length === 0)) throw new TypeError("internalUrlPlaceholder must be a non-empty string");
|
|
195
|
+
const suffixes = options.internalHostSuffixes ?? [];
|
|
196
|
+
if (!Array.isArray(suffixes)) throw new TypeError("internalHostSuffixes must be an array");
|
|
197
|
+
const extraSuffixes = suffixes.map((suffix) => {
|
|
198
|
+
const text = typeof suffix === "string" ? suffix.trim().toLowerCase() : "";
|
|
199
|
+
if (!text.startsWith(".") || text.length < 2) throw new TypeError("internalHostSuffixes entries must be dotted strings");
|
|
200
|
+
return text;
|
|
201
|
+
});
|
|
202
|
+
return { urlPlaceholder: typeof placeholder === "string" ? placeholder : INTERNAL_URL_PLACEHOLDER, extraSuffixes: [...new Set(extraSuffixes)] };
|
|
203
|
+
}
|
|
204
|
+
function prepareWorkflowPrivacy(workflow, options = {}) {
|
|
205
|
+
if (!isDict(workflow)) throw new TypeError("workflow must be an object");
|
|
206
|
+
const { urlPlaceholder, extraSuffixes } = readOptions(options);
|
|
207
|
+
const changes = [];
|
|
208
|
+
const ctx = { changes, suffixes: [...DEFAULT_INTERNAL_SUFFIXES, ...extraSuffixes], urlPlaceholder, nodeType: null };
|
|
209
|
+
const prepared = sanitizeObject(workflow, "", ctx);
|
|
210
|
+
const ordered = changes.sort((left, right) => cmp(left.path, right.path));
|
|
211
|
+
return Object.freeze({
|
|
212
|
+
schemaVersion: PRIVACY_PREPARE_SCHEMA_V1,
|
|
213
|
+
workflow: prepared,
|
|
214
|
+
changed: ordered.length > 0,
|
|
215
|
+
changes: Object.freeze(ordered),
|
|
216
|
+
diff: buildDiff(workflow, prepared),
|
|
217
|
+
completeRedactionGuaranteed: false,
|
|
218
|
+
disclaimer: PRIVACY_PREPARE_DISCLAIMER_V1
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
export {
|
|
222
|
+
CREDENTIAL_ID_PLACEHOLDER,
|
|
223
|
+
CREDENTIAL_NAME_PLACEHOLDER,
|
|
224
|
+
HEADER_VALUE_PLACEHOLDER,
|
|
225
|
+
INTERNAL_URL_PLACEHOLDER,
|
|
226
|
+
PRIVACY_PREPARE_DISCLAIMER_V1,
|
|
227
|
+
PRIVACY_PREPARE_SCHEMA_V1,
|
|
228
|
+
TOKEN_PLACEHOLDER,
|
|
229
|
+
WEBHOOK_ID_PLACEHOLDER,
|
|
230
|
+
WEBHOOK_PATH_PLACEHOLDER,
|
|
231
|
+
prepareWorkflowPrivacy
|
|
232
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/v2/privacy-prepare.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Opt-in workflow privacy preparation (PD-03): replaces credential references, sensitive\n * authorization headers, internal endpoints, webhook paths and embedded tokens with safe\n * placeholders before an export leaves the operator's machine. The result is a fresh copy plus an\n * exact change list and a reviewable diff; structure is preserved and the input is never mutated.\n * Redaction is heuristic and rule-bounded, so it never guarantees complete secret or PII removal.\n * Pure, deterministic and free of host APIs so the browser worker and CLI share one contract.\n */\nexport const PRIVACY_PREPARE_SCHEMA_V1 = 'n8n-workflow-privacy-prepare-v1' as const;\nexport const PRIVACY_PREPARE_DISCLAIMER_V1 = 'Heuristic and rule-bounded privacy preparation. Does NOT guarantee complete secret or PII removal. The raw diff and change excerpts contain the original removed values and are sensitive local review material; never share or upload them.' as const;\n\nexport const CREDENTIAL_ID_PLACEHOLDER = '[REDACTED_CREDENTIAL_ID]' as const;\nexport const CREDENTIAL_NAME_PLACEHOLDER = '[REDACTED_CREDENTIAL_NAME]' as const;\nexport const HEADER_VALUE_PLACEHOLDER = '[REDACTED_HEADER_VALUE]' as const;\nexport const INTERNAL_URL_PLACEHOLDER = 'https://internal.example.invalid/...' as const;\nexport const TOKEN_PLACEHOLDER = '[REDACTED_TOKEN]' as const;\nexport const WEBHOOK_PATH_PLACEHOLDER = '[REDACTED_WEBHOOK_PATH]' as const;\nexport const WEBHOOK_ID_PLACEHOLDER = '[REDACTED_WEBHOOK_ID]' as const;\n\nexport type PrivacyChangeKind = 'CREDENTIAL' | 'HEADER' | 'INTERNAL_URL' | 'TOKEN';\nexport type PrivacyChange = Readonly<{ path: string; kind: PrivacyChangeKind; originalExcerpt: string; replacement: string }>;\nexport type PrivacyPrepareOptions = Readonly<{ internalUrlPlaceholder?: string; internalHostSuffixes?: readonly string[] }>;\nexport type PrivacyPrepareResult = Readonly<{\n schemaVersion: typeof PRIVACY_PREPARE_SCHEMA_V1;\n workflow: Readonly<Record<string, unknown>>;\n changed: boolean;\n changes: readonly PrivacyChange[];\n diff: string;\n completeRedactionGuaranteed: false;\n disclaimer: typeof PRIVACY_PREPARE_DISCLAIMER_V1;\n}>;\n\ntype Dict = Readonly<Record<string, unknown>>;\ntype WalkContext = Readonly<{ changes: PrivacyChange[]; suffixes: readonly string[]; urlPlaceholder: string; nodeType: string | null }>;\n\nconst DEFAULT_INTERNAL_SUFFIXES: readonly string[] = Object.freeze(['.local', '.internal', '.localdomain', '.lan', '.home.arpa', '.corp', '.intranet']);\nconst WEBHOOK_TYPE = 'n8n-nodes-base.webhook';\nconst EXCERPT_LIMIT = 80;\nconst SENSITIVE_HEADER_NAMES: Readonly<Record<string, true>> = Object.freeze({\n authorization: true, 'proxy-authorization': true, cookie: true, 'set-cookie': true, 'x-api-key': true,\n 'x-auth-token': true, 'x-access-token': true, 'x-secret': true, 'x-api-token': true, 'api-key': true, apikey: true,\n});\nconst SECRET_PARAMETER_KEYS: Readonly<Record<string, true>> = Object.freeze({\n password: true, passwd: true, apikey: true, api_key: true, token: true, secret: true, clientsecret: true, client_secret: true,\n privatekey: true, private_key: true, accesstoken: true, access_token: true, refreshtoken: true, refresh_token: true, authorization: true,\n});\nconst URL_HOST_RE = /^[a-z][a-z0-9+.-]*:\\/\\/(?:[^/?#@]*@)?(\\[[^\\]]+\\]|[^:/?#]+)/i;\nconst EMBEDDED_TOKEN_RULES: readonly (readonly [RegExp, string])[] = Object.freeze([\n [/([?&](?:access_token|refresh_token|id_token|token|api_key|apikey|key|secret|sig|signature|auth|password|code)=)[^&#\\s]+/gi, `$1${TOKEN_PLACEHOLDER}`],\n [/\\bBearer\\s+[A-Za-z0-9._~+/=-]+/g, `Bearer ${TOKEN_PLACEHOLDER}`],\n [/\\bBasic\\s+[A-Za-z0-9+/=]+/g, `Basic ${TOKEN_PLACEHOLDER}`],\n] as const);\n\nconst isDict = (value: unknown): value is Dict => typeof value === 'object' && value !== null && !Array.isArray(value);\n/** Writes an own enumerable property so keys such as `__proto__` stay data instead of mutating the prototype. */\nconst defineOwn = (target: Record<string, unknown>, key: string, value: unknown): void => {\n Object.defineProperty(target, key, { value, enumerable: true, configurable: true, writable: true });\n};\nconst escapePointer = (key: string): string => key.replace(/~/g, '~0').replace(/\\//g, '~1');\nconst excerpt = (value: string): string => (value.length <= EXCERPT_LIMIT ? value : `${value.slice(0, EXCERPT_LIMIT - 3)}...`);\nconst cmp = (left: string, right: string): number => (left < right ? -1 : left > right ? 1 : 0);\n\n/** Records a replacement unless the value is already the placeholder, which keeps reruns idempotent. */\nfunction recordChange(changes: PrivacyChange[], path: string, kind: PrivacyChangeKind, original: string, replacement: string): string {\n if (original !== replacement) changes.push(Object.freeze({ path, kind, originalExcerpt: excerpt(original), replacement }));\n return replacement;\n}\n\nfunction isPrivateIpv4(host: string): boolean {\n const parts = host.split('.');\n if (parts.length !== 4 || parts.some((part) => !/^[0-9]{1,3}$/.test(part))) return false;\n const octets = parts.map(Number);\n if (octets.some((octet) => octet > 255)) return false;\n const first = octets[0] ?? -1;\n const second = octets[1] ?? -1;\n return first === 0 || first === 10 || first === 127 || (first === 192 && second === 168) ||\n (first === 172 && second >= 16 && second <= 31) || (first === 169 && second === 254) ||\n (first === 100 && second >= 64 && second <= 127);\n}\n\nconst IPV4_MAPPED_RE = /^::ffff:(\\d{1,3}(?:\\.\\d{1,3}){3}|[0-9a-f]{1,4}:[0-9a-f]{1,4})$/i;\n\n/** Reads the IPv4 address behind an `::ffff:` prefix, accepting dotted and hex-pair spellings. */\nfunction mappedIpv4(host: string): string | null {\n const text = IPV4_MAPPED_RE.exec(host)?.[1];\n if (text === undefined) return null;\n if (text.includes('.')) return text;\n const [high = 0, low = 0] = text.split(':').map((group) => Number.parseInt(group, 16));\n return [high >> 8, high & 0xff, low >> 8, low & 0xff].join('.');\n}\n\nfunction isInternalIpv6(host: string): boolean {\n const mapped = mappedIpv4(host);\n if (mapped !== null) return isPrivateIpv4(mapped);\n if (host === '::' || host === '::1') return true;\n // Unique-local fc00::/7 and link-local fe80::/10 ranges never route off the local network.\n return /^f[cd][0-9a-f]{2}:/.test(host) || /^fe[89ab][0-9a-f]:/.test(host);\n}\n\nfunction isInternalHost(host: string, suffixes: readonly string[]): boolean {\n const normalized = host.toLowerCase().replace(/^\\[|\\]$/g, '').replace(/\\.+$/, '');\n if (normalized === 'localhost' || normalized === '0.0.0.0') return true;\n if (normalized.includes(':')) return isInternalIpv6(normalized);\n if (isPrivateIpv4(normalized)) return true;\n if (suffixes.some((suffix) => normalized.endsWith(suffix))) return true;\n return !normalized.includes('.');\n}\n\n/** Resolves a URL host through the WHATWG parser, which canonicalizes octal, dword and hex IPv4 spellings. */\nfunction resolveUrlHost(value: string): string {\n const match = URL_HOST_RE.exec(value);\n if (match === null) return '';\n const fallback = match[1] ?? '';\n try {\n return new URL(value).hostname || fallback;\n } catch {\n return fallback;\n }\n}\n\nfunction isInternalUrl(value: string, suffixes: readonly string[]): boolean {\n const host = resolveUrlHost(value);\n return host.length > 0 && isInternalHost(host, suffixes);\n}\n\nfunction sanitizeString(value: string, path: string, ctx: WalkContext): string {\n if (value.length === 0) return value;\n if (isInternalUrl(value, ctx.suffixes)) return recordChange(ctx.changes, path, 'INTERNAL_URL', value, ctx.urlPlaceholder);\n if (!path.includes('/parameters/')) return value;\n if (SECRET_PARAMETER_KEYS[path.slice(path.lastIndexOf('/') + 1).toLowerCase()] === true) return recordChange(ctx.changes, path, 'TOKEN', value, TOKEN_PLACEHOLDER);\n const scrubbed = EMBEDDED_TOKEN_RULES.reduce((current, [pattern, replacement]) => current.replace(pattern, replacement), value);\n return scrubbed === value ? value : recordChange(ctx.changes, path, 'TOKEN', value, scrubbed);\n}\n\nfunction sanitizeValue(value: unknown, path: string, ctx: WalkContext): unknown {\n if (Array.isArray(value)) return value.map((item, index) => sanitizeValue(item, `${path}/${index}`, ctx));\n if (isDict(value)) return sanitizeObject(value, path, ctx);\n return typeof value === 'string' ? sanitizeString(value, path, ctx) : value;\n}\n\nfunction sanitizeCredentials(input: Dict, path: string, ctx: WalkContext): Dict {\n const out: Record<string, unknown> = {};\n for (const [credentialType, details] of Object.entries(input)) {\n const typePath = `${path}/${escapePointer(credentialType)}`;\n if (!isDict(details)) {\n defineOwn(out, credentialType, sanitizeValue(details, typePath, ctx));\n continue;\n }\n const cleaned: Record<string, unknown> = {};\n for (const [key, raw] of Object.entries(details)) {\n const valuePath = `${typePath}/${escapePointer(key)}`;\n if (key === 'id' && (typeof raw === 'string' || typeof raw === 'number') && String(raw).length > 0) {\n defineOwn(cleaned, key, recordChange(ctx.changes, valuePath, 'CREDENTIAL', String(raw), CREDENTIAL_ID_PLACEHOLDER));\n } else if (key === 'name' && typeof raw === 'string' && raw.length > 0) {\n defineOwn(cleaned, key, recordChange(ctx.changes, valuePath, 'CREDENTIAL', raw, CREDENTIAL_NAME_PLACEHOLDER));\n } else {\n defineOwn(cleaned, key, sanitizeValue(raw, valuePath, ctx));\n }\n }\n defineOwn(out, credentialType, cleaned);\n }\n return out;\n}\n\nfunction sanitizeObject(input: Dict, path: string, ctx: WalkContext): Dict {\n const nodeType = typeof input.type === 'string' ? input.type : ctx.nodeType;\n const scoped: WalkContext = { ...ctx, nodeType };\n const headerName = typeof input.name === 'string' ? input.name.trim().toLowerCase() : null;\n const isHeaderEntry = headerName !== null && path.includes('/headerParameters/parameters/') && SENSITIVE_HEADER_NAMES[headerName] === true;\n const out: Record<string, unknown> = {};\n for (const [key, raw] of Object.entries(input)) {\n const childPath = `${path}/${escapePointer(key)}`;\n if (key === 'credentials' && isDict(raw)) {\n defineOwn(out, key, sanitizeCredentials(raw, childPath, scoped));\n } else if (isHeaderEntry && key === 'value' && typeof raw === 'string' && raw.length > 0) {\n defineOwn(out, key, recordChange(scoped.changes, childPath, 'HEADER', raw, HEADER_VALUE_PLACEHOLDER));\n } else if (key === 'webhookId' && typeof raw === 'string' && raw.length > 0) {\n defineOwn(out, key, recordChange(scoped.changes, childPath, 'TOKEN', raw, WEBHOOK_ID_PLACEHOLDER));\n } else if (key === 'path' && nodeType === WEBHOOK_TYPE && typeof raw === 'string' && raw.length > 0) {\n defineOwn(out, key, recordChange(scoped.changes, childPath, 'TOKEN', raw, WEBHOOK_PATH_PLACEHOLDER));\n } else {\n defineOwn(out, key, sanitizeValue(raw, childPath, scoped));\n }\n }\n return out;\n}\n\n/** Every replacement keeps one JSON line per value, so changed lines diff one-to-one without an LCS pass. */\nfunction buildDiff(before: unknown, after: unknown): string {\n const beforeLines = JSON.stringify(before, null, 2).split('\\n');\n const afterLines = JSON.stringify(after, null, 2).split('\\n');\n const total = Math.max(beforeLines.length, afterLines.length);\n const hunks: string[] = [];\n let index = 0;\n while (index < total) {\n if (beforeLines[index] === afterLines[index]) { index += 1; continue; }\n const start = index;\n const removed: string[] = [];\n const added: string[] = [];\n while (index < total && beforeLines[index] !== afterLines[index]) {\n const beforeLine = beforeLines[index];\n const afterLine = afterLines[index];\n if (beforeLine !== undefined) removed.push(beforeLine);\n if (afterLine !== undefined) added.push(afterLine);\n index += 1;\n }\n hunks.push(`@@ -${start + 1},${removed.length} +${start + 1},${added.length} @@`, ...removed.map((line) => `-${line}`), ...added.map((line) => `+${line}`));\n }\n return hunks.length === 0 ? '' : ['--- workflow.before.json', '+++ workflow.after.json', ...hunks].join('\\n');\n}\n\n/** Validates caller options, which may arrive as untyped JSON, before any redaction runs. */\nfunction readOptions(options: unknown): Readonly<{ urlPlaceholder: string; extraSuffixes: readonly string[] }> {\n if (!isDict(options)) throw new TypeError('options must be an object');\n const unknownKey = Object.keys(options).find((key) => key !== 'internalUrlPlaceholder' && key !== 'internalHostSuffixes');\n if (unknownKey !== undefined) throw new TypeError(`unknown option: ${unknownKey}`);\n const placeholder = options.internalUrlPlaceholder;\n if (placeholder !== undefined && (typeof placeholder !== 'string' || placeholder.trim().length === 0)) throw new TypeError('internalUrlPlaceholder must be a non-empty string');\n const suffixes = options.internalHostSuffixes ?? [];\n if (!Array.isArray(suffixes)) throw new TypeError('internalHostSuffixes must be an array');\n // A bare top-level label such as `com` would redact every public host, so a leading dot is required.\n const extraSuffixes = suffixes.map((suffix: unknown) => {\n const text = typeof suffix === 'string' ? suffix.trim().toLowerCase() : '';\n if (!text.startsWith('.') || text.length < 2) throw new TypeError('internalHostSuffixes entries must be dotted strings');\n return text;\n });\n return { urlPlaceholder: typeof placeholder === 'string' ? placeholder : INTERNAL_URL_PLACEHOLDER, extraSuffixes: [...new Set(extraSuffixes)] };\n}\n\n/** Returns a scrubbed deep copy plus the change list and diff; the input workflow is never mutated. */\nexport function prepareWorkflowPrivacy(workflow: unknown, options: PrivacyPrepareOptions = {}): PrivacyPrepareResult {\n if (!isDict(workflow)) throw new TypeError('workflow must be an object');\n const { urlPlaceholder, extraSuffixes } = readOptions(options);\n const changes: PrivacyChange[] = [];\n const ctx: WalkContext = { changes, suffixes: [...DEFAULT_INTERNAL_SUFFIXES, ...extraSuffixes], urlPlaceholder, nodeType: null };\n const prepared = sanitizeObject(workflow, '', ctx);\n const ordered = changes.sort((left, right) => cmp(left.path, right.path));\n return Object.freeze({\n schemaVersion: PRIVACY_PREPARE_SCHEMA_V1,\n workflow: prepared,\n changed: ordered.length > 0,\n changes: Object.freeze(ordered),\n diff: buildDiff(workflow, prepared),\n completeRedactionGuaranteed: false as const,\n disclaimer: PRIVACY_PREPARE_DISCLAIMER_V1,\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sfscanner",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "SecureFlow Scanner — Deterministic, offline security & dataflow analyzer for n8n workflows",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n",
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
],
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
|
-
"url": "git+https://github.com/jitwark/
|
|
15
|
+
"url": "git+https://github.com/jitwark/sfscanner.git"
|
|
16
16
|
},
|
|
17
|
-
"homepage": "https://github.com/jitwark/
|
|
17
|
+
"homepage": "https://github.com/jitwark/sfscanner#readme",
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"private": false,
|
|
20
20
|
"type": "module",
|
|
@@ -55,6 +55,14 @@
|
|
|
55
55
|
"./v2/metadata-output": {
|
|
56
56
|
"types": "./dist/v2/metadata-output.d.ts",
|
|
57
57
|
"import": "./dist/v2/metadata-output.js"
|
|
58
|
+
},
|
|
59
|
+
"./v2/preflight-readiness": {
|
|
60
|
+
"types": "./dist/v2/preflight-readiness.d.ts",
|
|
61
|
+
"import": "./dist/v2/preflight-readiness.js"
|
|
62
|
+
},
|
|
63
|
+
"./v2/privacy-prepare": {
|
|
64
|
+
"types": "./dist/v2/privacy-prepare.d.ts",
|
|
65
|
+
"import": "./dist/v2/privacy-prepare.js"
|
|
58
66
|
}
|
|
59
67
|
},
|
|
60
68
|
"scripts": {
|
|
@@ -69,6 +77,6 @@
|
|
|
69
77
|
"@types/node": "^22.0.0",
|
|
70
78
|
"esbuild": "^0.28.2",
|
|
71
79
|
"typescript": "^5.7.2",
|
|
72
|
-
"vitest": "
|
|
80
|
+
"vitest": "4.1.12"
|
|
73
81
|
}
|
|
74
82
|
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { Stats } from 'node:fs';
|
|
2
|
-
export type AtomicOutputFailureCodeV2 = 'INVALID_CONFIG' | 'SERIALIZATION_FAILED' | 'OUTPUT_WRITE_FAILED' | 'CANCELLED';
|
|
3
|
-
export type AtomicOutputResultV2 = Readonly<{
|
|
4
|
-
ok: true;
|
|
5
|
-
} | {
|
|
6
|
-
ok: false;
|
|
7
|
-
code: AtomicOutputFailureCodeV2;
|
|
8
|
-
message: string;
|
|
9
|
-
}>;
|
|
10
|
-
export type AtomicOutputHandleV2 = Readonly<{
|
|
11
|
-
write: (bytes: Uint8Array) => Promise<void>;
|
|
12
|
-
sync: () => Promise<void>;
|
|
13
|
-
close: () => Promise<void>;
|
|
14
|
-
}>;
|
|
15
|
-
export type AtomicOutputStatV2 = Pick<Stats, 'isFile' | 'isDirectory' | 'isSymbolicLink'>;
|
|
16
|
-
export type AtomicOutputHostV2 = Readonly<{
|
|
17
|
-
lstat?: (path: string) => Promise<AtomicOutputStatV2>;
|
|
18
|
-
realpath?: (path: string) => Promise<string>;
|
|
19
|
-
open?: (path: string) => Promise<AtomicOutputHandleV2>;
|
|
20
|
-
write?: (path: string, bytes: Uint8Array) => Promise<void>;
|
|
21
|
-
sync?: (path: string) => Promise<void>;
|
|
22
|
-
rename?: (from: string, to: string) => Promise<void>;
|
|
23
|
-
unlink?: (path: string) => Promise<void>;
|
|
24
|
-
}>;
|
|
25
|
-
export type AtomicOutputOptionsV2 = Readonly<{
|
|
26
|
-
baseDirectory?: string;
|
|
27
|
-
inputPaths?: readonly string[];
|
|
28
|
-
signal?: AbortSignal;
|
|
29
|
-
maxTempAttempts?: number;
|
|
30
|
-
host?: AtomicOutputHostV2;
|
|
31
|
-
}>;
|
|
32
|
-
export declare function writeAtomicUtf8V2(target: string, text: string, options?: AtomicOutputOptionsV2): Promise<AtomicOutputResultV2>;
|
|
33
|
-
export declare function writeAtomicBytesV2(target: string, bytes: Uint8Array, options?: AtomicOutputOptionsV2): Promise<AtomicOutputResultV2>;
|
|
34
|
-
export declare function writeAtomicOutputV2(target: string, content: string | Uint8Array, options?: AtomicOutputOptionsV2): Promise<AtomicOutputResultV2>;
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { type ScannerLimitsV2 } from '@hub/contracts';
|
|
2
|
-
import type { ScanWorkflowV2FailureCode, ScanWorkflowV2Result } from '@hub/scanner-core/v2';
|
|
3
|
-
import { type ScanConfigV2 } from './config';
|
|
4
|
-
import { type CliStdin, type InputReadHost, type ReadInputResult } from './input-read';
|
|
5
|
-
import type { SelectedInputV2 } from './input-selection';
|
|
6
|
-
import { type ScanWorkerFactory } from './scan-worker-host';
|
|
7
|
-
import { type SigintSource } from './worker-host';
|
|
8
|
-
export type BatchErrorCodeV2 = ScanWorkflowV2FailureCode | 'INPUT_READ_FAILED';
|
|
9
|
-
export type BatchItemStatusV2 = 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'TIMEOUT';
|
|
10
|
-
export type BatchItemV2 = Readonly<{
|
|
11
|
-
label: string;
|
|
12
|
-
status: BatchItemStatusV2;
|
|
13
|
-
result?: ScanWorkflowV2Result;
|
|
14
|
-
error?: Readonly<{
|
|
15
|
-
code: BatchErrorCodeV2;
|
|
16
|
-
}>;
|
|
17
|
-
}>;
|
|
18
|
-
export type BatchResultV2 = Readonly<{
|
|
19
|
-
ok: true;
|
|
20
|
-
items: readonly BatchItemV2[];
|
|
21
|
-
cancelled: boolean;
|
|
22
|
-
completed: number;
|
|
23
|
-
failed: number;
|
|
24
|
-
concurrency: number;
|
|
25
|
-
}> | Readonly<{
|
|
26
|
-
ok: false;
|
|
27
|
-
code: 'INVALID_CONFIG' | 'CANCELLED';
|
|
28
|
-
message: string;
|
|
29
|
-
}>;
|
|
30
|
-
export type BatchScanV2 = (bytes: Uint8Array, item: SelectedInputV2) => ScanWorkflowV2Result | PromiseLike<ScanWorkflowV2Result>;
|
|
31
|
-
export type BatchReadV2 = (item: SelectedInputV2) => Uint8Array | ReadInputResult | PromiseLike<Uint8Array | ReadInputResult>;
|
|
32
|
-
export type BatchRunnerInput = Readonly<{
|
|
33
|
-
items: readonly SelectedInputV2[];
|
|
34
|
-
config: ScanConfigV2;
|
|
35
|
-
scan?: BatchScanV2;
|
|
36
|
-
read?: BatchReadV2;
|
|
37
|
-
stdin?: CliStdin;
|
|
38
|
-
readHost?: InputReadHost;
|
|
39
|
-
baseDirectory?: string;
|
|
40
|
-
maxInputBytes?: number;
|
|
41
|
-
limitsDocument?: ScannerLimitsV2;
|
|
42
|
-
targetDocument?: unknown;
|
|
43
|
-
approvedDomains?: readonly string[];
|
|
44
|
-
scanWorker?: ScanWorkerFactory;
|
|
45
|
-
timeoutMs?: number;
|
|
46
|
-
signal?: AbortSignal;
|
|
47
|
-
sigint?: SigintSource;
|
|
48
|
-
now?: () => number;
|
|
49
|
-
}>;
|
|
50
|
-
export declare function runBatchV2(input: BatchRunnerInput): Promise<BatchResultV2>;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { CliStdin, InputReadHost } from './input-read';
|
|
2
|
-
import { type InputSelectionHost } from './input-selection';
|
|
3
|
-
import type { CliCommandV2 } from './arguments';
|
|
4
|
-
import { type BatchReadV2, type BatchScanV2 } from './batch-runner';
|
|
5
|
-
import { type CliEnvelopeV2 } from './json-output';
|
|
6
|
-
import { type ExitCodeV2 } from './exit-reducer';
|
|
7
|
-
import type { SigintSource } from './worker-host';
|
|
8
|
-
export type V2CliRunOptions = Readonly<{
|
|
9
|
-
cwd?: string;
|
|
10
|
-
stdin?: CliStdin;
|
|
11
|
-
signal?: AbortSignal;
|
|
12
|
-
read?: BatchReadV2;
|
|
13
|
-
scan?: BatchScanV2;
|
|
14
|
-
readHost?: InputReadHost;
|
|
15
|
-
selectionHost?: InputSelectionHost;
|
|
16
|
-
sigint?: SigintSource;
|
|
17
|
-
}>;
|
|
18
|
-
export type V2CliRunResult = Readonly<{
|
|
19
|
-
exitCode: ExitCodeV2;
|
|
20
|
-
stdout: string;
|
|
21
|
-
stderr: string;
|
|
22
|
-
document?: CliEnvelopeV2 | string;
|
|
23
|
-
}>;
|
|
24
|
-
type ScanCommand = Extract<CliCommandV2, {
|
|
25
|
-
kind: 'scan';
|
|
26
|
-
}>;
|
|
27
|
-
export declare function runCliV2Internal(command: ScanCommand, options?: V2CliRunOptions): Promise<V2CliRunResult>;
|
|
28
|
-
export {};
|
package/dist/v2/config.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { type ScanConfigV2 } from './contracts';
|
|
2
|
-
export type ConfigLoadOptions = Readonly<{
|
|
3
|
-
configDirectory?: string;
|
|
4
|
-
cli?: unknown;
|
|
5
|
-
}>;
|
|
6
|
-
export type ConfigLoadResult = Readonly<{
|
|
7
|
-
ok: true;
|
|
8
|
-
config: ScanConfigV2;
|
|
9
|
-
} | {
|
|
10
|
-
ok: false;
|
|
11
|
-
code: 'INVALID_CONFIG';
|
|
12
|
-
errors: readonly string[];
|
|
13
|
-
}>;
|
|
14
|
-
export declare function loadExplicitConfig(input: unknown, options?: ConfigLoadOptions): ConfigLoadResult;
|
|
15
|
-
export declare function validateScanConfigV2(input: unknown): ConfigLoadResult;
|
|
16
|
-
export { ADMITTED_PROFILE_V2, BUILTIN_AUTHORITIES_V2, BUILTIN_LIMITS_AUTHORITY_V2, BUILTIN_RULESET_AUTHORITY_V2, BUILTIN_SEMANTICS_AUTHORITY_V2, CLI_TOOL_V2, DEFAULT_SCAN_CONFIG_V2, PROFILE_MANIFEST_DIGEST_V2, RULE_PACK_DIGEST_V2, SCAN_CONFIG_SCHEMA_V2, SEMANTIC_CATALOGUE_DIGEST_V2, type ScanConfigV2, } from './contracts';
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { BatchResultV2 } from './batch-runner';
|
|
2
|
-
import type { ScanSeverityV2 } from './contracts';
|
|
3
|
-
export type ExitReasonV2 = 'UNDER_THRESHOLD' | 'FINDING_THRESHOLD' | 'REQUIRE_COMPLETE' | 'ITEM_FAILURE' | 'USAGE' | 'INTERNAL' | 'CANCELLED';
|
|
4
|
-
export type ExitCodeV2 = 0 | 1 | 2 | 3 | 4 | 130;
|
|
5
|
-
export type ExitReductionV2 = Readonly<{
|
|
6
|
-
reason: ExitReasonV2;
|
|
7
|
-
code: ExitCodeV2;
|
|
8
|
-
}>;
|
|
9
|
-
export type ExitReducerOptionsV2 = Readonly<{
|
|
10
|
-
failOn?: ScanSeverityV2;
|
|
11
|
-
requireComplete?: boolean;
|
|
12
|
-
interrupted?: boolean;
|
|
13
|
-
outputFailed?: boolean;
|
|
14
|
-
}>;
|
|
15
|
-
export type ExitReducerInputV2 = Readonly<{
|
|
16
|
-
batch: BatchResultV2;
|
|
17
|
-
failOn?: ScanSeverityV2;
|
|
18
|
-
requireComplete?: boolean;
|
|
19
|
-
interrupted?: boolean;
|
|
20
|
-
outputFailed?: boolean;
|
|
21
|
-
}>;
|
|
22
|
-
export declare function reduceExitV2(batch: BatchResultV2, options?: ExitReducerOptionsV2): ExitReductionV2;
|
|
23
|
-
export declare function reduceExitV2(input: ExitReducerInputV2): ExitReductionV2;
|
package/dist/v2/input-read.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { SelectedInputV2 } from './input-selection';
|
|
2
|
-
export declare const DEFAULT_MAX_INPUT_BYTES = 1536000;
|
|
3
|
-
export type CliStdin = string | Uint8Array | AsyncIterable<Uint8Array | string>;
|
|
4
|
-
export type InputReadHost = Readonly<{
|
|
5
|
-
readFile: (path: string, maxBytes?: number) => Promise<Uint8Array>;
|
|
6
|
-
}>;
|
|
7
|
-
export declare const defaultInputReadHost: InputReadHost;
|
|
8
|
-
export type ReadInputOptions = Readonly<{
|
|
9
|
-
stdin?: CliStdin;
|
|
10
|
-
maxBytes?: number;
|
|
11
|
-
baseDirectory?: string;
|
|
12
|
-
host?: InputReadHost;
|
|
13
|
-
}>;
|
|
14
|
-
export type ReadInputSuccess = Readonly<{
|
|
15
|
-
ok: true;
|
|
16
|
-
bytes: Uint8Array;
|
|
17
|
-
label: string;
|
|
18
|
-
}>;
|
|
19
|
-
export type ReadInputFailure = Readonly<{
|
|
20
|
-
ok: false;
|
|
21
|
-
code: 'WORKFLOW_TOO_LARGE' | 'INPUT_READ_FAILED' | 'INVALID_CONFIG';
|
|
22
|
-
message: string;
|
|
23
|
-
label: string;
|
|
24
|
-
}>;
|
|
25
|
-
export type ReadInputResult = ReadInputSuccess | ReadInputFailure;
|
|
26
|
-
export declare function readInputBytes(input: SelectedInputV2 | string, options?: ReadInputOptions): Promise<ReadInputResult>;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
export type HostEntry = Readonly<{
|
|
2
|
-
name: string;
|
|
3
|
-
isDirectory: () => boolean;
|
|
4
|
-
isFile: () => boolean;
|
|
5
|
-
isSymbolicLink: () => boolean;
|
|
6
|
-
}>;
|
|
7
|
-
export type InputSelectionHost = Readonly<{
|
|
8
|
-
readdir: (path: string) => Promise<readonly HostEntry[]>;
|
|
9
|
-
stat: (path: string) => Promise<Pick<HostEntry, 'isDirectory' | 'isFile' | 'isSymbolicLink'>>;
|
|
10
|
-
lstat: (path: string) => Promise<Pick<HostEntry, 'isDirectory' | 'isFile' | 'isSymbolicLink'>>;
|
|
11
|
-
realpath: (path: string) => Promise<string>;
|
|
12
|
-
}>;
|
|
13
|
-
export declare const defaultInputSelectionHost: InputSelectionHost;
|
|
14
|
-
export type CollectInputsOptions = Readonly<{
|
|
15
|
-
inputs: readonly string[];
|
|
16
|
-
include?: readonly string[];
|
|
17
|
-
exclude?: readonly string[];
|
|
18
|
-
baseDirectory?: string;
|
|
19
|
-
outputPath?: string;
|
|
20
|
-
host?: InputSelectionHost;
|
|
21
|
-
signal?: AbortSignal;
|
|
22
|
-
}>;
|
|
23
|
-
export type SelectedInputV2 = Readonly<{
|
|
24
|
-
label: string;
|
|
25
|
-
realPath?: string;
|
|
26
|
-
isStdin: boolean;
|
|
27
|
-
}>;
|
|
28
|
-
export type CollectInputsResult = Readonly<{
|
|
29
|
-
ok: true;
|
|
30
|
-
inputs: readonly SelectedInputV2[];
|
|
31
|
-
}> | Readonly<{
|
|
32
|
-
ok: false;
|
|
33
|
-
code: 'INVALID_CONFIG' | 'INPUT_READ_FAILED' | 'CANCELLED';
|
|
34
|
-
message: string;
|
|
35
|
-
}>;
|
|
36
|
-
export declare function collectInputs(options: CollectInputsOptions): Promise<CollectInputsResult>;
|
|
37
|
-
export declare function detectOutputCollision(outputPath: string, inputs: readonly SelectedInputV2[], host?: InputSelectionHost, baseDir?: string, signal?: AbortSignal): Promise<boolean>;
|
|
38
|
-
export declare function compilePosixGlob(pattern: string): RegExp;
|
package/dist/v2/json-output.d.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { type ScannerResultV2 } from '@hub/contracts/scanner-v2-result';
|
|
2
|
-
import type { BatchErrorCodeV2, BatchResultV2 } from './batch-runner';
|
|
3
|
-
export type ProjectionFormatV2 = 'json' | 'ndjson' | 'sarif' | 'table';
|
|
4
|
-
export type ProjectionSourceV2 = Readonly<{
|
|
5
|
-
profile: 'BUILT_IN' | 'CONFIG' | 'CLI';
|
|
6
|
-
rules: 'BUILT_IN' | 'CONFIG' | 'CLI';
|
|
7
|
-
semantics: 'BUILT_IN' | 'CONFIG' | 'CLI';
|
|
8
|
-
limits: 'BUILT_IN' | 'CONFIG' | 'CLI';
|
|
9
|
-
target: 'DEFAULT' | 'CONFIG' | 'CLI';
|
|
10
|
-
unsignedDevelopment: 'DEFAULT' | 'CONFIG' | 'CLI';
|
|
11
|
-
}>;
|
|
12
|
-
export type ProjectionOptionsV2 = Readonly<{
|
|
13
|
-
format: ProjectionFormatV2;
|
|
14
|
-
profile: string;
|
|
15
|
-
failOn: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' | 'INFO';
|
|
16
|
-
requireComplete: boolean;
|
|
17
|
-
concurrency: number;
|
|
18
|
-
allowUnsignedDevelopmentRules: boolean;
|
|
19
|
-
sources: ProjectionSourceV2;
|
|
20
|
-
}>;
|
|
21
|
-
type ArtifactV2 = Readonly<{
|
|
22
|
-
profile: {
|
|
23
|
-
id: string;
|
|
24
|
-
version: string;
|
|
25
|
-
};
|
|
26
|
-
ruleset: {
|
|
27
|
-
id: string;
|
|
28
|
-
version: string;
|
|
29
|
-
digest: string;
|
|
30
|
-
signatureStatus: 'VERIFIED' | 'UNVERIFIED';
|
|
31
|
-
};
|
|
32
|
-
semantics: {
|
|
33
|
-
id: string;
|
|
34
|
-
version: string;
|
|
35
|
-
digest: string;
|
|
36
|
-
};
|
|
37
|
-
target: ScannerResultV2['target'];
|
|
38
|
-
limits: {
|
|
39
|
-
schemaVersion: 'scanner-limits-v2';
|
|
40
|
-
digest: string;
|
|
41
|
-
};
|
|
42
|
-
}>;
|
|
43
|
-
type CliErrorCodeV2 = BatchErrorCodeV2 | 'OUTPUT_WRITE_FAILED';
|
|
44
|
-
type CliErrorV2 = Readonly<{
|
|
45
|
-
kind: 'INPUT' | 'INCOMPLETE' | 'INTERNAL' | 'CANCELLED';
|
|
46
|
-
code: CliErrorCodeV2;
|
|
47
|
-
message: string;
|
|
48
|
-
}>;
|
|
49
|
-
type CliItemV2 = Readonly<{
|
|
50
|
-
input: string;
|
|
51
|
-
ok: true;
|
|
52
|
-
result: ScannerResultV2;
|
|
53
|
-
} | {
|
|
54
|
-
input: string;
|
|
55
|
-
ok: false;
|
|
56
|
-
error: CliErrorV2;
|
|
57
|
-
}>;
|
|
58
|
-
type SummaryV2 = Readonly<{
|
|
59
|
-
total: number;
|
|
60
|
-
complete: number;
|
|
61
|
-
degraded: number;
|
|
62
|
-
indeterminate: number;
|
|
63
|
-
inputErrors: number;
|
|
64
|
-
internalErrors: number;
|
|
65
|
-
cancelled: number;
|
|
66
|
-
findings: number;
|
|
67
|
-
}>;
|
|
68
|
-
export type CliEnvelopeV2 = Readonly<{
|
|
69
|
-
schemaVersion: 'n8n-scan-v2';
|
|
70
|
-
tool: Readonly<{
|
|
71
|
-
name: 'n8n-scan';
|
|
72
|
-
version: string;
|
|
73
|
-
buildDigest: string;
|
|
74
|
-
}>;
|
|
75
|
-
artifacts: ArtifactV2;
|
|
76
|
-
invocation: Readonly<{
|
|
77
|
-
schema: 'v2';
|
|
78
|
-
format: 'json' | 'ndjson' | 'sarif' | 'table';
|
|
79
|
-
profile: string;
|
|
80
|
-
failOn: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' | 'INFO';
|
|
81
|
-
requireComplete: boolean;
|
|
82
|
-
concurrency: number;
|
|
83
|
-
allowUnsignedDevelopmentRules: boolean;
|
|
84
|
-
sources: ProjectionSourceV2;
|
|
85
|
-
}>;
|
|
86
|
-
results: readonly CliItemV2[];
|
|
87
|
-
summary: SummaryV2;
|
|
88
|
-
exitReason: 'UNDER_THRESHOLD' | 'FINDING_THRESHOLD' | 'REQUIRE_COMPLETE' | 'ITEM_FAILURE' | 'USAGE' | 'INTERNAL' | 'CANCELLED';
|
|
89
|
-
}>;
|
|
90
|
-
export type ProjectedBatchV2 = Readonly<{
|
|
91
|
-
envelope: CliEnvelopeV2;
|
|
92
|
-
items: readonly CliItemV2[];
|
|
93
|
-
}>;
|
|
94
|
-
export declare const DEFAULT_PROJECTION_OPTIONS: ProjectionOptionsV2;
|
|
95
|
-
export declare function serializeEnvelopeV2(batch: BatchResultV2, options?: ProjectionOptionsV2): string;
|
|
96
|
-
export declare function projectBatchV2(batch: BatchResultV2, options?: ProjectionOptionsV2 | ProjectionFormatV2): ProjectedBatchV2;
|
|
97
|
-
export {};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { CliMetadataDocumentV2 } from './commands.js';
|
|
2
|
-
export declare function checkPlain(o: unknown): Record<string, unknown>;
|
|
3
|
-
export declare function checkDenseArray<T>(arr: unknown): T[];
|
|
4
|
-
export declare function validateDocumentShape(doc: unknown): asserts doc is CliMetadataDocumentV2;
|