From 91032cd4735902271b94e935add1630fabed71e5 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Tue, 21 Jan 2025 20:28:10 +0600 Subject: [PATCH] fix(checks): check no-cache flag only with add command in DS025 Signed-off-by: Nikita Pivkin --- checks/docker/missing_apk_no_cache.rego | 30 ++++------- checks/docker/missing_apk_no_cache_test.rego | 54 ++++++++++++++++++++ lib/docker/cmdutil.rego | 23 +++++++++ lib/docker/cmdutil_test.rego | 23 +++++++++ 4 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 lib/docker/cmdutil.rego create mode 100644 lib/docker/cmdutil_test.rego diff --git a/checks/docker/missing_apk_no_cache.rego b/checks/docker/missing_apk_no_cache.rego index d6bbc2a1..f33f8712 100644 --- a/checks/docker/missing_apk_no_cache.rego +++ b/checks/docker/missing_apk_no_cache.rego @@ -19,28 +19,16 @@ package builtin.dockerfile.DS025 import rego.v1 +import data.lib.cmdutil import data.lib.docker -get_apk contains output if { - run := docker.run[_] - arg := run.Value[0] - - regex.match("apk (-[a-zA-Z]+\\s*)*add", arg) - - not contains_no_cache(arg) - - output := { - "cmd": run, - "arg": arg, - } -} - deny contains res if { - output := get_apk[_] - msg := sprintf("'--no-cache' is missed: %s", [output.arg]) - res := result.new(msg, output.cmd) -} - -contains_no_cache(cmd) if { - split(cmd, " ")[_] == "--no-cache" + some run in docker.run + raw_cmd := cmdutil.to_command_string(run.Value) + some tokens in sh.parse_commands(raw_cmd) + cmdutil.is_tool(tokens, "apk") + cmdutil.is_command(tokens, "add") + not cmdutil.has_flag(tokens, "--no-cache") + msg := sprintf("'--no-cache' is missed: %s", [raw_cmd]) + res := result.new(msg, run) } diff --git a/checks/docker/missing_apk_no_cache_test.rego b/checks/docker/missing_apk_no_cache_test.rego index c5a432ea..7421c51d 100644 --- a/checks/docker/missing_apk_no_cache_test.rego +++ b/checks/docker/missing_apk_no_cache_test.rego @@ -41,6 +41,33 @@ test_basic_denied if { r[_].msg == "'--no-cache' is missed: apk add python3" } +test_deny_run_is_array if { + r := deny with input as {"Stages": [{"Name": "alpine:3.17", "Commands": [ + { + "Cmd": "from", + "Value": ["alpine:3.17"], + }, + { + "Cmd": "run", + "Value": ["apk", "add", "python3"], + }, + { + "Cmd": "run", + "Value": ["pip install --no-cache-dir -r /usr/src/app/requirements.txt"], + }, + { + "Cmd": "cmd", + "Value": [ + "python", + "/usr/src/app/app.py", + ], + }, + ]}]} + + count(r) == 1 + r[_].msg == "'--no-cache' is missed: apk add python3" +} + test_wrong_flag_name_denied if { r := deny with input as {"Stages": [{"Name": "alpine:3.5", "Commands": [ { @@ -97,3 +124,30 @@ test_basic_allowed if { count(r) == 0 } + +test_deny_no_cache_flag_with_other_command if { + r := deny with input as {"Stages": [{"Name": "alpine:3.17", "Commands": [ + { + "Cmd": "from", + "Value": ["alpine:3.17"], + }, + { + "Cmd": "run", + "Value": ["apk upgrade --no-cache && apk add python3"], + }, + { + "Cmd": "run", + "Value": ["pip install --no-cache-dir -r /usr/src/app/requirements.txt"], + }, + { + "Cmd": "cmd", + "Value": [ + "python", + "/usr/src/app/app.py", + ], + }, + ]}]} + + count(r) == 1 + r[_].msg == "'--no-cache' is missed: apk upgrade --no-cache && apk add python3" +} diff --git a/lib/docker/cmdutil.rego b/lib/docker/cmdutil.rego new file mode 100644 index 00000000..384583db --- /dev/null +++ b/lib/docker/cmdutil.rego @@ -0,0 +1,23 @@ +# METADATA +# custom: +# library: true +# input: +# selector: +# - type: dockerfile +package lib.cmdutil + +import rego.v1 + +is_tool(tokens, tool) if tokens[0] == tool + +is_command(tokens, command) if { + no_flags := [t | some t in tokens; not startswith(t, "-")] + no_flags[1] == command +} + +has_flag(tokens, flag) if { + some token in tokens + flag == token +} + +to_command_string(value) := concat(" ", value) diff --git a/lib/docker/cmdutil_test.rego b/lib/docker/cmdutil_test.rego new file mode 100644 index 00000000..f49913e5 --- /dev/null +++ b/lib/docker/cmdutil_test.rego @@ -0,0 +1,23 @@ +package lib.cmdutil_test + +import rego.v1 + +import data.lib.cmdutil + +cmd := ["apk", "-q", "add", "--no-cache", "bash"] + +test_is_tool if { + cmdutil.is_tool(cmd, "apk") + not cmdutil.is_tool(cmd, "apt") +} + +test_is_command if { + cmdutil.is_command(cmd, "add") + not cmdutil.is_command(cmd, "apk") + not cmdutil.is_command(cmd, "bash") +} + +test_has_flag if { + cmdutil.has_flag(cmd, "--no-cache") + not cmdutil.has_flag(cmd, "--foo") +}