fix(scanner): ignore benign member exec matches

This commit is contained in:
Vincent Koc
2026-05-02 16:56:20 -07:00
parent 3c8de6eb72
commit 4be4c475ea
2 changed files with 36 additions and 0 deletions

View File

@@ -164,6 +164,14 @@ exec(cmd);
source: `
const cp = require("child_process");
cp.spawn("node", ["server.js"]);
`,
expected: { ruleId: "dangerous-exec", severity: "critical" as const },
},
{
name: "detects child_process namespaced exec usage",
source: `
const cp = require("child_process");
cp.exec("node server.js");
`,
expected: { ruleId: "dangerous-exec", severity: "critical" as const },
},
@@ -247,6 +255,16 @@ const options: ExecOptions = { timeout: 5000 };
expect(findings.some((f) => f.ruleId === "dangerous-exec")).toBe(false);
});
it("does not flag RegExp.exec when child_process appears elsewhere", () => {
const source = `
import type { ExecOptions } from "child_process";
const options: ExecOptions = {};
const match = /^keychain:(.+)$/.exec(value);
`;
const findings = scanSource(source, "plugin.ts");
expect(findings.some((f) => f.ruleId === "dangerous-exec")).toBe(false);
});
it("returns empty array for clean plugin code", () => {
const source = `
export function greet(name: string): string {

View File

@@ -219,6 +219,20 @@ function truncateEvidence(evidence: string, maxLen = 120): string {
return `${evidence.slice(0, maxLen)}`;
}
function isBenignMemberExecMatch(line: string, match: RegExpExecArray): boolean {
const command = match[1];
if (command !== "exec") {
return false;
}
const matchIndex = match.index;
if (matchIndex <= 0 || line[matchIndex - 1] !== ".") {
return false;
}
return !/\b(?:cp|childProcess|child_process)\s*\.\s*exec\s*\(/.test(line);
}
export function scanSource(source: string, filePath: string): SkillScanFinding[] {
const findings: SkillScanFinding[] = [];
const lines = source.split("\n");
@@ -242,6 +256,10 @@ export function scanSource(source: string, filePath: string): SkillScanFinding[]
continue;
}
if (rule.ruleId === "dangerous-exec" && isBenignMemberExecMatch(line, match)) {
continue;
}
// Special handling for suspicious-network: check port
if (rule.ruleId === "suspicious-network") {
const port = Number.parseInt(match[1], 10);