aislop 0.5.0 → 0.5.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/dist/cli.js +744 -127
- package/dist/index.js +396 -128
- package/dist/{json-BScQXSOX.js → json-D_i2_5_-.js} +1 -1
- package/dist/{version-CxBRws3M.js → version-CIlgPf8Q.js} +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as ENGINE_INFO, r as getEngineLabel, t as APP_VERSION } from "./version-
|
|
1
|
+
import { n as ENGINE_INFO, r as getEngineLabel, t as APP_VERSION } from "./version-CIlgPf8Q.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;
|
|
@@ -1627,6 +1627,19 @@ const SUPPORTED_EXTS = new Set([
|
|
|
1627
1627
|
".java",
|
|
1628
1628
|
".php"
|
|
1629
1629
|
]);
|
|
1630
|
+
const DECL_START = /^(\s*)(export\s+)?(async\s+)?(const|let|var|function|class|type|interface|enum|abstract\s+class)\s+/;
|
|
1631
|
+
const EXPORT_DEFAULT = /^\s*export\s+default\b/;
|
|
1632
|
+
const TS_MEMBER_DECL_START = /^\s*(?:readonly\s+|static\s+|public\s+|private\s+|protected\s+|abstract\s+|override\s+)*[\w$]+\??\s*:/;
|
|
1633
|
+
const PY_DECL_START = /^\s*(async\s+def|def|class)\s+/;
|
|
1634
|
+
const GO_DECL_START = /^\s*(func|type|var|const)\s+/;
|
|
1635
|
+
const RUST_DECL_START = /^\s*(pub\s+)?(async\s+)?(fn|struct|enum|trait|impl|const|static|type|mod)\s+/;
|
|
1636
|
+
const RUBY_DECL_START = /^\s*(class|module|def)\s+/;
|
|
1637
|
+
const JAVA_DECL_START = /^\s*(?:public|private|protected|static|final|abstract|sealed|non-sealed|\s)+(?:class|interface|enum|record|@interface|\w[^(){};=]*\s+\w+\s*\()/;
|
|
1638
|
+
const JAVA_DECL_START_FALLBACK = /^\s*(class|interface|enum|record|@interface)\s+/;
|
|
1639
|
+
const PHP_DECL_START = /^\s*(?:public|private|protected|static|final|abstract|readonly\s+)*(function|class|interface|trait|enum|const)\s+/;
|
|
1640
|
+
|
|
1641
|
+
//#endregion
|
|
1642
|
+
//#region src/engines/ai-slop/narrative-comments.ts
|
|
1630
1643
|
const stripJsdocLine = (line) => line.replace(/^\s*\/\*\*+\s?/, "").replace(/\s*\*+\/\s*$/, "").replace(/^\s*\*\s?/, "").trim();
|
|
1631
1644
|
const stripLineComment = (line) => line.replace(/^\s*(?:(?:\/\/)|#)\s?/, "");
|
|
1632
1645
|
const getCommentSyntax = (ext) => {
|
|
@@ -1718,16 +1731,6 @@ const collectBlocks = (sourceLines, syntax) => {
|
|
|
1718
1731
|
}
|
|
1719
1732
|
return blocks;
|
|
1720
1733
|
};
|
|
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
1734
|
const looksLikeDeclarationPreamble = (nextLine, ext) => {
|
|
1732
1735
|
if (nextLine === null) return false;
|
|
1733
1736
|
if (DECL_START.test(nextLine) || EXPORT_DEFAULT.test(nextLine)) return true;
|
|
@@ -2256,72 +2259,12 @@ const architectureEngine = {
|
|
|
2256
2259
|
};
|
|
2257
2260
|
|
|
2258
2261
|
//#endregion
|
|
2259
|
-
//#region src/engines/code-quality/
|
|
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
|
-
};
|
|
2262
|
+
//#region src/engines/code-quality/function-boundaries.ts
|
|
2320
2263
|
const PYTHON_CONTROL_FLOW_RE = /^\s*(?:if|for|while|with|try|except|else|elif|finally|def|class)\b/;
|
|
2321
|
-
const
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2264
|
+
const ARROW_BLOCK_RE = /* @__PURE__ */ new RegExp("=>\\s*\\{");
|
|
2265
|
+
const ARROW_END_RE = /* @__PURE__ */ new RegExp("=>\\s*$");
|
|
2266
|
+
const BRACE_START_RE = /* @__PURE__ */ new RegExp("^\\s*\\{");
|
|
2267
|
+
const NEW_STATEMENT_RE = /* @__PURE__ */ new RegExp("^(?:export\\s+)?(?:const|let|var|function|class)\\s");
|
|
2325
2268
|
const isControlFlowBrace = (lineText, braceIndex) => {
|
|
2326
2269
|
const before = lineText.substring(0, braceIndex).trimEnd();
|
|
2327
2270
|
if (before.endsWith(")")) return true;
|
|
@@ -2401,16 +2344,10 @@ const findPythonFunctionEnd = (lines, startIndex) => {
|
|
|
2401
2344
|
maxNesting
|
|
2402
2345
|
};
|
|
2403
2346
|
};
|
|
2404
|
-
const
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
const dataLinePattern = /^\s*[{}[\]"']/;
|
|
2408
|
-
return nonEmpty.filter((l) => dataLinePattern.test(l)).length / nonEmpty.length > .8;
|
|
2347
|
+
const findFunctionEnd = (lines, startIndex, isPython) => {
|
|
2348
|
+
if (isPython) return findPythonFunctionEnd(lines, startIndex);
|
|
2349
|
+
return findBraceFunctionEnd(lines, startIndex);
|
|
2409
2350
|
};
|
|
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
2351
|
const isBlockArrow = (lines, startIndex) => {
|
|
2415
2352
|
if (ARROW_BLOCK_RE.test(lines[startIndex])) return true;
|
|
2416
2353
|
if (ARROW_END_RE.test(lines[startIndex])) {
|
|
@@ -2446,6 +2383,75 @@ const countTemplateLines = (bodyLines) => {
|
|
|
2446
2383
|
}
|
|
2447
2384
|
return templateLineCount;
|
|
2448
2385
|
};
|
|
2386
|
+
|
|
2387
|
+
//#endregion
|
|
2388
|
+
//#region src/engines/code-quality/complexity.ts
|
|
2389
|
+
const FUNCTION_PATTERNS = [
|
|
2390
|
+
{
|
|
2391
|
+
regex: /^\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/,
|
|
2392
|
+
langFilter: [
|
|
2393
|
+
".js",
|
|
2394
|
+
".ts",
|
|
2395
|
+
".jsx",
|
|
2396
|
+
".tsx",
|
|
2397
|
+
".mjs",
|
|
2398
|
+
".cjs"
|
|
2399
|
+
]
|
|
2400
|
+
},
|
|
2401
|
+
{
|
|
2402
|
+
regex: /^\s*(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s+)?\(([^)]*)\)\s*(?:=>|:\s*\w)/,
|
|
2403
|
+
langFilter: [
|
|
2404
|
+
".js",
|
|
2405
|
+
".ts",
|
|
2406
|
+
".jsx",
|
|
2407
|
+
".tsx",
|
|
2408
|
+
".mjs",
|
|
2409
|
+
".cjs"
|
|
2410
|
+
]
|
|
2411
|
+
},
|
|
2412
|
+
{
|
|
2413
|
+
regex: /^\s*def\s+(\w+)\s*\(([^)]*)\)/,
|
|
2414
|
+
langFilter: [".py"]
|
|
2415
|
+
},
|
|
2416
|
+
{
|
|
2417
|
+
regex: /^\s*func\s+(?:\([^)]*\)\s+)?(\w+)\s*\(([^)]*)\)/,
|
|
2418
|
+
langFilter: [".go"]
|
|
2419
|
+
},
|
|
2420
|
+
{
|
|
2421
|
+
regex: /^\s*fn\s+(\w+)\s*\(([^)]*)\)/,
|
|
2422
|
+
langFilter: [".rs"]
|
|
2423
|
+
},
|
|
2424
|
+
{
|
|
2425
|
+
regex: /^\s*(?:public|private|protected)?\s*(?:static\s+)?(?:\w+\s+)(\w+)\s*\(([^)]*)\)/,
|
|
2426
|
+
langFilter: [
|
|
2427
|
+
".java",
|
|
2428
|
+
".cs",
|
|
2429
|
+
".cpp",
|
|
2430
|
+
".c",
|
|
2431
|
+
".php"
|
|
2432
|
+
]
|
|
2433
|
+
}
|
|
2434
|
+
];
|
|
2435
|
+
const countParams = (p) => p.trim() ? p.split(",").length : 0;
|
|
2436
|
+
const matchFunctionOnLine = (line, ext) => {
|
|
2437
|
+
for (let i = 0; i < FUNCTION_PATTERNS.length; i++) {
|
|
2438
|
+
const pattern = FUNCTION_PATTERNS[i];
|
|
2439
|
+
if (!pattern.langFilter.includes(ext)) continue;
|
|
2440
|
+
const match = line.match(pattern.regex);
|
|
2441
|
+
if (match) return {
|
|
2442
|
+
name: match[1],
|
|
2443
|
+
params: match[2] ?? "",
|
|
2444
|
+
patternIndex: i
|
|
2445
|
+
};
|
|
2446
|
+
}
|
|
2447
|
+
return null;
|
|
2448
|
+
};
|
|
2449
|
+
const isDataFile = (content) => {
|
|
2450
|
+
const nonEmpty = content.split("\n").filter((l) => l.trim().length > 0);
|
|
2451
|
+
if (nonEmpty.length === 0) return false;
|
|
2452
|
+
const dataLinePattern = /^\s*[{}[\]"']/;
|
|
2453
|
+
return nonEmpty.filter((l) => dataLinePattern.test(l)).length / nonEmpty.length > .8;
|
|
2454
|
+
};
|
|
2449
2455
|
const analyzeFunctions = (content, ext) => {
|
|
2450
2456
|
const lines = content.split("\n");
|
|
2451
2457
|
const functions = [];
|
|
@@ -2468,13 +2474,14 @@ const analyzeFunctions = (content, ext) => {
|
|
|
2468
2474
|
}
|
|
2469
2475
|
return functions;
|
|
2470
2476
|
};
|
|
2477
|
+
const JSX_FILE_LOC_MULTIPLIER = 1.5;
|
|
2471
2478
|
const checkFileDiagnostics = (relativePath, content, limits) => {
|
|
2472
2479
|
const results = [];
|
|
2473
2480
|
const lineCount = content.split("\n").length;
|
|
2474
2481
|
const ext = path.extname(relativePath).toLowerCase();
|
|
2475
2482
|
if (isDataFile(content)) return results;
|
|
2476
|
-
const effectiveMax = ext === ".jsx" || ext === ".tsx" ? limits.maxFileLoc *
|
|
2477
|
-
if (lineCount >
|
|
2483
|
+
const effectiveMax = ext === ".jsx" || ext === ".tsx" ? Math.ceil(limits.maxFileLoc * JSX_FILE_LOC_MULTIPLIER) : limits.maxFileLoc;
|
|
2484
|
+
if (lineCount > effectiveMax) results.push({
|
|
2478
2485
|
filePath: relativePath,
|
|
2479
2486
|
engine: "code-quality",
|
|
2480
2487
|
rule: "complexity/file-too-large",
|
|
@@ -3957,6 +3964,216 @@ const runCargoAudit = async (rootDir, timeout) => {
|
|
|
3957
3964
|
}
|
|
3958
3965
|
};
|
|
3959
3966
|
|
|
3967
|
+
//#endregion
|
|
3968
|
+
//#region src/utils/source-masker.ts
|
|
3969
|
+
const JS_EXTS = new Set([
|
|
3970
|
+
".ts",
|
|
3971
|
+
".tsx",
|
|
3972
|
+
".js",
|
|
3973
|
+
".jsx",
|
|
3974
|
+
".mjs",
|
|
3975
|
+
".cjs"
|
|
3976
|
+
]);
|
|
3977
|
+
const PY_EXTS = new Set([".py"]);
|
|
3978
|
+
const RB_EXTS = new Set([".rb"]);
|
|
3979
|
+
const PHP_EXTS = new Set([".php"]);
|
|
3980
|
+
const familyForExt = (ext) => {
|
|
3981
|
+
if (JS_EXTS.has(ext)) return "js";
|
|
3982
|
+
if (PY_EXTS.has(ext)) return "py";
|
|
3983
|
+
if (RB_EXTS.has(ext)) return "rb";
|
|
3984
|
+
if (PHP_EXTS.has(ext)) return "php";
|
|
3985
|
+
return "none";
|
|
3986
|
+
};
|
|
3987
|
+
const maskStringsAndComments = (content, ext) => {
|
|
3988
|
+
const family = familyForExt(ext);
|
|
3989
|
+
if (family === "none") return content;
|
|
3990
|
+
if (family === "js") return maskJs(content);
|
|
3991
|
+
return maskSimple(content, family);
|
|
3992
|
+
};
|
|
3993
|
+
const maskJs = (content) => {
|
|
3994
|
+
const out = content.split("");
|
|
3995
|
+
const len = content.length;
|
|
3996
|
+
const tplStack = [];
|
|
3997
|
+
let i = 0;
|
|
3998
|
+
const mask = (start, end) => {
|
|
3999
|
+
for (let k = start; k < end; k++) if (out[k] !== "\n") out[k] = " ";
|
|
4000
|
+
};
|
|
4001
|
+
while (i < len) {
|
|
4002
|
+
const c = content[i];
|
|
4003
|
+
const next = content[i + 1];
|
|
4004
|
+
if (tplStack.length > 0) {
|
|
4005
|
+
if (c === "{") {
|
|
4006
|
+
tplStack[tplStack.length - 1]++;
|
|
4007
|
+
i++;
|
|
4008
|
+
continue;
|
|
4009
|
+
}
|
|
4010
|
+
if (c === "}") {
|
|
4011
|
+
if (tplStack[tplStack.length - 1] === 0) {
|
|
4012
|
+
tplStack.pop();
|
|
4013
|
+
const scan = consumeTemplateString(content, i + 1);
|
|
4014
|
+
mask(i + 1, scan.maskEnd);
|
|
4015
|
+
if (scan.openedInterp) tplStack.push(0);
|
|
4016
|
+
i = scan.resumeAt;
|
|
4017
|
+
continue;
|
|
4018
|
+
}
|
|
4019
|
+
tplStack[tplStack.length - 1]--;
|
|
4020
|
+
i++;
|
|
4021
|
+
continue;
|
|
4022
|
+
}
|
|
4023
|
+
if (c === "\"" || c === "'") {
|
|
4024
|
+
const strStart = i;
|
|
4025
|
+
i = consumeQuotedString(content, i, c);
|
|
4026
|
+
mask(strStart + 1, i - 1);
|
|
4027
|
+
continue;
|
|
4028
|
+
}
|
|
4029
|
+
if (c === "`") {
|
|
4030
|
+
const scan = consumeTemplateString(content, i + 1);
|
|
4031
|
+
mask(i + 1, scan.maskEnd);
|
|
4032
|
+
if (scan.openedInterp) tplStack.push(0);
|
|
4033
|
+
i = scan.resumeAt;
|
|
4034
|
+
continue;
|
|
4035
|
+
}
|
|
4036
|
+
if (c === "/" && next === "/") {
|
|
4037
|
+
const strStart = i;
|
|
4038
|
+
while (i < len && content[i] !== "\n") i++;
|
|
4039
|
+
mask(strStart, i);
|
|
4040
|
+
continue;
|
|
4041
|
+
}
|
|
4042
|
+
if (c === "/" && next === "*") {
|
|
4043
|
+
const strStart = i;
|
|
4044
|
+
i += 2;
|
|
4045
|
+
while (i < len - 1 && !(content[i] === "*" && content[i + 1] === "/")) i++;
|
|
4046
|
+
if (i < len - 1) i += 2;
|
|
4047
|
+
mask(strStart, i);
|
|
4048
|
+
continue;
|
|
4049
|
+
}
|
|
4050
|
+
i++;
|
|
4051
|
+
continue;
|
|
4052
|
+
}
|
|
4053
|
+
if (c === "\"" || c === "'") {
|
|
4054
|
+
const strStart = i;
|
|
4055
|
+
i = consumeQuotedString(content, i, c);
|
|
4056
|
+
mask(strStart + 1, i - 1);
|
|
4057
|
+
continue;
|
|
4058
|
+
}
|
|
4059
|
+
if (c === "`") {
|
|
4060
|
+
const scan = consumeTemplateString(content, i + 1);
|
|
4061
|
+
mask(i + 1, scan.maskEnd);
|
|
4062
|
+
if (scan.openedInterp) tplStack.push(0);
|
|
4063
|
+
i = scan.resumeAt;
|
|
4064
|
+
continue;
|
|
4065
|
+
}
|
|
4066
|
+
if (c === "/" && next === "/") {
|
|
4067
|
+
const strStart = i;
|
|
4068
|
+
while (i < len && content[i] !== "\n") i++;
|
|
4069
|
+
mask(strStart, i);
|
|
4070
|
+
continue;
|
|
4071
|
+
}
|
|
4072
|
+
if (c === "/" && next === "*") {
|
|
4073
|
+
const strStart = i;
|
|
4074
|
+
i += 2;
|
|
4075
|
+
while (i < len - 1 && !(content[i] === "*" && content[i + 1] === "/")) i++;
|
|
4076
|
+
if (i < len - 1) i += 2;
|
|
4077
|
+
mask(strStart, i);
|
|
4078
|
+
continue;
|
|
4079
|
+
}
|
|
4080
|
+
i++;
|
|
4081
|
+
}
|
|
4082
|
+
return out.join("");
|
|
4083
|
+
};
|
|
4084
|
+
const consumeQuotedString = (content, start, quote) => {
|
|
4085
|
+
const len = content.length;
|
|
4086
|
+
let i = start + 1;
|
|
4087
|
+
while (i < len) {
|
|
4088
|
+
const c = content[i];
|
|
4089
|
+
if (c === "\\" && i + 1 < len) {
|
|
4090
|
+
i += 2;
|
|
4091
|
+
continue;
|
|
4092
|
+
}
|
|
4093
|
+
if (c === quote) return i + 1;
|
|
4094
|
+
if (c === "\n") return i;
|
|
4095
|
+
i++;
|
|
4096
|
+
}
|
|
4097
|
+
return i;
|
|
4098
|
+
};
|
|
4099
|
+
const consumeTemplateString = (content, start) => {
|
|
4100
|
+
const len = content.length;
|
|
4101
|
+
let i = start;
|
|
4102
|
+
while (i < len) {
|
|
4103
|
+
const c = content[i];
|
|
4104
|
+
if (c === "\\" && i + 1 < len) {
|
|
4105
|
+
i += 2;
|
|
4106
|
+
continue;
|
|
4107
|
+
}
|
|
4108
|
+
if (c === "`") return {
|
|
4109
|
+
maskEnd: i,
|
|
4110
|
+
resumeAt: i + 1,
|
|
4111
|
+
openedInterp: false
|
|
4112
|
+
};
|
|
4113
|
+
if (c === "$" && content[i + 1] === "{") return {
|
|
4114
|
+
maskEnd: i,
|
|
4115
|
+
resumeAt: i + 2,
|
|
4116
|
+
openedInterp: true
|
|
4117
|
+
};
|
|
4118
|
+
i++;
|
|
4119
|
+
}
|
|
4120
|
+
return {
|
|
4121
|
+
maskEnd: i,
|
|
4122
|
+
resumeAt: i,
|
|
4123
|
+
openedInterp: false
|
|
4124
|
+
};
|
|
4125
|
+
};
|
|
4126
|
+
const maskSimple = (content, family) => {
|
|
4127
|
+
const out = content.split("");
|
|
4128
|
+
const len = content.length;
|
|
4129
|
+
let i = 0;
|
|
4130
|
+
const mask = (start, end) => {
|
|
4131
|
+
for (let k = start; k < end; k++) if (out[k] !== "\n") out[k] = " ";
|
|
4132
|
+
};
|
|
4133
|
+
while (i < len) {
|
|
4134
|
+
const c = content[i];
|
|
4135
|
+
const next = content[i + 1];
|
|
4136
|
+
if (family === "py" && (c === "\"" || c === "'")) {
|
|
4137
|
+
if (content[i + 1] === c && content[i + 2] === c) {
|
|
4138
|
+
const triple = c + c + c;
|
|
4139
|
+
const end = content.indexOf(triple, i + 3);
|
|
4140
|
+
const stop = end === -1 ? len : end + 3;
|
|
4141
|
+
mask(i + 3, stop - 3);
|
|
4142
|
+
i = stop;
|
|
4143
|
+
continue;
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
if (c === "\"" || c === "'") {
|
|
4147
|
+
const strStart = i;
|
|
4148
|
+
i = consumeQuotedString(content, i, c);
|
|
4149
|
+
mask(strStart + 1, i - 1);
|
|
4150
|
+
continue;
|
|
4151
|
+
}
|
|
4152
|
+
if ((family === "py" || family === "rb" || family === "php") && c === "#") {
|
|
4153
|
+
const strStart = i;
|
|
4154
|
+
while (i < len && content[i] !== "\n") i++;
|
|
4155
|
+
mask(strStart, i);
|
|
4156
|
+
continue;
|
|
4157
|
+
}
|
|
4158
|
+
if (family === "php" && c === "/" && next === "/") {
|
|
4159
|
+
const strStart = i;
|
|
4160
|
+
while (i < len && content[i] !== "\n") i++;
|
|
4161
|
+
mask(strStart, i);
|
|
4162
|
+
continue;
|
|
4163
|
+
}
|
|
4164
|
+
if (family === "php" && c === "/" && next === "*") {
|
|
4165
|
+
const strStart = i;
|
|
4166
|
+
i += 2;
|
|
4167
|
+
while (i < len - 1 && !(content[i] === "*" && content[i + 1] === "/")) i++;
|
|
4168
|
+
if (i < len - 1) i += 2;
|
|
4169
|
+
mask(strStart, i);
|
|
4170
|
+
continue;
|
|
4171
|
+
}
|
|
4172
|
+
i++;
|
|
4173
|
+
}
|
|
4174
|
+
return out.join("");
|
|
4175
|
+
};
|
|
4176
|
+
|
|
3960
4177
|
//#endregion
|
|
3961
4178
|
//#region src/engines/security/risky.ts
|
|
3962
4179
|
const ev = "eval";
|
|
@@ -4086,12 +4303,13 @@ const detectRiskyConstructs = async (context) => {
|
|
|
4086
4303
|
const relativePath = path.relative(context.rootDirectory, filePath);
|
|
4087
4304
|
const normalizedPath = relativePath.split(path.sep).join("/");
|
|
4088
4305
|
const isMigrationOrSeeder = /(?:^|\/)(migrations|seeders|seeds|migrate)\//.test(normalizedPath);
|
|
4306
|
+
const masked = maskStringsAndComments(content, ext);
|
|
4089
4307
|
for (const { pattern, extensions, name, message, help } of RISKY_PATTERNS) {
|
|
4090
4308
|
if (!extensions.includes(ext)) continue;
|
|
4091
4309
|
if (isMigrationOrSeeder && name === "sql-injection") continue;
|
|
4092
4310
|
const regex = new RegExp(pattern.source, pattern.flags);
|
|
4093
4311
|
let match;
|
|
4094
|
-
while ((match = regex.exec(
|
|
4312
|
+
while ((match = regex.exec(masked)) !== null) {
|
|
4095
4313
|
const line = content.slice(0, match.index).split("\n").length;
|
|
4096
4314
|
if (name === "innerhtml") {
|
|
4097
4315
|
const beforeMatch = content.slice(Math.max(0, match.index - 200), match.index);
|
|
@@ -4915,7 +5133,7 @@ const scanCommand = async (directory, config, options) => {
|
|
|
4915
5133
|
});
|
|
4916
5134
|
}
|
|
4917
5135
|
if (options.json) {
|
|
4918
|
-
const { buildJsonOutput } = await import("./json-
|
|
5136
|
+
const { buildJsonOutput } = await import("./json-D_i2_5_-.js");
|
|
4919
5137
|
const jsonOut = buildJsonOutput(results, scoreResult, projectInfo.sourceFileCount, elapsedMs);
|
|
4920
5138
|
console.log(JSON.stringify(jsonOut, null, 2));
|
|
4921
5139
|
return { exitCode };
|
|
@@ -5686,42 +5904,40 @@ const removeUnusedDeclarations = (rootDirectory, declarations) => {
|
|
|
5686
5904
|
|
|
5687
5905
|
//#endregion
|
|
5688
5906
|
//#region src/commands/fix-force.ts
|
|
5689
|
-
const
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
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
|
-
};
|
|
5907
|
+
const INSTALL_TIMEOUT = 1800 * 1e3;
|
|
5908
|
+
const AUDIT_TIMEOUT = 60 * 1e3;
|
|
5909
|
+
const detectPackageManager = (rootDirectory) => {
|
|
5910
|
+
if (fs.existsSync(path.join(rootDirectory, "pnpm-lock.yaml"))) return "pnpm";
|
|
5911
|
+
if (fs.existsSync(path.join(rootDirectory, "package-lock.json")) || fs.existsSync(path.join(rootDirectory, "package.json"))) return "npm";
|
|
5698
5912
|
return null;
|
|
5699
5913
|
};
|
|
5700
|
-
const INSTALL_TIMEOUT = 1800 * 1e3;
|
|
5701
5914
|
const fixDependencyAudit = async (context, onProgress) => {
|
|
5702
|
-
const
|
|
5703
|
-
if (!
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5915
|
+
const pm = detectPackageManager(context.rootDirectory);
|
|
5916
|
+
if (!pm) return;
|
|
5917
|
+
if (pm === "npm") {
|
|
5918
|
+
await runNpmAuditFix(context.rootDirectory, onProgress);
|
|
5919
|
+
await tryNpmOverrides(context.rootDirectory, onProgress);
|
|
5920
|
+
return;
|
|
5921
|
+
}
|
|
5922
|
+
await tryPnpmOverrides(context.rootDirectory, onProgress);
|
|
5923
|
+
};
|
|
5924
|
+
const runNpmAuditFix = async (rootDir, onProgress) => {
|
|
5925
|
+
onProgress?.("Dependency audit fixes · running npm audit fix (can take a few minutes)");
|
|
5926
|
+
const result = await runSubprocess("npm", ["audit", "fix"], {
|
|
5927
|
+
cwd: rootDir,
|
|
5707
5928
|
timeout: INSTALL_TIMEOUT
|
|
5708
5929
|
});
|
|
5709
|
-
if (result.exitCode !== 0 && !result.stdout && !result.stderr) throw new Error(
|
|
5710
|
-
onProgress?.(
|
|
5711
|
-
const installResult = await runSubprocess(
|
|
5712
|
-
cwd:
|
|
5930
|
+
if (result.exitCode !== 0 && !result.stdout && !result.stderr) throw new Error("npm audit fix failed");
|
|
5931
|
+
onProgress?.("Dependency audit fixes · running npm install");
|
|
5932
|
+
const installResult = await runSubprocess("npm", ["install"], {
|
|
5933
|
+
cwd: rootDir,
|
|
5713
5934
|
timeout: INSTALL_TIMEOUT
|
|
5714
5935
|
});
|
|
5715
|
-
if (installResult.exitCode !== 0) throw new Error(installResult.stderr || installResult.stdout ||
|
|
5716
|
-
if (auditFix.command === "npm") await tryNpmOverrides(context.rootDirectory, onProgress);
|
|
5936
|
+
if (installResult.exitCode !== 0) throw new Error(installResult.stderr || installResult.stdout || "npm install failed after audit fix");
|
|
5717
5937
|
};
|
|
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) => {
|
|
5938
|
+
const fetchLatestVersion = async (rootDir, pkgName, pm) => {
|
|
5723
5939
|
try {
|
|
5724
|
-
const result = await runSubprocess(
|
|
5940
|
+
const result = await runSubprocess(pm, [
|
|
5725
5941
|
"view",
|
|
5726
5942
|
pkgName,
|
|
5727
5943
|
"version",
|
|
@@ -5735,11 +5951,11 @@ const fetchLatestVersion = async (rootDir, pkgName) => {
|
|
|
5735
5951
|
return null;
|
|
5736
5952
|
}
|
|
5737
5953
|
};
|
|
5738
|
-
const
|
|
5954
|
+
const collectNpmOverrides = async (rootDir, vulnerabilities) => {
|
|
5739
5955
|
const overrides = {};
|
|
5740
5956
|
for (const [pkgName, vuln] of Object.entries(vulnerabilities)) {
|
|
5741
5957
|
if (vuln.fixAvailable !== false || !vuln.range) continue;
|
|
5742
|
-
const latest = await fetchLatestVersion(rootDir, pkgName);
|
|
5958
|
+
const latest = await fetchLatestVersion(rootDir, pkgName, "npm");
|
|
5743
5959
|
if (latest) overrides[pkgName] = latest;
|
|
5744
5960
|
}
|
|
5745
5961
|
return overrides;
|
|
@@ -5748,12 +5964,12 @@ const tryNpmOverrides = async (rootDir, onProgress) => {
|
|
|
5748
5964
|
try {
|
|
5749
5965
|
const auditResult = await runSubprocess("npm", ["audit", "--json"], {
|
|
5750
5966
|
cwd: rootDir,
|
|
5751
|
-
timeout:
|
|
5967
|
+
timeout: AUDIT_TIMEOUT
|
|
5752
5968
|
});
|
|
5753
5969
|
if (!auditResult.stdout) return;
|
|
5754
5970
|
const vulnerabilities = JSON.parse(auditResult.stdout).vulnerabilities;
|
|
5755
5971
|
if (!vulnerabilities) return;
|
|
5756
|
-
const overrides = await
|
|
5972
|
+
const overrides = await collectNpmOverrides(rootDir, vulnerabilities);
|
|
5757
5973
|
if (Object.keys(overrides).length === 0) return;
|
|
5758
5974
|
const pkgPath = path.join(rootDir, "package.json");
|
|
5759
5975
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
@@ -5761,7 +5977,7 @@ const tryNpmOverrides = async (rootDir, onProgress) => {
|
|
|
5761
5977
|
...pkg.overrides || {},
|
|
5762
5978
|
...overrides
|
|
5763
5979
|
};
|
|
5764
|
-
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2)
|
|
5980
|
+
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
5765
5981
|
onProgress?.("Dependency audit fixes · applying npm overrides (npm install)");
|
|
5766
5982
|
await runSubprocess("npm", ["install"], {
|
|
5767
5983
|
cwd: rootDir,
|
|
@@ -5769,6 +5985,61 @@ const tryNpmOverrides = async (rootDir, onProgress) => {
|
|
|
5769
5985
|
});
|
|
5770
5986
|
} catch {}
|
|
5771
5987
|
};
|
|
5988
|
+
const patchedRangeToVersion = (patched) => {
|
|
5989
|
+
const match = patched.match(/^\s*>=?\s*([0-9]+\.[0-9]+\.[0-9]+[^\s]*)/);
|
|
5990
|
+
return match ? `^${match[1]}` : null;
|
|
5991
|
+
};
|
|
5992
|
+
const overrideKey = (name, vulnerable, patched) => {
|
|
5993
|
+
if (vulnerable && vulnerable.trim().length > 0 && !/^\*$/.test(vulnerable.trim())) return `${name}@${vulnerable.trim()}`;
|
|
5994
|
+
const first = patched.match(/([0-9]+\.[0-9]+\.[0-9]+)/)?.[1];
|
|
5995
|
+
return first ? `${name}@<${first}` : name;
|
|
5996
|
+
};
|
|
5997
|
+
const collectPnpmOverrides = (advisories) => {
|
|
5998
|
+
const overrides = {};
|
|
5999
|
+
for (const adv of Object.values(advisories)) {
|
|
6000
|
+
if (!adv.module_name || !adv.patched_versions) continue;
|
|
6001
|
+
const target = patchedRangeToVersion(adv.patched_versions);
|
|
6002
|
+
if (!target) continue;
|
|
6003
|
+
const key = overrideKey(adv.module_name, adv.vulnerable_versions, adv.patched_versions);
|
|
6004
|
+
overrides[key] = target;
|
|
6005
|
+
}
|
|
6006
|
+
return overrides;
|
|
6007
|
+
};
|
|
6008
|
+
const tryPnpmOverrides = async (rootDir, onProgress) => {
|
|
6009
|
+
onProgress?.("Dependency audit fixes · running pnpm audit");
|
|
6010
|
+
const auditResult = await runSubprocess("pnpm", ["audit", "--json"], {
|
|
6011
|
+
cwd: rootDir,
|
|
6012
|
+
timeout: AUDIT_TIMEOUT
|
|
6013
|
+
});
|
|
6014
|
+
if (!auditResult.stdout) return;
|
|
6015
|
+
let parsed;
|
|
6016
|
+
try {
|
|
6017
|
+
parsed = JSON.parse(auditResult.stdout);
|
|
6018
|
+
} catch {
|
|
6019
|
+
return;
|
|
6020
|
+
}
|
|
6021
|
+
const advisories = parsed.advisories;
|
|
6022
|
+
if (!advisories || Object.keys(advisories).length === 0) return;
|
|
6023
|
+
const overrides = collectPnpmOverrides(advisories);
|
|
6024
|
+
if (Object.keys(overrides).length === 0) return;
|
|
6025
|
+
const pkgPath = path.join(rootDir, "package.json");
|
|
6026
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
6027
|
+
const pnpmBlock = pkg.pnpm ?? {};
|
|
6028
|
+
const existing = pnpmBlock.overrides ?? {};
|
|
6029
|
+
pkg.pnpm = {
|
|
6030
|
+
...pnpmBlock,
|
|
6031
|
+
overrides: {
|
|
6032
|
+
...existing,
|
|
6033
|
+
...overrides
|
|
6034
|
+
}
|
|
6035
|
+
};
|
|
6036
|
+
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
6037
|
+
onProgress?.("Dependency audit fixes · applying pnpm overrides (pnpm install)");
|
|
6038
|
+
await runSubprocess("pnpm", ["install"], {
|
|
6039
|
+
cwd: rootDir,
|
|
6040
|
+
timeout: INSTALL_TIMEOUT
|
|
6041
|
+
});
|
|
6042
|
+
};
|
|
5772
6043
|
const fixExpoDependencies = async (context, onProgress) => {
|
|
5773
6044
|
await removeDisallowedExpoPackages(context.rootDirectory, onProgress);
|
|
5774
6045
|
onProgress?.("Expo dependency alignment · running expo install --fix (can take a few minutes)");
|
|
@@ -5793,10 +6064,6 @@ const fixExpoDependencies = async (context, onProgress) => {
|
|
|
5793
6064
|
});
|
|
5794
6065
|
if (checkResult.exitCode !== 0) throw new Error(checkResult.stderr || checkResult.stdout || "expo dependency check failed");
|
|
5795
6066
|
};
|
|
5796
|
-
/**
|
|
5797
|
-
* Run expo-doctor to detect packages that should not be installed directly,
|
|
5798
|
-
* then uninstall them. No hardcoded list — expo-doctor is the source of truth.
|
|
5799
|
-
*/
|
|
5800
6067
|
const removeDisallowedExpoPackages = async (rootDir, onProgress) => {
|
|
5801
6068
|
try {
|
|
5802
6069
|
onProgress?.("Expo dependency alignment · running expo-doctor");
|
|
@@ -5883,8 +6150,9 @@ const runFormattingStep = async (deps) => {
|
|
|
5883
6150
|
const runForceSteps = async (deps) => {
|
|
5884
6151
|
if (!deps.force) return;
|
|
5885
6152
|
if (deps.config.engines["code-quality"] && hasJsOrTs(deps.projectInfo)) await deps.runStep("Remove unused files", () => runKnipUnusedFiles(deps.resolvedDir), () => fixUnusedFiles(deps.resolvedDir));
|
|
5886
|
-
|
|
5887
|
-
if (deps.
|
|
6153
|
+
const railUpdate = (label) => deps.rail.setActiveLabel(label);
|
|
6154
|
+
if (deps.config.engines.security) await deps.runStep("Dependency audit fixes", () => runDependencyAudit(deps.context), () => fixDependencyAudit(deps.context, railUpdate));
|
|
6155
|
+
if (deps.projectInfo.frameworks.includes("expo")) await deps.runStep("Expo dependency alignment", () => runExpoDoctor(deps.context), () => fixExpoDependencies(deps.context, railUpdate));
|
|
5888
6156
|
};
|
|
5889
6157
|
|
|
5890
6158
|
//#endregion
|
|
@@ -33,7 +33,7 @@ const getEngineLabel = (engine) => ENGINE_INFO[engine].label;
|
|
|
33
33
|
* Application version — injected at build time by tsdown from package.json.
|
|
34
34
|
* The fallback should always match the "version" field in package.json.
|
|
35
35
|
*/
|
|
36
|
-
const APP_VERSION = "0.5.
|
|
36
|
+
const APP_VERSION = "0.5.1";
|
|
37
37
|
|
|
38
38
|
//#endregion
|
|
39
39
|
export { ENGINE_INFO as n, getEngineLabel as r, APP_VERSION as t };
|