eslint-plugin-ai-guard 1.2.6 → 1.2.7
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 +100 -4
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1206,6 +1206,7 @@ async function runEslint(options) {
|
|
|
1206
1206
|
}
|
|
1207
1207
|
|
|
1208
1208
|
// cli/utils/sarif.ts
|
|
1209
|
+
var import_crypto = require("crypto");
|
|
1209
1210
|
var import_path3 = __toESM(require("path"));
|
|
1210
1211
|
|
|
1211
1212
|
// cli/utils/version.ts
|
|
@@ -1233,6 +1234,8 @@ function readVersion() {
|
|
|
1233
1234
|
var PKG_VERSION = _version ?? (_version = readVersion());
|
|
1234
1235
|
|
|
1235
1236
|
// cli/utils/sarif.ts
|
|
1237
|
+
var SARIF_AUTOMATION_ID = "ai-guard";
|
|
1238
|
+
var FINGERPRINT_KEY = "ai-guard/v1";
|
|
1236
1239
|
var RULE_DOCS = {
|
|
1237
1240
|
"ai-guard/no-empty-catch": { shortDesc: "Empty catch block silently swallows errors", tags: ["reliability"] },
|
|
1238
1241
|
"ai-guard/no-broad-exception": { shortDesc: "Catching broad Exception/Error masks specific failures", tags: ["reliability"] },
|
|
@@ -1375,6 +1378,16 @@ function normalizeSarifPath(filePath, repoRoot) {
|
|
|
1375
1378
|
}
|
|
1376
1379
|
return normalized;
|
|
1377
1380
|
}
|
|
1381
|
+
function generateStableFingerprint(normalizedUri, ruleId, startLine, message) {
|
|
1382
|
+
const normalizedMessage = message.trim().toLowerCase().replace(/\s+/g, " ");
|
|
1383
|
+
const input = [
|
|
1384
|
+
ruleId,
|
|
1385
|
+
normalizedUri.toLowerCase(),
|
|
1386
|
+
String(startLine),
|
|
1387
|
+
normalizedMessage
|
|
1388
|
+
].join(":");
|
|
1389
|
+
return (0, import_crypto.createHash)("sha256").update(input, "utf8").digest("hex");
|
|
1390
|
+
}
|
|
1378
1391
|
function collectUsedRuleIds(result) {
|
|
1379
1392
|
const ids = /* @__PURE__ */ new Set();
|
|
1380
1393
|
for (const file of result.files) {
|
|
@@ -1429,12 +1442,23 @@ function buildSarifResult(issue, file, repoRoot) {
|
|
|
1429
1442
|
const asyncRiskType = ISSUE_ASYNC_RISK_TYPE[issue.ruleId];
|
|
1430
1443
|
const category = ISSUE_CATEGORY[issue.ruleId];
|
|
1431
1444
|
const sarifUri = normalizeSarifPath(file.filePath, repoRoot);
|
|
1445
|
+
const fingerprint = generateStableFingerprint(
|
|
1446
|
+
sarifUri,
|
|
1447
|
+
issue.ruleId,
|
|
1448
|
+
Math.max(1, issue.line),
|
|
1449
|
+
issue.message
|
|
1450
|
+
);
|
|
1432
1451
|
return {
|
|
1433
1452
|
ruleId: issue.ruleId,
|
|
1434
1453
|
level: confidenceToSarifLevel(confidence),
|
|
1435
1454
|
kind: confidence === "informational" ? "informational" : "fail",
|
|
1436
1455
|
rank: confidenceToRank(confidence),
|
|
1437
1456
|
message: { text: issue.message },
|
|
1457
|
+
// partialFingerprints: per-result hash for GitHub alert deduplication.
|
|
1458
|
+
// Without this, GitHub closes and reopens every alert on every scan run.
|
|
1459
|
+
partialFingerprints: {
|
|
1460
|
+
[FINGERPRINT_KEY]: fingerprint
|
|
1461
|
+
},
|
|
1438
1462
|
locations: [
|
|
1439
1463
|
{
|
|
1440
1464
|
physicalLocation: {
|
|
@@ -1557,6 +1581,12 @@ function buildSarifLog(result, version = PKG_VERSION, repoRoot) {
|
|
|
1557
1581
|
rules
|
|
1558
1582
|
}
|
|
1559
1583
|
},
|
|
1584
|
+
// automationDetails.id: stable tool identifier for GitHub Code Scanning.
|
|
1585
|
+
// Groups all ai-guard scan runs into one persistent analysis slot.
|
|
1586
|
+
// MUST remain "ai-guard" forever — changing this orphans all existing alerts.
|
|
1587
|
+
automationDetails: {
|
|
1588
|
+
id: SARIF_AUTOMATION_ID
|
|
1589
|
+
},
|
|
1560
1590
|
results: sarifResults,
|
|
1561
1591
|
properties: {
|
|
1562
1592
|
preset: "recommended",
|
|
@@ -1616,6 +1646,13 @@ function buildSarifDebugInfo(result, repoRoot) {
|
|
|
1616
1646
|
}
|
|
1617
1647
|
for (const issue of file.issues) {
|
|
1618
1648
|
const confidence = ISSUE_CONFIDENCE[issue.ruleId];
|
|
1649
|
+
const normalizedUri = normalizeSarifPath(file.filePath, repoRoot);
|
|
1650
|
+
const fingerprint = generateStableFingerprint(
|
|
1651
|
+
normalizedUri,
|
|
1652
|
+
issue.ruleId,
|
|
1653
|
+
Math.max(1, issue.line),
|
|
1654
|
+
issue.message
|
|
1655
|
+
);
|
|
1619
1656
|
resultsEmitted.push({
|
|
1620
1657
|
ruleId: issue.ruleId,
|
|
1621
1658
|
level: confidenceToSarifLevel(confidence),
|
|
@@ -1623,12 +1660,15 @@ function buildSarifDebugInfo(result, repoRoot) {
|
|
|
1623
1660
|
securitySeverity: confidenceToSecuritySeverity(confidence),
|
|
1624
1661
|
precision: confidenceToPrecision(confidence),
|
|
1625
1662
|
filePath: file.filePath,
|
|
1626
|
-
normalizedUri
|
|
1627
|
-
line: issue.line
|
|
1663
|
+
normalizedUri,
|
|
1664
|
+
line: issue.line,
|
|
1665
|
+
fingerprint
|
|
1628
1666
|
});
|
|
1629
1667
|
}
|
|
1630
1668
|
}
|
|
1631
1669
|
return {
|
|
1670
|
+
automationId: SARIF_AUTOMATION_ID,
|
|
1671
|
+
fingerprintKey: FINGERPRINT_KEY,
|
|
1632
1672
|
rulesEmitted,
|
|
1633
1673
|
resultsEmitted,
|
|
1634
1674
|
pathsDebug,
|
|
@@ -2113,7 +2153,7 @@ function registerRunCommand(program2) {
|
|
|
2113
2153
|
"--max-warnings <n>",
|
|
2114
2154
|
"Fail with exit code 1 if warnings exceed this count",
|
|
2115
2155
|
(value) => Number.parseInt(value, 10)
|
|
2116
|
-
).option("--verbose", "Show all issues \u2014 disable grouping of repeated warnings").option("--quiet", "Only show errors \u2014 suppress warnings and informational hints").option("--debug-timing", "Print per-phase timing diagnostics").option("--debug-sarif", "Print SARIF tag/level normalization trace \u2014 diagnose schema issues").option("--debug-sarif-paths", "Print path normalization trace \u2014 diagnose GitHub Code Scanning path resolution").option("--sarif-output <file>", "Write SARIF to this file instead of stdout").action(async (opts) => {
|
|
2156
|
+
).option("--verbose", "Show all issues \u2014 disable grouping of repeated warnings").option("--quiet", "Only show errors \u2014 suppress warnings and informational hints").option("--debug-timing", "Print per-phase timing diagnostics").option("--debug-sarif", "Print SARIF tag/level normalization trace \u2014 diagnose schema issues").option("--debug-sarif-paths", "Print path normalization trace \u2014 diagnose GitHub Code Scanning path resolution").option("--debug-sarif-persistence", "Print persistence identity trace \u2014 fingerprints, automationDetails, category").option("--sarif-output <file>", "Write SARIF to this file instead of stdout").action(async (opts) => {
|
|
2117
2157
|
if (opts.maxWarnings !== void 0 && (!Number.isInteger(opts.maxWarnings) || opts.maxWarnings < 0)) {
|
|
2118
2158
|
log.blank();
|
|
2119
2159
|
log.error("--max-warnings must be a non-negative integer.");
|
|
@@ -2241,6 +2281,34 @@ function registerRunCommand(program2) {
|
|
|
2241
2281
|
}
|
|
2242
2282
|
process.stderr.write("[debug-sarif-paths] \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");
|
|
2243
2283
|
}
|
|
2284
|
+
if (opts.debugSarifPersistence) {
|
|
2285
|
+
const debugInfo = buildSarifDebugInfo(result, repoRoot);
|
|
2286
|
+
process.stderr.write("\n[debug-sarif-persistence] GitHub Persistence Identity Trace\n");
|
|
2287
|
+
process.stderr.write("[debug-sarif-persistence] \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");
|
|
2288
|
+
process.stderr.write(`[debug-sarif-persistence] automationDetails.id: ${debugInfo.automationId}
|
|
2289
|
+
`);
|
|
2290
|
+
process.stderr.write(`[debug-sarif-persistence] fingerprintKey: ${debugInfo.fingerprintKey}
|
|
2291
|
+
`);
|
|
2292
|
+
process.stderr.write(`[debug-sarif-persistence] GITHUB_WORKSPACE: ${repoRoot ?? "(not set)"}
|
|
2293
|
+
`);
|
|
2294
|
+
process.stderr.write(`[debug-sarif-persistence] totalResults: ${debugInfo.totalResults}
|
|
2295
|
+
|
|
2296
|
+
`);
|
|
2297
|
+
if (debugInfo.resultsEmitted.length === 0) {
|
|
2298
|
+
process.stderr.write("[debug-sarif-persistence] (no findings \u2014 no fingerprints to show)\n");
|
|
2299
|
+
} else {
|
|
2300
|
+
process.stderr.write("[debug-sarif-persistence] Fingerprints (stable identity per finding):\n");
|
|
2301
|
+
for (const res of debugInfo.resultsEmitted) {
|
|
2302
|
+
process.stderr.write(`[debug-sarif-persistence] \u2500 ${res.ruleId}
|
|
2303
|
+
`);
|
|
2304
|
+
process.stderr.write(`[debug-sarif-persistence] file: ${res.normalizedUri}:${res.line}
|
|
2305
|
+
`);
|
|
2306
|
+
process.stderr.write(`[debug-sarif-persistence] fingerprint: ${res.fingerprint}
|
|
2307
|
+
`);
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
process.stderr.write("[debug-sarif-persistence] \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");
|
|
2311
|
+
}
|
|
2244
2312
|
if (opts.sarifOutput) {
|
|
2245
2313
|
import_fs4.default.writeFileSync(opts.sarifOutput, sarifJson, "utf-8");
|
|
2246
2314
|
if (opts.debugSarif) {
|
|
@@ -2592,7 +2660,7 @@ function registerChangedCommand(program2) {
|
|
|
2592
2660
|
"--max-warnings <n>",
|
|
2593
2661
|
"Fail if warnings exceed this count",
|
|
2594
2662
|
(v) => Number.parseInt(v, 10)
|
|
2595
|
-
).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/level normalization trace \u2014 diagnose schema issues").option("--debug-sarif-paths", "Print path normalization trace \u2014 diagnose GitHub Code Scanning path resolution").action(async (opts) => {
|
|
2663
|
+
).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/level normalization trace \u2014 diagnose schema issues").option("--debug-sarif-paths", "Print path normalization trace \u2014 diagnose GitHub Code Scanning path resolution").option("--debug-sarif-persistence", "Print persistence identity trace \u2014 fingerprints, automationDetails, category").action(async (opts) => {
|
|
2596
2664
|
const preset = opts.strict ? "strict" : opts.security ? "security" : "recommended";
|
|
2597
2665
|
const inCI = isGitHubActions();
|
|
2598
2666
|
const baseRef = opts.base ?? (opts.pr || inCI ? getGitHubBaseRef() : void 0);
|
|
@@ -2777,6 +2845,34 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI,
|
|
|
2777
2845
|
}
|
|
2778
2846
|
process.stderr.write("[debug-sarif-paths] \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");
|
|
2779
2847
|
}
|
|
2848
|
+
if (opts.debugSarifPersistence) {
|
|
2849
|
+
const debugInfo = buildSarifDebugInfo(result, repoRoot);
|
|
2850
|
+
process.stderr.write("\n[debug-sarif-persistence] GitHub Persistence Identity Trace\n");
|
|
2851
|
+
process.stderr.write("[debug-sarif-persistence] \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");
|
|
2852
|
+
process.stderr.write(`[debug-sarif-persistence] automationDetails.id: ${debugInfo.automationId}
|
|
2853
|
+
`);
|
|
2854
|
+
process.stderr.write(`[debug-sarif-persistence] fingerprintKey: ${debugInfo.fingerprintKey}
|
|
2855
|
+
`);
|
|
2856
|
+
process.stderr.write(`[debug-sarif-persistence] GITHUB_WORKSPACE: ${repoRoot ?? "(not set)"}
|
|
2857
|
+
`);
|
|
2858
|
+
process.stderr.write(`[debug-sarif-persistence] totalResults: ${debugInfo.totalResults}
|
|
2859
|
+
|
|
2860
|
+
`);
|
|
2861
|
+
if (debugInfo.resultsEmitted.length === 0) {
|
|
2862
|
+
process.stderr.write("[debug-sarif-persistence] (no findings \u2014 no fingerprints to show)\n");
|
|
2863
|
+
} else {
|
|
2864
|
+
process.stderr.write("[debug-sarif-persistence] Fingerprints (stable identity per finding):\n");
|
|
2865
|
+
for (const res of debugInfo.resultsEmitted) {
|
|
2866
|
+
process.stderr.write(`[debug-sarif-persistence] \u2500 ${res.ruleId}
|
|
2867
|
+
`);
|
|
2868
|
+
process.stderr.write(`[debug-sarif-persistence] file: ${res.normalizedUri}:${res.line}
|
|
2869
|
+
`);
|
|
2870
|
+
process.stderr.write(`[debug-sarif-persistence] fingerprint: ${res.fingerprint}
|
|
2871
|
+
`);
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
process.stderr.write("[debug-sarif-persistence] \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");
|
|
2875
|
+
}
|
|
2780
2876
|
if (resolvedSarifPath) {
|
|
2781
2877
|
import_fs5.default.writeFileSync(resolvedSarifPath, sarifJson, "utf-8");
|
|
2782
2878
|
if (opts.debugSarif) {
|