eslint-plugin-ai-guard 1.2.3 → 1.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/index.js CHANGED
@@ -1259,11 +1259,13 @@ function sanitizeSarifTags(tags) {
1259
1259
  );
1260
1260
  }
1261
1261
  function sanitizeSarifProperties(props) {
1262
+ const confidence = props.confidence ?? "low";
1262
1263
  return {
1263
1264
  tags: sanitizeSarifTags(props.tags),
1264
- confidence: props.confidence ?? "low",
1265
+ confidence,
1265
1266
  category: props.category ?? "AI Patterns",
1266
- "problem.severity": props.problemSeverity ?? "low"
1267
+ "security-severity": confidenceToSecuritySeverity(confidence),
1268
+ precision: confidenceToPrecision(confidence)
1267
1269
  };
1268
1270
  }
1269
1271
  function sanitizeSarifRule(rule) {
@@ -1295,6 +1297,34 @@ function sanitizeSarifLog(log2) {
1295
1297
  }))
1296
1298
  };
1297
1299
  }
1300
+ function confidenceToSecuritySeverity(confidence) {
1301
+ switch (confidence) {
1302
+ case "high":
1303
+ return "8.0";
1304
+ case "medium":
1305
+ return "5.0";
1306
+ case "low":
1307
+ return "3.0";
1308
+ case "informational":
1309
+ return "1.0";
1310
+ default:
1311
+ return "3.0";
1312
+ }
1313
+ }
1314
+ function confidenceToPrecision(confidence) {
1315
+ switch (confidence) {
1316
+ case "high":
1317
+ return "high";
1318
+ case "medium":
1319
+ return "medium";
1320
+ case "low":
1321
+ return "low";
1322
+ case "informational":
1323
+ return "low";
1324
+ default:
1325
+ return "low";
1326
+ }
1327
+ }
1298
1328
  function confidenceToSarifLevel(confidence) {
1299
1329
  switch (confidence) {
1300
1330
  case "high":
@@ -1323,20 +1353,6 @@ function confidenceToRank(confidence) {
1323
1353
  return 30;
1324
1354
  }
1325
1355
  }
1326
- function confidenceToGitHubSeverity(confidence) {
1327
- switch (confidence) {
1328
- case "high":
1329
- return "high";
1330
- case "medium":
1331
- return "medium";
1332
- case "low":
1333
- return "low";
1334
- case "informational":
1335
- return "recommendation";
1336
- default:
1337
- return "low";
1338
- }
1339
- }
1340
1356
  function filePathToUri(filePath) {
1341
1357
  return filePath.replace(/\\/g, "/");
1342
1358
  }
@@ -1384,8 +1400,7 @@ function buildRuleDescriptors(ruleIds) {
1384
1400
  properties: sanitizeSarifProperties({
1385
1401
  tags: rawTags,
1386
1402
  confidence,
1387
- category,
1388
- problemSeverity: confidenceToGitHubSeverity(confidence)
1403
+ category
1389
1404
  })
1390
1405
  });
1391
1406
  });
@@ -1419,11 +1434,87 @@ function buildSarifResult(issue, file) {
1419
1434
  ],
1420
1435
  properties: {
1421
1436
  confidence: confidence ?? "low",
1437
+ "security-severity": confidenceToSecuritySeverity(confidence),
1438
+ precision: confidenceToPrecision(confidence),
1422
1439
  ...asyncRiskType && { asyncRiskType },
1423
1440
  ...category && { category }
1424
1441
  }
1425
1442
  };
1426
1443
  }
