Skip to content

Commit 56aa535

Browse files
authored
fix: enable exclude base image vulns in monitor (#761)
This commit fixes an issue where the exclude-base-image-vulns flag did not function in monitor mode (filtering base image vulns does work in test mode). Root Cause: Base image vulnerabilities are filtered in `build-response.ts`, but the filtered dependency tree was not stored and returned to the cli. This was partially masked by the fact that the cli performed its own filtering in the test path (`run-test.ts`) but not in the monitor path (`ecosystems/monitor.ts`). Changes: - In `response-builder.ts`, stores the `finalDeps` to the `depTree`. - Adds unit tests for this functionality. Impact: Restores the ability to filter base images vulnerabilities in monitor mode. Tickets: CN-568
1 parent 66cda90 commit 56aa535

File tree

2 files changed

+117
-0
lines changed

2 files changed

+117
-0
lines changed

lib/response-builder.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ async function buildResponse(
3535
/** WARNING! Mutates the depTree.dependencies! */
3636
annotateLayerIds(finalDeps, dockerfilePkgs);
3737

38+
// Apply the filtered dependencies back to the depTree
39+
depsAnalysis.depTree.dependencies = finalDeps;
40+
3841
/** This must be called after all final changes to the DependencyTree. */
3942
const depGraph = await legacy.depTreeToGraph(
4043
depsAnalysis.depTree,

test/lib/response-builder.spec.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,4 +1351,118 @@ describe("buildResponse", () => {
13511351
});
13521352
});
13531353
});
1354+
1355+
describe("supports exclude-base-image-vulns flag", () => {
1356+
const defaultAnalysis = createMockAnalysis();
1357+
const baseDepTree = {
1358+
...defaultAnalysis.depTree,
1359+
targetOS: {
1360+
name: "alpine",
1361+
version: "3.12",
1362+
prettyName: "Alpine 3.12",
1363+
},
1364+
dependencies: {
1365+
basePkg: {
1366+
name: "basePkg",
1367+
version: "1.0.0",
1368+
dependencies: {},
1369+
},
1370+
dockerfilePkg: {
1371+
name: "dockerfilePkg",
1372+
version: "2.0.0",
1373+
dependencies: {},
1374+
},
1375+
},
1376+
};
1377+
1378+
const dockerfileAnalysisWithDockerfilePkgOnly = {
1379+
baseImage: "alpine:3.12",
1380+
dockerfilePackages: {
1381+
dockerfilePkg: {
1382+
instruction: "RUN apk add dockerfilePkg",
1383+
installCommand: "apk add dockerfilePkg",
1384+
},
1385+
},
1386+
dockerfileLayers: {},
1387+
};
1388+
1389+
function getDepPkgNames(scanResult: {
1390+
facts?: Array<{ type: string; data: any }>;
1391+
}): string[] {
1392+
const depGraphFact = scanResult.facts?.find((f) => f.type === "depGraph");
1393+
const depGraph = depGraphFact?.data;
1394+
if (!depGraph || typeof depGraph.getPkgs !== "function") {
1395+
return [];
1396+
}
1397+
return depGraph.getPkgs().map((p: { name: string }) => p.name);
1398+
}
1399+
1400+
const rootPkgName = defaultAnalysis.depTree.name;
1401+
1402+
it("includes all dependencies in depGraph when excludeBaseImageVulns is false", async () => {
1403+
const mockAnalysis = createMockAnalysis({
1404+
depTree: JSON.parse(JSON.stringify(baseDepTree)),
1405+
packageFormat: "apk",
1406+
});
1407+
1408+
const result = await buildResponse(
1409+
mockAnalysis as any,
1410+
dockerfileAnalysisWithDockerfilePkgOnly as any,
1411+
false,
1412+
undefined,
1413+
undefined,
1414+
undefined,
1415+
);
1416+
1417+
const pkgNames = getDepPkgNames(result.scanResults[0]);
1418+
expect(pkgNames).toContain(rootPkgName);
1419+
expect(pkgNames).toContain("basePkg");
1420+
expect(pkgNames).toContain("dockerfilePkg");
1421+
expect(pkgNames).toHaveLength(3);
1422+
});
1423+
1424+
it("includes only dockerfile-introduced dependencies in depGraph when excludeBaseImageVulns is true", async () => {
1425+
const mockAnalysis = createMockAnalysis({
1426+
depTree: JSON.parse(JSON.stringify(baseDepTree)),
1427+
packageFormat: "apk",
1428+
});
1429+
1430+
const result = await buildResponse(
1431+
mockAnalysis as any,
1432+
dockerfileAnalysisWithDockerfilePkgOnly as any,
1433+
true,
1434+
undefined,
1435+
undefined,
1436+
undefined,
1437+
);
1438+
1439+
const pkgNames = getDepPkgNames(result.scanResults[0]);
1440+
expect(pkgNames).toContain(rootPkgName);
1441+
expect(pkgNames).toContain("dockerfilePkg");
1442+
expect(pkgNames).not.toContain("basePkg");
1443+
expect(pkgNames).toHaveLength(2);
1444+
});
1445+
1446+
it("includes all dependencies when excludeBaseImageVulns is true but dockerfileAnalysis is undefined", async () => {
1447+
const mockAnalysis = createMockAnalysis({
1448+
depTree: JSON.parse(JSON.stringify(baseDepTree)),
1449+
packageFormat: "apk",
1450+
});
1451+
1452+
const result = await buildResponse(
1453+
mockAnalysis as any,
1454+
undefined,
1455+
true,
1456+
undefined,
1457+
undefined,
1458+
undefined,
1459+
);
1460+
1461+
const pkgNames = getDepPkgNames(result.scanResults[0]);
1462+
expect(pkgNames).toContain(rootPkgName);
1463+
expect(pkgNames).toContain("basePkg");
1464+
expect(pkgNames).toContain("dockerfilePkg");
1465+
expect(pkgNames).toHaveLength(3);
1466+
});
1467+
});
13541468
});

0 commit comments

Comments
 (0)