aislop 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as ENGINE_INFO, r as getEngineLabel, t as APP_VERSION } from "./version-CxBRws3M.js";
1
+ import { n as ENGINE_INFO, r as getEngineLabel, t as APP_VERSION } from "./version-C2lM_2fE.js";
2
2
  import { n as runSubprocess, t as isToolInstalled } from "./subprocess-CQUJDGgn.js";
3
3
  import { r as runGenericLinter, t as fixRubyLint } from "./generic-BrcWMW7E.js";
4
4
  import { n as runExpoDoctor } from "./expo-doctor-Bz0LZhQ6.js";
@@ -1568,7 +1568,7 @@ const detectSwallowedExceptions = async (context) => {
1568
1568
  };
1569
1569
 
1570
1570
  //#endregion
1571
- //#region src/engines/ai-slop/narrative-comments.ts
1571
+ //#region src/engines/ai-slop/narrative-comments-patterns.ts
1572
1572
  const DECORATIVE_SEPARATOR = /^[-=─━~_*#]{6,}$/;
1573
1573
  const DECORATIVE_SECTION_HEADER = /^[-=─━~_*#]{3,}[\s\S]+?[-=─━~_*#]{3,}$/;
1574
1574
  const SECTION_HEADER = /^(Phase|Step|Section|Part)\s+\d+[:.-]/i;
@@ -1611,7 +1611,49 @@ const MEANINGFUL_JSDOC_TAGS = new Set([
1611
1611
  "todo",
1612
1612
  "link",
1613
1613
  "license",
1614
- "preserve"
1614
+ "preserve",
1615
+ "swagger",
1616
+ "openapi",
1617
+ "route",
1618
+ "group",
1619
+ "summary",
1620
+ "description",
1621
+ "operationid",
1622
+ "response",
1623
+ "responses",
1624
+ "request",
1625
+ "requestbody",
1626
+ "security",
1627
+ "tag",
1628
+ "tags",
1629
+ "path",
1630
+ "body",
1631
+ "query",
1632
+ "queryparam",
1633
+ "header",
1634
+ "headers",
1635
+ "produces",
1636
+ "accept",
1637
+ "middleware",
1638
+ "api",
1639
+ "apiname",
1640
+ "apidefine",
1641
+ "apigroup",
1642
+ "apiparam",
1643
+ "apiquery",
1644
+ "apibody",
1645
+ "apiheader",
1646
+ "apisuccess",
1647
+ "apierror",
1648
+ "apiexample",
1649
+ "apiversion",
1650
+ "apidescription",
1651
+ "apipermission",
1652
+ "apiuse",
1653
+ "apiignore",
1654
+ "apiprivate",
1655
+ "namespace",
1656
+ "category"
1615
1657
  ]);
1616
1658
  const SUPPORTED_EXTS = new Set([
1617
1659
  ".ts",
@@ -1627,6 +1669,19 @@ const SUPPORTED_EXTS = new Set([
1627
1669
  ".java",
1628
1670
  ".php"
1629
1671
  ]);
1672
+ const DECL_START = /^(\s*)(export\s+)?(async\s+)?(const|let|var|function|class|type|interface|enum|abstract\s+class)\s+/;
1673
+ const EXPORT_DEFAULT = /^\s*export\s+default\b/;
1674
+ const TS_MEMBER_DECL_START = /^\s*(?:readonly\s+|static\s+|public\s+|private\s+|protected\s+|abstract\s+|override\s+)*[\w$]+\??\s*:/;
1675
+ const PY_DECL_START = /^\s*(async\s+def|def|class)\s+/;
1676
+ const GO_DECL_START = /^\s*(func|type|var|const)\s+/;
1677
+ const RUST_DECL_START = /^\s*(pub\s+)?(async\s+)?(fn|struct|enum|trait|impl|const|static|type|mod)\s+/;
1678
+ const RUBY_DECL_START = /^\s*(class|module|def)\s+/;
1679
+ const JAVA_DECL_START = /^\s*(?:public|private|protected|static|final|abstract|sealed|non-sealed|\s)+(?:class|interface|enum|record|@interface|\w[^(){};=]*\s+\w+\s*\()/;
1680
+ const JAVA_DECL_START_FALLBACK = /^\s*(class|interface|enum|record|@interface)\s+/;
1681
+ const PHP_DECL_START = /^\s*(?:public|private|protected|static|final|abstract|readonly\s+)*(function|class|interface|trait|enum|const)\s+/;
1682
+
1683
+ //#endregion
1684
+ //#region src/engines/ai-slop/narrative-comments.ts
1630
1685
  const stripJsdocLine = (line) => line.replace(/^\s*\/\*\*+\s?/, "").replace(/\s*\*+\/\s*$/, "").replace(/^\s*\*\s?/, "").trim();
1631
1686
  const stripLineComment = (line) => line.replace(/^\s*(?:(?:\/\/)|#)\s?/, "");
1632
1687
  const getCommentSyntax = (ext) => {
@@ -1718,16 +1773,6 @@ const collectBlocks = (sourceLines, syntax) => {
1718
1773
  }
1719
1774
  return blocks;
1720
1775
  };
1721
- const DECL_START = /^(\s*)(export\s+)?(async\s+)?(const|let|var|function|class|type|interface|enum|abstract\s+class)\s+/;
1722
- const EXPORT_DEFAULT = /^\s*export\s+default\b/;
1723
- const TS_MEMBER_DECL_START = /^\s*(?:readonly\s+|static\s+|public\s+|private\s+|protected\s+|abstract\s+|override\s+)*[\w$]+\??\s*:/;
1724
- const PY_DECL_START = /^\s*(async\s+def|def|class)\s+/;
1725
- const GO_DECL_START = /^\s*(func|type|var|const)\s+/;
1726
- const RUST_DECL_START = /^\s*(pub\s+)?(async\s+)?(fn|struct|enum|trait|impl|const|static|type|mod)\s+/;
1727
- const RUBY_DECL_START = /^\s*(class|module|def)\s+/;
1728
- const JAVA_DECL_START = /^\s*(?:public|private|protected|static|final|abstract|sealed|non-sealed|\s)+(?:class|interface|enum|record|@interface|\w[^(){};=]*\s+\w+\s*\()/;
1729
- const JAVA_DECL_START_FALLBACK = /^\s*(class|interface|enum|record|@interface)\s+/;
1730
- const PHP_DECL_START = /^\s*(?:public|private|protected|static|final|abstract|readonly\s+)*(function|class|interface|trait|enum|const)\s+/;
1731
1776
  const looksLikeDeclarationPreamble = (nextLine, ext) => {
1732
1777
  if (nextLine === null) return false;
1733
1778
  if (DECL_START.test(nextLine) || EXPORT_DEFAULT.test(nextLine)) return true;
@@ -2256,72 +2301,12 @@ const architectureEngine = {
2256
2301
  };
2257
2302
 
2258
2303
  //#endregion
2259
- //#region src/engines/code-quality/complexity.ts
2260
- const FUNCTION_PATTERNS = [
2261
- {
2262
- regex: /^\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/,
2263
- langFilter: [
2264
- ".js",
2265
- ".ts",
2266
- ".jsx",
2267
- ".tsx",
2268
- ".mjs",
2269
- ".cjs"
2270
- ]
2271
- },
2272
- {
2273
- regex: /^\s*(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s+)?\(([^)]*)\)\s*(?:=>|:\s*\w)/,
2274
- langFilter: [
2275
- ".js",
2276
- ".ts",
2277
- ".jsx",
2278
- ".tsx",
2279
- ".mjs",
2280
- ".cjs"
2281
- ]
2282
- },
2283
- {
2284
- regex: /^\s*def\s+(\w+)\s*\(([^)]*)\)/,
2285
- langFilter: [".py"]
2286
- },
2287
- {
2288
- regex: /^\s*func\s+(?:\([^)]*\)\s+)?(\w+)\s*\(([^)]*)\)/,
2289
- langFilter: [".go"]
2290
- },
2291
- {
2292
- regex: /^\s*fn\s+(\w+)\s*\(([^)]*)\)/,
2293
- langFilter: [".rs"]
2294
- },
2295
- {
2296
- regex: /^\s*(?:public|private|protected)?\s*(?:static\s+)?(?:\w+\s+)(\w+)\s*\(([^)]*)\)/,
2297
- langFilter: [
2298
- ".java",
2299
- ".cs",
2300
- ".cpp",
2301
- ".c",
2302
- ".php"
2303
- ]
2304
- }
2305
- ];
2306
- const countParams = (p) => p.trim() ? p.split(",").length : 0;
2307
- const matchFunctionOnLine = (line, ext) => {
2308
- for (let i = 0; i < FUNCTION_PATTERNS.length; i++) {
2309
- const pattern = FUNCTION_PATTERNS[i];
2310
- if (!pattern.langFilter.includes(ext)) continue;
2311
- const match = line.match(pattern.regex);
2312
- if (match) return {
2313
- name: match[1],
2314
- params: match[2] ?? "",
2315
- patternIndex: i
2316
- };
2317
- }
2318
- return null;
2319
- };
2304
+ //#region src/engines/code-quality/function-boundaries.ts
2320
2305
  const PYTHON_CONTROL_FLOW_RE = /^\s*(?:if|for|while|with|try|except|else|elif|finally|def|class)\b/;
2321
- const findFunctionEnd = (lines, startIndex, isPython) => {
2322
- if (isPython) return findPythonFunctionEnd(lines, startIndex);
2323
- return findBraceFunctionEnd(lines, startIndex);
2324
- };
2306
+ const ARROW_BLOCK_RE = /* @__PURE__ */ new RegExp("=>\\s*\\{");
2307
+ const ARROW_END_RE = /* @__PURE__ */ new RegExp("=>\\s*$");
2308
+ const BRACE_START_RE = /* @__PURE__ */ new RegExp("^\\s*\\{");
2309
+ const NEW_STATEMENT_RE = /* @__PURE__ */ new RegExp("^(?:export\\s+)?(?:const|let|var|function|class)\\s");
2325
2310
  const isControlFlowBrace = (lineText, braceIndex) => {
2326
2311
  const before = lineText.substring(0, braceIndex).trimEnd();
2327
2312
  if (before.endsWith(")")) return true;
@@ -2401,16 +2386,10 @@ const findPythonFunctionEnd = (lines, startIndex) => {
2401
2386
  maxNesting
2402
2387
  };
2403
2388
  };
2404
- const isDataFile = (content) => {
2405
- const nonEmpty = content.split("\n").filter((l) => l.trim().length > 0);
2406
- if (nonEmpty.length === 0) return false;
2407
- const dataLinePattern = /^\s*[{}[\]"']/;
2408
- return nonEmpty.filter((l) => dataLinePattern.test(l)).length / nonEmpty.length > .8;
2389
+ const findFunctionEnd = (lines, startIndex, isPython) => {
2390
+ if (isPython) return findPythonFunctionEnd(lines, startIndex);
2391
+ return findBraceFunctionEnd(lines, startIndex);
2409
2392
  };
2410
- const ARROW_BLOCK_RE = /* @__PURE__ */ new RegExp("=>\\s*\\{");
2411
- const ARROW_END_RE = /* @__PURE__ */ new RegExp("=>\\s*$");
2412
- const BRACE_START_RE = /* @__PURE__ */ new RegExp("^\\s*\\{");
2413
- const NEW_STATEMENT_RE = /* @__PURE__ */ new RegExp("^(?:export\\s+)?(?:const|let|var|function|class)\\s");
2414
2393
  const isBlockArrow = (lines, startIndex) => {
2415
2394
  if (ARROW_BLOCK_RE.test(lines[startIndex])) return true;
2416
2395
  if (ARROW_END_RE.test(lines[startIndex])) {
@@ -2446,6 +2425,75 @@ const countTemplateLines = (bodyLines) => {
2446
2425
  }
2447
2426
  return templateLineCount;
2448
2427
  };
2428
+
2429
+ //#endregion
2430
+ //#region src/engines/code-quality/complexity.ts
2431
+ const FUNCTION_PATTERNS = [
2432
+ {
2433
+ regex: /^\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/,
2434
+ langFilter: [
2435
+ ".js",
2436
+ ".ts",
2437
+ ".jsx",
2438
+ ".tsx",
2439
+ ".mjs",
2440
+ ".cjs"
2441
+ ]
2442
+ },
2443
+ {
2444
+ regex: /^\s*(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s+)?\(([^)]*)\)\s*(?:=>|:\s*\w)/,
2445
+ langFilter: [
2446
+ ".js",
2447
+ ".ts",
2448
+ ".jsx",
2449
+ ".tsx",
2450
+ ".mjs",
2451
+ ".cjs"
2452
+ ]
2453
+ },
2454
+ {
2455
+ regex: /^\s*def\s+(\w+)\s*\(([^)]*)\)/,
2456
+ langFilter: [".py"]
2457
+ },
2458
+ {
2459
+ regex: /^\s*func\s+(?:\([^)]*\)\s+)?(\w+)\s*\(([^)]*)\)/,
2460
+ langFilter: [".go"]
2461
+ },
2462
+ {
2463
+ regex: /^\s*fn\s+(\w+)\s*\(([^)]*)\)/,
2464
+ langFilter: [".rs"]
2465
+ },
2466
+ {
2467
+ regex: /^\s*(?:public|private|protected)?\s*(?:static\s+)?(?:\w+\s+)(\w+)\s*\(([^)]*)\)/,
2468
+ langFilter: [
2469
+ ".java",
2470
+ ".cs",
2471
+ ".cpp",
2472
+ ".c",
2473
+ ".php"
2474
+ ]
2475
+ }
2476
+ ];
2477
+ const countParams = (p) => p.trim() ? p.split(",").length : 0;
2478
+ const matchFunctionOnLine = (line, ext) => {
2479
+ for (let i = 0; i < FUNCTION_PATTERNS.length; i++) {
2480
+ const pattern = FUNCTION_PATTERNS[i];
2481
+ if (!pattern.langFilter.includes(ext)) continue;
2482
+ const match = line.match(pattern.regex);
2483
+ if (match) return {
2484
+ name: match[1],
2485
+ params: match[2] ?? "",
2486
+ patternIndex: i
2487
+ };
2488
+ }
2489
+ return null;
2490
+ };
2491
+ const isDataFile = (content) => {
2492
+ const nonEmpty = content.split("\n").filter((l) => l.trim().length > 0);
2493
+ if (nonEmpty.length === 0) return false;
2494
+ const dataLinePattern = /^\s*[{}[\]"']/;
2495
+ return nonEmpty.filter((l) => dataLinePattern.test(l)).length / nonEmpty.length > .8;
2496
+ };
2449
2497
  const analyzeFunctions = (content, ext) => {
2450
2498
  const lines = content.split("\n");
2451
2499
  const functions = [];
@@ -2468,13 +2516,14 @@ const analyzeFunctions = (content, ext) => {
2468
2516
  }
2469
2517
  return functions;
2470
2518
  };
2519
+ const JSX_FILE_LOC_MULTIPLIER = 1.5;
2471
2520
  const checkFileDiagnostics = (relativePath, content, limits) => {
2472
2521
  const results = [];
2473
2522
  const lineCount = content.split("\n").length;
2474
2523
  const ext = path.extname(relativePath).toLowerCase();
2475
2524
  if (isDataFile(content)) return results;
2476
- const effectiveMax = ext === ".jsx" || ext === ".tsx" ? limits.maxFileLoc * 2 : limits.maxFileLoc;
2477
- if (lineCount > Math.ceil(effectiveMax * 1.1)) results.push({
2525
+ const effectiveMax = ext === ".jsx" || ext === ".tsx" ? Math.ceil(limits.maxFileLoc * JSX_FILE_LOC_MULTIPLIER) : limits.maxFileLoc;
2526
+ if (lineCount > effectiveMax) results.push({
2478
2527
  filePath: relativePath,
2479
2528
  engine: "code-quality",
2480
2529
  rule: "complexity/file-too-large",
@@ -3957,6 +4006,216 @@ const runCargoAudit = async (rootDir, timeout) => {
3957
4006
  }
3958
4007
  };
3959
4008
 
4009
+ //#endregion
4010
+ //#region src/utils/source-masker.ts
4011
+ const JS_EXTS = new Set([
4012
+ ".ts",
4013
+ ".tsx",
4014
+ ".js",
4015
+ ".jsx",
4016
+ ".mjs",
4017
+ ".cjs"
4018
+ ]);
4019
+ const PY_EXTS = new Set([".py"]);
4020
+ const RB_EXTS = new Set([".rb"]);
4021
+ const PHP_EXTS = new Set([".php"]);
4022
+ const familyForExt = (ext) => {
4023
+ if (JS_EXTS.has(ext)) return "js";
4024
+ if (PY_EXTS.has(ext)) return "py";
4025
+ if (RB_EXTS.has(ext)) return "rb";
4026
+ if (PHP_EXTS.has(ext)) return "php";
4027
+ return "none";
4028
+ };
4029
+ const maskStringsAndComments = (content, ext) => {
4030
+ const family = familyForExt(ext);
4031
+ if (family === "none") return content;
4032
+ if (family === "js") return maskJs(content);
4033
+ return maskSimple(content, family);
4034
+ };
4035
+ const maskJs = (content) => {
4036
+ const out = content.split("");
4037
+ const len = content.length;
4038
+ const tplStack = [];
4039
+ let i = 0;
4040
+ const mask = (start, end) => {
4041
+ for (let k = start; k < end; k++) if (out[k] !== "\n") out[k] = " ";
4042
+ };
4043
+ while (i < len) {
4044
+ const c = content[i];
4045
+ const next = content[i + 1];
4046
+ if (tplStack.length > 0) {
4047
+ if (c === "{") {
4048
+ tplStack[tplStack.length - 1]++;
4049
+ i++;
4050
+ continue;
4051
+ }
4052
+ if (c === "}") {
4053
+ if (tplStack[tplStack.length - 1] === 0) {
4054
+ tplStack.pop();
4055
+ const scan = consumeTemplateString(content, i + 1);
4056
+ mask(i + 1, scan.maskEnd);
4057
+ if (scan.openedInterp) tplStack.push(0);
4058
+ i = scan.resumeAt;
4059
+ continue;
4060
+ }
4061
+ tplStack[tplStack.length - 1]--;
4062
+ i++;
4063
+ continue;
4064
+ }
4065
+ if (c === "\"" || c === "'") {
4066
+ const strStart = i;
4067
+ i = consumeQuotedString(content, i, c);
4068
+ mask(strStart + 1, i - 1);
4069
+ continue;
4070
+ }
4071
+ if (c === "`") {
4072
+ const scan = consumeTemplateString(content, i + 1);
4073
+ mask(i + 1, scan.maskEnd);
4074
+ if (scan.openedInterp) tplStack.push(0);
4075
+ i = scan.resumeAt;
4076
+ continue;
4077
+ }
4078
+ if (c === "/" && next === "/") {
4079
+ const strStart = i;
4080
+ while (i < len && content[i] !== "\n") i++;
4081
+ mask(strStart, i);
4082
+ continue;
4083
+ }
4084
+ if (c === "/" && next === "*") {
4085
+ const strStart = i;
4086
+ i += 2;
4087
+ while (i < len - 1 && !(content[i] === "*" && content[i + 1] === "/")) i++;
4088
+ if (i < len - 1) i += 2;
4089
+ mask(strStart, i);
4090
+ continue;
4091
+ }
4092
+ i++;
4093
+ continue;
4094
+ }
4095
+ if (c === "\"" || c === "'") {
4096
+ const strStart = i;
4097
+ i = consumeQuotedString(content, i, c);
4098
+ mask(strStart + 1, i - 1);
4099
+ continue;
4100
+ }
4101
+ if (c === "`") {
4102
+ const scan = consumeTemplateString(content, i + 1);
4103
+ mask(i + 1, scan.maskEnd);
4104
+ if (scan.openedInterp) tplStack.push(0);
4105
+ i = scan.resumeAt;
4106
+ continue;
4107
+ }
4108
+ if (c === "/" && next === "/") {
4109
+ const strStart = i;
4110
+ while (i < len && content[i] !== "\n") i++;
4111
+ mask(strStart, i);
4112
+ continue;
4113
+ }
4114
+ if (c === "/" && next === "*") {
4115
+ const strStart = i;
4116
+ i += 2;
4117
+ while (i < len - 1 && !(content[i] === "*" && content[i + 1] === "/")) i++;
4118
+ if (i < len - 1) i += 2;
4119
+ mask(strStart, i);
4120
+ continue;
4121
+ }
4122
+ i++;
4123
+ }
4124
+ return out.join("");
4125
+ };
4126
+ const consumeQuotedString = (content, start, quote) => {
4127
+ const len = content.length;
4128
+ let i = start + 1;
4129
+ while (i < len) {
4130
+ const c = content[i];
4131
+ if (c === "\\" && i + 1 < len) {
4132
+ i += 2;
4133
+ continue;
4134
+ }
4135
+ if (c === quote) return i + 1;
4136
+ if (c === "\n") return i;
4137
+ i++;
4138
+ }
4139
+ return i;
4140
+ };
4141
+ const consumeTemplateString = (content, start) => {
4142
+ const len = content.length;
4143
+ let i = start;
4144
+ while (i < len) {
4145
+ const c = content[i];
4146
+ if (c === "\\" && i + 1 < len) {
4147
+ i += 2;
4148
+ continue;
4149
+ }
4150
+ if (c === "`") return {
4151
+ maskEnd: i,
4152
+ resumeAt: i + 1,
4153
+ openedInterp: false
4154
+ };
4155
+ if (c === "$" && content[i + 1] === "{") return {
4156
+ maskEnd: i,
4157
+ resumeAt: i + 2,
4158
+ openedInterp: true
4159
+ };
4160
+ i++;
4161
+ }
4162
+ return {
4163
+ maskEnd: i,
4164
+ resumeAt: i,
4165
+ openedInterp: false
4166
+ };
4167
+ };
4168
+ const maskSimple = (content, family) => {
4169
+ const out = content.split("");
4170
+ const len = content.length;
4171
+ let i = 0;
4172
+ const mask = (start, end) => {
4173
+ for (let k = start; k < end; k++) if (out[k] !== "\n") out[k] = " ";
4174
+ };
4175
+ while (i < len) {
4176
+ const c = content[i];
4177
+ const next = content[i + 1];
4178
+ if (family === "py" && (c === "\"" || c === "'")) {
4179
+ if (content[i + 1] === c && content[i + 2] === c) {
4180
+ const triple = c + c + c;
4181
+ const end = content.indexOf(triple, i + 3);
4182
+ const stop = end === -1 ? len : end + 3;
4183
+ mask(i + 3, stop - 3);
4184
+ i = stop;
4185
+ continue;
4186
+ }
4187
+ }
4188
+ if (c === "\"" || c === "'") {
4189
+ const strStart = i;
4190
+ i = consumeQuotedString(content, i, c);
4191
+ mask(strStart + 1, i - 1);
4192
+ continue;
4193
+ }
4194
+ if ((family === "py" || family === "rb" || family === "php") && c === "#") {
4195
+ const strStart = i;
4196
+ while (i < len && content[i] !== "\n") i++;
4197
+ mask(strStart, i);
4198
+ continue;
4199
+ }
4200
+ if (family === "php" && c === "/" && next === "/") {
4201
+ const strStart = i;
4202
+ while (i < len && content[i] !== "\n") i++;
4203
+ mask(strStart, i);
4204
+ continue;
4205
+ }
4206
+ if (family === "php" && c === "/" && next === "*") {
4207
+ const strStart = i;
4208
+ i += 2;
4209
+ while (i < len - 1 && !(content[i] === "*" && content[i + 1] === "/")) i++;
4210
+ if (i < len - 1) i += 2;
4211
+ mask(strStart, i);
4212
+ continue;
4213
+ }
4214
+ i++;
4215
+ }
4216
+ return out.join("");
4217
+ };
4218
+
3960
4219
  //#endregion
3961
4220
  //#region src/engines/security/risky.ts
3962
4221
  const ev = "eval";
@@ -4086,12 +4345,13 @@ const detectRiskyConstructs = async (context) => {
4086
4345
  const relativePath = path.relative(context.rootDirectory, filePath);
4087
4346
  const normalizedPath = relativePath.split(path.sep).join("/");
4088
4347
  const isMigrationOrSeeder = /(?:^|\/)(migrations|seeders|seeds|migrate)\//.test(normalizedPath);
4348
+ const masked = maskStringsAndComments(content, ext);
4089
4349
  for (const { pattern, extensions, name, message, help } of RISKY_PATTERNS) {
4090
4350
  if (!extensions.includes(ext)) continue;
4091
4351
  if (isMigrationOrSeeder && name === "sql-injection") continue;
4092
4352
  const regex = new RegExp(pattern.source, pattern.flags);
4093
4353
  let match;
4094
- while ((match = regex.exec(content)) !== null) {
4354
+ while ((match = regex.exec(masked)) !== null) {
4095
4355
  const line = content.slice(0, match.index).split("\n").length;
4096
4356
  if (name === "innerhtml") {
4097
4357
  const beforeMatch = content.slice(Math.max(0, match.index - 200), match.index);
@@ -4733,7 +4993,7 @@ const renderCleanRun = (input, deps = {}) => {
4733
4993
  //#region src/utils/git.ts
4734
4994
  const MAX_BUFFER = 50 * 1024 * 1024;
4735
4995
  const getChangedFiles = (cwd, base) => {
4736
- const result = spawnSync("git", [
4996
+ const diff = spawnSync("git", [
4737
4997
  "diff",
4738
4998
  "--name-only",
4739
4999
  "--diff-filter=ACMR",
@@ -4743,8 +5003,22 @@ const getChangedFiles = (cwd, base) => {
4743
5003
  encoding: "utf-8",
4744
5004
  maxBuffer: MAX_BUFFER
4745
5005
  });
4746
- if (result.error || result.status !== 0) return [];
4747
- return result.stdout.split("\n").filter((f) => f.length > 0).map((f) => path.resolve(cwd, f));
5006
+ if (diff.error || diff.status !== 0) return [];
5007
+ const untracked = spawnSync("git", [
5008
+ "ls-files",
5009
+ "--others",
5010
+ "--exclude-standard"
5011
+ ], {
5012
+ cwd,
5013
+ encoding: "utf-8",
5014
+ maxBuffer: MAX_BUFFER
5015
+ });
5016
+ const names = /* @__PURE__ */ new Set();
5017
+ for (const line of diff.stdout.split("\n")) if (line.length > 0) names.add(line);
5018
+ if (!untracked.error && untracked.status === 0) {
5019
+ for (const line of untracked.stdout.split("\n")) if (line.length > 0) names.add(line);
5020
+ }
5021
+ return Array.from(names).map((f) => path.resolve(cwd, f));
4748
5022
  };
4749
5023
  const getStagedFiles = (cwd) => {
4750
5024
  const result = spawnSync("git", [
@@ -4915,7 +5189,7 @@ const scanCommand = async (directory, config, options) => {
4915
5189
  });
4916
5190
  }
4917
5191
  if (options.json) {
4918
- const { buildJsonOutput } = await import("./json-BScQXSOX.js");
5192
+ const { buildJsonOutput } = await import("./json-DcE9soYJ.js");
4919
5193
  const jsonOut = buildJsonOutput(results, scoreResult, projectInfo.sourceFileCount, elapsedMs);
4920
5194
  console.log(JSON.stringify(jsonOut, null, 2));
4921
5195
  return { exitCode };
@@ -5686,42 +5960,46 @@ const removeUnusedDeclarations = (rootDirectory, declarations) => {
5686
5960
 
5687
5961
  //#endregion
5688
5962
  //#region src/commands/fix-force.ts
5689
- const getJsAuditFixCommand = (rootDirectory) => {
5690
- if (fs.existsSync(path.join(rootDirectory, "pnpm-lock.yaml"))) return {
5691
- command: "pnpm",
5692
- args: ["audit", "--fix"]
5693
- };
5694
- if (fs.existsSync(path.join(rootDirectory, "package-lock.json")) || fs.existsSync(path.join(rootDirectory, "package.json"))) return {
5695
- command: "npm",
5696
- args: ["audit", "fix"]
5697
- };
5963
+ const INSTALL_TIMEOUT = 1800 * 1e3;
5964
+ const AUDIT_TIMEOUT = 60 * 1e3;
5965
+ const detectPackageManager = (rootDirectory) => {
5966
+ if (fs.existsSync(path.join(rootDirectory, "pnpm-lock.yaml"))) return "pnpm";
5967
+ if (fs.existsSync(path.join(rootDirectory, "package-lock.json")) || fs.existsSync(path.join(rootDirectory, "package.json"))) return "npm";
5698
5968
  return null;
5699
5969
  };
5700
- const INSTALL_TIMEOUT = 1800 * 1e3;
5701
5970
  const fixDependencyAudit = async (context, onProgress) => {
5702
- const auditFix = getJsAuditFixCommand(context.rootDirectory);
5703
- if (!auditFix) return;
5704
- onProgress?.(`Dependency audit fixes · running ${auditFix.command} audit fix (can take a few minutes)`);
5705
- const result = await runSubprocess(auditFix.command, auditFix.args, {
5706
- cwd: context.rootDirectory,
5971
+ const pm = detectPackageManager(context.rootDirectory);
5972
+ if (!pm) return;
5973
+ if (pm === "npm") {
5974
+ await runNpmAuditFix(context.rootDirectory, onProgress);
5975
+ await tryNpmOverrides(context.rootDirectory, onProgress);
5976
+ return;
5977
+ }
5978
+ if (await tryPnpmOverrides(context.rootDirectory, onProgress)) return;
5979
+ if (fs.existsSync(path.join(context.rootDirectory, "package-lock.json"))) {
5980
+ await runNpmAuditFix(context.rootDirectory, onProgress);
5981
+ await tryNpmOverrides(context.rootDirectory, onProgress);
5982
+ return;
5983
+ }
5984
+ onProgress?.("Dependency audit fixes · skipping (pnpm audit unavailable and no package-lock.json for npm fallback)");
5985
+ };
5986
+ const runNpmAuditFix = async (rootDir, onProgress) => {
5987
+ onProgress?.("Dependency audit fixes · running npm audit fix (can take a few minutes)");
5988
+ const result = await runSubprocess("npm", ["audit", "fix"], {
5989
+ cwd: rootDir,
5707
5990
  timeout: INSTALL_TIMEOUT
5708
5991
  });
5709
- if (result.exitCode !== 0 && !result.stdout && !result.stderr) throw new Error(`${auditFix.command} audit fix failed`);
5710
- onProgress?.(`Dependency audit fixes · running ${auditFix.command} install`);
5711
- const installResult = await runSubprocess(auditFix.command, ["install"], {
5712
- cwd: context.rootDirectory,
5992
+ if (result.exitCode !== 0 && !result.stdout && !result.stderr) throw new Error("npm audit fix failed");
5993
+ onProgress?.("Dependency audit fixes · running npm install");
5994
+ const installResult = await runSubprocess("npm", ["install"], {
5995
+ cwd: rootDir,
5713
5996
  timeout: INSTALL_TIMEOUT
5714
5997
  });
5715
- if (installResult.exitCode !== 0) throw new Error(installResult.stderr || installResult.stdout || `${auditFix.command} install failed after audit fix`);
5716
- if (auditFix.command === "npm") await tryNpmOverrides(context.rootDirectory, onProgress);
5998
+ if (installResult.exitCode !== 0) throw new Error(installResult.stderr || installResult.stdout || "npm install failed after audit fix");
5717
5999
  };
5718
- /**
5719
- * For unresolvable transitive vulnerabilities, attempt to add npm overrides
5720
- * in package.json. This forces a newer version of the vulnerable transitive dep.
5721
- */
5722
- const fetchLatestVersion = async (rootDir, pkgName) => {
6000
+ const fetchLatestVersion = async (rootDir, pkgName, pm) => {
5723
6001
  try {
5724
- const result = await runSubprocess("npm", [
6002
+ const result = await runSubprocess(pm, [
5725
6003
  "view",
5726
6004
  pkgName,
5727
6005
  "version",
@@ -5735,11 +6013,11 @@ const fetchLatestVersion = async (rootDir, pkgName) => {
5735
6013
  return null;
5736
6014
  }
5737
6015
  };
5738
- const collectOverrides = async (rootDir, vulnerabilities) => {
6016
+ const collectOverrides = async (rootDir, vulnerabilities, pm) => {
5739
6017
  const overrides = {};
5740
6018
  for (const [pkgName, vuln] of Object.entries(vulnerabilities)) {
5741
6019
  if (vuln.fixAvailable !== false || !vuln.range) continue;
5742
- const latest = await fetchLatestVersion(rootDir, pkgName);
6020
+ const latest = await fetchLatestVersion(rootDir, pkgName, pm);
5743
6021
  if (latest) overrides[pkgName] = latest;
5744
6022
  }
5745
6023
  return overrides;
@@ -5748,12 +6026,12 @@ const tryNpmOverrides = async (rootDir, onProgress) => {
5748
6026
  try {
5749
6027
  const auditResult = await runSubprocess("npm", ["audit", "--json"], {
5750
6028
  cwd: rootDir,
5751
- timeout: 3e4
6029
+ timeout: AUDIT_TIMEOUT
5752
6030
  });
5753
6031
  if (!auditResult.stdout) return;
5754
6032
  const vulnerabilities = JSON.parse(auditResult.stdout).vulnerabilities;
5755
6033
  if (!vulnerabilities) return;
5756
- const overrides = await collectOverrides(rootDir, vulnerabilities);
6034
+ const overrides = await collectOverrides(rootDir, vulnerabilities, "npm");
5757
6035
  if (Object.keys(overrides).length === 0) return;
5758
6036
  const pkgPath = path.join(rootDir, "package.json");
5759
6037
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
@@ -5761,7 +6039,7 @@ const tryNpmOverrides = async (rootDir, onProgress) => {
5761
6039
  ...pkg.overrides || {},
5762
6040
  ...overrides
5763
6041
  };
5764
- fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
6042
+ fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
5765
6043
  onProgress?.("Dependency audit fixes · applying npm overrides (npm install)");
5766
6044
  await runSubprocess("npm", ["install"], {
5767
6045
  cwd: rootDir,
@@ -5769,6 +6047,70 @@ const tryNpmOverrides = async (rootDir, onProgress) => {
5769
6047
  });
5770
6048
  } catch {}
5771
6049
  };
6050
+ const patchedRangeToVersion = (patched) => {
6051
+ const match = patched.match(/^\s*>=?\s*([0-9]+\.[0-9]+\.[0-9]+[^\s]*)/);
6052
+ return match ? `^${match[1]}` : null;
6053
+ };
6054
+ const overrideKey = (name, vulnerable, patched) => {
6055
+ if (vulnerable && vulnerable.trim().length > 0 && !/^\*$/.test(vulnerable.trim())) return `${name}@${vulnerable.trim()}`;
6056
+ const first = patched.match(/([0-9]+\.[0-9]+\.[0-9]+)/)?.[1];
6057
+ return first ? `${name}@<${first}` : name;
6058
+ };
6059
+ const collectPnpmOverrides = (advisories) => {
6060
+ const overrides = {};
6061
+ for (const adv of Object.values(advisories)) {
6062
+ if (!adv.module_name || !adv.patched_versions) continue;
6063
+ const target = patchedRangeToVersion(adv.patched_versions);
6064
+ if (!target) continue;
6065
+ const key = overrideKey(adv.module_name, adv.vulnerable_versions, adv.patched_versions);
6066
+ overrides[key] = target;
6067
+ }
6068
+ return overrides;
6069
+ };
6070
+ const isPnpmAuditRetired = (stdout, stderr) => {
6071
+ const haystack = `${stdout}\n${stderr}`.toLowerCase();
6072
+ return haystack.includes("410") || haystack.includes("gone") || haystack.includes("retired") || haystack.includes("endpoint") || haystack.includes("err_pnpm_audit") || haystack.includes("audit endpoint");
6073
+ };
6074
+ const tryPnpmOverrides = async (rootDir, onProgress) => {
6075
+ onProgress?.("Dependency audit fixes · running pnpm audit");
6076
+ const auditResult = await runSubprocess("pnpm", ["audit", "--json"], {
6077
+ cwd: rootDir,
6078
+ timeout: AUDIT_TIMEOUT
6079
+ });
6080
+ if (!auditResult.stdout) {
6081
+ if (isPnpmAuditRetired(auditResult.stdout ?? "", auditResult.stderr ?? "")) return false;
6082
+ return auditResult.exitCode === 0;
6083
+ }
6084
+ let parsed;
6085
+ try {
6086
+ parsed = JSON.parse(auditResult.stdout);
6087
+ } catch {
6088
+ if (auditResult.exitCode !== 0 || isPnpmAuditRetired(auditResult.stdout, auditResult.stderr ?? "")) return false;
6089
+ return true;
6090
+ }
6091
+ const advisories = parsed.advisories;
6092
+ if (!advisories || Object.keys(advisories).length === 0) return true;
6093
+ const overrides = collectPnpmOverrides(advisories);
6094
+ if (Object.keys(overrides).length === 0) return true;
6095
+ const pkgPath = path.join(rootDir, "package.json");
6096
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
6097
+ const pnpmBlock = pkg.pnpm ?? {};
6098
+ const existing = pnpmBlock.overrides ?? {};
6099
+ pkg.pnpm = {
6100
+ ...pnpmBlock,
6101
+ overrides: {
6102
+ ...existing,
6103
+ ...overrides
6104
+ }
6105
+ };
6106
+ fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
6107
+ onProgress?.("Dependency audit fixes · applying pnpm overrides (pnpm install)");
6108
+ await runSubprocess("pnpm", ["install"], {
6109
+ cwd: rootDir,
6110
+ timeout: INSTALL_TIMEOUT
6111
+ });
6112
+ return true;
6113
+ };
5772
6114
  const fixExpoDependencies = async (context, onProgress) => {
5773
6115
  await removeDisallowedExpoPackages(context.rootDirectory, onProgress);
5774
6116
  onProgress?.("Expo dependency alignment · running expo install --fix (can take a few minutes)");
@@ -5883,8 +6225,9 @@ const runFormattingStep = async (deps) => {
5883
6225
  const runForceSteps = async (deps) => {
5884
6226
  if (!deps.force) return;
5885
6227
  if (deps.config.engines["code-quality"] && hasJsOrTs(deps.projectInfo)) await deps.runStep("Remove unused files", () => runKnipUnusedFiles(deps.resolvedDir), () => fixUnusedFiles(deps.resolvedDir));
5886
- if (deps.config.engines.security) await deps.runStep("Dependency audit fixes", () => runDependencyAudit(deps.context), () => fixDependencyAudit(deps.context, deps.rail.setActiveLabel));
5887
- if (deps.projectInfo.frameworks.includes("expo")) await deps.runStep("Expo dependency alignment", () => runExpoDoctor(deps.context), () => fixExpoDependencies(deps.context, deps.rail.setActiveLabel));
6228
+ const railUpdate = (label) => deps.rail.setActiveLabel(label);
6229
+ if (deps.config.engines.security) await deps.runStep("Dependency audit fixes", () => runDependencyAudit(deps.context), () => fixDependencyAudit(deps.context, railUpdate));
6230
+ if (deps.projectInfo.frameworks.includes("expo")) await deps.runStep("Expo dependency alignment", () => runExpoDoctor(deps.context), () => fixExpoDependencies(deps.context, railUpdate));
5888
6231
  };
5889
6232
 
5890
6233
  //#endregion