1444
+ function enhanceTagsWithGitHubSemantics(ruleId, baseTags) {
1445
+ const category = ISSUE_CATEGORY[ruleId];
1446
+ const tags = [...baseTags];
1447
+ if (category === "Security" || ruleId.includes("secret") || ruleId.includes("eval") || ruleId.includes("sql") || ruleId.includes("deserialize") || ruleId.includes("auth")) {
1448
+ tags.push("security", "correctness", "reliability");
1449
+ } else {
1450
+ tags.push("correctness", "reliability");
1451
+ }
1452
+ return sanitizeSarifTags(tags);
1453
+ }
1454
+ function normalizeSarifForGitHub(log2) {
1455
+ const normalizedRuns = log2.runs.map((run) => {
1456
+ const rules = run.tool.driver.rules.map((rule) => {
1457
+ const confidence = ISSUE_CONFIDENCE[rule.id];
1458
+ const properties = rule.properties || {};
1459
+ properties["security-severity"] = confidenceToSecuritySeverity(confidence);
1460
+ properties["precision"] = confidenceToPrecision(confidence);
1461
+ const currentTags = properties.tags || [];
1462
+ properties.tags = enhanceTagsWithGitHubSemantics(rule.id, currentTags);
1463
+ delete properties["problem.severity"];
1464
+ const defaultConfiguration = rule.defaultConfiguration || { level: "warning" };
1465
+ let normalizedConfigLevel = defaultConfiguration.level;
1466
+ if (!["error", "warning", "note", "none"].includes(normalizedConfigLevel)) {
1467
+ normalizedConfigLevel = confidenceToSarifLevel(confidence);
1468
+ }
1469
+ if (normalizedConfigLevel === "none") {
1470
+ normalizedConfigLevel = "note";
1471
+ }
1472
+ return {
1473
+ ...rule,
1474
+ defaultConfiguration: {
1475
+ ...defaultConfiguration,
1476
+ level: normalizedConfigLevel
1477
+ },
1478
+ properties
1479
+ };
1480
+ });
1481
+ const results = run.results.map((result) => {
1482
+ const confidence = ISSUE_CONFIDENCE[result.ruleId];
1483
+ const properties = result.properties || {};
1484
+ properties["security-severity"] = confidenceToSecuritySeverity(confidence);
1485
+ properties["precision"] = confidenceToPrecision(confidence);
1486
+ let normalizedLevel = result.level;
1487
+ if (!["error", "warning", "note", "none"].includes(normalizedLevel)) {
1488
+ normalizedLevel = confidenceToSarifLevel(confidence);
1489
+ }
1490
+ if (normalizedLevel === "none") {
1491
+ normalizedLevel = "note";
1492
+ }
1493
+ const kind = confidence === "informational" ? "informational" : "fail";
1494
+ return {
1495
+ ...result,
1496
+ level: normalizedLevel,
1497
+ kind,
1498
+ properties
1499
+ };
1500
+ });
1501
+ return {
1502
+ ...run,
1503
+ tool: {
1504
+ ...run.tool,
1505
+ driver: {
1506
+ ...run.tool.driver,
1507
+ rules
1508
+ }
1509
+ },
1510
+ results
1511
+ };
1512
+ });
1513
+ return {
1514
+ ...log2,
1515
+ runs: normalizedRuns
1516
+ };
1517
+ }
1427
1518
  function buildSarifLog(result, version = PKG_VERSION) {
1428
1519
  const usedRuleIds = collectUsedRuleIds(result);
1429
1520
  const rules = buildRuleDescriptors(usedRuleIds);
@@ -1457,7 +1548,7 @@ function buildSarifLog(result, version = PKG_VERSION) {
1457
1548
  }
1458
1549
  ]
1459
1550
  };
1460
- return sanitizeSarifLog(log2);
1551
+ return normalizeSarifForGitHub(sanitizeSarifLog(log2));
1461
1552
  }
