diff --git a/assets/queries/dockerfile/changing_default_shell_using_run_command/metadata.json b/assets/queries/dockerfile/changing_default_shell_using_run_command/metadata.json new file mode 100644 index 00000000000..0714beb7ec5 --- /dev/null +++ b/assets/queries/dockerfile/changing_default_shell_using_run_command/metadata.json @@ -0,0 +1,10 @@ +{ + "id": "8a301064-c291-4b20-adcb-403fe7fd95fd", + "queryName": "Changing Default Shell Using RUN Command", + "severity": "MEDIUM", + "category": "Best Practices", + "descriptionText": "Using the command RUN to override the default shell instead of the SHELL command leads to inefficiencies. It also does not make sense since Docker provides the SHELL command for this exact purpose.", + "descriptionUrl": "https://docs.docker.com/engine/reference/builder/#shell", + "platform": "Dockerfile", + "descriptionID": "d859b2eb" +} diff --git a/assets/queries/dockerfile/changing_default_shell_using_run_command/query.rego b/assets/queries/dockerfile/changing_default_shell_using_run_command/query.rego new file mode 100644 index 00000000000..d217ce50692 --- /dev/null +++ b/assets/queries/dockerfile/changing_default_shell_using_run_command/query.rego @@ -0,0 +1,56 @@ +package Cx + +import data.generic.common as common_lib + +shell_possibilities := { + "/bin/bash", + "/bin/tcsh", + "/bin/ksh", + "/bin/csh", + "/bin/dash", + "etc/shells", + "/bin/zsh", + "/bin/fish", + "/bin/tmux", + "/bin/rbash", + "/bin/sh", + "/usr/bin/zsh", +} + +CxPolicy[result] { + resource := input.document[i].command[name][_] + resource.Cmd == "run" + value := resource.Value + + contains(value[v], shell_possibilities[p]) + run_values := split(value[v], " ") + command := run_values[0] + command_possibilities := {"mv", "chsh", "usermod", "ln"} + command == command_possibilities[cp] + + result := { + "debug": sprintf("%s", [value[v]]), + "documentId": input.document[i].id, + "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("FROM={{%s}}.{{%s}} should use the SHELL command to change the default shell", [name, resource.Original]), + "keyActualValue": sprintf("FROM={{%s}}.{{%s}} uses the RUN command to change the default shell", [name, resource.Original]), + } +} + +CxPolicy[result] { + resource := input.document[i].command[name][_] + resource.Cmd == "run" + value := resource.Value + run_values := split(value[v], " ") + command := run_values[0] + contains(command, "powershell") + + result := { + "documentId": input.document[i].id, + "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("FROM={{%s}}.{{%s}} should use the SHELL command to change the default shell", [name, resource.Original]), + "keyActualValue": sprintf("FROM={{%s}}.{{%s}} uses the RUN command to change the default shell", [name, resource.Original]), + } +} diff --git a/assets/queries/dockerfile/changing_default_shell_using_shell_command/test/negative.dockerfile b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative1.dockerfile similarity index 80% rename from assets/queries/dockerfile/changing_default_shell_using_shell_command/test/negative.dockerfile rename to assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative1.dockerfile index 2c6c823cd61..f94f4356f19 100644 --- a/assets/queries/dockerfile/changing_default_shell_using_shell_command/test/negative.dockerfile +++ b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative1.dockerfile @@ -2,7 +2,7 @@ FROM alpine:3.5 RUN apk add --update py2-pip RUN sudo yum install -y bundler RUN yum install -RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 "c:\foo.txt""] +SHELL ["/bin/bash", "-c"] COPY requirements.txt /usr/src/app/ RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt COPY app.py /usr/src/app/ diff --git a/assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative2.dockerfile b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative2.dockerfile new file mode 100644 index 00000000000..ff58ecf92d3 --- /dev/null +++ b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative2.dockerfile @@ -0,0 +1,11 @@ +FROM alpine:3.5 +RUN apk add --update py2-pip +RUN sudo yum install -y bundler +RUN yum install +SHELL ["cmd", "/S", "/C"] +COPY requirements.txt /usr/src/app/ +RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt +COPY app.py /usr/src/app/ +COPY templates/index.html /usr/src/app/templates/ +EXPOSE 5000 +CMD ["python", "/usr/src/app/app.py"] diff --git a/assets/queries/dockerfile/changing_default_shell_using_shell_command/test/positive.dockerfile b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative3.dockerfile similarity index 100% rename from assets/queries/dockerfile/changing_default_shell_using_shell_command/test/positive.dockerfile rename to assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative3.dockerfile diff --git a/assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative4.dockerfile b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative4.dockerfile new file mode 100644 index 00000000000..18b828c94de --- /dev/null +++ b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative4.dockerfile @@ -0,0 +1,11 @@ +FROM alpine:3.5 +RUN apk add --update py2-pip +RUN sudo yum install -y bundler +RUN yum install +SHELL ["/bin/sh", "-c"] +COPY requirements.txt /usr/src/app/ +RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt +COPY app.py /usr/src/app/ +COPY templates/index.html /usr/src/app/templates/ +EXPOSE 5000 +CMD ["python", "/usr/src/app/app.py"] diff --git a/assets/queries/dockerfile/changing_default_shell_using_run_command/test/positive1.dockerfile b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/positive1.dockerfile new file mode 100644 index 00000000000..ab7639f03c3 --- /dev/null +++ b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/positive1.dockerfile @@ -0,0 +1,11 @@ +FROM alpine:3.5 +RUN apk add --update py2-pip +RUN sudo yum install -y bundler +RUN yum install +RUN ln -sfv /bin/bash /bin/sh +COPY requirements.txt /usr/src/app/ +RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt +COPY app.py /usr/src/app/ +COPY templates/index.html /usr/src/app/templates/ +EXPOSE 5000 +CMD ["python", "/usr/src/app/app.py"] diff --git a/assets/queries/dockerfile/changing_default_shell_using_run_command/test/positive2.dockerfile b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/positive2.dockerfile new file mode 100644 index 00000000000..b81cb2e145f --- /dev/null +++ b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/positive2.dockerfile @@ -0,0 +1,11 @@ +FROM alpine:3.5 +RUN apk add --update py2-pip +RUN sudo yum install -y bundler +RUN yum install +RUN powershell -command +COPY requirements.txt /usr/src/app/ +RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt +COPY app.py /usr/src/app/ +COPY templates/index.html /usr/src/app/templates/ +EXPOSE 5000 +CMD ["python", "/usr/src/app/app.py"] diff --git a/assets/queries/dockerfile/changing_default_shell_using_run_command/test/positive_expected_result.json b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/positive_expected_result.json new file mode 100644 index 00000000000..c9854941220 --- /dev/null +++ b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/positive_expected_result.json @@ -0,0 +1,14 @@ +[ + { + "queryName": "Changing Default Shell Using RUN Command", + "severity": "MEDIUM", + "line": 5, + "filename": "positive1.dockerfile" + }, + { + "queryName": "Changing Default Shell Using RUN Command", + "severity": "MEDIUM", + "line": 5, + "filename": "positive2.dockerfile" + } +] diff --git a/assets/queries/dockerfile/changing_default_shell_using_shell_command/metadata.json b/assets/queries/dockerfile/changing_default_shell_using_shell_command/metadata.json deleted file mode 100644 index e6bc21bb0a3..00000000000 --- a/assets/queries/dockerfile/changing_default_shell_using_shell_command/metadata.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "id": "8a301064-c291-4b20-adcb-403fe7fd95fd", - "queryName": "Changing Default Shell Using SHELL Command", - "severity": "MEDIUM", - "category": "Insecure Defaults", - "descriptionText": "Using the command SHELL to override the default shell instead of the RUN command", - "descriptionUrl": "https://docs.docker.com/engine/reference/builder/#shell", - "platform": "Dockerfile", - "descriptionID": "d859b2eb" -} diff --git a/assets/queries/dockerfile/changing_default_shell_using_shell_command/query.rego b/assets/queries/dockerfile/changing_default_shell_using_shell_command/query.rego deleted file mode 100644 index bfbad039d76..00000000000 --- a/assets/queries/dockerfile/changing_default_shell_using_shell_command/query.rego +++ /dev/null @@ -1,14 +0,0 @@ -package Cx - -CxPolicy[result] { - resource := input.document[i].command[name][_] - resource.Cmd == "shell" - - result := { - "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), - "issueType": "IncorrectValue", - "keyExpectedValue": sprintf("FROM={{%s}}.{{%s}} uses the RUN command to change the default shell", [name, resource.Original]), - "keyActualValue": sprintf("FROM={{%s}}.{{%s}} uses the SHELL command to change the default shell", [name, resource.Original]), - } -} diff --git a/assets/queries/dockerfile/changing_default_shell_using_shell_command/test/positive_expected_result.json b/assets/queries/dockerfile/changing_default_shell_using_shell_command/test/positive_expected_result.json deleted file mode 100644 index 0fbbc5c68db..00000000000 --- a/assets/queries/dockerfile/changing_default_shell_using_shell_command/test/positive_expected_result.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "queryName": "Changing Default Shell Using SHELL Command", - "severity": "MEDIUM", - "line": 5 - } -] diff --git a/assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/metadata.json b/assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/metadata.json deleted file mode 100644 index 9ea6d8e430f..00000000000 --- a/assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/metadata.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "id": "68a51e22-ae5a-4d48-8e87-b01a323605c9", - "queryName": "COPY '--from' Without FROM Alias Defined Previously", - "severity": "MEDIUM", - "category": "Build Process", - "descriptionText": "COPY command with the flag '--from' should mention a previously defined FROM alias", - "descriptionUrl": "https://docs.docker.com/develop/develop-images/multistage-build/", - "platform": "Dockerfile", - "descriptionID": "dea09829" -} diff --git a/assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/query.rego b/assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/query.rego deleted file mode 100644 index 4bdb470b56b..00000000000 --- a/assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/query.rego +++ /dev/null @@ -1,31 +0,0 @@ -package Cx - -CxPolicy[result] { - resource := input.document[i].command[name][_] - resource.Cmd == "copy" - - contains(resource.Flags[x], "--from=") - resource.Flags[x] != "--from=0" - aux_split := split(resource.Flags[x], "=") - - not isPreviousAlias(resource._kics_line, aux_split[1]) - - result := { - "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), - "issueType": "IncorrectValue", - "keyExpectedValue": "COPY '--from' references a previous defined FROM alias", - "keyActualValue": "COPY '--from' does not reference a previous defined FROM alias", - } -} - -isPreviousAlias(startLine, currentAlias) = allow { - resource := input.document[i].command[name][_] - resource.Cmd == "from" - previousAlias := resource.Value[2] - - previousAlias == currentAlias - resource.EndLine < startLine - - allow = true -} diff --git a/assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/test/positive.dockerfile b/assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/test/positive.dockerfile deleted file mode 100644 index 1132a415898..00000000000 --- a/assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/test/positive.dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM golang:1.7.3 AS builder -WORKDIR /go/src/github.com/alexellis/href-counter/ -RUN go get -d -v golang.org/x/net/html -COPY app.go . -RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . - - -FROM alpine:latest -RUN apk --no-cache add ca-certificates -WORKDIR /root/ -COPY --from=builder2 /go/src/github.com/alexellis/href-counter/app . -CMD ["./app"] \ No newline at end of file diff --git a/assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/test/positive_expected_result.json b/assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/test/positive_expected_result.json deleted file mode 100644 index aae698b5213..00000000000 --- a/assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/test/positive_expected_result.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "queryName": "COPY '--from' Without FROM Alias Defined Previously", - "severity": "MEDIUM", - "line": 11 - } -] diff --git a/assets/queries/dockerfile/run_command_cd_instead_of_workdir/metadata.json b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/metadata.json index eca2a3750ee..20ad068fe88 100644 --- a/assets/queries/dockerfile/run_command_cd_instead_of_workdir/metadata.json +++ b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/metadata.json @@ -3,7 +3,7 @@ "queryName": "RUN Instruction Using 'cd' Instead of WORKDIR", "severity": "MEDIUM", "category": "Build Process", - "descriptionText": "Use WORKDIR instead of proliferating instructions like RUN cd \u2026 && do-something, which are hard to read, troubleshoot, and maintain.", + "descriptionText": "When using RUN command 'cd' should only be used for full path. For relative path make use of WORKDIR command instead.", "descriptionUrl": "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#workdir", "platform": "Dockerfile", "descriptionID": "edd9f7d3" diff --git a/assets/queries/dockerfile/run_command_cd_instead_of_workdir/query.rego b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/query.rego index 6f6d560fb9c..9a75bbd392b 100644 --- a/assets/queries/dockerfile/run_command_cd_instead_of_workdir/query.rego +++ b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/query.rego @@ -3,13 +3,25 @@ package Cx CxPolicy[result] { resource := input.document[i].command[name][_] resource.Cmd == "run" - contains(resource.Value[_], "cd ") + run_command := resource.Value[_] + values := split(run_command, " ") + trim_space(values[index]) == "cd" + path := trim_space(values[index+1]) + not is_full_path(path) + result := { "documentId": input.document[i].id, "searchKey": sprintf("FROM={{%s}}.RUN={{%s}}", [name, resource.Value[0]]), - "issueType": "IncorrectValue", #"MissingAttribute" / "RedundantAttribute" + "issueType": "IncorrectValue", "keyExpectedValue": "Using WORKDIR to change directory", "keyActualValue": sprintf("RUN %s'", [resource.Value[0]]), } } + +is_full_path(path){ + regex.match("^[a-zA-Z]:[\\\/]", path) +}else { + startswith( path,"/") + not contains(path, "/.") +} diff --git a/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/negative2.dockerfile b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/negative2.dockerfile new file mode 100644 index 00000000000..c389c424c34 --- /dev/null +++ b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/negative2.dockerfile @@ -0,0 +1,5 @@ +FROM nginx +ENV AUTHOR=Docker +RUN cd /usr/share/nginx/html +COPY Hello_docker.html /usr/share/nginx/html +CMD cd /usr/share/nginx/html && sed -e s/Docker/"$AUTHOR"/ Hello_docker.html > index.html ; nginx -g 'daemon off;' diff --git a/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/positive.dockerfile b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/positive.dockerfile index 76717f5f418..2a34f9c9b19 100644 --- a/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/positive.dockerfile +++ b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/positive.dockerfile @@ -1,5 +1,17 @@ FROM nginx ENV AUTHOR=Docker -RUN cd /usr/share/nginx/html +RUN cd /../share/nginx/html COPY Hello_docker.html /usr/share/nginx/html -CMD cd /usr/share/nginx/html && sed -e s/Docker/"$AUTHOR"/ Hello_docker.html > index.html ; nginx -g 'daemon off;' \ No newline at end of file +CMD cd /usr/share/nginx/html && sed -e s/Docker/"$AUTHOR"/ Hello_docker.html > index.html ; nginx -g 'daemon off;' + +FROM nginx +ENV AUTHOR=Docker +RUN cd ../share/nginx/html +COPY Hello_docker.html /usr/share/nginx/html +CMD cd /usr/share/nginx/html && sed -e s/Docker/"$AUTHOR"/ Hello_docker.html > index.html ; nginx -g 'daemon off;' + +FROM nginx +ENV AUTHOR=Docker +RUN cd /usr/../share/nginx/html +COPY Hello_docker.html /usr/share/nginx/html +CMD cd /usr/share/nginx/html && sed -e s/Docker/"$AUTHOR"/ Hello_docker.html > index.html ; nginx -g 'daemon off;' diff --git a/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/positive_expected_result.json b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/positive_expected_result.json index 758167166d5..81f43312460 100644 --- a/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/positive_expected_result.json +++ b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/positive_expected_result.json @@ -2,6 +2,19 @@ { "queryName": "RUN Instruction Using 'cd' Instead of WORKDIR", "severity": "MEDIUM", - "line": 3 + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "RUN Instruction Using 'cd' Instead of WORKDIR", + "severity": "MEDIUM", + "line": 9, + "fileName": "positive.dockerfile" + }, + { + "queryName": "RUN Instruction Using 'cd' Instead of WORKDIR", + "severity": "MEDIUM", + "line": 15, + "fileName": "positive.dockerfile" } ] diff --git a/assets/queries/dockerfile/using_unnamed_build_stages/metadata.json b/assets/queries/dockerfile/using_unnamed_build_stages/metadata.json new file mode 100644 index 00000000000..a86ba7ad0e7 --- /dev/null +++ b/assets/queries/dockerfile/using_unnamed_build_stages/metadata.json @@ -0,0 +1,10 @@ +{ + "id": "68a51e22-ae5a-4d48-8e87-b01a323605c9", + "queryName": "Using Unnamed Build Stages", + "severity": "LOW", + "category": "Build Process", + "descriptionText": " This query is used to ensure that build stages are named. This way even if the Dockerfile is re-ordered, the COPY instruction doesn’t break.", + "descriptionUrl": "https://docs.docker.com/develop/develop-images/multistage-build/", + "platform": "Dockerfile", + "descriptionID": "dea09829" +} diff --git a/assets/queries/dockerfile/using_unnamed_build_stages/query.rego b/assets/queries/dockerfile/using_unnamed_build_stages/query.rego new file mode 100644 index 00000000000..ff621dc15f8 --- /dev/null +++ b/assets/queries/dockerfile/using_unnamed_build_stages/query.rego @@ -0,0 +1,21 @@ +package Cx + +CxPolicy[result] { + + commands := input.document[i].command[name][_] + + commands.Cmd == "copy" + flags := commands.Flags + contains(flags[f], "--from=") + flag_split := split(flags[f], "=") + to_number(flag_split[1]) > -1 + + + result := { + "documentId": input.document[i].id, + "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, commands.Original]), + "issueType": "IncorrectValue", + "keyExpectedValue": "COPY '--from' should reference a previously defined FROM alias", + "keyActualValue": "COPY '--from' does not reference a previously defined FROM alias", + } +} diff --git a/assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/test/negative.dockerfile b/assets/queries/dockerfile/using_unnamed_build_stages/test/negative1.dockerfile similarity index 100% rename from assets/queries/dockerfile/copy_from_without_from_alias_defined_previously/test/negative.dockerfile rename to assets/queries/dockerfile/using_unnamed_build_stages/test/negative1.dockerfile diff --git a/assets/queries/dockerfile/using_unnamed_build_stages/test/positive1.dockerfile b/assets/queries/dockerfile/using_unnamed_build_stages/test/positive1.dockerfile new file mode 100644 index 00000000000..5ff63e3c9d7 --- /dev/null +++ b/assets/queries/dockerfile/using_unnamed_build_stages/test/positive1.dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.16 +WORKDIR /go/src/github.com/alexellis/href-counter/ +RUN go get -d -v golang.org/x/net/html +COPY app.go ./ +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . + +FROM alpine:latest +RUN apk --no-cache add ca-certificates +WORKDIR /root/ +COPY --from=0 /go/src/github.com/alexellis/href-counter/app ./ +CMD ["./app"] diff --git a/assets/queries/dockerfile/using_unnamed_build_stages/test/positive_expected_result.json b/assets/queries/dockerfile/using_unnamed_build_stages/test/positive_expected_result.json new file mode 100644 index 00000000000..d0e9eb1f3db --- /dev/null +++ b/assets/queries/dockerfile/using_unnamed_build_stages/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Using Unnamed Build Stages", + "severity": "LOW", + "line": 10, + "filename": "positive1.dockerfile" + } +]