@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/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { t as APP_VERSION } from "./version-9Cnt1TiK.js";
2
- import { a as getEngineLabel, i as ENGINE_INFO, r as scoreImpactForRule, t as summarizeFindingAssessments } from "./finding-assessment-M5fUTE-C.js";
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
- if (JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")).name === "aislop") return current;
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$2 = /* @__PURE__ */ new Set([
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$2.has(ext)) return "js";
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$1 = /* @__PURE__ */ new Set([
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$1
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$1
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 aislop fix to merge them automatically.",
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 an aislop-ignore comment if so)",
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 `aislop fix`.",
10699
- devDependencies: "This package is listed in package.json but not imported anywhere. Remove it with `npm uninstall` or `aislop fix`.",
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 = ".aislop-dotnet-format.json";
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 `aislop fix` to auto-format with gofmt",
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 `aislop fix` to auto-format with ruff",
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 `aislop scan`.",
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 `aislop scan` for dependency vulnerability checks.",
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 = [scanSecrets(context), detectRiskyConstructs(context)];
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.2", 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");
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\/)([^/]+)\/([^/.\s]+?)(?:\.git)?\s*$/;
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; aislop will remove it.",
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; aislop will remove it.",
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\` (or \`.aislop/config.yaml\`) - thresholds and engine toggles. Treat as authoritative; don't edit without user consent.
18489
- - \`.raigal/rules.yaml\` (or \`.aislop/rules.yaml\`) - project-specific architecture rules (may be absent). When a finding cites \`architecture/*\`, open this file and follow it.
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, `.aislop-tmp-${process.pid}-${rand}`);
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: "aislop hook claude",
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: "aislop hook claude",
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: "aislop hook claude --stop" });
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: "aislop hook claude --stop",
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: "aislop hook claude --on-file-changed",
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: "aislop hook claude --on-file-changed",
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: "aislop hook cursor",
19076
+ command: "raigal hook cursor",
18890
19077
  timeout: 5e3
18891
19078
  });
18892
19079
  return {
18893
- command: "aislop hook cursor",
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: "aislop hook gemini",
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: "aislop",
19153
+ name: "raigal",
18967
19154
  type: "command",
18968
- command: "aislop hook gemini",
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 = `// aislop — auto-generated pi extension. Do not edit by hand.
19060
- // Reinstall with: aislop hook install --pi
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 || "aislop";
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 aislop extension");
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-Ba5D8TPH.js");
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-DBoMe4xB.js");
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
  ];