1462
1553
  function sarifToJson(log2) {
1463
1554
  return JSON.stringify(log2, null, 2);
@@ -1470,7 +1561,7 @@ function buildSarifDebugInfo(result) {
1470
1561
  const category = ISSUE_CATEGORY[id] ?? "AI Patterns";
1471
1562
  const categorySlug = category.toLowerCase().replace(/\s+/g, "-");
1472
1563
  const rawTags = [...meta?.tags ?? ["ai-guard"], categorySlug];
1473
- const sanitizedTags = sanitizeSarifTags(rawTags);
1564
+ const sanitizedTags = enhanceTagsWithGitHubSemantics(id, rawTags);
1474
1565
  return {
1475
1566
  id,
1476
1567
  rawTags,
@@ -1478,11 +1569,29 @@ function buildSarifDebugInfo(result) {
1478
1569
  hasDuplicatesInRaw: rawTags.length !== new Set(rawTags).size,
1479
1570
  confidence: confidence ?? "low",
1480
1571
  category,
1481
- level: confidenceToSarifLevel(confidence)
1572
+ level: confidenceToSarifLevel(confidence),
1573
+ securitySeverity: confidenceToSecuritySeverity(confidence),
1574
+ precision: confidenceToPrecision(confidence)
1482
1575
  };
1483
1576
  });
1577
+ const resultsEmitted = [];
1578
+ for (const file of result.files) {
1579
+ for (const issue of file.issues) {
1580
+ const confidence = ISSUE_CONFIDENCE[issue.ruleId];
1581
+ resultsEmitted.push({
1582
+ ruleId: issue.ruleId,
1583
+ level: confidenceToSarifLevel(confidence),
1584
+ kind: confidence === "informational" ? "informational" : "fail",
1585
+ securitySeverity: confidenceToSecuritySeverity(confidence),
1586
+ precision: confidenceToPrecision(confidence),
1587
+ filePath: file.filePath,
1588
+ line: issue.line
1589
+ });
1590
+ }
1591
+ }
1484
1592
  return {
1485
1593
  rulesEmitted,
1594
+ resultsEmitted,
1486
1595
  totalResults: result.files.reduce((n, f) => n + f.issues.length, 0),
1487
1596
  version: PKG_VERSION
1488
1597
  };
@@ -2021,21 +2130,47 @@ function registerRunCommand(program2) {
2021
2130
  const sarifJson = sarifToJson(sarifLog);
2022
2131
  if (opts.debugSarif) {
2023
2132
  const debugInfo = buildSarifDebugInfo(result);
2024
- process.stderr.write("\n[debug-sarif] SARIF Tag Normalization Trace\n");
2133
+ process.stderr.write("\n[debug-sarif] SARIF GitHub Compatibility Trace\n");
2025
2134
  process.stderr.write("[debug-sarif] \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
2135
+ process.stderr.write("[debug-sarif] Rules Emitted:\n");
2026
2136
  for (const r of debugInfo.rulesEmitted) {
2027
- process.stderr.write(`[debug-sarif] ${r.id}
2137
+ process.stderr.write(`[debug-sarif] - ${r.id}:
2138
+ `);
2139
+ process.stderr.write(`[debug-sarif] confidence: ${r.confidence}
2140
+ `);
2141
+ process.stderr.write(`[debug-sarif] level: ${r.level}
2142
+ `);
2143
+ process.stderr.write(`[debug-sarif] security-severity: ${r.securitySeverity}
2144
+ `);
2145
+ process.stderr.write(`[debug-sarif] precision: ${r.precision}
2028
2146
  `);
2029
- process.stderr.write(`[debug-sarif] raw: ${JSON.stringify(r.rawTags)}
2147
+ process.stderr.write(`[debug-sarif] raw tags: ${JSON.stringify(r.rawTags)}
2030
2148
  `);
2031
- process.stderr.write(`[debug-sarif] sanitized: ${JSON.stringify(r.sanitizedTags)}
2149
+ process.stderr.write(`[debug-sarif] normalized tags: ${JSON.stringify(r.sanitizedTags)}
2032
2150
  `);
2033
2151
  if (r.hasDuplicatesInRaw) {
2034
- process.stderr.write(`[debug-sarif] \u26A0 had duplicates \u2014 deduplicated
2152
+ process.stderr.write(`[debug-sarif] \u26A0 had duplicates \u2014 deduplicated
2153
+ `);
2154
+ }
2155
+ }
2156
+ process.stderr.write("\n[debug-sarif] Results Emitted:\n");
2157
+ if (debugInfo.resultsEmitted.length === 0) {
2158
+ process.stderr.write("[debug-sarif] (none)\n");
2159
+ } else {
2160
+ for (const res of debugInfo.resultsEmitted) {
2161
+ process.stderr.write(`[debug-sarif] - ${res.ruleId} (${res.filePath}:${res.line}):
2162
+ `);
2163
+ process.stderr.write(`[debug-sarif] level: ${res.level}
2164
+ `);
2165
+ process.stderr.write(`[debug-sarif] kind: ${res.kind}
2166
+ `);
2167
+ process.stderr.write(`[debug-sarif] security-severity: ${res.securitySeverity}
2168
+ `);
2169
+ process.stderr.write(`[debug-sarif] precision: ${res.precision}
2035
2170
  `);
2036
2171
  }
2037
2172
  }
2038
- process.stderr.write("\n");
2173
+ process.stderr.write("[debug-sarif] \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n");
2039
2174
  }
2040
2175
  if (opts.sarifOutput) {
2041
2176
  import_fs4.default.writeFileSync(opts.sarifOutput, sarifJson, "utf-8");
@@ -2388,7 +2523,7 @@ function registerChangedCommand(program2) {
2388
2523
  "--max-warnings <n>",
2389
2524
  "Fail if warnings exceed this count",
2390
2525
  (v) => Number.parseInt(v, 10)
2391
- ).option("--verbose", "Expand all findings (disable grouping)").option("--quiet", "Show errors only").option("--debug-timing", "Print per-phase timing diagnostics").option("--debug-git", "Print full git resolution trace (useful for diagnosing zero-file issues)").option("--debug-ci", "Print CI environment state (GITHUB_* vars, SARIF path, summary path)").action(async (opts) => {
2526
+ ).option("--verbose", "Expand all findings (disable grouping)").option("--quiet", "Show errors only").option("--debug-timing", "Print per-phase timing diagnostics").option("--debug-git", "Print full git resolution trace (useful for diagnosing zero-file issues)").option("--debug-ci", "Print CI environment state (GITHUB_* vars, SARIF path, summary path)").option("--debug-sarif", "Print SARIF tag normalization trace \u2014 diagnose duplicate-tag schema issues").action(async (opts) => {
2392
2527
  const preset = opts.strict ? "strict" : opts.security ? "security" : "recommended";
2393
2528
  const inCI = isGitHubActions();
2394
2529
  const baseRef = opts.base ?? (opts.pr || inCI ? getGitHubBaseRef() : void 0);
@@ -2500,8 +2635,56 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI,
2500
2635
  if (opts.sarif) {
2501
2636
  const sarifLog = buildSarifLog(result);
2502
2637
  const sarifJson = sarifToJson(sarifLog);
2638
+ if (opts.debugSarif) {
2639
+ const debugInfo = buildSarifDebugInfo(result);
2640
+ process.stderr.write("\n[debug-sarif] SARIF GitHub Compatibility Trace\n");
2641
+ process.stderr.write("[debug-sarif] \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
2642
+ process.stderr.write("[debug-sarif] Rules Emitted:\n");
2643
+ for (const r of debugInfo.rulesEmitted) {
2644
+ process.stderr.write(`[debug-sarif] - ${r.id}:
2645
+ `);
2646
+ process.stderr.write(`[debug-sarif] confidence: ${r.confidence}
2647
+ `);
2648
+ process.stderr.write(`[debug-sarif] level: ${r.level}
2649
+ `);
2650
+ process.stderr.write(`[debug-sarif] security-severity: ${r.securitySeverity}
2651
+ `);
2652
+ process.stderr.write(`[debug-sarif] precision: ${r.precision}
2653
+ `);
2654
+ process.stderr.write(`[debug-sarif] raw tags: ${JSON.stringify(r.rawTags)}
2655
+ `);
2656
+ process.stderr.write(`[debug-sarif] normalized tags: ${JSON.stringify(r.sanitizedTags)}
2657
+ `);
2658
+ if (r.hasDuplicatesInRaw) {
2659
+ process.stderr.write(`[debug-sarif] \u26A0 had duplicates \u2014 deduplicated
2660
+ `);
2661
+ }
2662
+ }
2663
+ process.stderr.write("\n[debug-sarif] Results Emitted:\n");
2664
+ if (debugInfo.resultsEmitted.length === 0) {
2665
+ process.stderr.write("[debug-sarif] (none)\n");
2666
+ } else {
2667
+ for (const res of debugInfo.resultsEmitted) {
2668
+ process.stderr.write(`[debug-sarif] - ${res.ruleId} (${res.filePath}:${res.line}):
2669
+ `);
2670
+ process.stderr.write(`[debug-sarif] level: ${res.level}
2671
+ `);
2672
+ process.stderr.write(`[debug-sarif] kind: ${res.kind}
2673
+ `);
2674
+ process.stderr.write(`[debug-sarif] security-severity: ${res.securitySeverity}
2675
+ `);
2676
+ process.stderr.write(`[debug-sarif] precision: ${res.precision}
2677
+ `);
2678
+ }
2679
+ }
2680
+ process.stderr.write("[debug-sarif] \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n");
2681
+ }
2503
2682
  if (resolvedSarifPath) {
2504
2683
  import_fs5.default.writeFileSync(resolvedSarifPath, sarifJson, "utf-8");
2684
+ if (opts.debugSarif) {
2685
+ process.stderr.write(`[debug-sarif] SARIF written to: ${resolvedSarifPath}
2686
+ `);
2687
+ }
2505
2688
  } else {
2506
2689
  console.log(sarifJson);
2507
2690
  }