unguard 0.15.2 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import {
2
2
  allRules,
3
3
  analyzeFiles,
4
4
  getRuleMetadata
5
- } from "./chunk-BLCVXVLX.js";
5
+ } from "./chunk-Z34GRD3S.js";
6
6
 
7
7
  // src/scan/config.ts
8
8
  var BUILTIN_IGNORE = [
@@ -20,20 +20,30 @@ var GENERATED_IGNORE = [
20
20
  var DEFAULT_FAIL_ON = "info";
21
21
  function resolveScanConfig(options) {
22
22
  const paths = options.paths.length > 0 ? options.paths : ["."];
23
- const ignore2 = [...BUILTIN_IGNORE, ...GENERATED_IGNORE, ...options.ignore ?? []];
23
+ const ignore3 = [...BUILTIN_IGNORE, ...GENERATED_IGNORE, ...options.ignore ?? []];
24
24
  return {
25
25
  paths,
26
+ mode: options.mode ?? "scan",
26
27
  strict: options.strict ?? false,
27
28
  rules: options.rules ? [...options.rules] : null,
28
- ignore: ignore2,
29
+ ignore: ignore3,
29
30
  rulePolicy: toRulePolicyEntries(options.rulePolicy),
31
+ overrides: toResolvedOverrides(options.overrides),
30
32
  showSeverities: normalizeSeveritySet(options.showSeverities),
31
33
  failOn: options.failOn ?? DEFAULT_FAIL_ON,
32
34
  useGitIgnore: options.useGitIgnore ?? true,
33
35
  concurrency: options.concurrency,
34
- cache: options.cache ?? true
36
+ cache: options.cache ?? true,
37
+ baseline: options.baseline ?? null
35
38
  };
36
39
  }
40
+ function toResolvedOverrides(overrides) {
41
+ if (overrides === void 0) return [];
42
+ return overrides.map((entry) => ({
43
+ files: [...entry.files],
44
+ rulePolicy: toRulePolicyEntries(entry.rules)
45
+ }));
46
+ }
37
47
  function toRulePolicyEntries(policy) {
38
48
  if (policy === void 0) return [];
39
49
  if (Array.isArray(policy)) return [...policy];
@@ -57,12 +67,193 @@ function isFailOn(value) {
57
67
  return value === "none" || isSeverity(value);
58
68
  }
59
69
 
70
+ // src/scan/baseline.ts
71
+ import { existsSync, readFileSync, writeFileSync } from "fs";
72
+ import { relative, resolve } from "path";
73
+ var BASELINE_FILENAME = "unguard.baseline.json";
74
+ function buildBaseline(diagnostics, cwd) {
75
+ const rules = {};
76
+ for (const diagnostic of diagnostics) {
77
+ const file = relative(cwd, diagnostic.file);
78
+ rules[file] ??= {};
79
+ const forFile = rules[file];
80
+ forFile[diagnostic.ruleId] = (forFile[diagnostic.ruleId] ?? 0) + 1;
81
+ }
82
+ return { version: 1, rules };
83
+ }
84
+ function writeBaseline(baseline, cwd) {
85
+ const path = resolve(cwd, BASELINE_FILENAME);
86
+ writeFileSync(path, `${JSON.stringify(baseline, null, 2)}
87
+ `, "utf8");
88
+ return path;
89
+ }
90
+ function loadBaseline(cwd) {
91
+ const path = resolve(cwd, BASELINE_FILENAME);
92
+ if (!existsSync(path)) return null;
93
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
94
+ if (!isBaselineData(parsed)) {
95
+ throw new Error(`Invalid baseline in ${BASELINE_FILENAME}: expected {"version": 1, "rules": {<file>: {<ruleId>: count}}}.`);
96
+ }
97
+ return parsed;
98
+ }
99
+ function isBaselineData(value) {
100
+ if (typeof value !== "object" || value === null) return false;
101
+ if (!("version" in value) || value.version !== 1) return false;
102
+ if (!("rules" in value) || typeof value.rules !== "object" || value.rules === null) return false;
103
+ return Object.values(value.rules).every(
104
+ (forFile) => typeof forFile === "object" && forFile !== null && Object.values(forFile).every((count) => typeof count === "number")
105
+ );
106
+ }
107
+ function applyBaseline(diagnostics, baseline, cwd) {
108
+ const groups = /* @__PURE__ */ new Map();
109
+ for (const diagnostic of diagnostics) {
110
+ const key = `${relative(cwd, diagnostic.file)}\0${diagnostic.ruleId}`;
111
+ let list = groups.get(key);
112
+ if (list === void 0) {
113
+ list = [];
114
+ groups.set(key, list);
115
+ }
116
+ list.push(diagnostic);
117
+ }
118
+ const kept = [];
119
+ for (const [key, group] of groups) {
120
+ const [file, ruleId] = key.split("\0");
121
+ const allowed = baseline.rules[file]?.[ruleId] ?? 0;
122
+ if (group.length <= allowed) continue;
123
+ kept.push(...group);
124
+ }
125
+ return kept;
126
+ }
127
+
128
+ // src/scan/policy.ts
129
+ import { relative as relative2 } from "path";
130
+ import ignore from "ignore";
131
+ function buildRuleDescriptors(rules) {
132
+ return rules.map((rule) => {
133
+ const metadata = getRuleMetadata(rule.id);
134
+ return {
135
+ rule,
136
+ category: metadata.category,
137
+ tags: metadata.tags,
138
+ confidence: metadata.confidence
139
+ };
140
+ });
141
+ }
142
+ function resolveActiveRules(descriptors, config) {
143
+ const selectedRules = config.rules ? new Set(config.rules) : null;
144
+ const wantedConfidence = config.mode === "audit" ? "heuristic" : "proven";
145
+ const active = [];
146
+ for (const descriptor of descriptors) {
147
+ if (selectedRules) {
148
+ if (!selectedRules.has(descriptor.rule.id)) continue;
149
+ } else if (descriptor.confidence !== wantedConfidence) {
150
+ continue;
151
+ }
152
+ const resolvedSeverity = resolveRuleSeverity(descriptor, config);
153
+ if (resolvedSeverity === "off") continue;
154
+ if (descriptor.rule.severity === resolvedSeverity) {
155
+ active.push(descriptor.rule);
156
+ continue;
157
+ }
158
+ active.push({ ...descriptor.rule, severity: resolvedSeverity });
159
+ }
160
+ return active;
161
+ }
162
+ function resolveRuleSeverity(descriptor, config) {
163
+ let severity = descriptor.rule.severity;
164
+ for (const entry of config.rulePolicy) {
165
+ if (!matchesSelector(descriptor, entry.selector)) continue;
166
+ severity = entry.severity;
167
+ }
168
+ if (severity === "off") return "off";
169
+ if (config.strict) return "error";
170
+ return severity;
171
+ }
172
+ function matchesSelector(descriptor, selector) {
173
+ if (selector.startsWith("category:")) {
174
+ return descriptor.category === selector.slice("category:".length);
175
+ }
176
+ if (selector.startsWith("tag:")) {
177
+ return descriptor.tags.includes(selector.slice("tag:".length));
178
+ }
179
+ if (selector.startsWith("confidence:")) {
180
+ return descriptor.confidence === selector.slice("confidence:".length);
181
+ }
182
+ if (selector === descriptor.rule.id) return true;
183
+ if (!selector.includes("*")) return false;
184
+ const regex = new RegExp(`^${escapeRegex(selector).replaceAll("\\*", ".*")}$`);
185
+ return regex.test(descriptor.rule.id);
186
+ }
187
+ function escapeRegex(value) {
188
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
189
+ }
190
+ function finalizeScanResult(diagnostics, fileCount, config) {
191
+ const afterOverrides = applyOverrides(diagnostics, config.overrides, process.cwd());
192
+ const effective = config.baseline ? applyBaseline(afterOverrides, config.baseline, process.cwd()) : afterOverrides;
193
+ const visibleDiagnostics = config.showSeverities ? effective.filter((d) => config.showSeverities?.has(d.severity)) : effective;
194
+ return {
195
+ diagnostics,
196
+ visibleDiagnostics,
197
+ fileCount,
198
+ exitCode: computeExitCode(effective, config.failOn)
199
+ };
200
+ }
201
+ function applyOverrides(diagnostics, overrides, cwd) {
202
+ if (overrides.length === 0) return diagnostics;
203
+ const matchers = overrides.map((entry) => ({
204
+ matcher: ignore().add(entry.files),
205
+ rulePolicy: entry.rulePolicy
206
+ }));
207
+ const result = [];
208
+ for (const diagnostic of diagnostics) {
209
+ const relPath = relative2(cwd, diagnostic.file).replaceAll("\\", "/");
210
+ const metadata = getRuleMetadata(diagnostic.ruleId);
211
+ const descriptor = {
212
+ rule: { id: diagnostic.ruleId },
213
+ category: metadata.category,
214
+ tags: metadata.tags,
215
+ confidence: metadata.confidence
216
+ };
217
+ let severity = diagnostic.severity;
218
+ for (const { matcher, rulePolicy } of matchers) {
219
+ if (relPath.startsWith("..") || !matcher.ignores(relPath)) continue;
220
+ for (const entry of rulePolicy) {
221
+ if (!matchesSelector(descriptor, entry.selector)) continue;
222
+ severity = entry.severity;
223
+ }
224
+ }
225
+ if (severity === "off") continue;
226
+ result.push(severity === diagnostic.severity ? diagnostic : { ...diagnostic, severity });
227
+ }
228
+ return result;
229
+ }
230
+ function toScanResult(execution) {
231
+ return {
232
+ diagnostics: execution.diagnostics,
233
+ fileCount: execution.fileCount
234
+ };
235
+ }
236
+ function computeExitCode(diagnostics, failOn) {
237
+ if (failOn === "none") return 0;
238
+ const hasError = diagnostics.some((d) => d.severity === "error");
239
+ const hasWarning = diagnostics.some((d) => d.severity === "warning");
240
+ const hasInfo = diagnostics.some((d) => d.severity === "info");
241
+ if (failOn === "error") return hasError ? 2 : 0;
242
+ if (failOn === "warning") {
243
+ if (hasError) return 2;
244
+ return hasWarning ? 1 : 0;
245
+ }
246
+ if (hasError) return 2;
247
+ if (hasWarning || hasInfo) return 1;
248
+ return 0;
249
+ }
250
+
60
251
  // src/engine.ts
61
252
  import { availableParallelism } from "os";
62
253
 
63
254
  // src/scan/cache.ts
64
255
  import { createHash } from "crypto";
65
- import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
256
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
66
257
  import { join } from "path";
67
258
  var CACHE_VERSION = 1;
68
259
  var CACHE_FILENAME = "scan-cache.json";
@@ -70,7 +261,7 @@ function resolveCacheDir(startDir) {
70
261
  let dir = startDir;
71
262
  for (; ; ) {
72
263
  const nm = join(dir, "node_modules");
73
- if (existsSync(nm)) return join(nm, ".cache", "unguard", `v${CACHE_VERSION}`);
264
+ if (existsSync2(nm)) return join(nm, ".cache", "unguard", `v${CACHE_VERSION}`);
74
265
  const parent = join(dir, "..");
75
266
  if (parent === dir) return null;
76
267
  dir = parent;
@@ -96,7 +287,7 @@ function hashFile(path, prevFingerprint) {
96
287
  const prevStat = prevFingerprint.split("@", 1)[0];
97
288
  if (prevStat === statTag) return prevFingerprint;
98
289
  }
99
- const buf = readFileSync(path);
290
+ const buf = readFileSync2(path);
100
291
  const contentHash = createHash("sha1").update(buf).digest("hex").slice(0, 16);
101
292
  return `${statTag}@${contentHash}`;
102
293
  }
@@ -109,8 +300,8 @@ function hashFiles(paths, prev) {
109
300
  }
110
301
  function readScanCache(dir) {
111
302
  const path = join(dir, CACHE_FILENAME);
112
- if (!existsSync(path)) return null;
113
- const text = trySync(() => readFileSync(path, "utf8"), "read failed");
303
+ if (!existsSync2(path)) return null;
304
+ const text = trySync(() => readFileSync2(path, "utf8"), "read failed");
114
305
  if (text === null) return null;
115
306
  const raw = trySync(() => JSON.parse(text), "parse failed");
116
307
  if (raw === null) return null;
@@ -119,7 +310,7 @@ function readScanCache(dir) {
119
310
  function writeScanCache(dir, entry) {
120
311
  trySync(() => {
121
312
  mkdirSync(dir, { recursive: true });
122
- writeFileSync(join(dir, CACHE_FILENAME), JSON.stringify(entry));
313
+ writeFileSync2(join(dir, CACHE_FILENAME), JSON.stringify(entry));
123
314
  return true;
124
315
  }, "write failed");
125
316
  }
@@ -217,9 +408,9 @@ function debugLog(label, err) {
217
408
 
218
409
  // src/scan/discover.ts
219
410
  import fg from "fast-glob";
220
- import ignore from "ignore";
221
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
222
- import { relative, resolve, sep } from "path";
411
+ import ignore2 from "ignore";
412
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
413
+ import { relative as relative3, resolve as resolve2, sep } from "path";
223
414
  async function discoverFiles(config) {
224
415
  const globs = expandGlobs(config.paths);
225
416
  const discoveredFiles = await fg(globs, {
@@ -240,100 +431,19 @@ function expandGlobs(paths) {
240
431
  });
241
432
  }
242
433
  function applyGitIgnore(files) {
243
- const gitIgnorePath = resolve(process.cwd(), ".gitignore");
244
- if (!existsSync2(gitIgnorePath)) return files;
245
- const matcher = ignore().add(readFileSync2(gitIgnorePath, "utf8"));
434
+ const gitIgnorePath = resolve2(process.cwd(), ".gitignore");
435
+ if (!existsSync3(gitIgnorePath)) return files;
436
+ const matcher = ignore2().add(readFileSync3(gitIgnorePath, "utf8"));
246
437
  return files.filter((file) => {
247
- const rel = relative(process.cwd(), file);
438
+ const rel = relative3(process.cwd(), file);
248
439
  if (rel.startsWith("..")) return true;
249
440
  const normalized = rel.split(sep).join("/");
250
441
  return !matcher.ignores(normalized);
251
442
  });
252
443
  }
253
444
 
254
- // src/scan/policy.ts
255
- function buildRuleDescriptors(rules) {
256
- return rules.map((rule) => {
257
- const metadata = getRuleMetadata(rule.id);
258
- return {
259
- rule,
260
- category: metadata.category,
261
- tags: metadata.tags
262
- };
263
- });
264
- }
265
- function resolveActiveRules(descriptors, config) {
266
- const selectedRules = config.rules ? new Set(config.rules) : null;
267
- const active = [];
268
- for (const descriptor of descriptors) {
269
- if (selectedRules && !selectedRules.has(descriptor.rule.id)) continue;
270
- const resolvedSeverity = resolveRuleSeverity(descriptor, config);
271
- if (resolvedSeverity === "off") continue;
272
- if (descriptor.rule.severity === resolvedSeverity) {
273
- active.push(descriptor.rule);
274
- continue;
275
- }
276
- active.push({ ...descriptor.rule, severity: resolvedSeverity });
277
- }
278
- return active;
279
- }
280
- function resolveRuleSeverity(descriptor, config) {
281
- let severity = descriptor.rule.severity;
282
- for (const entry of config.rulePolicy) {
283
- if (!matchesSelector(descriptor, entry.selector)) continue;
284
- severity = entry.severity;
285
- }
286
- if (severity === "off") return "off";
287
- if (config.strict) return "error";
288
- return severity;
289
- }
290
- function matchesSelector(descriptor, selector) {
291
- if (selector.startsWith("category:")) {
292
- return descriptor.category === selector.slice("category:".length);
293
- }
294
- if (selector.startsWith("tag:")) {
295
- return descriptor.tags.includes(selector.slice("tag:".length));
296
- }
297
- if (selector === descriptor.rule.id) return true;
298
- if (!selector.includes("*")) return false;
299
- const regex = new RegExp(`^${escapeRegex(selector).replaceAll("\\*", ".*")}$`);
300
- return regex.test(descriptor.rule.id);
301
- }
302
- function escapeRegex(value) {
303
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
304
- }
305
- function finalizeScanResult(diagnostics, fileCount, config) {
306
- const visibleDiagnostics = config.showSeverities ? diagnostics.filter((d) => config.showSeverities?.has(d.severity)) : diagnostics;
307
- return {
308
- diagnostics,
309
- visibleDiagnostics,
310
- fileCount,
311
- exitCode: computeExitCode(diagnostics, config.failOn)
312
- };
313
- }
314
- function toScanResult(execution) {
315
- return {
316
- diagnostics: execution.diagnostics,
317
- fileCount: execution.fileCount
318
- };
319
- }
320
- function computeExitCode(diagnostics, failOn) {
321
- if (failOn === "none") return 0;
322
- const hasError = diagnostics.some((d) => d.severity === "error");
323
- const hasWarning = diagnostics.some((d) => d.severity === "warning");
324
- const hasInfo = diagnostics.some((d) => d.severity === "info");
325
- if (failOn === "error") return hasError ? 2 : 0;
326
- if (failOn === "warning") {
327
- if (hasError) return 2;
328
- return hasWarning ? 1 : 0;
329
- }
330
- if (hasError) return 2;
331
- if (hasWarning || hasInfo) return 1;
332
- return 0;
333
- }
334
-
335
445
  // src/engine.ts
336
- var UNGUARD_VERSION = "0.15.1";
446
+ var UNGUARD_VERSION = "0.16.0";
337
447
  async function executeScan(options) {
338
448
  const config = resolveScanConfig(options);
339
449
  const files = await discoverFiles(config);
@@ -390,7 +500,12 @@ export {
390
500
  isRulePolicySeverity,
391
501
  isSeverity,
392
502
  isFailOn,
503
+ BASELINE_FILENAME,
504
+ buildBaseline,
505
+ writeBaseline,
506
+ loadBaseline,
507
+ computeExitCode,
393
508
  executeScan,
394
509
  scan
395
510
  };
396
- //# sourceMappingURL=chunk-WXUUG77Y.js.map
511
+ //# sourceMappingURL=chunk-3Y76J2K2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scan/config.ts","../src/scan/baseline.ts","../src/scan/policy.ts","../src/engine.ts","../src/scan/cache.ts","../src/scan/discover.ts"],"sourcesContent":["import type {\n FailOn,\n ResolvedRuleOverride,\n ResolvedScanConfig,\n RuleOverride,\n RulePolicy,\n RulePolicyEntry,\n RulePolicySeverity,\n ScanOptions,\n Severity,\n} from \"./types.ts\";\n\nconst BUILTIN_IGNORE = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/.git/**\",\n \"**/*.d.ts\",\n \"**/*.d.cts\",\n \"**/*.d.mts\",\n];\n\nconst GENERATED_IGNORE = [\n \"**/*.gen.*\",\n \"**/*.generated.*\",\n];\n\nconst DEFAULT_FAIL_ON: FailOn = \"info\";\n\nexport function resolveScanConfig(options: ScanOptions): ResolvedScanConfig {\n const paths = options.paths.length > 0 ? options.paths : [\".\"];\n const ignore = [...BUILTIN_IGNORE, ...GENERATED_IGNORE, ...(options.ignore ?? [])];\n return {\n paths,\n mode: options.mode ?? \"scan\",\n strict: options.strict ?? false,\n rules: options.rules ? [...options.rules] : null,\n ignore,\n rulePolicy: toRulePolicyEntries(options.rulePolicy),\n overrides: toResolvedOverrides(options.overrides),\n showSeverities: normalizeSeveritySet(options.showSeverities),\n failOn: options.failOn ?? DEFAULT_FAIL_ON,\n useGitIgnore: options.useGitIgnore ?? true,\n concurrency: options.concurrency,\n cache: options.cache ?? true,\n baseline: options.baseline ?? null,\n };\n}\n\nfunction toResolvedOverrides(overrides: RuleOverride[] | undefined): ResolvedRuleOverride[] {\n if (overrides === undefined) return [];\n return overrides.map((entry) => ({\n files: [...entry.files],\n rulePolicy: toRulePolicyEntries(entry.rules),\n }));\n}\n\nexport function toRulePolicyEntries(policy: RulePolicy | undefined): RulePolicyEntry[] {\n if (policy === undefined) return [];\n if (Array.isArray(policy)) return [...policy];\n\n const entries: RulePolicyEntry[] = [];\n for (const [selector, severity] of Object.entries(policy)) {\n entries.push({ selector, severity });\n }\n return entries;\n}\n\nfunction normalizeSeveritySet(levels: Severity[] | undefined): Set<Severity> | null {\n if (levels === undefined || levels.length === 0) return null;\n return new Set(levels);\n}\n\nexport function isRulePolicySeverity(value: string): value is RulePolicySeverity {\n return value === \"off\" || value === \"info\" || value === \"warning\" || value === \"error\";\n}\n\nexport function isSeverity(value: string): value is Severity {\n return value === \"info\" || value === \"warning\" || value === \"error\";\n}\n\nexport function isFailOn(value: string): value is FailOn {\n return value === \"none\" || isSeverity(value);\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { relative, resolve } from \"node:path\";\nimport type { Diagnostic } from \"../rules/types.ts\";\n\nexport const BASELINE_FILENAME = \"unguard.baseline.json\";\n\n/**\n * Ratchet file: known-issue counts per (file, rule). A scan suppresses a\n * group while its count stays at or below the recorded number; the moment it\n * exceeds the baseline, the whole group surfaces — line numbers shift too\n * easily to pin individual diagnostics, counts only ratchet downward.\n */\nexport interface BaselineData {\n version: 1;\n rules: Record<string, Record<string, number>>;\n}\n\nexport function buildBaseline(diagnostics: Diagnostic[], cwd: string): BaselineData {\n const rules: Record<string, Record<string, number>> = {};\n for (const diagnostic of diagnostics) {\n const file = relative(cwd, diagnostic.file);\n rules[file] ??= {};\n const forFile = rules[file];\n forFile[diagnostic.ruleId] = (forFile[diagnostic.ruleId] ?? 0) + 1;\n }\n return { version: 1, rules };\n}\n\nexport function writeBaseline(baseline: BaselineData, cwd: string): string {\n const path = resolve(cwd, BASELINE_FILENAME);\n writeFileSync(path, `${JSON.stringify(baseline, null, 2)}\\n`, \"utf8\");\n return path;\n}\n\nexport function loadBaseline(cwd: string): BaselineData | null {\n const path = resolve(cwd, BASELINE_FILENAME);\n if (!existsSync(path)) return null;\n const parsed: unknown = JSON.parse(readFileSync(path, \"utf8\"));\n if (!isBaselineData(parsed)) {\n throw new Error(`Invalid baseline in ${BASELINE_FILENAME}: expected {\"version\": 1, \"rules\": {<file>: {<ruleId>: count}}}.`);\n }\n return parsed;\n}\n\nfunction isBaselineData(value: unknown): value is BaselineData {\n if (typeof value !== \"object\" || value === null) return false;\n if (!(\"version\" in value) || value.version !== 1) return false;\n if (!(\"rules\" in value) || typeof value.rules !== \"object\" || value.rules === null) return false;\n return Object.values(value.rules).every(\n (forFile) =>\n typeof forFile === \"object\" &&\n forFile !== null &&\n Object.values(forFile).every((count) => typeof count === \"number\"),\n );\n}\n\n/** Drop diagnostic groups still within their baselined count. */\nexport function applyBaseline(diagnostics: Diagnostic[], baseline: BaselineData, cwd: string): Diagnostic[] {\n const groups = new Map<string, Diagnostic[]>();\n for (const diagnostic of diagnostics) {\n const key = `${relative(cwd, diagnostic.file)}\\0${diagnostic.ruleId}`;\n let list = groups.get(key);\n if (list === undefined) {\n list = [];\n groups.set(key, list);\n }\n list.push(diagnostic);\n }\n\n const kept: Diagnostic[] = [];\n for (const [key, group] of groups) {\n const [file, ruleId] = key.split(\"\\0\") as [string, string];\n const allowed = baseline.rules[file]?.[ruleId] ?? 0;\n if (group.length <= allowed) continue;\n kept.push(...group);\n }\n return kept;\n}\n","import { relative } from \"node:path\";\nimport ignore from \"ignore\";\nimport type { Diagnostic, Rule } from \"../rules/types.ts\";\nimport { getRuleMetadata } from \"../rules/index.ts\";\nimport { applyBaseline } from \"./baseline.ts\";\nimport type {\n ResolvedRuleOverride,\n ResolvedScanConfig,\n RuleDescriptor,\n RulePolicySeverity,\n ScanExecutionResult,\n ScanResult,\n Severity,\n} from \"./types.ts\";\n\nexport function buildRuleDescriptors(rules: Rule[]): RuleDescriptor[] {\n return rules.map((rule) => {\n const metadata = getRuleMetadata(rule.id);\n return {\n rule,\n category: metadata.category,\n tags: metadata.tags,\n confidence: metadata.confidence,\n };\n });\n}\n\nexport function resolveActiveRules(\n descriptors: RuleDescriptor[],\n config: ResolvedScanConfig,\n): Rule[] {\n const selectedRules = config.rules ? new Set(config.rules) : null;\n const wantedConfidence = config.mode === \"audit\" ? \"heuristic\" : \"proven\";\n const active: Rule[] = [];\n\n for (const descriptor of descriptors) {\n if (selectedRules) {\n // An explicit rule filter names what to run; the mode split is moot.\n if (!selectedRules.has(descriptor.rule.id)) continue;\n } else if (descriptor.confidence !== wantedConfidence) {\n continue;\n }\n const resolvedSeverity = resolveRuleSeverity(descriptor, config);\n if (resolvedSeverity === \"off\") continue;\n if (descriptor.rule.severity === resolvedSeverity) {\n active.push(descriptor.rule);\n continue;\n }\n active.push({ ...descriptor.rule, severity: resolvedSeverity });\n }\n\n return active;\n}\n\nfunction resolveRuleSeverity(descriptor: RuleDescriptor, config: ResolvedScanConfig): RulePolicySeverity {\n let severity: RulePolicySeverity = descriptor.rule.severity;\n for (const entry of config.rulePolicy) {\n if (!matchesSelector(descriptor, entry.selector)) continue;\n severity = entry.severity;\n }\n\n if (severity === \"off\") return \"off\";\n if (config.strict) return \"error\";\n return severity;\n}\n\nfunction matchesSelector(\n descriptor: Pick<RuleDescriptor, \"category\" | \"tags\" | \"confidence\"> & { rule: { id: string } },\n selector: string,\n): boolean {\n if (selector.startsWith(\"category:\")) {\n return descriptor.category === selector.slice(\"category:\".length);\n }\n if (selector.startsWith(\"tag:\")) {\n return descriptor.tags.includes(selector.slice(\"tag:\".length));\n }\n if (selector.startsWith(\"confidence:\")) {\n return descriptor.confidence === selector.slice(\"confidence:\".length);\n }\n if (selector === descriptor.rule.id) return true;\n if (!selector.includes(\"*\")) return false;\n const regex = new RegExp(`^${escapeRegex(selector).replaceAll(\"\\\\*\", \".*\")}$`);\n return regex.test(descriptor.rule.id);\n}\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport function finalizeScanResult(\n diagnostics: Diagnostic[],\n fileCount: number,\n config: ResolvedScanConfig,\n): ScanExecutionResult {\n // `diagnostics` stays raw (and cached raw); overrides and the baseline\n // ratchet only shape what is reported and what fails the run.\n const afterOverrides = applyOverrides(diagnostics, config.overrides, process.cwd());\n const effective = config.baseline\n ? applyBaseline(afterOverrides, config.baseline, process.cwd())\n : afterOverrides;\n const visibleDiagnostics = config.showSeverities\n ? effective.filter((d) => config.showSeverities?.has(d.severity))\n : effective;\n\n return {\n diagnostics,\n visibleDiagnostics,\n fileCount,\n exitCode: computeExitCode(effective, config.failOn),\n };\n}\n\n/**\n * Re-resolve severities per file: each override whose globs (gitignore\n * syntax, matched against the cwd-relative path) cover the diagnostic's file\n * applies its rule policy on top of the already-resolved severity. `off`\n * drops the diagnostic.\n */\nexport function applyOverrides(\n diagnostics: Diagnostic[],\n overrides: ResolvedRuleOverride[],\n cwd: string,\n): Diagnostic[] {\n if (overrides.length === 0) return diagnostics;\n const matchers = overrides.map((entry) => ({\n matcher: ignore().add(entry.files),\n rulePolicy: entry.rulePolicy,\n }));\n\n const result: Diagnostic[] = [];\n for (const diagnostic of diagnostics) {\n const relPath = relative(cwd, diagnostic.file).replaceAll(\"\\\\\", \"/\");\n const metadata = getRuleMetadata(diagnostic.ruleId);\n const descriptor = {\n rule: { id: diagnostic.ruleId },\n category: metadata.category,\n tags: metadata.tags,\n confidence: metadata.confidence,\n };\n\n let severity: RulePolicySeverity = diagnostic.severity;\n for (const { matcher, rulePolicy } of matchers) {\n if (relPath.startsWith(\"..\") || !matcher.ignores(relPath)) continue;\n for (const entry of rulePolicy) {\n if (!matchesSelector(descriptor, entry.selector)) continue;\n severity = entry.severity;\n }\n }\n\n if (severity === \"off\") continue;\n result.push(severity === diagnostic.severity ? diagnostic : { ...diagnostic, severity });\n }\n return result;\n}\n\nexport function toScanResult(execution: ScanExecutionResult): ScanResult {\n return {\n diagnostics: execution.diagnostics,\n fileCount: execution.fileCount,\n };\n}\n\nexport function computeExitCode(diagnostics: Diagnostic[], failOn: \"none\" | Severity): number {\n if (failOn === \"none\") return 0;\n\n const hasError = diagnostics.some((d) => d.severity === \"error\");\n const hasWarning = diagnostics.some((d) => d.severity === \"warning\");\n const hasInfo = diagnostics.some((d) => d.severity === \"info\");\n\n if (failOn === \"error\") return hasError ? 2 : 0;\n if (failOn === \"warning\") {\n if (hasError) return 2;\n return hasWarning ? 1 : 0;\n }\n\n if (hasError) return 2;\n if (hasWarning || hasInfo) return 1;\n return 0;\n}\n","import { availableParallelism } from \"node:os\";\nimport { allRules } from \"./rules/index.ts\";\nimport { analyzeFiles } from \"./scan/analyze.ts\";\nimport { cacheCovers, computeScanKey, hashFiles, readScanCache, resolveCacheDir, writeScanCache } from \"./scan/cache.ts\";\nimport { resolveScanConfig } from \"./scan/config.ts\";\nimport { discoverFiles } from \"./scan/discover.ts\";\nimport { buildRuleDescriptors, finalizeScanResult, resolveActiveRules, toScanResult } from \"./scan/policy.ts\";\nimport type { ScanExecutionResult, ScanOptions, ScanResult } from \"./scan/types.ts\";\n\nexport type {\n FailOn,\n RulePolicy,\n RulePolicyEntry,\n RulePolicySeverity,\n ScanExecutionResult,\n ScanOptions,\n ScanResult,\n Severity,\n} from \"./scan/types.ts\";\n\nconst UNGUARD_VERSION = \"0.16.0\";\n\nexport async function executeScan(options: ScanOptions): Promise<ScanExecutionResult> {\n const config = resolveScanConfig(options);\n const files = await discoverFiles(config);\n const descriptors = buildRuleDescriptors(allRules);\n const activeRules = resolveActiveRules(descriptors, config);\n\n const cacheDir = config.cache ? resolveCacheDir(process.cwd()) : null;\n if (cacheDir !== null) {\n const scanKey = computeScanKey({\n unguardVersion: UNGUARD_VERSION,\n rules: activeRules,\n paths: config.paths,\n ignore: config.ignore,\n strict: config.strict,\n failOn: config.failOn,\n showSeverities: config.showSeverities,\n });\n const cached = readScanCache(cacheDir);\n const sortedFiles = [...files].sort();\n const currentHashes = hashFiles(sortedFiles, cached?.fileHashes ?? null);\n\n if (cached !== null && cacheCovers(cached, { unguardVersion: UNGUARD_VERSION, scanKey }, currentHashes)) {\n return finalizeScanResult(cached.diagnostics, cached.fileCount, config);\n }\n\n const concurrency = resolveDefaultConcurrency(config.concurrency);\n const diagnostics = await analyzeFiles(files, activeRules, { concurrency });\n writeScanCache(cacheDir, {\n version: 1,\n unguardVersion: UNGUARD_VERSION,\n scanKey,\n fileHashes: currentHashes,\n diagnostics,\n fileCount: files.length,\n });\n return finalizeScanResult(diagnostics, files.length, config);\n }\n\n const concurrency = resolveDefaultConcurrency(config.concurrency);\n const diagnostics = await analyzeFiles(files, activeRules, { concurrency });\n return finalizeScanResult(diagnostics, files.length, config);\n}\n\nfunction resolveDefaultConcurrency(requested: number | undefined): number {\n if (requested !== undefined) {\n if (!Number.isFinite(requested) || requested < 1) return 1;\n return Math.floor(requested);\n }\n // Each worker holds its own ts.Program; cap at half the CPUs to avoid memory blow-up on shared CI.\n const cores = availableParallelism();\n return Math.min(8, Math.max(1, Math.ceil(cores / 2)));\n}\n\nexport async function scan(options: ScanOptions): Promise<ScanResult> {\n const execution = await executeScan(options);\n return toScanResult(execution);\n}\n","import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Diagnostic, Rule } from \"../rules/types.ts\";\nimport type { Severity } from \"./types.ts\";\n\nconst CACHE_VERSION = 1;\nconst CACHE_FILENAME = \"scan-cache.json\";\n\nexport interface ScanCacheEntry {\n version: number;\n unguardVersion: string;\n /** Hash of active rules + ignore globs + paths + severity policy. Mismatch -> cache miss. */\n scanKey: string;\n /** `<size>:<mtimeNs>@<contentHash>` per file. Stat fast-path skips hashing when stat matches. */\n fileHashes: Record<string, string>;\n diagnostics: Diagnostic[];\n fileCount: number;\n}\n\nexport interface ScanKeyInput {\n unguardVersion: string;\n rules: readonly Rule[];\n paths: readonly string[];\n ignore: readonly string[];\n strict: boolean;\n failOn: string;\n showSeverities: ReadonlySet<Severity> | null;\n}\n\nexport interface CacheCheckContext {\n unguardVersion: string;\n scanKey: string;\n}\n\n/** Walk up from `startDir` to the nearest `node_modules`, returning its `.cache/unguard/v<N>` subdir. */\nexport function resolveCacheDir(startDir: string): string | null {\n let dir = startDir;\n for (;;) {\n const nm = join(dir, \"node_modules\");\n if (existsSync(nm)) return join(nm, \".cache\", \"unguard\", `v${CACHE_VERSION}`);\n const parent = join(dir, \"..\");\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nexport function computeScanKey(input: ScanKeyInput): string {\n const ruleSig = [...input.rules]\n .map((r) => `${r.id}=${r.severity}`)\n .sort()\n .join(\",\");\n const payload = JSON.stringify({\n unguardVersion: input.unguardVersion,\n ruleSig,\n paths: [...input.paths].sort(),\n ignore: [...input.ignore].sort(),\n strict: input.strict,\n failOn: input.failOn,\n showSeverities: input.showSeverities ? [...input.showSeverities].sort() : null,\n });\n return createHash(\"sha256\").update(payload).digest(\"hex\").slice(0, 16);\n}\n\nexport function hashFile(path: string, prevFingerprint: string | undefined): string {\n const s = statSync(path, { bigint: true });\n const statTag = `${s.size}:${s.mtimeNs.toString()}`;\n if (prevFingerprint !== undefined) {\n const prevStat = prevFingerprint.split(\"@\", 1)[0];\n if (prevStat === statTag) return prevFingerprint;\n }\n const buf = readFileSync(path);\n const contentHash = createHash(\"sha1\").update(buf).digest(\"hex\").slice(0, 16);\n return `${statTag}@${contentHash}`;\n}\n\nexport function hashFiles(paths: readonly string[], prev: Record<string, string> | null): Record<string, string> {\n const result: Record<string, string> = {};\n for (const path of paths) {\n result[path] = hashFile(path, prev?.[path]);\n }\n return result;\n}\n\nexport function readScanCache(dir: string): ScanCacheEntry | null {\n const path = join(dir, CACHE_FILENAME);\n if (!existsSync(path)) return null;\n\n const text = trySync(() => readFileSync(path, \"utf8\"), \"read failed\");\n if (text === null) return null;\n\n const raw = trySync(() => JSON.parse(text) as unknown, \"parse failed\");\n if (raw === null) return null;\n\n return validateScanCache(raw);\n}\n\nexport function writeScanCache(dir: string, entry: ScanCacheEntry): void {\n trySync(() => {\n mkdirSync(dir, { recursive: true });\n writeFileSync(join(dir, CACHE_FILENAME), JSON.stringify(entry));\n return true;\n }, \"write failed\");\n}\n\n/** Cache hit iff scan config matches and every file's content hash matches. Ignores stat. */\nexport function cacheCovers(\n cached: ScanCacheEntry,\n context: CacheCheckContext,\n currentHashes: Record<string, string>,\n): boolean {\n if (cached.unguardVersion !== context.unguardVersion) return false;\n if (cached.scanKey !== context.scanKey) return false;\n\n const cachedFiles = Object.keys(cached.fileHashes);\n const currentFiles = Object.keys(currentHashes);\n if (cachedFiles.length !== currentFiles.length) return false;\n\n for (const file of currentFiles) {\n const prev = cached.fileHashes[file];\n const cur = currentHashes[file];\n if (prev === undefined || cur === undefined) return false;\n if (contentHashOf(prev) !== contentHashOf(cur)) return false;\n }\n return true;\n}\n\nfunction contentHashOf(fingerprint: string): string {\n const idx = fingerprint.indexOf(\"@\");\n return idx >= 0 ? fingerprint.slice(idx + 1) : fingerprint;\n}\n\nfunction validateScanCache(raw: unknown): ScanCacheEntry | null {\n if (typeof raw !== \"object\" || raw === null) return null;\n const entry = raw as Record<string, unknown>;\n if (entry.version !== CACHE_VERSION) return null;\n if (typeof entry.scanKey !== \"string\") return null;\n if (typeof entry.unguardVersion !== \"string\") return null;\n if (typeof entry.fileCount !== \"number\") return null;\n\n const fileHashes = validateFileHashes(entry.fileHashes);\n if (fileHashes === null) return null;\n\n const diagnostics = validateDiagnostics(entry.diagnostics);\n if (diagnostics === null) return null;\n\n return {\n version: entry.version,\n unguardVersion: entry.unguardVersion,\n scanKey: entry.scanKey,\n fileHashes,\n diagnostics,\n fileCount: entry.fileCount,\n };\n}\n\nfunction validateFileHashes(raw: unknown): Record<string, string> | null {\n if (typeof raw !== \"object\" || raw === null) return null;\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(raw)) {\n if (typeof value !== \"string\") return null;\n result[key] = value;\n }\n return result;\n}\n\nfunction validateDiagnostics(raw: unknown): Diagnostic[] | null {\n if (!Array.isArray(raw)) return null;\n const list: Diagnostic[] = [];\n for (const item of raw) {\n const diag = validateDiagnostic(item);\n if (diag === null) return null;\n list.push(diag);\n }\n return list;\n}\n\nfunction validateDiagnostic(item: unknown): Diagnostic | null {\n if (typeof item !== \"object\" || item === null) return null;\n if (!(\"file\" in item) || typeof item.file !== \"string\") return null;\n if (!(\"ruleId\" in item) || typeof item.ruleId !== \"string\") return null;\n if (!(\"line\" in item) || typeof item.line !== \"number\") return null;\n if (!(\"column\" in item) || typeof item.column !== \"number\") return null;\n if (!(\"severity\" in item)) return null;\n if (item.severity !== \"error\" && item.severity !== \"warning\" && item.severity !== \"info\") return null;\n if (!(\"message\" in item) || typeof item.message !== \"string\") return null;\n const annotation = \"annotation\" in item ? item.annotation : undefined;\n if (annotation !== undefined && typeof annotation !== \"string\") return null;\n return {\n file: item.file,\n ruleId: item.ruleId,\n line: item.line,\n column: item.column,\n severity: item.severity,\n message: item.message,\n annotation,\n };\n}\n\nfunction trySync<T>(fn: () => T, label: string): T | null {\n try {\n return fn();\n } catch (err) {\n return debugLog(label, err);\n }\n}\n\nfunction debugLog(label: string, err: unknown): null {\n if (process.env.UNGUARD_DEBUG !== undefined) {\n console.error(`[unguard] cache ${label}:`, err);\n }\n return null;\n}\n","import fg from \"fast-glob\";\nimport ignore from \"ignore\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { relative, resolve, sep } from \"node:path\";\nimport type { ResolvedScanConfig } from \"./types.ts\";\n\nexport async function discoverFiles(config: ResolvedScanConfig): Promise<string[]> {\n const globs = expandGlobs(config.paths);\n const discoveredFiles = await fg(globs, {\n ignore: config.ignore,\n absolute: true,\n });\n if (!config.useGitIgnore) return discoveredFiles;\n return applyGitIgnore(discoveredFiles);\n}\n\nfunction expandGlobs(paths: string[]): string[] {\n return paths.map((p) => {\n if (p === \".\") return \"./**/*.{ts,cts,mts,tsx}\";\n if (p.endsWith(\"/\")) return `${p}**/*.{ts,cts,mts,tsx}`;\n if (!p.includes(\"*\") && !p.endsWith(\".ts\") && !p.endsWith(\".tsx\") && !p.endsWith(\".cts\") && !p.endsWith(\".mts\")) {\n return `${p}/**/*.{ts,cts,mts,tsx}`;\n }\n return p;\n });\n}\n\nfunction applyGitIgnore(files: string[]): string[] {\n const gitIgnorePath = resolve(process.cwd(), \".gitignore\");\n if (!existsSync(gitIgnorePath)) return files;\n\n const matcher = ignore().add(readFileSync(gitIgnorePath, \"utf8\"));\n return files.filter((file) => {\n const rel = relative(process.cwd(), file);\n if (rel.startsWith(\"..\")) return true;\n const normalized = rel.split(sep).join(\"/\");\n return !matcher.ignores(normalized);\n });\n}\n"],"mappings":"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","names":["ignore","relative","relative","existsSync","readFileSync","writeFileSync","ignore","existsSync","readFileSync","relative","resolve","concurrency","diagnostics"]}