fix(llm-security): supply-chain gate bypasses — npm/yarn blocklist + pip-audit (#14-#19,#48)
#18 dep-auditor called 'pip audit' (no such subcommand) so Python CVE detection was a permanent silent no-op; now spawns 'pip-audit' with an argv array. #19 it audited the scanner HOST's env with results mislabelled to the target's requirements.txt; now audits the target via -r and skips cleanly when absent. #14 the offline npm blocklist was skipped for bare/range/tag installs because it used the (null) parsed-spec version; now re-checks the resolved version before the OSV network path. #15 non-hoisted nested lockfile keys derived the wrong package name (leading-only node_modules/ strip); now strips to the last segment. #48 lockfileVersion-1 nested dependency trees are now walked recursively. #16/#17 the yarn.lock matcher paired two unassociated whole-file substrings with an unanchored pkg@ (false BLOCK of a legit package) and only matched Yarn Classic quoted versions (Berry known-malware allowed); rewritten as a per-entry parser that associates version to its own entry, anchors the name, and matches both Classic and Berry. Suite 1931/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcQyMTQfyrsAapaCMPxTtQ
This commit is contained in:
parent
76f190cf78
commit
196517f38a
4 changed files with 330 additions and 24 deletions
|
|
@ -10,7 +10,7 @@ import { levenshtein, tokenize, tokenOverlap, TYPOSQUAT_SUSPICIOUS_TOKENS } from
|
|||
import { readFile } from 'node:fs/promises';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { execSync } from 'node:child_process';
|
||||
import { execSync, spawnSync } from 'node:child_process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -181,23 +181,24 @@ function runNpmAudit(targetPath) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Run pip audit --format json and return findings.
|
||||
* Gracefully handles pip audit not installed, timeout, parse errors.
|
||||
* Run pip-audit --format json against the TARGET's requirements.txt.
|
||||
* Skips cleanly when the target has no requirements.txt — auditing the
|
||||
* scanner host's environment would misattribute host findings to the target.
|
||||
* Gracefully handles pip-audit not installed, timeout, parse errors.
|
||||
* @param {string} targetPath
|
||||
* @returns {object[]} findings
|
||||
*/
|
||||
function runPipAudit(targetPath) {
|
||||
const findings = [];
|
||||
let raw;
|
||||
try {
|
||||
raw = execSync('pip audit --format json', {
|
||||
cwd: targetPath,
|
||||
timeout: 30_000,
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
}).toString();
|
||||
} catch (err) {
|
||||
raw = err.stdout ? err.stdout.toString() : null;
|
||||
}
|
||||
const reqPath = join(targetPath, 'requirements.txt');
|
||||
if (!existsSync(reqPath)) return findings;
|
||||
const res = spawnSync('pip-audit', ['--format', 'json', '-r', reqPath], {
|
||||
cwd: targetPath,
|
||||
timeout: 30_000,
|
||||
encoding: 'utf-8',
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
});
|
||||
const raw = res.stdout || null;
|
||||
|
||||
if (!raw || raw.trim().length === 0) return findings;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue