eslint-plugin-ai-guard 1.2.2 → 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 +351 -68
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -201,14 +201,14 @@ __export(config_manager_exports, {
|
|
|
201
201
|
});
|
|
202
202
|
function backupConfig(configPath) {
|
|
203
203
|
const backupPath = `${configPath}.bak`;
|
|
204
|
-
|
|
204
|
+
import_fs7.default.copyFileSync(configPath, backupPath);
|
|
205
205
|
return backupPath;
|
|
206
206
|
}
|
|
207
207
|
function readConfig(configPath) {
|
|
208
|
-
return
|
|
208
|
+
return import_fs7.default.readFileSync(configPath, "utf-8");
|
|
209
209
|
}
|
|
210
210
|
function writeConfig(configPath, content) {
|
|
211
|
-
|
|
211
|
+
import_fs7.default.writeFileSync(configPath, content, "utf-8");
|
|
212
212
|
}
|
|
213
213
|
function generateFlatConfig(preset) {
|
|
214
214
|
return `import aiGuard from 'eslint-plugin-ai-guard';
|
|
@@ -461,11 +461,11 @@ function validateLegacyConfigText(content) {
|
|
|
461
461
|
}
|
|
462
462
|
return problems;
|
|
463
463
|
}
|
|
464
|
-
var
|
|
464
|
+
var import_fs7, import_path6, AI_GUARD_MARKER, DEFAULT_FLAT_IGNORES, DEFAULT_LEGACY_IGNORES;
|
|
465
465
|
var init_config_manager = __esm({
|
|
466
466
|
"cli/utils/config-manager.ts"() {
|
|
467
467
|
"use strict";
|
|
468
|
-
|
|
468
|
+
import_fs7 = __toESM(require("fs"));
|
|
469
469
|
import_path6 = __toESM(require("path"));
|
|
470
470
|
AI_GUARD_MARKER = "eslint-plugin-ai-guard";
|
|
471
471
|
DEFAULT_FLAT_IGNORES = [
|
|
@@ -489,10 +489,11 @@ var init_config_manager = __esm({
|
|
|
489
489
|
|
|
490
490
|
// cli/index.ts
|
|
491
491
|
var import_commander = require("commander");
|
|
492
|
-
var
|
|
492
|
+
var import_fs12 = __toESM(require("fs"));
|
|
493
493
|
var import_path14 = __toESM(require("path"));
|
|
494
494
|
|
|
495
495
|
// cli/commands/run.ts
|
|
496
|
+
var import_fs4 = __toESM(require("fs"));
|
|
496
497
|
var import_ora = __toESM(require("ora"));
|
|
497
498
|
var import_chalk3 = __toESM(require("chalk"));
|
|
498
499
|
|
|
@@ -1250,6 +1251,80 @@ var RULE_DOCS = {
|
|
|
1250
1251
|
"ai-guard/no-dead-branch": { shortDesc: "Dead code branch that can never execute", tags: ["ai-patterns"] }
|
|
1251
1252
|
};
|
|
1252
1253
|
var BASE_DOCS_URL = "https://github.com/YashJadhav21/eslint-plugin-ai-guard/blob/main/docs/rules";
|
|
1254
|
+
function sanitizeSarifTags(tags) {
|
|
1255
|
+
return Array.from(
|
|
1256
|
+
new Set(
|
|
1257
|
+
tags.filter((t) => typeof t === "string" && t.trim().length > 0).map((t) => t.toLowerCase().trim().replace(/\s+/g, "-"))
|
|
1258
|
+
)
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
function sanitizeSarifProperties(props) {
|
|
1262
|
+
const confidence = props.confidence ?? "low";
|
|
1263
|
+
return {
|
|
1264
|
+
tags: sanitizeSarifTags(props.tags),
|
|
1265
|
+
confidence,
|
|
1266
|
+
category: props.category ?? "AI Patterns",
|
|
1267
|
+
"security-severity": confidenceToSecuritySeverity(confidence),
|
|
1268
|
+
precision: confidenceToPrecision(confidence)
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
function sanitizeSarifRule(rule) {
|
|
1272
|
+
return {
|
|
1273
|
+
...rule,
|
|
1274
|
+
shortDescription: {
|
|
1275
|
+
text: rule.shortDescription.text || `ai-guard rule: ${rule.id}`
|
|
1276
|
+
},
|
|
1277
|
+
fullDescription: rule.fullDescription?.text ? rule.fullDescription : { text: rule.shortDescription.text || `ai-guard rule: ${rule.id}` },
|
|
1278
|
+
properties: rule.properties ? {
|
|
1279
|
+
...rule.properties,
|
|
1280
|
+
// Re-deduplicate tags at the rule level as a final safety net
|
|
1281
|
+
tags: rule.properties.tags ? sanitizeSarifTags(rule.properties.tags) : []
|
|
1282
|
+
} : void 0
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
function sanitizeSarifLog(log2) {
|
|
1286
|
+
return {
|
|
1287
|
+
...log2,
|
|
1288
|
+
runs: log2.runs.map((run) => ({
|
|
1289
|
+
...run,
|
|
1290
|
+
tool: {
|
|
1291
|
+
...run.tool,
|
|
1292
|
+
driver: {
|
|
1293
|
+
...run.tool.driver,
|
|
1294
|
+
rules: run.tool.driver.rules.map(sanitizeSarifRule)
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}))
|
|
1298
|
+
};
|
|
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
|
+
}
|
|
1253
1328
|
function confidenceToSarifLevel(confidence) {
|
|
1254
1329
|
switch (confidence) {
|
|
1255
1330
|
case "high":
|
|
@@ -1278,20 +1353,6 @@ function confidenceToRank(confidence) {
|
|
|
1278
1353
|
return 30;
|
|
1279
1354
|
}
|
|
1280
1355
|
}
|
|
1281
|
-
function confidenceToGitHubSeverity(confidence) {
|
|
1282
|
-
switch (confidence) {
|
|
1283
|
-
case "high":
|
|
1284
|
-
return "high";
|
|
1285
|
-
case "medium":
|
|
1286
|
-
return "medium";
|
|
1287
|
-
case "low":
|
|
1288
|
-
return "low";
|
|
1289
|
-
case "informational":
|
|
1290
|
-
return "recommendation";
|
|
1291
|
-
default:
|
|
1292
|
-
return "low";
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
1356
|
function filePathToUri(filePath) {
|
|
1296
1357
|
return filePath.replace(/\\/g, "/");
|
|
1297
1358
|
}
|
|
@@ -1311,6 +1372,8 @@ function buildRuleDescriptors(ruleIds) {
|
|
|
1311
1372
|
const confidence = ISSUE_CONFIDENCE[id];
|
|
1312
1373
|
const category = ISSUE_CATEGORY[id] ?? "AI Patterns";
|
|
1313
1374
|
const remediation = ISSUE_REMEDIATION[id];
|
|
1375
|
+
const categorySlug = category.toLowerCase().replace(/\s+/g, "-");
|
|
1376
|
+
const rawTags = [...meta?.tags ?? ["ai-guard"], categorySlug];
|
|
1314
1377
|
const helpMarkdown = [
|
|
1315
1378
|
`**${meta?.shortDesc ?? shortName}**`,
|
|
1316
1379
|
"",
|
|
@@ -1318,7 +1381,7 @@ function buildRuleDescriptors(ruleIds) {
|
|
|
1318
1381
|
"",
|
|
1319
1382
|
`[View rule documentation](${BASE_DOCS_URL}/${shortName}.md)`
|
|
1320
1383
|
].filter(Boolean).join("\n");
|
|
1321
|
-
return {
|
|
1384
|
+
return sanitizeSarifRule({
|
|
1322
1385
|
id,
|
|
1323
1386
|
shortDescription: {
|
|
1324
1387
|
text: meta?.shortDesc ?? `ai-guard rule: ${shortName}`
|
|
@@ -1334,13 +1397,12 @@ function buildRuleDescriptors(ruleIds) {
|
|
|
1334
1397
|
defaultConfiguration: {
|
|
1335
1398
|
level: confidenceToSarifLevel(confidence)
|
|
1336
1399
|
},
|
|
1337
|
-
properties: {
|
|
1338
|
-
tags:
|
|
1339
|
-
confidence
|
|
1340
|
-
category
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
};
|
|
1400
|
+
properties: sanitizeSarifProperties({
|
|
1401
|
+
tags: rawTags,
|
|
1402
|
+
confidence,
|
|
1403
|
+
category
|
|
1404
|
+
})
|
|
1405
|
+
});
|
|
1344
1406
|
});
|
|
1345
1407
|
}
|
|
1346
1408
|
function buildSarifResult(issue, file) {
|
|
@@ -1372,11 +1434,87 @@ function buildSarifResult(issue, file) {
|
|
|
1372
1434
|
],
|
|
1373
1435
|
properties: {
|
|
1374
1436
|
confidence: confidence ?? "low",
|
|
1437
|
+
"security-severity": confidenceToSecuritySeverity(confidence),
|
|
1438
|
+
precision: confidenceToPrecision(confidence),
|
|
1375
1439
|
...asyncRiskType && { asyncRiskType },
|
|
1376
1440
|
...category && { category }
|
|
1377
1441
|
}
|
|
1378
1442
|
};
|
|
1379
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
|
+
}
|
|
1380
1518
|
function buildSarifLog(result, version = PKG_VERSION) {
|
|
1381
1519
|
const usedRuleIds = collectUsedRuleIds(result);
|
|
1382
1520
|
const rules = buildRuleDescriptors(usedRuleIds);
|
|
@@ -1386,7 +1524,7 @@ function buildSarifLog(result, version = PKG_VERSION) {
|
|
|
1386
1524
|
sarifResults.push(buildSarifResult(issue, file));
|
|
1387
1525
|
}
|
|
1388
1526
|
}
|
|
1389
|
-
|
|
1527
|
+
const log2 = {
|
|
1390
1528
|
$schema: "https://json.schemastore.org/sarif-2.1.0.json",
|
|
1391
1529
|
version: "2.1.0",
|
|
1392
1530
|
runs: [
|
|
@@ -1410,10 +1548,54 @@ function buildSarifLog(result, version = PKG_VERSION) {
|
|
|
1410
1548
|
}
|
|
1411
1549
|
]
|
|
1412
1550
|
};
|
|
1551
|
+
return normalizeSarifForGitHub(sanitizeSarifLog(log2));
|
|
1413
1552
|
}
|
|
1414
1553
|
function sarifToJson(log2) {
|
|
1415
1554
|
return JSON.stringify(log2, null, 2);
|
|
1416
1555
|
}
|
|
1556
|
+
function buildSarifDebugInfo(result) {
|
|
1557
|
+
const usedRuleIds = collectUsedRuleIds(result);
|
|
1558
|
+
const rulesEmitted = usedRuleIds.map((id) => {
|
|
1559
|
+
const meta = RULE_DOCS[id];
|
|
1560
|
+
const confidence = ISSUE_CONFIDENCE[id];
|
|
1561
|
+
const category = ISSUE_CATEGORY[id] ?? "AI Patterns";
|
|
1562
|
+
const categorySlug = category.toLowerCase().replace(/\s+/g, "-");
|
|
1563
|
+
const rawTags = [...meta?.tags ?? ["ai-guard"], categorySlug];
|
|
1564
|
+
const sanitizedTags = enhanceTagsWithGitHubSemantics(id, rawTags);
|
|
1565
|
+
return {
|
|
1566
|
+
id,
|
|
1567
|
+
rawTags,
|
|
1568
|
+
sanitizedTags,
|
|
1569
|
+
hasDuplicatesInRaw: rawTags.length !== new Set(rawTags).size,
|
|
1570
|
+
confidence: confidence ?? "low",
|
|
1571
|
+
category,
|
|
1572
|
+
level: confidenceToSarifLevel(confidence),
|
|
1573
|
+
securitySeverity: confidenceToSecuritySeverity(confidence),
|
|
1574
|
+
precision: confidenceToPrecision(confidence)
|
|
1575
|
+
};
|
|
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
|
+
}
|
|
1592
|
+
return {
|
|
1593
|
+
rulesEmitted,
|
|
1594
|
+
resultsEmitted,
|
|
1595
|
+
totalResults: result.files.reduce((n, f) => n + f.issues.length, 0),
|
|
1596
|
+
version: PKG_VERSION
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1417
1599
|
|
|
1418
1600
|
// cli/utils/github-summary.ts
|
|
1419
1601
|
var import_fs2 = __toESM(require("fs"));
|
|
@@ -1891,7 +2073,7 @@ function registerRunCommand(program2) {
|
|
|
1891
2073
|
"--max-warnings <n>",
|
|
1892
2074
|
"Fail with exit code 1 if warnings exceed this count",
|
|
1893
2075
|
(value) => Number.parseInt(value, 10)
|
|
1894
|
-
).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").action(async (opts) => {
|
|
2076
|
+
).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 normalization trace \u2014 diagnose duplicate-tag schema issues").option("--sarif-output <file>", "Write SARIF to this file instead of stdout").action(async (opts) => {
|
|
1895
2077
|
if (opts.maxWarnings !== void 0 && (!Number.isInteger(opts.maxWarnings) || opts.maxWarnings < 0)) {
|
|
1896
2078
|
log.blank();
|
|
1897
2079
|
log.error("--max-warnings must be a non-negative integer.");
|
|
@@ -1943,9 +2125,62 @@ function registerRunCommand(program2) {
|
|
|
1943
2125
|
writeGitHubSummary(result, { preset, scanMode: "full" });
|
|
1944
2126
|
writeGitHubOutputs(result);
|
|
1945
2127
|
}
|
|
1946
|
-
if (opts.sarif) {
|
|
2128
|
+
if (opts.sarif || opts.sarifOutput) {
|
|
1947
2129
|
const sarifLog = buildSarifLog(result);
|
|
1948
|
-
|
|
2130
|
+
const sarifJson = sarifToJson(sarifLog);
|
|
2131
|
+
if (opts.debugSarif) {
|
|
2132
|
+
const debugInfo = buildSarifDebugInfo(result);
|
|
2133
|
+
process.stderr.write("\n[debug-sarif] SARIF GitHub Compatibility Trace\n");
|
|
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");
|
|
2136
|
+
for (const r of debugInfo.rulesEmitted) {
|
|
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}
|
|
2146
|
+
`);
|
|
2147
|
+
process.stderr.write(`[debug-sarif] raw tags: ${JSON.stringify(r.rawTags)}
|
|
2148
|
+
`);
|
|
2149
|
+
process.stderr.write(`[debug-sarif] normalized tags: ${JSON.stringify(r.sanitizedTags)}
|
|
2150
|
+
`);
|
|
2151
|
+
if (r.hasDuplicatesInRaw) {
|
|
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}
|
|
2170
|
+
`);
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
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");
|
|
2174
|
+
}
|
|
2175
|
+
if (opts.sarifOutput) {
|
|
2176
|
+
import_fs4.default.writeFileSync(opts.sarifOutput, sarifJson, "utf-8");
|
|
2177
|
+
if (opts.debugSarif) {
|
|
2178
|
+
process.stderr.write(`[debug-sarif] SARIF written to: ${opts.sarifOutput}
|
|
2179
|
+
`);
|
|
2180
|
+
}
|
|
2181
|
+
} else {
|
|
2182
|
+
console.log(sarifJson);
|
|
2183
|
+
}
|
|
1949
2184
|
process.exit(resolveExitCode(result, opts.failOn ?? "errors", opts.maxWarnings));
|
|
1950
2185
|
return;
|
|
1951
2186
|
}
|
|
@@ -2280,7 +2515,7 @@ function resolveExitCode(result, failOn, maxWarnings) {
|
|
|
2280
2515
|
|
|
2281
2516
|
// cli/commands/changed.ts
|
|
2282
2517
|
var import_path4 = __toESM(require("path"));
|
|
2283
|
-
var
|
|
2518
|
+
var import_fs5 = __toESM(require("fs"));
|
|
2284
2519
|
var import_ora2 = __toESM(require("ora"));
|
|
2285
2520
|
var import_chalk5 = __toESM(require("chalk"));
|
|
2286
2521
|
function registerChangedCommand(program2) {
|
|
@@ -2288,7 +2523,7 @@ function registerChangedCommand(program2) {
|
|
|
2288
2523
|
"--max-warnings <n>",
|
|
2289
2524
|
"Fail if warnings exceed this count",
|
|
2290
2525
|
(v) => Number.parseInt(v, 10)
|
|
2291
|
-
).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) => {
|
|
2292
2527
|
const preset = opts.strict ? "strict" : opts.security ? "security" : "recommended";
|
|
2293
2528
|
const inCI = isGitHubActions();
|
|
2294
2529
|
const baseRef = opts.base ?? (opts.pr || inCI ? getGitHubBaseRef() : void 0);
|
|
@@ -2400,8 +2635,56 @@ function handleResults(result, opts, preset, scanMode, changedFilesCount, inCI,
|
|
|
2400
2635
|
if (opts.sarif) {
|
|
2401
2636
|
const sarifLog = buildSarifLog(result);
|
|
2402
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
|
+
}
|
|
2403
2682
|
if (resolvedSarifPath) {
|
|
2404
|
-
|
|
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
|
+
}
|
|
2405
2688
|
} else {
|
|
2406
2689
|
console.log(sarifJson);
|
|
2407
2690
|
}
|
|
@@ -2532,10 +2815,10 @@ function getFailOnExitCode(result, failOn, maxWarnings) {
|
|
|
2532
2815
|
// cli/commands/init.ts
|
|
2533
2816
|
var import_chalk6 = __toESM(require("chalk"));
|
|
2534
2817
|
var import_path7 = __toESM(require("path"));
|
|
2535
|
-
var
|
|
2818
|
+
var import_fs8 = __toESM(require("fs"));
|
|
2536
2819
|
|
|
2537
2820
|
// cli/utils/detector.ts
|
|
2538
|
-
var
|
|
2821
|
+
var import_fs6 = __toESM(require("fs"));
|
|
2539
2822
|
var import_path5 = __toESM(require("path"));
|
|
2540
2823
|
var import_module2 = require("module");
|
|
2541
2824
|
var FLAT_CONFIG_FILES = [
|
|
@@ -2554,7 +2837,7 @@ var ALL_CONFIG_FILES = [...FLAT_CONFIG_FILES, ...LEGACY_CONFIG_FILES];
|
|
|
2554
2837
|
function isPackageInstalled(pkgName, cwd = process.cwd()) {
|
|
2555
2838
|
try {
|
|
2556
2839
|
const pkgPath = import_path5.default.join(cwd, "node_modules", pkgName, "package.json");
|
|
2557
|
-
if (
|
|
2840
|
+
if (import_fs6.default.existsSync(pkgPath)) {
|
|
2558
2841
|
return true;
|
|
2559
2842
|
}
|
|
2560
2843
|
const req = (0, import_module2.createRequire)(import_path5.default.join(cwd, "package.json"));
|
|
@@ -2567,8 +2850,8 @@ function isPackageInstalled(pkgName, cwd = process.cwd()) {
|
|
|
2567
2850
|
function getPackageVersion(pkgName, cwd = process.cwd()) {
|
|
2568
2851
|
try {
|
|
2569
2852
|
const pkgPath = import_path5.default.join(cwd, "node_modules", pkgName, "package.json");
|
|
2570
|
-
const resolvedPath =
|
|
2571
|
-
const raw =
|
|
2853
|
+
const resolvedPath = import_fs6.default.existsSync(pkgPath) ? pkgPath : (0, import_module2.createRequire)(import_path5.default.join(cwd, "package.json")).resolve(`${pkgName}/package.json`);
|
|
2854
|
+
const raw = import_fs6.default.readFileSync(resolvedPath, "utf-8");
|
|
2572
2855
|
const json = JSON.parse(raw);
|
|
2573
2856
|
return json.version ?? null;
|
|
2574
2857
|
} catch {
|
|
@@ -2585,7 +2868,7 @@ function detectConfigType(cwd = process.cwd()) {
|
|
|
2585
2868
|
let first = null;
|
|
2586
2869
|
for (const { file, type } of ALL_CONFIG_FILES) {
|
|
2587
2870
|
const fullPath = import_path5.default.join(cwd, file);
|
|
2588
|
-
if (
|
|
2871
|
+
if (import_fs6.default.existsSync(fullPath)) {
|
|
2589
2872
|
found.push(fullPath);
|
|
2590
2873
|
if (!first) first = { type, path: fullPath };
|
|
2591
2874
|
}
|
|
@@ -2608,7 +2891,7 @@ function requiresFlatConfig(eslintMajor) {
|
|
|
2608
2891
|
function detectNukeIgnore(configPath) {
|
|
2609
2892
|
if (!configPath) return false;
|
|
2610
2893
|
try {
|
|
2611
|
-
const content =
|
|
2894
|
+
const content = import_fs6.default.readFileSync(configPath, "utf-8");
|
|
2612
2895
|
return content.includes('"**/*"') || content.includes("'**/*'");
|
|
2613
2896
|
} catch {
|
|
2614
2897
|
return false;
|
|
@@ -2843,10 +3126,10 @@ function registerInitCommand(program2) {
|
|
|
2843
3126
|
const legacyConfigs = env.allConfigPaths.filter((p) => !p.includes("eslint.config."));
|
|
2844
3127
|
let cleanedUp = false;
|
|
2845
3128
|
for (const p of legacyConfigs) {
|
|
2846
|
-
if (
|
|
3129
|
+
if (import_fs8.default.existsSync(p)) {
|
|
2847
3130
|
if (!isDryRun) {
|
|
2848
3131
|
try {
|
|
2849
|
-
|
|
3132
|
+
import_fs8.default.renameSync(p, `${p}.bak`);
|
|
2850
3133
|
cleanedUp = true;
|
|
2851
3134
|
} catch (err) {
|
|
2852
3135
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -2872,7 +3155,7 @@ function registerInitCommand(program2) {
|
|
|
2872
3155
|
}
|
|
2873
3156
|
log.blank();
|
|
2874
3157
|
log.section("Validation");
|
|
2875
|
-
if (!
|
|
3158
|
+
if (!import_fs8.default.existsSync(finalConfigPath)) {
|
|
2876
3159
|
log.error(`Config file not found after write: ${import_path7.default.relative(cwd, finalConfigPath)}`);
|
|
2877
3160
|
log.blank();
|
|
2878
3161
|
log.print(` ${import_chalk6.default.bold("Problem:")} File write may have failed.`);
|
|
@@ -2929,7 +3212,7 @@ function registerInitCommand(program2) {
|
|
|
2929
3212
|
let cleanedUpInfo = false;
|
|
2930
3213
|
if (eslintNeedsFlat) {
|
|
2931
3214
|
const legacyConfigs = env.allConfigPaths.filter((p) => !p.includes("eslint.config."));
|
|
2932
|
-
cleanedUpInfo = legacyConfigs.some((p) =>
|
|
3215
|
+
cleanedUpInfo = legacyConfigs.some((p) => import_fs8.default.existsSync(`${p}.bak`) && !import_fs8.default.existsSync(p));
|
|
2933
3216
|
}
|
|
2934
3217
|
const lingeringConflicts = env.hasConflictingConfigs && !eslintNeedsFlat && !cleanedUpInfo;
|
|
2935
3218
|
if (loadError === null && !lingeringConflicts) {
|
|
@@ -3319,7 +3602,7 @@ function registerIgnoreCommand(program2) {
|
|
|
3319
3602
|
}
|
|
3320
3603
|
|
|
3321
3604
|
// cli/commands/baseline.ts
|
|
3322
|
-
var
|
|
3605
|
+
var import_fs9 = __toESM(require("fs"));
|
|
3323
3606
|
var import_path11 = __toESM(require("path"));
|
|
3324
3607
|
var import_chalk10 = __toESM(require("chalk"));
|
|
3325
3608
|
var import_ora3 = __toESM(require("ora"));
|
|
@@ -3342,14 +3625,14 @@ function saveBaseline(result, preset, mode, cwd) {
|
|
|
3342
3625
|
entries
|
|
3343
3626
|
};
|
|
3344
3627
|
const baselinePath = import_path11.default.join(cwd, BASELINE_FILE);
|
|
3345
|
-
|
|
3628
|
+
import_fs9.default.writeFileSync(baselinePath, JSON.stringify(baseline, null, 2), "utf-8");
|
|
3346
3629
|
return baselinePath;
|
|
3347
3630
|
}
|
|
3348
3631
|
function loadBaseline(cwd) {
|
|
3349
3632
|
const baselinePath = import_path11.default.join(cwd, BASELINE_FILE);
|
|
3350
|
-
if (!
|
|
3633
|
+
if (!import_fs9.default.existsSync(baselinePath)) return null;
|
|
3351
3634
|
try {
|
|
3352
|
-
const raw =
|
|
3635
|
+
const raw = import_fs9.default.readFileSync(baselinePath, "utf-8");
|
|
3353
3636
|
return JSON.parse(raw);
|
|
3354
3637
|
} catch {
|
|
3355
3638
|
return null;
|
|
@@ -3516,7 +3799,7 @@ function registerBaselineCommand(program2) {
|
|
|
3516
3799
|
}
|
|
3517
3800
|
|
|
3518
3801
|
// cli/commands/init-context.ts
|
|
3519
|
-
var
|
|
3802
|
+
var import_fs10 = __toESM(require("fs"));
|
|
3520
3803
|
var import_path12 = __toESM(require("path"));
|
|
3521
3804
|
var import_chalk11 = __toESM(require("chalk"));
|
|
3522
3805
|
var import_prompts2 = require("@inquirer/prompts");
|
|
@@ -3888,16 +4171,16 @@ var AGENTS = [
|
|
|
3888
4171
|
function getPackageVersion2(cwd) {
|
|
3889
4172
|
try {
|
|
3890
4173
|
const pkgPath = import_path12.default.join(cwd, "node_modules", "eslint-plugin-ai-guard", "package.json");
|
|
3891
|
-
if (
|
|
3892
|
-
const pkg = JSON.parse(
|
|
4174
|
+
if (import_fs10.default.existsSync(pkgPath)) {
|
|
4175
|
+
const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
|
|
3893
4176
|
if (pkg.version) return pkg.version;
|
|
3894
4177
|
}
|
|
3895
4178
|
} catch {
|
|
3896
4179
|
}
|
|
3897
4180
|
try {
|
|
3898
4181
|
const pkgPath = import_path12.default.join(cwd, "package.json");
|
|
3899
|
-
if (
|
|
3900
|
-
const pkg = JSON.parse(
|
|
4182
|
+
if (import_fs10.default.existsSync(pkgPath)) {
|
|
4183
|
+
const pkg = JSON.parse(import_fs10.default.readFileSync(pkgPath, "utf-8"));
|
|
3901
4184
|
if (pkg.version) return pkg.version;
|
|
3902
4185
|
}
|
|
3903
4186
|
} catch {
|
|
@@ -3965,7 +4248,7 @@ function registerInitContextCommand(program2) {
|
|
|
3965
4248
|
const results = [];
|
|
3966
4249
|
for (const agent of selectedAgents) {
|
|
3967
4250
|
const fullPath = import_path12.default.join(cwd, agent.filePath);
|
|
3968
|
-
const exists =
|
|
4251
|
+
const exists = import_fs10.default.existsSync(fullPath);
|
|
3969
4252
|
if (exists && !isForce && !isDryRun) {
|
|
3970
4253
|
const overwrite = await (0, import_prompts2.confirm)({
|
|
3971
4254
|
message: `${agent.filePath} already exists. Overwrite?`,
|
|
@@ -3990,10 +4273,10 @@ function registerInitContextCommand(program2) {
|
|
|
3990
4273
|
continue;
|
|
3991
4274
|
}
|
|
3992
4275
|
const parentDir = import_path12.default.dirname(fullPath);
|
|
3993
|
-
if (!
|
|
3994
|
-
|
|
4276
|
+
if (!import_fs10.default.existsSync(parentDir)) {
|
|
4277
|
+
import_fs10.default.mkdirSync(parentDir, { recursive: true });
|
|
3995
4278
|
}
|
|
3996
|
-
|
|
4279
|
+
import_fs10.default.writeFileSync(fullPath, content, "utf-8");
|
|
3997
4280
|
results.push({ file: agent.filePath, status: "created" });
|
|
3998
4281
|
}
|
|
3999
4282
|
log.section("Summary");
|
|
@@ -4027,7 +4310,7 @@ function registerInitContextCommand(program2) {
|
|
|
4027
4310
|
}
|
|
4028
4311
|
|
|
4029
4312
|
// cli/commands/report.ts
|
|
4030
|
-
var
|
|
4313
|
+
var import_fs11 = __toESM(require("fs"));
|
|
4031
4314
|
var import_path13 = __toESM(require("path"));
|
|
4032
4315
|
var import_ora4 = __toESM(require("ora"));
|
|
4033
4316
|
var RULE_META = {
|
|
@@ -4053,8 +4336,8 @@ var RULE_META = {
|
|
|
4053
4336
|
function getProjectName(cwd) {
|
|
4054
4337
|
try {
|
|
4055
4338
|
const pkgPath = import_path13.default.join(cwd, "package.json");
|
|
4056
|
-
if (
|
|
4057
|
-
const pkg = JSON.parse(
|
|
4339
|
+
if (import_fs11.default.existsSync(pkgPath)) {
|
|
4340
|
+
const pkg = JSON.parse(import_fs11.default.readFileSync(pkgPath, "utf-8"));
|
|
4058
4341
|
if (pkg.name) return pkg.name;
|
|
4059
4342
|
}
|
|
4060
4343
|
} catch {
|
|
@@ -4064,16 +4347,16 @@ function getProjectName(cwd) {
|
|
|
4064
4347
|
function getPluginVersion(cwd) {
|
|
4065
4348
|
try {
|
|
4066
4349
|
const pkgPath = import_path13.default.resolve(__dirname, "../../package.json");
|
|
4067
|
-
if (
|
|
4068
|
-
const pkg = JSON.parse(
|
|
4350
|
+
if (import_fs11.default.existsSync(pkgPath)) {
|
|
4351
|
+
const pkg = JSON.parse(import_fs11.default.readFileSync(pkgPath, "utf-8"));
|
|
4069
4352
|
if (pkg.version) return pkg.version;
|
|
4070
4353
|
}
|
|
4071
4354
|
} catch {
|
|
4072
4355
|
}
|
|
4073
4356
|
try {
|
|
4074
4357
|
const pkgPath = import_path13.default.join(cwd, "node_modules", "eslint-plugin-ai-guard", "package.json");
|
|
4075
|
-
if (
|
|
4076
|
-
const pkg = JSON.parse(
|
|
4358
|
+
if (import_fs11.default.existsSync(pkgPath)) {
|
|
4359
|
+
const pkg = JSON.parse(import_fs11.default.readFileSync(pkgPath, "utf-8"));
|
|
4077
4360
|
if (pkg.version) return pkg.version;
|
|
4078
4361
|
}
|
|
4079
4362
|
} catch {
|
|
@@ -4451,7 +4734,7 @@ function registerReportCommand(program2) {
|
|
|
4451
4734
|
log.blank();
|
|
4452
4735
|
const html = generateHtml(reportData);
|
|
4453
4736
|
const outputPath = import_path13.default.resolve(cwd, opts.output);
|
|
4454
|
-
|
|
4737
|
+
import_fs11.default.writeFileSync(outputPath, html, "utf-8");
|
|
4455
4738
|
log.success(`Report saved: ${opts.output}`);
|
|
4456
4739
|
log.info(
|
|
4457
4740
|
`Score: ${reportData.score}/100 \xB7 ${reportData.totalErrors} errors \xB7 ${reportData.totalWarnings} warnings`
|
|
@@ -4485,7 +4768,7 @@ function registerReportCommand(program2) {
|
|
|
4485
4768
|
function getVersion() {
|
|
4486
4769
|
try {
|
|
4487
4770
|
const pkgPath = import_path14.default.resolve(__dirname, "../../package.json");
|
|
4488
|
-
const raw =
|
|
4771
|
+
const raw = import_fs12.default.readFileSync(pkgPath, "utf-8");
|
|
4489
4772
|
const pkg = JSON.parse(raw);
|
|
4490
4773
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
4491
4774
|
} catch {
|