Skip to content

Commit a23cb80

Browse files
authored
feat: add dnf and microdnf support to Dockerfile instruction parser (#763)
The installRegex did not recognize dnf install or microdnf install commands, causing layer attribution and --exclude-base-image-vulns to potentially fail for RPM-based images that use DNF (e.g., RHEL 8+, Fedora, CentOS Stream, Rocky Linux).
1 parent 56aa535 commit a23cb80

File tree

7 files changed

+45
-2
lines changed

7 files changed

+45
-2
lines changed

lib/dockerfile/instruction-parser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export {
1818
// Naive regex; see tests for cases
1919
// tslint:disable-next-line:max-line-length
2020
const installRegex =
21-
/(rpm\s+-i|rpm\s+--install|apk\s+((--update|-u|--no-cache)\s+)*add(\s+(--update|-u|--no-cache))*|apt-get\s+((--assume-yes|--yes|-y)\s+)*install(\s+(--assume-yes|--yes|-y))*|apt\s+((--assume-yes|--yes|-y)\s+)*install|yum\s+install|aptitude\s+install)\s+/;
21+
/(rpm\s+-i|rpm\s+--install|apk\s+((--update|-u|--no-cache)\s+)*add(\s+(--update|-u|--no-cache))*|apt-get\s+((--assume-yes|--yes|-y)\s+)*install(\s+(--assume-yes|--yes|-y))*|apt\s+((--assume-yes|--yes|-y)\s+)*install|dnf\s+((--assumeyes|--best|--nodocs|--allowerasing|-y)\s+)*install(\s+(--assumeyes|--best|--nodocs|--allowerasing|-y))*|microdnf\s+((--nodocs|--best|--assumeyes|-y)\s+)*install(\s+(--nodocs|--best|--assumeyes|-y))*|yum\s+install|aptitude\s+install)\s+/;
2222

2323
function getPackagesFromDockerfile(dockerfile: Dockerfile): DockerFilePackages {
2424
const runInstructions = getRunInstructionsFromDockerfile(dockerfile);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM registry.access.redhat.com/ubi9/ubi-minimal:latest
2+
3+
RUN microdnf install -y curl && microdnf clean all
4+
5+
CMD ["bash"]

test/lib/dockerfile.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,28 @@ describe("readDockerfileAndAnalyse() correctly parses...", () => {
245245
},
246246
},
247247
],
248+
[
249+
"a Dockerfile with a microdnf installation instruction",
250+
{
251+
fixture: "with-dnf-installation",
252+
expected: {
253+
baseImage: "registry.access.redhat.com/ubi9/ubi-minimal:latest",
254+
dockerfilePackages: {
255+
curl: {
256+
instruction: "RUN microdnf install -y curl && microdnf clean all",
257+
},
258+
},
259+
dockerfileLayers: {
260+
"UlVOIG1pY3JvZG5mIGluc3RhbGwgLXkgY3VybCAmJiBtaWNyb2RuZiBjbGVhbiBhbGw=":
261+
{
262+
instruction:
263+
"RUN microdnf install -y curl && microdnf clean all",
264+
},
265+
},
266+
error: undefined,
267+
},
268+
},
269+
],
248270
];
249271
// tslint:disable-next-line: variable-name
250272
test.each<TestCaseTuple>(cases)("%s", async (_description, item) => {

test/lib/instructions-parser.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ describe("getPackagesFromRunInstructions", () => {
3232
],
3333
[["apt install 389-admin"], ["389-admin"]],
3434
[["apt install apache2=2.3.35-4ubuntu1"], ["apache2"]],
35+
[["dnf install curl"], ["curl"]],
36+
[["dnf install -y curl"], ["curl"]],
37+
[["dnf -y install curl"], ["curl"]],
38+
[["dnf --nodocs install -y curl"], ["curl"]],
39+
[["dnf install -y curl wget vim"], ["vim", "curl", "wget"]],
40+
[["dnf install -y curl && dnf install -y wget"], ["curl", "wget"]],
41+
[["microdnf install curl"], ["curl"]],
42+
[["microdnf install --nodocs curl"], ["curl"]],
43+
[["microdnf install -y curl && microdnf clean all"], ["curl"]],
3544
];
3645

3746
describe("Verify package detection", () => {
@@ -78,6 +87,7 @@ describe("getPackagesFromDockerFile", () => {
7887
["library/nginx"],
7988
["with-args-package"],
8089
["with-multiple-run-instructions"],
90+
["with-dnf-installation"],
8191
];
8292

8393
test.each(dockerfileFixtures)(

test/lib/save/image.tar

45.2 MB
Binary file not shown.

test/matchers/dockerPackageInstallCommand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ declare global {
88

99
export function toBeDockerPackageInstallCommand(received, pkgName) {
1010
const installCmdRegex =
11-
/^(rpm\s+-i|rpm\s+--install|apk\s+((--update|-u|--no-cache)\s+)*add(\s+(--update|-u|--no-cache))*|apt-get\s+((--assume-yes|--yes|-y)\s+)*install(\s+(--assume-yes|--yes|-y))*|apt\s+((--assume-yes|--yes|-y)\s+)*install|yum\s+install|aptitude\s+install)\s+/;
11+
/^(rpm\s+-i|rpm\s+--install|apk\s+((--update|-u|--no-cache)\s+)*add(\s+(--update|-u|--no-cache))*|apt-get\s+((--assume-yes|--yes|-y)\s+)*install(\s+(--assume-yes|--yes|-y))*|apt\s+((--assume-yes|--yes|-y)\s+)*install|dnf\s+((--assumeyes|--best|--nodocs|--allowerasing|-y)\s+)*install(\s+(--assumeyes|--best|--nodocs|--allowerasing|-y))*|microdnf\s+((--nodocs|--best|--assumeyes|-y)\s+)*install(\s+(--nodocs|--best|--assumeyes|-y))*|yum\s+install|aptitude\s+install)\s+/;
1212
const pass =
1313
(installCmdRegex.test(received) &&
1414
received.indexOf(pkgName) > -1 &&

test/unit/dockerPackageInstallCommand.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ describe("valid package install commands", () => {
1515
{ command: "apt install curl", pkg: "curl" },
1616
{ command: "yum install curl", pkg: "curl" },
1717
{ command: "aptitude install curl", pkg: "curl" },
18+
{ command: "dnf install curl", pkg: "curl" },
19+
{ command: "dnf -y install curl", pkg: "curl" },
20+
{ command: "dnf --assumeyes install curl", pkg: "curl" },
21+
{ command: "dnf --nodocs install curl", pkg: "curl" },
22+
{ command: "microdnf install curl", pkg: "curl" },
23+
{ command: "microdnf --nodocs install curl", pkg: "curl" },
1824
];
1925

2026
testCases.forEach(({ command, pkg }) => {

0 commit comments

Comments
 (0)