@clear-capabilities/agentic-security-scanner 0.158.0 → 0.159.0
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/CHANGELOG.md +29 -0
- package/dist/1919.index.js +5 -2
- package/dist/2862.index.js +29 -9
- package/dist/3531.index.js +1 -1
- package/dist/4171.index.js +4 -4
- package/dist/{8551.index.js → 7033.index.js} +415 -22
- package/dist/9560.index.js +3 -1
- package/dist/agentic-security.mjs +15 -15
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +6 -4
- package/src/engine.js +6 -1
- package/src/language/CLAUDE.md +48 -1
- package/src/language/assurance.js +21 -4
- package/src/language/context.js +2 -0
- package/src/language/cvss.js +91 -0
- package/src/language/fix-lifecycle.js +114 -21
- package/src/language/haskell-adapter.js +4 -3
- package/src/language/haskell-advisory-feed.js +15 -0
- package/src/language/haskell-boundaries.js +220 -0
- package/src/language/haskell-cpp.js +460 -0
- package/src/language/haskell-ir.js +6 -3
- package/src/language/haskell-models.js +4 -3
- package/src/language/haskell-parser.js +122 -38
- package/src/language/haskell-sca.js +10 -6
- package/src/language/haskell-supply.js +42 -8
- package/src/language/haskell-web.js +128 -23
- package/src/language/nix-advisory-feed.js +275 -0
- package/src/language/nix-fix.js +162 -24
- package/src/language/nix-ir.js +16 -12
- package/src/language/nix-sca.js +71 -12
- package/src/language/nixos-eval-forms.js +162 -0
- package/src/language/nixos-module-resolver.js +346 -51
- package/src/language/resolved-pass.js +57 -3
- package/src/lsp/server.js +5 -2
- package/src/mcp/tools.js +3 -1
- package/src/posture/fix-history.js +30 -3
- package/src/report/index.js +4 -1
- package/dist/1310.index.js +0 -3161
- package/dist/2376.index.js +0 -3151
- package/dist/3526.index.js +0 -1019
- package/dist/7310.index.js +0 -520
- package/dist/8520.index.js +0 -13
- package/dist/9091.index.js +0 -3135
|
@@ -362,7 +362,7 @@ export async function applyFix({ scanRoot, file, originalContent, newContent, fi
|
|
|
362
362
|
* MITIGATION, WORKAROUND) and the gates it passed are kept on the entry beside the timestamp. No second write happens
|
|
363
363
|
* here; `backup.original` is the file the lifecycle saved before it wrote.
|
|
364
364
|
*/
|
|
365
|
-
export async function recordExternalFix({ scanRoot, file, originalContent, newContent, findingId, ruleId, vuln, stableId, fixLabel = null, verification = null, backup = null, source = 'language-lifecycle', findingProvenance = null }) {
|
|
365
|
+
export async function recordExternalFix({ scanRoot, file, originalContent, newContent, findingId, ruleId, vuln, stableId, fixLabel = null, verification = null, backup = null, source = 'language-lifecycle', findingProvenance = null, languageGroupId = null }) {
|
|
366
366
|
return _withLogLock(scanRoot, async () => {
|
|
367
367
|
if (!ensure(scanRoot)) return null;
|
|
368
368
|
const log = readLog(scanRoot);
|
|
@@ -376,6 +376,7 @@ export async function recordExternalFix({ scanRoot, file, originalContent, newCo
|
|
|
376
376
|
appliedAt: new Date().toISOString(), status: 'applied', reverted: false, attemptOrdinal: priorAttempts + 1,
|
|
377
377
|
provenanceAtFix: _snapshotProvenanceAtFix(findingProvenance, new Date().toISOString()),
|
|
378
378
|
fixLabel, verification, source, languageBackupId: backup ? backup.id : null,
|
|
379
|
+
...(languageGroupId ? { languageGroupId } : {}),
|
|
379
380
|
};
|
|
380
381
|
log.push(entry);
|
|
381
382
|
await _writeLogAndSync(scanRoot, log);
|
|
@@ -517,13 +518,38 @@ async function _revertEntryInner(scanRoot, entry) {
|
|
|
517
518
|
return entry;
|
|
518
519
|
}
|
|
519
520
|
|
|
521
|
+
// A multi-file language fix is recorded as one entry per file sharing `languageGroupId`. They are reverted as a unit: every
|
|
522
|
+
// backup is read first, and if a restore fails the files already restored are put back, so a half-undone fix is never left.
|
|
523
|
+
async function _revertGroupInner(scanRoot, entries) {
|
|
524
|
+
const items = [];
|
|
525
|
+
for (const entry of entries) {
|
|
526
|
+
const bak = path.resolve(scanRoot, entry.backupPath);
|
|
527
|
+
if (!fs.existsSync(bak)) return { error: `backup missing: ${bak}` };
|
|
528
|
+
const abs = path.resolve(scanRoot, entry.file);
|
|
529
|
+
items.push({ entry, abs, original: await fsp.readFile(bak, 'utf8'), current: fs.existsSync(abs) ? await fsp.readFile(abs, 'utf8') : null });
|
|
530
|
+
}
|
|
531
|
+
const done = [];
|
|
532
|
+
for (const it of items) {
|
|
533
|
+
try { await _writeAtomicAndSync(it.abs, it.original); done.push(it); } catch (err) {
|
|
534
|
+
const failed = [];
|
|
535
|
+
for (const d of done.reverse()) { try { if (d.current !== null) await _writeAtomicAndSync(d.abs, d.current); } catch { failed.push(d.entry.file); } }
|
|
536
|
+
return { error: `group undo failed at ${it.entry.file} (${err.message}); ${failed.length ? `ROLLBACK INCOMPLETE for ${failed.join(', ')}` : 'no file was changed'}` };
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const now = new Date().toISOString();
|
|
540
|
+
for (const it of items) { it.entry.reverted = true; it.entry.revertedAt = now; }
|
|
541
|
+
return items[items.length - 1].entry;
|
|
542
|
+
}
|
|
543
|
+
const _groupOf = (log, entry) => (entry.languageGroupId ? log.filter((e) => e.languageGroupId === entry.languageGroupId && !e.reverted) : null);
|
|
544
|
+
|
|
520
545
|
// Revert the most recent un-reverted fix. Returns the entry or null.
|
|
521
546
|
export async function undoLast(scanRoot) {
|
|
522
547
|
return _withLogLock(scanRoot, async () => {
|
|
523
548
|
const log = readLog(scanRoot);
|
|
524
549
|
for (let i = log.length - 1; i >= 0; i--) {
|
|
525
550
|
if (!log[i].reverted) {
|
|
526
|
-
const
|
|
551
|
+
const group = _groupOf(log, log[i]);
|
|
552
|
+
const result = group ? await _revertGroupInner(scanRoot, group) : await _revertEntryInner(scanRoot, log[i]);
|
|
527
553
|
if (!result.error) await _writeLogAndSync(scanRoot, log);
|
|
528
554
|
return result;
|
|
529
555
|
}
|
|
@@ -550,7 +576,8 @@ export async function revertEntryById(scanRoot, entryId) {
|
|
|
550
576
|
const entry = log.find(e => e.id === entryId);
|
|
551
577
|
if (!entry) return { error: `no such history entry: ${entryId}` };
|
|
552
578
|
if (entry.reverted) return entry;
|
|
553
|
-
const
|
|
579
|
+
const group = _groupOf(log, entry);
|
|
580
|
+
const result = group ? await _revertGroupInner(scanRoot, group) : await _revertEntryInner(scanRoot, entry);
|
|
554
581
|
if (!result.error) await _writeLogAndSync(scanRoot, log);
|
|
555
582
|
return result;
|
|
556
583
|
});
|
package/src/report/index.js
CHANGED
|
@@ -542,7 +542,10 @@ export function normalizeFindings(scan){
|
|
|
542
542
|
version: sc.version,
|
|
543
543
|
cveAliases: sc.cveAliases || [],
|
|
544
544
|
osvId: sc.osvId || null,
|
|
545
|
-
advisory: sc.advisory || sc.description || '',
|
|
545
|
+
advisory: sc.advisory || sc.description || sc.summary || '',
|
|
546
|
+
// Hackage (and any matcher that decides from ranges): WHY the dependency matched. Without these a `possibly-affected` finding
|
|
547
|
+
// (only a declared range is known) reads exactly like an `affected` one (the resolved version is inside the range).
|
|
548
|
+
...(sc.matchStatus ? { matchStatus: sc.matchStatus, matchReason: sc.matchReason || null, declaredRange: sc.declaredRange || null, resolution: sc.resolution || null, ghcComponent: sc.ghcComponent === true } : {}),
|
|
546
549
|
fixedIn: sc.range || (Array.isArray(sc.fixedIn) && sc.fixedIn.length ? sc.fixedIn : null),
|
|
547
550
|
...(Number.isInteger(sc.line) && sc.line > 0 ? { declaringLine: sc.line, declaringFile: sc.file || null } : {}),
|
|
548
551
|
// Feat-9: real-world risk signals
|