@methiu/raigal 0.2.2 → 0.2.4
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 +330 -82
- package/dist/{finding-assessment-M5fUTE-C.js → finding-assessment-CGpt5YUR.js} +3 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +255 -58
- package/dist/{json-DBoMe4xB.js → json-BBZKvm0E.js} +2 -2
- package/dist/mcp.js +254 -79
- package/dist/{sarif--DI_2p2r.js → sarif-CdtsAfJ1.js} +1 -1
- package/dist/{sarif-Ba5D8TPH.js → sarif-Lt31Ld5t.js} +2 -2
- package/dist/{version-9Cnt1TiK.js → version-CukuIVX4.js} +1 -1
- package/package.json +1 -1
- package/scripts/release-guard.mjs +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as APP_VERSION } from "./version-
|
|
2
|
-
import { a as getEngineLabel, i as ENGINE_INFO, r as scoreImpactForRule, t as summarizeFindingAssessments } from "./finding-assessment-
|
|
1
|
+
import { t as APP_VERSION } from "./version-CukuIVX4.js";
|
|
2
|
+
import { a as getEngineLabel, i as ENGINE_INFO, r as scoreImpactForRule, t as summarizeFindingAssessments } from "./finding-assessment-CGpt5YUR.js";
|
|
3
3
|
import { n as isSecretClassRule, r as maskSecrets, t as FIXED_SECRET_DIAGNOSTIC_MESSAGE } from "./mask-secrets-BXn-ZSEh.js";
|
|
4
4
|
import { createRequire, isBuiltin } from "node:module";
|
|
5
5
|
import path from "node:path";
|
|
@@ -313,6 +313,9 @@ const CiSchema = z.object({
|
|
|
313
313
|
format: z.enum(["json"]).default("json")
|
|
314
314
|
}).strict();
|
|
315
315
|
const TelemetrySchema = z.object({ enabled: z.boolean().default(true) }).strict();
|
|
316
|
+
const BaselineConfigSchema = z.object({ mode: z.enum(["strict", "relaxed"]).default("relaxed") }).strict();
|
|
317
|
+
const LicensingConfigSchema = z.object({ onLapse: z.enum(["fail-open", "fail-closed"]).default("fail-open") }).strict();
|
|
318
|
+
const WorkspaceConfigSchema = z.object({ minimumFloor: z.number().min(0).max(100).default(50) }).strict();
|
|
316
319
|
const RuleSeverityOverride = z.enum([
|
|
317
320
|
"error",
|
|
318
321
|
"warning",
|
|
@@ -383,6 +386,9 @@ const AislopConfigSchema = z.object({
|
|
|
383
386
|
format: "json"
|
|
384
387
|
})),
|
|
385
388
|
telemetry: TelemetrySchema.default(() => ({ enabled: true })),
|
|
389
|
+
baseline: BaselineConfigSchema.optional(),
|
|
390
|
+
licensing: LicensingConfigSchema.optional(),
|
|
391
|
+
workspace: WorkspaceConfigSchema.optional(),
|
|
386
392
|
rules: RulesSchema,
|
|
387
393
|
overrides: z.array(FileOverrideSchema).default(() => []),
|
|
388
394
|
exclude: z.array(z.string()).default(() => [
|
|
@@ -417,6 +423,9 @@ const KNOWN_KEYS_BY_PATH = {
|
|
|
417
423
|
"scoring",
|
|
418
424
|
"ci",
|
|
419
425
|
"telemetry",
|
|
426
|
+
"baseline",
|
|
427
|
+
"licensing",
|
|
428
|
+
"workspace",
|
|
420
429
|
"rules",
|
|
421
430
|
"exclude",
|
|
422
431
|
"include"
|
|
@@ -467,7 +476,10 @@ const KNOWN_KEYS_BY_PATH = {
|
|
|
467
476
|
],
|
|
468
477
|
"scoring.thresholds": ["good", "ok"],
|
|
469
478
|
ci: ["failBelow", "format"],
|
|
470
|
-
telemetry: ["enabled"]
|
|
479
|
+
telemetry: ["enabled"],
|
|
480
|
+
baseline: ["mode"],
|
|
481
|
+
licensing: ["onLapse"],
|
|
482
|
+
workspace: ["minimumFloor"]
|
|
471
483
|
};
|
|
472
484
|
const levenshtein = (a, b) => {
|
|
473
485
|
const m = a.length;
|
|
@@ -1664,7 +1676,8 @@ const resolvePackageRoot = (startFile) => {
|
|
|
1664
1676
|
while (true) {
|
|
1665
1677
|
const packageJsonPath = path.join(current, "package.json");
|
|
1666
1678
|
if (fs.existsSync(packageJsonPath)) try {
|
|
1667
|
-
|
|
1679
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
1680
|
+
if (packageJson.name === "aislop" || packageJson.name === "@methiu/raigal" || packageJson.name === "raigal") return current;
|
|
1668
1681
|
} catch {}
|
|
1669
1682
|
const parent = path.dirname(current);
|
|
1670
1683
|
if (parent === current) break;
|
|
@@ -3385,7 +3398,7 @@ const tokenizeJs = (content, ext = ".ts") => {
|
|
|
3385
3398
|
};
|
|
3386
3399
|
//#endregion
|
|
3387
3400
|
//#region src/utils/source-masker.ts
|
|
3388
|
-
const JS_EXTS$
|
|
3401
|
+
const JS_EXTS$3 = /* @__PURE__ */ new Set([
|
|
3389
3402
|
".ts",
|
|
3390
3403
|
".tsx",
|
|
3391
3404
|
".js",
|
|
@@ -3393,7 +3406,7 @@ const JS_EXTS$2 = /* @__PURE__ */ new Set([
|
|
|
3393
3406
|
".mjs",
|
|
3394
3407
|
".cjs"
|
|
3395
3408
|
]);
|
|
3396
|
-
const PY_EXTS = /* @__PURE__ */ new Set([".py"]);
|
|
3409
|
+
const PY_EXTS$1 = /* @__PURE__ */ new Set([".py"]);
|
|
3397
3410
|
const RB_EXTS = /* @__PURE__ */ new Set([".rb"]);
|
|
3398
3411
|
const PHP_EXTS = /* @__PURE__ */ new Set([".php"]);
|
|
3399
3412
|
const CSHARP_EXTS = /* @__PURE__ */ new Set([".cs"]);
|
|
@@ -3417,8 +3430,8 @@ const C_STYLE_COMMENT_EXTS = /* @__PURE__ */ new Set([
|
|
|
3417
3430
|
".zig"
|
|
3418
3431
|
]);
|
|
3419
3432
|
const familyForExt = (ext) => {
|
|
3420
|
-
if (JS_EXTS$
|
|
3421
|
-
if (PY_EXTS.has(ext)) return "py";
|
|
3433
|
+
if (JS_EXTS$3.has(ext)) return "js";
|
|
3434
|
+
if (PY_EXTS$1.has(ext)) return "py";
|
|
3422
3435
|
if (RB_EXTS.has(ext)) return "rb";
|
|
3423
3436
|
if (PHP_EXTS.has(ext)) return "php";
|
|
3424
3437
|
if (CSHARP_EXTS.has(ext)) return "csharp";
|
|
@@ -3688,7 +3701,7 @@ const areLiteralMultisetsEqual = (a, b) => {
|
|
|
3688
3701
|
};
|
|
3689
3702
|
//#endregion
|
|
3690
3703
|
//#region src/engines/ai-slop/abstractions.ts
|
|
3691
|
-
const JS_EXTS$
|
|
3704
|
+
const JS_EXTS$2 = /* @__PURE__ */ new Set([
|
|
3692
3705
|
".ts",
|
|
3693
3706
|
".tsx",
|
|
3694
3707
|
".js",
|
|
@@ -3699,11 +3712,11 @@ const JS_EXTS$1 = /* @__PURE__ */ new Set([
|
|
|
3699
3712
|
const THIN_WRAPPER_PATTERNS = [
|
|
3700
3713
|
{
|
|
3701
3714
|
pattern: /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\([^)]*\)\s*(?::\s*\w[^{]*)?\{\s*\n?\s*return\s+\w+\([^)]*\);\s*\n?\s*\}/g,
|
|
3702
|
-
extensions: JS_EXTS$
|
|
3715
|
+
extensions: JS_EXTS$2
|
|
3703
3716
|
},
|
|
3704
3717
|
{
|
|
3705
3718
|
pattern: /(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*(?::\s*\w[^=]*)?\s*=>\s*\w+\([^)]*\);/g,
|
|
3706
|
-
extensions: JS_EXTS$
|
|
3719
|
+
extensions: JS_EXTS$2
|
|
3707
3720
|
},
|
|
3708
3721
|
{
|
|
3709
3722
|
pattern: /def\s+(\w+)\s*\([^)]*\)(?:\s*->[^:]*)?:\s*\n\s+return\s+\w+\([^)]*\)\s*$/gm,
|
|
@@ -4951,7 +4964,7 @@ const scriptKindFor$1 = (ext) => {
|
|
|
4951
4964
|
}
|
|
4952
4965
|
};
|
|
4953
4966
|
const lineFor$1 = (sourceFile, node) => sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
|
|
4954
|
-
const makeDiagnostic = (filePath, line, rule, message, help) => ({
|
|
4967
|
+
const makeDiagnostic$1 = (filePath, line, rule, message, help) => ({
|
|
4955
4968
|
filePath,
|
|
4956
4969
|
engine: "ai-slop",
|
|
4957
4970
|
rule,
|
|
@@ -5001,7 +5014,7 @@ const detectRedundantTryCatch = (sourceFile, relativePath) => {
|
|
|
5001
5014
|
if (ts.isTryStatement(node) && node.catchClause && !node.finallyBlock) {
|
|
5002
5015
|
const catchNameNode = node.catchClause.variableDeclaration?.name;
|
|
5003
5016
|
const [onlyStatement] = node.catchClause.block.statements;
|
|
5004
|
-
if (catchNameNode && ts.isIdentifier(catchNameNode) && node.catchClause.block.statements.length === 1 && onlyStatement && (isRethrowStatement(onlyStatement, catchNameNode.text) || isPromiseRejectRethrow(onlyStatement, catchNameNode.text))) diagnostics.push(makeDiagnostic(relativePath, lineFor$1(sourceFile, node.catchClause), "ai-slop/redundant-try-catch", "Catch block only rethrows the same error", "Remove the try/catch or add useful context, cleanup, or recovery. Rethrowing unchanged errors is usually defensive agent noise."));
|
|
5017
|
+
if (catchNameNode && ts.isIdentifier(catchNameNode) && node.catchClause.block.statements.length === 1 && onlyStatement && (isRethrowStatement(onlyStatement, catchNameNode.text) || isPromiseRejectRethrow(onlyStatement, catchNameNode.text))) diagnostics.push(makeDiagnostic$1(relativePath, lineFor$1(sourceFile, node.catchClause), "ai-slop/redundant-try-catch", "Catch block only rethrows the same error", "Remove the try/catch or add useful context, cleanup, or recovery. Rethrowing unchanged errors is usually defensive agent noise."));
|
|
5005
5018
|
}
|
|
5006
5019
|
ts.forEachChild(node, visit);
|
|
5007
5020
|
};
|
|
@@ -5019,7 +5032,7 @@ const detectPrimitiveCoercions = (sourceFile, relativePath) => {
|
|
|
5019
5032
|
const [arg] = child.arguments;
|
|
5020
5033
|
if (arg && ts.isIdentifier(arg)) {
|
|
5021
5034
|
const primitive = params.get(arg.text);
|
|
5022
|
-
if (primitive && child.expression.text === COERCION_CTORS[primitive]) diagnostics.push(makeDiagnostic(relativePath, lineFor$1(sourceFile, child), "ai-slop/redundant-type-coercion", `Parameter '${arg.text}' is already typed as ${primitive} but is coerced again`, "Trust the typed boundary or validate unknown input before this function. Re-coercing already typed parameters is usually defensive agent noise."));
|
|
5035
|
+
if (primitive && child.expression.text === COERCION_CTORS[primitive]) diagnostics.push(makeDiagnostic$1(relativePath, lineFor$1(sourceFile, child), "ai-slop/redundant-type-coercion", `Parameter '${arg.text}' is already typed as ${primitive} but is coerced again`, "Trust the typed boundary or validate unknown input before this function. Re-coercing already typed parameters is usually defensive agent noise."));
|
|
5023
5036
|
}
|
|
5024
5037
|
}
|
|
5025
5038
|
ts.forEachChild(child, visitBody);
|
|
@@ -5062,7 +5075,7 @@ const detectDuplicateExportedTypes = (parsedSources) => {
|
|
|
5062
5075
|
continue;
|
|
5063
5076
|
}
|
|
5064
5077
|
if (previous.filePath === declaration.filePath) continue;
|
|
5065
|
-
diagnostics.push(makeDiagnostic(declaration.filePath, declaration.line, "ai-slop/duplicate-type-declaration", `Exported type '${declaration.name}' duplicates an existing declaration`, `Reuse or import the existing type from ${previous.filePath} instead of re-declaring the same shape in another file.`));
|
|
5078
|
+
diagnostics.push(makeDiagnostic$1(declaration.filePath, declaration.line, "ai-slop/duplicate-type-declaration", `Exported type '${declaration.name}' duplicates an existing declaration`, `Reuse or import the existing type from ${previous.filePath} instead of re-declaring the same shape in another file.`));
|
|
5066
5079
|
}
|
|
5067
5080
|
}
|
|
5068
5081
|
return diagnostics;
|
|
@@ -5164,7 +5177,7 @@ const detectDuplicateImports = async (context) => {
|
|
|
5164
5177
|
rule: "ai-slop/duplicate-import",
|
|
5165
5178
|
severity: "warning",
|
|
5166
5179
|
message: `"${spec}" is also imported on line ${firstLine}. Merge into a single import statement.`,
|
|
5167
|
-
help: "Two imports from the same module split readers' attention and grow the import block. Run
|
|
5180
|
+
help: "Two imports from the same module split readers' attention and grow the import block. Run raigal fix to merge them automatically.",
|
|
5168
5181
|
line: dup.line,
|
|
5169
5182
|
column: 1,
|
|
5170
5183
|
category: "AI Slop",
|
|
@@ -8992,7 +9005,7 @@ const detectRustPatterns = async (context) => {
|
|
|
8992
9005
|
};
|
|
8993
9006
|
//#endregion
|
|
8994
9007
|
//#region src/engines/ai-slop/silent-recovery.ts
|
|
8995
|
-
const JS_EXTS = /* @__PURE__ */ new Set([
|
|
9008
|
+
const JS_EXTS$1 = /* @__PURE__ */ new Set([
|
|
8996
9009
|
".ts",
|
|
8997
9010
|
".tsx",
|
|
8998
9011
|
".js",
|
|
@@ -9187,7 +9200,7 @@ const detectSilentRecovery = async (context) => {
|
|
|
9187
9200
|
for (const filePath of files) {
|
|
9188
9201
|
if (isAutoGenerated(filePath)) continue;
|
|
9189
9202
|
const ext = path.extname(filePath);
|
|
9190
|
-
const isJs = JS_EXTS.has(ext);
|
|
9203
|
+
const isJs = JS_EXTS$1.has(ext);
|
|
9191
9204
|
if (!isJs && !(ext === ".py")) continue;
|
|
9192
9205
|
const relPath = relativePosix(context.rootDirectory, filePath);
|
|
9193
9206
|
if (isNonProductionPath(relPath)) continue;
|
|
@@ -9472,7 +9485,7 @@ const detectUnusedCss = async (context) => {
|
|
|
9472
9485
|
rule: "ai-slop/unused-css",
|
|
9473
9486
|
severity: "warning",
|
|
9474
9487
|
message: `CSS class '.${def.name}' is defined but never referenced in the codebase`,
|
|
9475
|
-
help: "Remove the dead CSS rule, or confirm the class is used dynamically (and add
|
|
9488
|
+
help: "Remove the dead CSS rule, or confirm the class is used dynamically (and add a raigal-ignore comment if so)",
|
|
9476
9489
|
line: def.line,
|
|
9477
9490
|
column: 0,
|
|
9478
9491
|
category: "AI Slop",
|
|
@@ -10552,8 +10565,8 @@ const isTrivialLine = (line) => {
|
|
|
10552
10565
|
};
|
|
10553
10566
|
const SVG_MARKUP_RE = /<\/?(?:svg|path|polyline|line|circle|rect|g)\b|(?:xmlns|viewBox|stroke(?:-width|-linecap|-linejoin)?|fill|fill-opacity|d|points|x1|x2|y1|y2)=/;
|
|
10554
10567
|
const DATA_LITERAL_RE = /^\s*(?:[A-Za-z_$][\w$-]*:\s*(?:["'`[{]|\d|true\b|false\b|null\b)|["'`][^"'`]*["'`],?\s*$|[{}\]],?\s*$|\),?\s*$)/;
|
|
10555
|
-
const SUPPRESS_RE = /aislop[- ]ignore(?:-next-block|-file)?\b.*\b(?:duplicate-block|code-quality\/duplicate-block)\b/;
|
|
10556
|
-
const FILE_SUPPRESS_RE = /aislop[- ]ignore-file\b.*\b(?:duplicate-block|code-quality\/duplicate-block)\b/;
|
|
10568
|
+
const SUPPRESS_RE = /(?:raigal|aislop)[- ]ignore(?:-next-block|-file)?\b.*\b(?:duplicate-block|code-quality\/duplicate-block)\b/;
|
|
10569
|
+
const FILE_SUPPRESS_RE = /(?:raigal|aislop)[- ]ignore-file\b.*\b(?:duplicate-block|code-quality\/duplicate-block)\b/;
|
|
10557
10570
|
const fileHasSuppression = (content) => FILE_SUPPRESS_RE.test(content);
|
|
10558
10571
|
const isLowSignalMarkupWindow = (lines) => {
|
|
10559
10572
|
return lines.filter((line) => SVG_MARKUP_RE.test(line)).length >= Math.ceil(WINDOW_SIZE / 2);
|
|
@@ -10695,8 +10708,8 @@ const shouldIncludeIssue = (issueType, filePath) => {
|
|
|
10695
10708
|
return !filePath.replace(/\\/g, "/").includes(".github/workflows/");
|
|
10696
10709
|
};
|
|
10697
10710
|
const DEPENDENCY_HELP = {
|
|
10698
|
-
dependencies: "This package is listed in package.json but not imported anywhere. Remove it with `npm uninstall` or `
|
|
10699
|
-
devDependencies: "This package is listed in package.json but not imported anywhere. Remove it with `npm uninstall` or `
|
|
10711
|
+
dependencies: "This package is listed in package.json but not imported anywhere. Remove it with `npm uninstall` or `raigal fix`.",
|
|
10712
|
+
devDependencies: "This package is listed in package.json but not imported anywhere. Remove it with `npm uninstall` or `raigal fix`.",
|
|
10700
10713
|
unlisted: "This package is imported in code but not declared in package.json. Run `npm install` to add it.",
|
|
10701
10714
|
unresolved: "This import cannot be resolved. Check for typos or missing packages.",
|
|
10702
10715
|
binaries: "This binary is used but its package is not in package.json."
|
|
@@ -11366,7 +11379,7 @@ const fixClangFormat = async (context) => {
|
|
|
11366
11379
|
};
|
|
11367
11380
|
//#endregion
|
|
11368
11381
|
//#region src/engines/format/dotnet-format.ts
|
|
11369
|
-
const REPORT_FILENAME = ".
|
|
11382
|
+
const REPORT_FILENAME = ".raigal-dotnet-format.json";
|
|
11370
11383
|
const parseDotnetFormatReport = (json, rootDirectory) => {
|
|
11371
11384
|
if (!json) return [];
|
|
11372
11385
|
let report;
|
|
@@ -11655,7 +11668,7 @@ const runGofmt = async (context) => {
|
|
|
11655
11668
|
rule: "go-formatting",
|
|
11656
11669
|
severity: "warning",
|
|
11657
11670
|
message: "Go file is not formatted correctly",
|
|
11658
|
-
help: "Run `
|
|
11671
|
+
help: "Run `raigal fix` to auto-format with gofmt",
|
|
11659
11672
|
line: 0,
|
|
11660
11673
|
column: 0,
|
|
11661
11674
|
category: "Format",
|
|
@@ -11725,7 +11738,7 @@ const parseRuffFormatOutput = (output, rootDir) => {
|
|
|
11725
11738
|
rule: "python-formatting",
|
|
11726
11739
|
severity: "warning",
|
|
11727
11740
|
message: "Python file is not formatted correctly",
|
|
11728
|
-
help: "Run `
|
|
11741
|
+
help: "Run `raigal fix` to auto-format with ruff",
|
|
11729
11742
|
line: 0,
|
|
11730
11743
|
column: 0,
|
|
11731
11744
|
category: "Format",
|
|
@@ -12177,7 +12190,7 @@ const runExpoDoctor = async (context) => {
|
|
|
12177
12190
|
rule: "expo-doctor/config-error",
|
|
12178
12191
|
severity: "warning",
|
|
12179
12192
|
message: configError,
|
|
12180
|
-
help: "Install project dependencies, then re-run `
|
|
12193
|
+
help: "Install project dependencies, then re-run `raigal scan`.",
|
|
12181
12194
|
line: 0,
|
|
12182
12195
|
column: 0,
|
|
12183
12196
|
category: "Expo",
|
|
@@ -13555,6 +13568,174 @@ const lintEngine = {
|
|
|
13555
13568
|
}
|
|
13556
13569
|
};
|
|
13557
13570
|
//#endregion
|
|
13571
|
+
//#region src/engines/security/ai-security.ts
|
|
13572
|
+
const JS_EXTS = /* @__PURE__ */ new Set([
|
|
13573
|
+
".ts",
|
|
13574
|
+
".tsx",
|
|
13575
|
+
".js",
|
|
13576
|
+
".jsx",
|
|
13577
|
+
".mjs",
|
|
13578
|
+
".cjs"
|
|
13579
|
+
]);
|
|
13580
|
+
const PY_EXTS = /* @__PURE__ */ new Set([".py"]);
|
|
13581
|
+
const UNICODE_TAG_RE = /[\u{E0000}-\u{E007F}]/gu;
|
|
13582
|
+
const hasRuleIgnore = (lines, lineIndex, ruleSuffix) => {
|
|
13583
|
+
const start = Math.max(0, lineIndex - 2);
|
|
13584
|
+
const re = new RegExp(`(?:raigal|aislop)[- ]ignore(?:-file)?\\b.*(?:${ruleSuffix}|security\\/${ruleSuffix})`, "i");
|
|
13585
|
+
return lines.slice(start, lineIndex + 1).some((line) => re.test(line));
|
|
13586
|
+
};
|
|
13587
|
+
const hasFileIgnore = (lines, ruleSuffix) => {
|
|
13588
|
+
const re = new RegExp(`(?:raigal|aislop)[- ]ignore-file\\b.*(?:${ruleSuffix}|security\\/${ruleSuffix})`, "i");
|
|
13589
|
+
return lines.slice(0, 10).some((line) => re.test(line));
|
|
13590
|
+
};
|
|
13591
|
+
const makeDiagnostic = (filePath, rule, message, help, line, column) => ({
|
|
13592
|
+
filePath,
|
|
13593
|
+
engine: "security",
|
|
13594
|
+
rule,
|
|
13595
|
+
severity: "error",
|
|
13596
|
+
message,
|
|
13597
|
+
help,
|
|
13598
|
+
line,
|
|
13599
|
+
column,
|
|
13600
|
+
category: "Security",
|
|
13601
|
+
fixable: false
|
|
13602
|
+
});
|
|
13603
|
+
const P_BYPASS = "bypassPermissions";
|
|
13604
|
+
const P_EXEC_TYPE = "executor_type";
|
|
13605
|
+
const P_LOCAL = "local";
|
|
13606
|
+
const P_USE_DOCKER = "use_docker";
|
|
13607
|
+
const P_AUTO_RUN = "auto_run";
|
|
13608
|
+
const P_UNSAFE_MODE = "unsafe_mode";
|
|
13609
|
+
const P_PYTHON_REPL = "PythonREPLTool";
|
|
13610
|
+
const UNSANDBOXED_TOOL_PATTERNS = [
|
|
13611
|
+
{
|
|
13612
|
+
pattern: new RegExp(`\\bpermission_?mode["']?\\s*[:=]\\s*["']${P_BYPASS}["']`, "i"),
|
|
13613
|
+
message: `permission_mode '${P_BYPASS}' bypasses agent sandboxing and human confirmation`,
|
|
13614
|
+
help: `Do not use ${P_BYPASS} mode in production; use standard interactive approval`
|
|
13615
|
+
},
|
|
13616
|
+
{
|
|
13617
|
+
pattern: new RegExp(`\\b${P_EXEC_TYPE}["']?\\s*[:=]\\s*["']${P_LOCAL}["']`, "i"),
|
|
13618
|
+
message: `${P_EXEC_TYPE}='${P_LOCAL}' executes LLM-generated code directly on host machine without container isolation`,
|
|
13619
|
+
help: "Configure agent to use DockerCommandLineCodeExecutor or a containerized sandbox environment"
|
|
13620
|
+
},
|
|
13621
|
+
{
|
|
13622
|
+
pattern: new RegExp(`\\b(?:LocalCommandLineCodeExecutor|LocalCodeExecutor)\\s*\\(`),
|
|
13623
|
+
message: "Local code executor executes LLM-generated code on host machine without container isolation",
|
|
13624
|
+
help: "Use DockerCommandLineCodeExecutor or an isolated execution sandbox"
|
|
13625
|
+
},
|
|
13626
|
+
{
|
|
13627
|
+
pattern: new RegExp(`\\b${P_USE_DOCKER}\\s*[:=]\\s*(?:False|false)\\b|["']${P_USE_DOCKER}["']\\s*:\\s*(?:False|false)\\b`),
|
|
13628
|
+
message: `${P_USE_DOCKER}=False disables container sandboxing for agent code execution`,
|
|
13629
|
+
help: `Set ${P_USE_DOCKER}=True to isolate agent execution inside Docker containers`
|
|
13630
|
+
},
|
|
13631
|
+
{
|
|
13632
|
+
pattern: new RegExp(`\\b(?:interpreter\\.)?${P_AUTO_RUN}\\s*[:=]\\s*(?:True|true)\\b`),
|
|
13633
|
+
message: `${P_AUTO_RUN}=True allows autonomous shell execution without human confirmation`,
|
|
13634
|
+
help: `Keep ${P_AUTO_RUN}=False so dangerous commands require explicit human review`
|
|
13635
|
+
},
|
|
13636
|
+
{
|
|
13637
|
+
pattern: new RegExp(`\\bCodeInterpreterTool\\s*\\([^)]*${P_UNSAFE_MODE}\\s*=\\s*(?:True|true)\\b|\\b${P_UNSAFE_MODE}\\s*[:=]\\s*(?:True|true)\\b`),
|
|
13638
|
+
message: `${P_UNSAFE_MODE}=True allows code interpreter tool to execute without Docker isolation`,
|
|
13639
|
+
help: `Disable ${P_UNSAFE_MODE} and isolate code execution in containerized environments`
|
|
13640
|
+
},
|
|
13641
|
+
{
|
|
13642
|
+
pattern: new RegExp(`\\b(?:${P_PYTHON_REPL}|PythonAstREPLTool)\\s*\\(`),
|
|
13643
|
+
message: `${P_PYTHON_REPL} executes arbitrary Python code in host process without container isolation`,
|
|
13644
|
+
help: `Use containerized sandbox execution rather than unconfined ${P_PYTHON_REPL} in production`
|
|
13645
|
+
}
|
|
13646
|
+
];
|
|
13647
|
+
const P_SYS = "system";
|
|
13648
|
+
const P_DEV = "developer";
|
|
13649
|
+
const P_SM = "SystemMessage";
|
|
13650
|
+
const PROMPT_INJECTION_JS_PATTERNS = [
|
|
13651
|
+
new RegExp(`(?:role\\s*:\\s*["'](?:${P_SYS}|${P_DEV})["'][^}{;]{0,120}?content\\s*:\\s*\`[^\`]*\\$\\{[^}]+\\}[^\`]*\`)`, "g"),
|
|
13652
|
+
new RegExp(`(?:content\\s*:\\s*\`[^\`]*\\$\\{[^}]+\\}[^\`]*\`[^}{;]{0,120}?role\\s*:\\s*["'](?:${P_SYS}|${P_DEV})["'])`, "g"),
|
|
13653
|
+
new RegExp(`(?:role\\s*:\\s*["'](?:${P_SYS}|${P_DEV})["'][^}{;]{0,120}?content\\s*:\\s*(?:["'][^"'\\n]*["']\\s*\\+\\s*[a-zA-Z_$]|[a-zA-Z_$][\\w$]*\\s*\\+\\s*["']))`, "g"),
|
|
13654
|
+
new RegExp(`(?:content\\s*:\\s*(?:["'][^"'\\n]*["']\\s*\\+\\s*[a-zA-Z_$]|[a-zA-Z_$][\\w$]*\\s*\\+\\s*["'])[^}{;]{0,120}?role\\s*:\\s*["'](?:${P_SYS}|${P_DEV})["'])`, "g"),
|
|
13655
|
+
new RegExp(`\\b(?:${P_SYS}|systemPrompt|system_prompt|system_instruction)\\s*:\\s*\`[^\`]*\\$\\{[^}]+\\}[^\`]*\``, "g"),
|
|
13656
|
+
new RegExp(`\\b(?:${P_SYS}|systemPrompt|system_prompt|system_instruction)\\s*:\\s*(?:["'][^"'\\n]*["']\\s*\\+\\s*[a-zA-Z_$]|[a-zA-Z_$][\\w$]*\\s*\\+\\s*["'])`, "g"),
|
|
13657
|
+
new RegExp(`\\b(?:new\\s+)?${P_SM}\\s*\\(\\s*(?:\`[^\`]*\\$\\{[^}]+\\}[^\`]*\`|["'][^"'\\n]*["']\\s*\\+\\s*[a-zA-Z_$]|[a-zA-Z_$][\\w$]*\\s*\\+\\s*["']|\\{\\s*content\\s*:\\s*\`[^\`]*\\$\\{[^}]+\\}[^\`]*\`)`, "g"),
|
|
13658
|
+
new RegExp(`\\[\\s*["']${P_SYS}["']\\s*,\\s*\`[^\`]*\\$\\{[^}]+\\}[^\`]*\`\\s*\\]`, "g")
|
|
13659
|
+
];
|
|
13660
|
+
const PROMPT_INJECTION_PY_PATTERNS = [
|
|
13661
|
+
new RegExp(`(?:["']role["']\\s*:\\s*["'](?:${P_SYS}|${P_DEV})["'][^}{;]{0,120}?["']content["']\\s*:\\s*f(?:"""[\\s\\S]*?\\{[^}]+\\}[\\s\\S]*?"""|\'\'\'[\\s\\S]*?\\{[^}]+\\}[\\s\\S]*?\'\'\'|["'][^"'\\n]*?\\{[^}]+\\}[^"'\\n]*?["']))`, "g"),
|
|
13662
|
+
new RegExp(`(?:["']content["']\\s*:\\s*f(?:"""[\\s\\S]*?\\{[^}]+\\}[\\s\\S]*?"""|\'\'\'[\\s\\S]*?\\{[^}]+\\}[\\s\\S]*?\'\'\'|["'][^"'\\n]*?\\{[^}]+\\}[^"'\\n]*?["'])[^}{;]{0,120}?["']role["']\\s*:\\s*["'](?:${P_SYS}|${P_DEV})["'])`, "g"),
|
|
13663
|
+
new RegExp(`(?:["']role["']\\s*:\\s*["'](?:${P_SYS}|${P_DEV})["'][^}{;]{0,120}?["']content["']\\s*:\\s*(?:["'][^"'\\n]*["']\\s*\\+\\s*[a-zA-Z_]|[a-zA-Z_]\\w*\\s*\\+\\s*["']|["'][^"'\\n]*["']\\.format\\())`, "g"),
|
|
13664
|
+
new RegExp(`(?:["']content["']\\s*:\\s*(?:["'][^"'\\n]*["']\\s*\\+\\s*[a-zA-Z_]|[a-zA-Z_]\\w*\\s*\\+\\s*["']|["'][^"'\\n]*["']\\.format\\()[^}{;]{0,120}?["']role["']\\s*:\\s*["'](?:${P_SYS}|${P_DEV})["'])`, "g"),
|
|
13665
|
+
new RegExp(`\\b(?:${P_SYS}|system_prompt|system_message|system_instruction)\\s*=\\s*f(?:"""[\\s\\S]*?\\{[^}]+\\}[\\s\\S]*?"""|\'\'\'[\\s\\S]*?\\{[^}]+\\}[\\s\\S]*?\'\'\'|["'][^"'\\n]*?\\{[^}]+\\}[^"'\\n]*?["'])`, "g"),
|
|
13666
|
+
new RegExp(`\\b(?:${P_SYS}|system_prompt|system_message|system_instruction)\\s*=\\s*(?:["'][^"'\\n]*["']\\s*\\+\\s*[a-zA-Z_]|[a-zA-Z_]\\w*\\s*\\+\\s*["']|["'][^"'\\n]*["']\\.format\\()`, "g"),
|
|
13667
|
+
new RegExp(`\\b${P_SM}\\s*\\(\\s*(?:content\\s*=\\s*)?f(?:"""[\\s\\S]*?\\{[^}]+\\}[\\s\\S]*?"""|\'\'\'[\\s\\S]*?\\{[^}]+\\}[\\s\\S]*?\'\'\'|["'][^"'\\n]*?\\{[^}]+\\}[^"'\\n]*?["'])`, "g"),
|
|
13668
|
+
new RegExp(`\\b${P_SM}\\s*\\(\\s*(?:content\\s*=\\s*)?(?:["'][^"'\\n]*["']\\s*\\+\\s*[a-zA-Z_]|[a-zA-Z_]\\w*\\s*\\+\\s*["']|["'][^"'\\n]*["']\\.format\\()`, "g"),
|
|
13669
|
+
new RegExp(`\\(\\s*["']${P_SYS}["']\\s*,\\s*f(?:"""[\\s\\S]*?\\{[^}]+\\}[\\s\\S]*?"""|\'\'\'[\\s\\S]*?\\{[^}]+\\}[\\s\\S]*?\'\'\'|["'][^"'\\n]*?\\{[^}]+\\}[^"'\\n]*?["'])\\s*\\)`, "g")
|
|
13670
|
+
];
|
|
13671
|
+
const checkInvisibleUnicode = (relPath, content, lines) => {
|
|
13672
|
+
if (hasFileIgnore(lines, "invisible-unicode-tags")) return [];
|
|
13673
|
+
const results = [];
|
|
13674
|
+
const tagRegex = new RegExp(UNICODE_TAG_RE.source, UNICODE_TAG_RE.flags);
|
|
13675
|
+
for (const match of content.matchAll(tagRegex)) {
|
|
13676
|
+
const matchIndex = match.index ?? 0;
|
|
13677
|
+
const line = content.slice(0, matchIndex).split("\n").length;
|
|
13678
|
+
const col = matchIndex - content.lastIndexOf("\n", matchIndex - 1);
|
|
13679
|
+
if (hasRuleIgnore(lines, line - 1, "invisible-unicode-tags")) continue;
|
|
13680
|
+
results.push(makeDiagnostic(relPath, "security/invisible-unicode-tags", "Invisible Unicode Tag character (U+E0000-U+E007F) detected: potential stealth prompt injection or ASCII smuggling", "Remove invisible Unicode tag characters. Unicode Tag block (U+E0000-U+E007F) has no legitimate printable presentation in source code", line, col));
|
|
13681
|
+
}
|
|
13682
|
+
return results;
|
|
13683
|
+
};
|
|
13684
|
+
const checkUnsandboxedTools = (relPath, content, lines) => {
|
|
13685
|
+
if (hasFileIgnore(lines, "unsandboxed-agent-tool")) return [];
|
|
13686
|
+
const results = [];
|
|
13687
|
+
for (const { pattern, message, help } of UNSANDBOXED_TOOL_PATTERNS) {
|
|
13688
|
+
const regex = new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`);
|
|
13689
|
+
for (const match of content.matchAll(regex)) {
|
|
13690
|
+
const matchIndex = match.index ?? 0;
|
|
13691
|
+
const line = content.slice(0, matchIndex).split("\n").length;
|
|
13692
|
+
const col = matchIndex - content.lastIndexOf("\n", matchIndex - 1);
|
|
13693
|
+
if (hasRuleIgnore(lines, line - 1, "unsandboxed-agent-tool")) continue;
|
|
13694
|
+
results.push(makeDiagnostic(relPath, "security/unsandboxed-agent-tool", message, help, line, col));
|
|
13695
|
+
}
|
|
13696
|
+
}
|
|
13697
|
+
return results;
|
|
13698
|
+
};
|
|
13699
|
+
const checkPromptInjection = (relPath, content, lines, isJs) => {
|
|
13700
|
+
if (hasFileIgnore(lines, "prompt-injection")) return [];
|
|
13701
|
+
const results = [];
|
|
13702
|
+
const patterns = isJs ? PROMPT_INJECTION_JS_PATTERNS : PROMPT_INJECTION_PY_PATTERNS;
|
|
13703
|
+
for (const pattern of patterns) {
|
|
13704
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
13705
|
+
for (const match of content.matchAll(regex)) {
|
|
13706
|
+
const matchIndex = match.index ?? 0;
|
|
13707
|
+
const line = content.slice(0, matchIndex).split("\n").length;
|
|
13708
|
+
const col = matchIndex - content.lastIndexOf("\n", matchIndex - 1);
|
|
13709
|
+
if (hasRuleIgnore(lines, line - 1, "prompt-injection")) continue;
|
|
13710
|
+
results.push(makeDiagnostic(relPath, "security/prompt-injection", "Untrusted variable interpolation into system prompt creates prompt injection vulnerability", "Keep system prompts static. Pass dynamic user inputs via user messages or input parameters with strict delimiters", line, col));
|
|
13711
|
+
}
|
|
13712
|
+
}
|
|
13713
|
+
return results;
|
|
13714
|
+
};
|
|
13715
|
+
const scanAiSecurity = async (context) => {
|
|
13716
|
+
const files = getSourceFiles(context);
|
|
13717
|
+
const diagnostics = [];
|
|
13718
|
+
for (const filePath of files) {
|
|
13719
|
+
const ext = path.extname(filePath);
|
|
13720
|
+
let content;
|
|
13721
|
+
try {
|
|
13722
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
13723
|
+
} catch {
|
|
13724
|
+
continue;
|
|
13725
|
+
}
|
|
13726
|
+
const relativePath = relativePosix(context.rootDirectory, filePath);
|
|
13727
|
+
const lines = content.split("\n");
|
|
13728
|
+
diagnostics.push(...checkInvisibleUnicode(relativePath, content, lines));
|
|
13729
|
+
const isJs = JS_EXTS.has(ext);
|
|
13730
|
+
const isPy = PY_EXTS.has(ext);
|
|
13731
|
+
if (isJs || isPy) {
|
|
13732
|
+
diagnostics.push(...checkUnsandboxedTools(relativePath, content, lines));
|
|
13733
|
+
diagnostics.push(...checkPromptInjection(relativePath, content, lines, isJs));
|
|
13734
|
+
}
|
|
13735
|
+
}
|
|
13736
|
+
return diagnostics;
|
|
13737
|
+
};
|
|
13738
|
+
//#endregion
|
|
13558
13739
|
//#region src/engines/security/audit-value.ts
|
|
13559
13740
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13560
13741
|
const readString = (record, key) => {
|
|
@@ -13934,7 +14115,7 @@ const parseJsAudit = (output, source, manifest) => {
|
|
|
13934
14115
|
rule: "security/dependency-audit-skipped",
|
|
13935
14116
|
severity: "info",
|
|
13936
14117
|
message: `Dependency audit skipped (${source}): lockfile is missing`,
|
|
13937
|
-
help: errorDetail ?? "Generate a lockfile, then re-run `
|
|
14118
|
+
help: errorDetail ?? "Generate a lockfile, then re-run `raigal scan` for dependency vulnerability checks.",
|
|
13938
14119
|
line: 0,
|
|
13939
14120
|
column: 0,
|
|
13940
14121
|
category: "Security",
|
|
@@ -14492,7 +14673,7 @@ const RISKY_PATTERNS = [
|
|
|
14492
14673
|
//#region src/engines/security/risky.ts
|
|
14493
14674
|
const hasDangerouslySetInnerHtmlIgnore = (lines, lineIndex) => {
|
|
14494
14675
|
const start = Math.max(0, lineIndex - 2);
|
|
14495
|
-
return lines.slice(start, lineIndex + 1).some((line) => /(?:biome-ignore|eslint-disable|aislop-ignore).*(?:noDangerouslySetInnerHtml|dangerouslySetInnerHTML|dangerously-set-innerhtml)/i.test(line));
|
|
14676
|
+
return lines.slice(start, lineIndex + 1).some((line) => /(?:biome-ignore|eslint-disable|raigal-ignore|aislop-ignore).*(?:noDangerouslySetInnerHtml|dangerouslySetInnerHTML|dangerously-set-innerhtml)/i.test(line));
|
|
14496
14677
|
};
|
|
14497
14678
|
const isStructuredDataScript = (content, matchIndex) => {
|
|
14498
14679
|
const before = content.slice(Math.max(0, matchIndex - 300), matchIndex);
|
|
@@ -14820,7 +15001,11 @@ const ALL_ENGINES = [
|
|
|
14820
15001
|
name: "security",
|
|
14821
15002
|
async run(context) {
|
|
14822
15003
|
const diagnostics = [];
|
|
14823
|
-
const promises = [
|
|
15004
|
+
const promises = [
|
|
15005
|
+
scanSecrets(context),
|
|
15006
|
+
detectRiskyConstructs(context),
|
|
15007
|
+
scanAiSecurity(context)
|
|
15008
|
+
];
|
|
14824
15009
|
if (context.config.security.audit) promises.push(runDependencyAudit(context));
|
|
14825
15010
|
const results = await Promise.allSettled(promises);
|
|
14826
15011
|
for (const result of results) if (result.status === "fulfilled") diagnostics.push(...result.value);
|
|
@@ -16255,7 +16440,7 @@ const verifyEntitlementJwt = (jwt, options = {}) => {
|
|
|
16255
16440
|
const claims = parsed.data;
|
|
16256
16441
|
const expectedIssuer = process.env.RAIGAL_ISSUER?.trim() || "https://app.raigal.dev";
|
|
16257
16442
|
if (claims.iss !== expectedIssuer) throw new JwtVerificationError(`Untrusted issuer: ${claims.iss}`, "invalid_issuer");
|
|
16258
|
-
if (!isVersionAtLeast("0.2.
|
|
16443
|
+
if (!isVersionAtLeast("0.2.4", claims.min_cli_version)) throw new JwtVerificationError(`CLI version ${APP_VERSION} is below required minimum ${claims.min_cli_version}. Run "raigal update" or "npm install -g @methiu/raigal@latest".`, "cli_version_unsupported");
|
|
16259
16444
|
const now = options.nowSeconds ?? Math.floor(Date.now() / 1e3);
|
|
16260
16445
|
const grace = options.allowGraceSeconds ?? 0;
|
|
16261
16446
|
if (now > claims.exp + grace) {
|
|
@@ -16428,7 +16613,7 @@ const getEntitlement = async () => {
|
|
|
16428
16613
|
};
|
|
16429
16614
|
//#endregion
|
|
16430
16615
|
//#region src/cloud/owner.ts
|
|
16431
|
-
const GITHUB_REMOTE_RE = /^(?:(?:git@|ssh:\/\/(?:git@)?)github\.com(?::(?:\d+\/)?|[:/])|https:\/\/(?:[^@]+@)?github\.com\/)([^/]+)\/([
|
|
16616
|
+
const GITHUB_REMOTE_RE = /^(?:(?:git@|ssh:\/\/(?:git@)?)github\.com(?::(?:\d+\/)?|[:/])|https:\/\/(?:[^@]+@)?github\.com\/)([^/]+)\/([^/\s]+?)(?:\.git)?\s*$/;
|
|
16432
16617
|
const parseGitRemote = (rawUrl) => {
|
|
16433
16618
|
if (!rawUrl) return {
|
|
16434
16619
|
owner: null,
|
|
@@ -17327,7 +17512,7 @@ const detectUnusedDeclarations = async (context) => {
|
|
|
17327
17512
|
rule: "code-quality/unused-declaration",
|
|
17328
17513
|
severity: "warning",
|
|
17329
17514
|
message: `Unused ${extracted.kind}: ${extracted.name}`,
|
|
17330
|
-
help: "This top-level declaration is never used;
|
|
17515
|
+
help: "This top-level declaration is never used; raigal will remove it.",
|
|
17331
17516
|
line: d.line,
|
|
17332
17517
|
column: d.column,
|
|
17333
17518
|
category: "Dead Code",
|
|
@@ -17345,7 +17530,7 @@ const detectUnusedDeclarations = async (context) => {
|
|
|
17345
17530
|
rule: "code-quality/unused-declaration",
|
|
17346
17531
|
severity: "warning",
|
|
17347
17532
|
message: `Unused ${kind}: ${name}`,
|
|
17348
|
-
help: "This top-level declaration is never imported;
|
|
17533
|
+
help: "This top-level declaration is never imported; raigal will remove it.",
|
|
17349
17534
|
line: d.line,
|
|
17350
17535
|
column: d.column,
|
|
17351
17536
|
category: "Dead Code",
|
|
@@ -18357,6 +18542,8 @@ const resolveHistoryDir = (directory) => {
|
|
|
18357
18542
|
const resolved = path.resolve(directory);
|
|
18358
18543
|
const primary = path.join(resolved, CONFIG_DIR);
|
|
18359
18544
|
if (fs.existsSync(primary)) return primary;
|
|
18545
|
+
const legacy = path.join(resolved, ".aislop");
|
|
18546
|
+
if (fs.existsSync(legacy)) return legacy;
|
|
18360
18547
|
return null;
|
|
18361
18548
|
};
|
|
18362
18549
|
const historyPath = (directory) => {
|
|
@@ -18485,8 +18672,8 @@ A PostToolUse hook runs \`raigal hook claude\` after every Edit, Write, or Multi
|
|
|
18485
18672
|
|
|
18486
18673
|
## Rules
|
|
18487
18674
|
|
|
18488
|
-
- \`.raigal/config.yaml\`
|
|
18489
|
-
- \`.raigal/rules.yaml\`
|
|
18675
|
+
- \`.raigal/config.yaml\` - thresholds and engine toggles. Treat as authoritative; don't edit without user consent.
|
|
18676
|
+
- \`.raigal/rules.yaml\` - project-specific architecture rules (may be absent). When a finding cites \`architecture/*\`, open this file and follow it.
|
|
18490
18677
|
- Custom rules can change between sessions. Trust what the scan returns, not a cached understanding of what the rules are.
|
|
18491
18678
|
|
|
18492
18679
|
## Principles
|
|
@@ -18501,7 +18688,7 @@ const atomicWrite = (targetPath, content) => {
|
|
|
18501
18688
|
const dir = path.dirname(targetPath);
|
|
18502
18689
|
fs.mkdirSync(dir, { recursive: true });
|
|
18503
18690
|
const rand = Math.random().toString(36).slice(2, 10);
|
|
18504
|
-
const tmp = path.join(dir, `.
|
|
18691
|
+
const tmp = path.join(dir, `.raigal-tmp-${process.pid}-${rand}`);
|
|
18505
18692
|
fs.writeFileSync(tmp, content, "utf-8");
|
|
18506
18693
|
try {
|
|
18507
18694
|
fs.renameSync(tmp, targetPath);
|
|
@@ -18647,7 +18834,7 @@ const uninstallAntigravity = (opts) => {
|
|
|
18647
18834
|
//#endregion
|
|
18648
18835
|
//#region src/hooks/io/json-patch.ts
|
|
18649
18836
|
const AISLOP_SENTINEL_KEY = "__aislop";
|
|
18650
|
-
const isAislopManaged = (x) => typeof x === "object" && x !== null && "__aislop" in x && x["__aislop"] != null;
|
|
18837
|
+
const isAislopManaged = (x) => typeof x === "object" && x !== null && ("__aislop" in x && x["__aislop"] != null || "__raigal" in x && x["__raigal"] != null);
|
|
18651
18838
|
const groupIsAislop = (group) => {
|
|
18652
18839
|
if (typeof group !== "object" || group === null) return false;
|
|
18653
18840
|
const hooks = group.hooks;
|
|
@@ -18702,14 +18889,14 @@ const resolveClaudePaths = (opts) => {
|
|
|
18702
18889
|
};
|
|
18703
18890
|
const buildHookGroup$1 = () => {
|
|
18704
18891
|
const hashBody = JSON.stringify({
|
|
18705
|
-
command: "
|
|
18892
|
+
command: "raigal hook claude",
|
|
18706
18893
|
matcher: "Edit|Write|MultiEdit"
|
|
18707
18894
|
});
|
|
18708
18895
|
return {
|
|
18709
18896
|
matcher: "Edit|Write|MultiEdit",
|
|
18710
18897
|
hooks: [{
|
|
18711
18898
|
type: "command",
|
|
18712
|
-
command: "
|
|
18899
|
+
command: "raigal hook claude",
|
|
18713
18900
|
[AISLOP_SENTINEL_KEY]: {
|
|
18714
18901
|
v: 1,
|
|
18715
18902
|
managed: true,
|
|
@@ -18719,12 +18906,12 @@ const buildHookGroup$1 = () => {
|
|
|
18719
18906
|
};
|
|
18720
18907
|
};
|
|
18721
18908
|
const buildStopHookGroup = () => {
|
|
18722
|
-
const hashBody = JSON.stringify({ command: "
|
|
18909
|
+
const hashBody = JSON.stringify({ command: "raigal hook claude --stop" });
|
|
18723
18910
|
return {
|
|
18724
18911
|
matcher: "",
|
|
18725
18912
|
hooks: [{
|
|
18726
18913
|
type: "command",
|
|
18727
|
-
command: "
|
|
18914
|
+
command: "raigal hook claude --stop",
|
|
18728
18915
|
[AISLOP_SENTINEL_KEY]: {
|
|
18729
18916
|
v: 1,
|
|
18730
18917
|
managed: true,
|
|
@@ -18736,14 +18923,14 @@ const buildStopHookGroup = () => {
|
|
|
18736
18923
|
const FILE_CHANGED_MATCHER = ".raigal/config.yml|.raigal/rules.yml|.aislop/config.yml|.aislop/rules.yml|package.json";
|
|
18737
18924
|
const buildFileChangedHookGroup = () => {
|
|
18738
18925
|
const hashBody = JSON.stringify({
|
|
18739
|
-
command: "
|
|
18926
|
+
command: "raigal hook claude --on-file-changed",
|
|
18740
18927
|
matcher: FILE_CHANGED_MATCHER
|
|
18741
18928
|
});
|
|
18742
18929
|
return {
|
|
18743
18930
|
matcher: FILE_CHANGED_MATCHER,
|
|
18744
18931
|
hooks: [{
|
|
18745
18932
|
type: "command",
|
|
18746
|
-
command: "
|
|
18933
|
+
command: "raigal hook claude --on-file-changed",
|
|
18747
18934
|
[AISLOP_SENTINEL_KEY]: {
|
|
18748
18935
|
v: 1,
|
|
18749
18936
|
managed: true,
|
|
@@ -18886,11 +19073,11 @@ const resolveCursorPaths = (opts) => {
|
|
|
18886
19073
|
};
|
|
18887
19074
|
const buildHookEntry = () => {
|
|
18888
19075
|
const hashBody = JSON.stringify({
|
|
18889
|
-
command: "
|
|
19076
|
+
command: "raigal hook cursor",
|
|
18890
19077
|
timeout: 5e3
|
|
18891
19078
|
});
|
|
18892
19079
|
return {
|
|
18893
|
-
command: "
|
|
19080
|
+
command: "raigal hook cursor",
|
|
18894
19081
|
type: "command",
|
|
18895
19082
|
timeout: 5e3,
|
|
18896
19083
|
[AISLOP_SENTINEL_KEY]: {
|
|
@@ -18957,15 +19144,15 @@ const resolveGeminiPaths = (opts) => {
|
|
|
18957
19144
|
};
|
|
18958
19145
|
const buildHookGroup = () => {
|
|
18959
19146
|
const hashBody = JSON.stringify({
|
|
18960
|
-
command: "
|
|
19147
|
+
command: "raigal hook gemini",
|
|
18961
19148
|
matcher: "write_file|replace"
|
|
18962
19149
|
});
|
|
18963
19150
|
return {
|
|
18964
19151
|
matcher: "write_file|replace",
|
|
18965
19152
|
hooks: [{
|
|
18966
|
-
name: "
|
|
19153
|
+
name: "raigal",
|
|
18967
19154
|
type: "command",
|
|
18968
|
-
command: "
|
|
19155
|
+
command: "raigal hook gemini",
|
|
18969
19156
|
timeout: 5e3,
|
|
18970
19157
|
[AISLOP_SENTINEL_KEY]: {
|
|
18971
19158
|
v: 1,
|
|
@@ -19056,8 +19243,8 @@ const uninstallKilocode = (opts) => {
|
|
|
19056
19243
|
const resolvePiPaths = (opts) => {
|
|
19057
19244
|
return { extension: opts.scope === "project" ? path.join(opts.cwd, ".pi", "extensions", "aislop.js") : path.join(opts.home, ".pi", "agent", "extensions", "aislop.js") };
|
|
19058
19245
|
};
|
|
19059
|
-
const PI_EXTENSION_SOURCE = `//
|
|
19060
|
-
// Reinstall with:
|
|
19246
|
+
const PI_EXTENSION_SOURCE = `// raigal — auto-generated pi extension. Do not edit by hand.
|
|
19247
|
+
// Reinstall with: raigal hook install --pi
|
|
19061
19248
|
import { spawn } from "node:child_process";
|
|
19062
19249
|
|
|
19063
19250
|
// Runs the CLI async so a slow scan never blocks pi's event loop, and forwards
|
|
@@ -19102,7 +19289,7 @@ export default function (pi) {
|
|
|
19102
19289
|
const filePath = event.input && event.input.path;
|
|
19103
19290
|
if (typeof filePath !== "string" || filePath.length === 0) return;
|
|
19104
19291
|
|
|
19105
|
-
const bin = process.env.AISLOP_BIN || "
|
|
19292
|
+
const bin = process.env.RAIGAL_BIN || process.env.AISLOP_BIN || "raigal";
|
|
19106
19293
|
const payload = JSON.stringify({
|
|
19107
19294
|
cwd: ctx.cwd,
|
|
19108
19295
|
file_path: filePath,
|
|
@@ -19129,7 +19316,7 @@ export default function (pi) {
|
|
|
19129
19316
|
const installPi = (opts) => {
|
|
19130
19317
|
const paths = resolvePiPaths(opts);
|
|
19131
19318
|
const result = emptyResult();
|
|
19132
|
-
applyContent(result, opts, paths.extension, PI_EXTENSION_SOURCE, "write pi
|
|
19319
|
+
applyContent(result, opts, paths.extension, PI_EXTENSION_SOURCE, "write pi raigal extension");
|
|
19133
19320
|
return result;
|
|
19134
19321
|
};
|
|
19135
19322
|
const uninstallPi = (opts) => {
|
|
@@ -19768,6 +19955,9 @@ const RULE_LABELS = {
|
|
|
19768
19955
|
"security/shell-injection": "Possible shell injection",
|
|
19769
19956
|
"security/unsafe-deserialization": "Unsafe deserialization",
|
|
19770
19957
|
"security/unsafe-c-call": "Memory-unsafe C call",
|
|
19958
|
+
"security/prompt-injection": "Prompt injection (untrusted system prompt)",
|
|
19959
|
+
"security/unsandboxed-agent-tool": "Unsandboxed agent code execution tool",
|
|
19960
|
+
"security/invisible-unicode-tags": "Invisible Unicode tag character (stealth injection)",
|
|
19771
19961
|
"dotnet/projects-skipped": "C# projects skipped by build-backed lint",
|
|
19772
19962
|
"cppcheck/chunks-skipped": "C/C++ files skipped by cppcheck",
|
|
19773
19963
|
"eslint/no-undef": "Undefined identifier",
|
|
@@ -19856,6 +20046,9 @@ const RULE_DESCRIPTIONS = {
|
|
|
19856
20046
|
"security/shell-injection": "Shell command is built from unsanitized input.",
|
|
19857
20047
|
"security/unsafe-deserialization": "Legacy .NET formatter can deserialize arbitrary types (RCE).",
|
|
19858
20048
|
"security/unsafe-c-call": "Unbounded C string/exec function with buffer-overflow or shell risk.",
|
|
20049
|
+
"security/prompt-injection": "Untrusted dynamic variable interpolation into system or developer prompt.",
|
|
20050
|
+
"security/unsandboxed-agent-tool": "Agent executes code or commands on host without container or interactive gate.",
|
|
20051
|
+
"security/invisible-unicode-tags": "Invisible Unicode Tag block (U+E0000-U+E007F) used for prompt injection or ASCII smuggling.",
|
|
19859
20052
|
"dotnet/projects-skipped": "Build-backed C# lint only ran on restored projects; the rest were skipped.",
|
|
19860
20053
|
"cppcheck/chunks-skipped": "A cppcheck invocation timed out or failed on one or more chunks; their files were not analyzed.",
|
|
19861
20054
|
"oxlint/*": "JavaScript/TypeScript lint finding from oxlint.",
|
|
@@ -20293,12 +20486,12 @@ const runScanBody = async (resolvedDir, config, options, projectInfo, scanScope,
|
|
|
20293
20486
|
scoreFileCount
|
|
20294
20487
|
});
|
|
20295
20488
|
if (options.sarif) {
|
|
20296
|
-
const { buildSarifLog } = await import("./sarif-
|
|
20489
|
+
const { buildSarifLog } = await import("./sarif-Lt31Ld5t.js");
|
|
20297
20490
|
console.log(JSON.stringify(buildSarifLog(results), null, 2));
|
|
20298
20491
|
return completion;
|
|
20299
20492
|
}
|
|
20300
20493
|
if (options.json) {
|
|
20301
|
-
const { buildJsonOutput } = await import("./json-
|
|
20494
|
+
const { buildJsonOutput } = await import("./json-BBZKvm0E.js");
|
|
20302
20495
|
const jsonOut = buildJsonOutput(results, scoreResult, scoreFileCount, elapsedMs, scanCoverage);
|
|
20303
20496
|
console.log(JSON.stringify(jsonOut, null, 2));
|
|
20304
20497
|
return completion;
|
|
@@ -20678,7 +20871,7 @@ const promptForConfigChoices = async () => {
|
|
|
20678
20871
|
initialValues: DEFAULT_ENGINE_SELECTION,
|
|
20679
20872
|
required: false
|
|
20680
20873
|
}));
|
|
20681
|
-
if (enginesSelection === void 0) return null;
|
|
20874
|
+
if (enginesSelection === void 0 || isCancel(enginesSelection)) return null;
|
|
20682
20875
|
const failBelowRaw = await text({
|
|
20683
20876
|
message: "CI quality gate — fail the build when the score drops below (0-100)",
|
|
20684
20877
|
initialValue: "70",
|
|
@@ -21209,7 +21402,8 @@ const BUILTIN_RULES = [
|
|
|
21209
21402
|
"typescript/*",
|
|
21210
21403
|
"dotnet/*",
|
|
21211
21404
|
"cppcheck/*",
|
|
21212
|
-
"clang-tidy/*"
|
|
21405
|
+
"clang-tidy/*",
|
|
21406
|
+
"expo-doctor/*"
|
|
21213
21407
|
]
|
|
21214
21408
|
},
|
|
21215
21409
|
{
|
|
@@ -21308,7 +21502,10 @@ const BUILTIN_RULES = [
|
|
|
21308
21502
|
"security/shell-injection",
|
|
21309
21503
|
"security/unsafe-deserialization",
|
|
21310
21504
|
"security/unsafe-c-call",
|
|
21311
|
-
"security/dependency-audit-skipped"
|
|
21505
|
+
"security/dependency-audit-skipped",
|
|
21506
|
+
"security/prompt-injection",
|
|
21507
|
+
"security/unsandboxed-agent-tool",
|
|
21508
|
+
"security/invisible-unicode-tags"
|
|
21312
21509
|
]
|
|
21313
21510
|
}
|
|
21314
21511
|
];
|