diff --git a/.github/scripts/validate-search-line/validate_search_line.py b/.github/scripts/validate-search-line/validate_search_line.py index e06ca4ef955..5c5011f4684 100644 --- a/.github/scripts/validate-search-line/validate_search_line.py +++ b/.github/scripts/validate-search-line/validate_search_line.py @@ -30,6 +30,9 @@ def get_changed_queries(): dirs = [] for f in files: if f.endswith("/query.rego"): + if f.startswith("assets/queries/dockerfile/"): + print(f" [SKIP] {f}: Dockerfile queries do not support searchLine") + continue dirs.append(REPO_ROOT / Path(f).parent) return dirs diff --git a/assets/libraries/dockerfile.rego b/assets/libraries/dockerfile.rego index 1ef6f6244f2..dcf4452d95e 100644 --- a/assets/libraries/dockerfile.rego +++ b/assets/libraries/dockerfile.rego @@ -69,4 +69,16 @@ check_multi_stage(imageName, images) { sortedIndex := sort(unsortedIndex) imageName == sortedIndex[minus(count(sortedIndex), 1)].Name -} +} + +get_original_from_command(commands) = from_command { + commands[i].Cmd == "from" + from_command := { + "Value": substring(commands[i].Original, 0, 4), + "LineHint" : commands[i]._kics_line - 1 + } +} + +add_line_hint(raw_search_key, lineHint) = searchKey { + searchKey := sprintf("%s^%d", [raw_search_key, lineHint]) +} \ No newline at end of file diff --git a/assets/queries/dockerfile/add_instead_of_copy/query.rego b/assets/queries/dockerfile/add_instead_of_copy/query.rego index add0d6e9153..4561ec322e0 100644 --- a/assets/queries/dockerfile/add_instead_of_copy/query.rego +++ b/assets/queries/dockerfile/add_instead_of_copy/query.rego @@ -3,16 +3,18 @@ package Cx import data.generic.dockerfile as dockerLib CxPolicy[result] { - resource := input.document[i].command[name][_] - resource.Cmd == "add" + stage := input.document[i].command[name] - not dockerLib.arrayContains(resource.Value, {".tar", ".tar."}) + resource = stage[s] + stage[s].Cmd = "add" + not dockerLib.arrayContains(stage[s].Value, {".tar", ".tar."}) + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("'COPY' %s", [resource.Value[0]]), "keyActualValue": sprintf("'ADD' %s", [resource.Value[0]]), } -} +} \ No newline at end of file diff --git a/assets/queries/dockerfile/add_instead_of_copy/test/negative.dockerfile b/assets/queries/dockerfile/add_instead_of_copy/test/negative1.dockerfile similarity index 100% rename from assets/queries/dockerfile/add_instead_of_copy/test/negative.dockerfile rename to assets/queries/dockerfile/add_instead_of_copy/test/negative1.dockerfile diff --git a/assets/queries/dockerfile/add_instead_of_copy/test/negative2.dockerfile b/assets/queries/dockerfile/add_instead_of_copy/test/negative2.dockerfile new file mode 100644 index 00000000000..b9ed2bb2d3a --- /dev/null +++ b/assets/queries/dockerfile/add_instead_of_copy/test/negative2.dockerfile @@ -0,0 +1,10 @@ +from openjdk:10-jdk +volume /tmp +arg JAR_FILE +copy ${JAR_FILE} app.jar +entrypoint ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] +add http://source.file/package.file.tar.gz /temp +run tar -xjf /temp/package.file.tar.gz \ + && make -C /tmp/package.file \ + && rm /tmp/ package.file.tar.gz +# trigger validation diff --git a/assets/queries/dockerfile/add_instead_of_copy/test/positive.dockerfile b/assets/queries/dockerfile/add_instead_of_copy/test/positive1.dockerfile similarity index 100% rename from assets/queries/dockerfile/add_instead_of_copy/test/positive.dockerfile rename to assets/queries/dockerfile/add_instead_of_copy/test/positive1.dockerfile diff --git a/assets/queries/dockerfile/add_instead_of_copy/test/positive2.dockerfile b/assets/queries/dockerfile/add_instead_of_copy/test/positive2.dockerfile new file mode 100644 index 00000000000..2f1a27bb786 --- /dev/null +++ b/assets/queries/dockerfile/add_instead_of_copy/test/positive2.dockerfile @@ -0,0 +1,9 @@ +from openjdk:10-jdk +volume /tmp +add http://source.file/package.file.tar.gz /temp +run tar -xjf /temp/package.file.tar.gz \ + && make -C /tmp/package.file \ + && rm /tmp/ package.file.tar.gz +arg JAR_FILE +add ${JAR_FILE} app.jar +entrypoint ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] diff --git a/assets/queries/dockerfile/add_instead_of_copy/test/positive_expected_result.json b/assets/queries/dockerfile/add_instead_of_copy/test/positive_expected_result.json index 7e9efb25dd7..f67956ae373 100644 --- a/assets/queries/dockerfile/add_instead_of_copy/test/positive_expected_result.json +++ b/assets/queries/dockerfile/add_instead_of_copy/test/positive_expected_result.json @@ -1,7 +1,14 @@ [ - { - "queryName": "Add Instead of Copy", - "severity": "MEDIUM", - "line": 8 - } -] + { + "queryName": "Add Instead of Copy", + "severity": "MEDIUM", + "line": 8, + "fileName": "positive1.dockerfile" + }, + { + "queryName": "Add Instead of Copy", + "severity": "MEDIUM", + "line": 8, + "fileName": "positive2.dockerfile" + } +] \ No newline at end of file diff --git a/assets/queries/dockerfile/apk_add_using_local_cache_path/query.rego b/assets/queries/dockerfile/apk_add_using_local_cache_path/query.rego index 080ce74aab4..90df21e0979 100644 --- a/assets/queries/dockerfile/apk_add_using_local_cache_path/query.rego +++ b/assets/queries/dockerfile/apk_add_using_local_cache_path/query.rego @@ -10,9 +10,11 @@ CxPolicy[result] { runCommands := dockerLib.getCommands(command.Value[0]) containsApkAddWithoutNoCache(runCommands) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, command.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, command.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "'RUN' should not contain 'apk add' command without '--no-cache' switch", "keyActualValue": "'RUN' contains 'apk add' command without '--no-cache' switch", diff --git a/assets/queries/dockerfile/apk_add_using_local_cache_path/test/negative3.dockerfile b/assets/queries/dockerfile/apk_add_using_local_cache_path/test/negative3.dockerfile new file mode 100644 index 00000000000..0d45df135fd --- /dev/null +++ b/assets/queries/dockerfile/apk_add_using_local_cache_path/test/negative3.dockerfile @@ -0,0 +1,7 @@ +from gliderlabs/alpine:3.3 +run apk add --no-cache python +workdir /app +onbuild COPY . /app +onbuild RUN virtualenv /env && /env/bin/pip install -r /app/requirements.txt +expose 8080 +cmd ["/env/bin/python", "main.py"] \ No newline at end of file diff --git a/assets/queries/dockerfile/apk_add_using_local_cache_path/test/positive3.dockerfile b/assets/queries/dockerfile/apk_add_using_local_cache_path/test/positive3.dockerfile new file mode 100644 index 00000000000..767cb6d1e26 --- /dev/null +++ b/assets/queries/dockerfile/apk_add_using_local_cache_path/test/positive3.dockerfile @@ -0,0 +1,7 @@ +from gliderlabs/alpine:3.3 +run apk add --update-cache python +workdir /app +onbuild COPY . /app +onbuild RUN virtualenv /env && /env/bin/pip install -r /app/requirements.txt +expose 8080 +cmd ["/env/bin/python", "main.py"] \ No newline at end of file diff --git a/assets/queries/dockerfile/apk_add_using_local_cache_path/test/positive_expected_result.json b/assets/queries/dockerfile/apk_add_using_local_cache_path/test/positive_expected_result.json index ab094181dbb..2cf0bb0d814 100644 --- a/assets/queries/dockerfile/apk_add_using_local_cache_path/test/positive_expected_result.json +++ b/assets/queries/dockerfile/apk_add_using_local_cache_path/test/positive_expected_result.json @@ -10,5 +10,11 @@ "severity": "INFO", "line": 2, "fileName": "positive2.dockerfile" + }, + { + "queryName": "Apk Add Using Local Cache Path", + "severity": "INFO", + "line": 2, + "fileName": "positive3.dockerfile" } -] +] \ No newline at end of file diff --git a/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/query.rego b/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/query.rego index f39c4336de0..a19be16a2a2 100644 --- a/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/query.rego +++ b/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/query.rego @@ -1,5 +1,7 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { resource := input.document[i].command[name][_] resource.Cmd == "run" @@ -10,9 +12,12 @@ CxPolicy[result] { not hasClean(resource.Value[0], aptGet[0]) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) + run_command := substring(resource.Original, 0, 3) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.RUN={{%s}}", [name, commands]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.%s={{%s}}", [from_command.Value, name, run_command, commands]), from_command.LineHint), "issueType": "IncorrectValue", #"MissingAttribute" / "RedundantAttribute" "keyExpectedValue": "After using apt-get install, the apt-get lists should be deleted", "keyActualValue": "After using apt-get install, the apt-get lists were not deleted", diff --git a/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/test/negative3.dockerfile b/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/test/negative3.dockerfile new file mode 100644 index 00000000000..defd66e3610 --- /dev/null +++ b/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/test/negative3.dockerfile @@ -0,0 +1,15 @@ +from busyboxneg1 +run apt-get update && apt-get install --no-install-recommends -y python \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +from busyboxneg2 +run apt-get update && apt-get install --no-install-recommends -y python && apt-get clean + +from busyboxneg3 +run apt-get update && apt-get install --no-install-recommends -y python \ + && apt-get clean + +from busyboxneg4 +run apt-get update && apt-get install --no-install-recommends -y python \ + && rm -rf /var/lib/apt/lists/* diff --git a/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/test/positive3.dockerfile b/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/test/positive3.dockerfile new file mode 100644 index 00000000000..bc2347439ef --- /dev/null +++ b/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/test/positive3.dockerfile @@ -0,0 +1,14 @@ +from busybox1 +run apt-get update && apt-get install --no-install-recommends -y python + +from busybox2 +run apt-get install python + +from busybox3 +run apt-get update && apt-get install --no-install-recommends -y python +run rm -rf /var/lib/apt/lists/* + +from busybox4 +run apt-get update && apt-get install --no-install-recommends -y python +run rm -rf /var/lib/apt/lists/* +run apt-get clean diff --git a/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/test/positive_expected_result.json b/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/test/positive_expected_result.json index ab1370df2dd..4c73167b481 100644 --- a/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/test/positive_expected_result.json +++ b/assets/queries/dockerfile/apt_get_install_lists_were_not_deleted/test/positive_expected_result.json @@ -1,32 +1,56 @@ [ - { - "queryName": "Apt Get Install Lists Were Not Deleted", - "severity": "INFO", - "line": 2, - "fileName": "positive.dockerfile" - }, - { - "queryName": "Apt Get Install Lists Were Not Deleted", - "severity": "INFO", - "line": 5, - "fileName": "positive.dockerfile" - }, - { - "queryName": "Apt Get Install Lists Were Not Deleted", - "severity": "INFO", - "line": 8, - "fileName": "positive.dockerfile" - }, - { - "queryName": "Apt Get Install Lists Were Not Deleted", - "severity": "INFO", - "line": 12, - "fileName": "positive.dockerfile" - }, - { - "queryName": "Apt Get Install Lists Were Not Deleted", - "severity": "INFO", - "line": 2, - "fileName": "positive2.dockerfile" - } -] + { + "queryName": "Apt Get Install Lists Were Not Deleted", + "severity": "INFO", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Apt Get Install Lists Were Not Deleted", + "severity": "INFO", + "line": 5, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Apt Get Install Lists Were Not Deleted", + "severity": "INFO", + "line": 8, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Apt Get Install Lists Were Not Deleted", + "severity": "INFO", + "line": 12, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Apt Get Install Lists Were Not Deleted", + "severity": "INFO", + "line": 2, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Apt Get Install Lists Were Not Deleted", + "severity": "INFO", + "line": 2, + "fileName": "positive3.dockerfile" + }, + { + "queryName": "Apt Get Install Lists Were Not Deleted", + "severity": "INFO", + "line": 5, + "fileName": "positive3.dockerfile" + }, + { + "queryName": "Apt Get Install Lists Were Not Deleted", + "severity": "INFO", + "line": 8, + "fileName": "positive3.dockerfile" + }, + { + "queryName": "Apt Get Install Lists Were Not Deleted", + "severity": "INFO", + "line": 12, + "fileName": "positive3.dockerfile" + } +] \ No newline at end of file diff --git a/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/query.rego b/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/query.rego index f1637cf3309..807c9b49310 100644 --- a/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/query.rego +++ b/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/query.rego @@ -18,9 +18,12 @@ CxPolicy[result] { packageName := packages[j] analyzePackages(j, packageName, packages, length) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) + run_command := substring(resource.Original, 0, 3) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.RUN={{%s}}", [name, commands]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.%s={{%s}}", [from_command.Value, name, run_command, commands]), from_command.LineHint), "searchValue": packageName, "issueType": "MissingAttribute", "keyExpectedValue": sprintf("Package '%s' has version defined", [packageName]), @@ -44,9 +47,11 @@ CxPolicy[result] { regex.match("^[a-zA-Z]", packageName) == true not dockerLib.withVersion(packageName) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "searchValue": packageName, "issueType": "IncorrectValue", "keyExpectedValue": sprintf("Package '%s' has version defined", [packageName]), diff --git a/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/test/negative4.dockerfile b/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/test/negative4.dockerfile new file mode 100644 index 00000000000..6935edccbec --- /dev/null +++ b/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/test/negative4.dockerfile @@ -0,0 +1,2 @@ +from busybox +run apt-get install python=2.7 \ No newline at end of file diff --git a/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/test/positive3.dockerfile b/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/test/positive3.dockerfile new file mode 100644 index 00000000000..eea397c247c --- /dev/null +++ b/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/test/positive3.dockerfile @@ -0,0 +1,14 @@ +from busybox +run apt-get install python +run ["apt-get", "install", "python"] + +from busybox2 +run apt-get install -y -t python + +from busybox3 +run apt-get update && apt-get install -y \ + python-qt4 \ + python-pyside \ + python-pip \ + python3-pip \ + python3-pyqt5 diff --git a/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/test/positive_expected_result.json b/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/test/positive_expected_result.json index f2afe4b4ae2..e726c0c30ec 100644 --- a/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/test/positive_expected_result.json +++ b/assets/queries/dockerfile/apt_get_install_pin_version_not_defined/test/positive_expected_result.json @@ -94,5 +94,53 @@ "severity": "MEDIUM", "line": 9, "fileName": "positive2.dockerfile" + }, + { + "queryName": "Apt Get Install Pin Version Not Defined", + "severity": "MEDIUM", + "line": 2, + "fileName": "positive3.dockerfile" + }, + { + "queryName": "Apt Get Install Pin Version Not Defined", + "severity": "MEDIUM", + "line": 3, + "fileName": "positive3.dockerfile" + }, + { + "queryName": "Apt Get Install Pin Version Not Defined", + "severity": "MEDIUM", + "line": 6, + "fileName": "positive3.dockerfile" + }, + { + "queryName": "Apt Get Install Pin Version Not Defined", + "severity": "MEDIUM", + "line": 9, + "fileName": "positive3.dockerfile" + }, + { + "queryName": "Apt Get Install Pin Version Not Defined", + "severity": "MEDIUM", + "line": 9, + "fileName": "positive3.dockerfile" + }, + { + "queryName": "Apt Get Install Pin Version Not Defined", + "severity": "MEDIUM", + "line": 9, + "fileName": "positive3.dockerfile" + }, + { + "queryName": "Apt Get Install Pin Version Not Defined", + "severity": "MEDIUM", + "line": 9, + "fileName": "positive3.dockerfile" + }, + { + "queryName": "Apt Get Install Pin Version Not Defined", + "severity": "MEDIUM", + "line": 9, + "fileName": "positive3.dockerfile" } ] diff --git a/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/query.rego b/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/query.rego index 251c8de4840..ee2c364a4e3 100644 --- a/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/query.rego +++ b/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/query.rego @@ -15,9 +15,11 @@ CxPolicy[result] { not avoidManualInput(command) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("{{%s}} should avoid manual input", [resource.Original]), "keyActualValue": sprintf("{{%s}} doesn't avoid manual input", [resource.Original]), @@ -33,10 +35,12 @@ CxPolicy[result] { dockerLib.arrayContains(resource.Value, {"apt-get", "install"}) not avoidManualInputInList(resource.Value) - + + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("{{%s}} should avoid manual input", [resource.Original]), "keyActualValue": sprintf("{{%s}} doesn't avoid manual input", [resource.Original]), diff --git a/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/test/negative9.dockerfile b/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/test/negative9.dockerfile new file mode 100644 index 00000000000..6e921a95752 --- /dev/null +++ b/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/test/negative9.dockerfile @@ -0,0 +1,4 @@ +from node:12 +run apt-get -y install apt-utils +run apt-get -qy install git gcc +run ["apt-get", "-y", "install", "apt-utils"] diff --git a/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/test/positive8.dockerfile b/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/test/positive8.dockerfile new file mode 100644 index 00000000000..1bf13042873 --- /dev/null +++ b/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/test/positive8.dockerfile @@ -0,0 +1,4 @@ +from node:12 +run apt-get install python=2.7 +run apt-get install apt-utils +run ["apt-get", "install", "apt-utils"] diff --git a/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/test/positive_expected_result.json b/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/test/positive_expected_result.json index eb501cf7739..07ff384695a 100644 --- a/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/test/positive_expected_result.json +++ b/assets/queries/dockerfile/apt_get_missing_flags_to_avoid_manual_input/test/positive_expected_result.json @@ -1,92 +1,110 @@ [ - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 2, - "filename": "positive1.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 3, - "filename": "positive1.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 4, - "filename": "positive1.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 2, - "filename": "positive2.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 3, - "filename": "positive2.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 4, - "filename": "positive2.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 2, - "filename": "positive3.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 2, - "filename": "positive4.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 3, - "filename": "positive4.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 3, - "filename": "positive5.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 2, - "filename": "positive5.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 3, - "filename": "positive6.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 2, - "filename": "positive6.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 3, - "filename": "positive7.dockerfile" - }, - { - "queryName": "APT-GET Missing Flags To Avoid Manual Input", - "severity": "LOW", - "line": 2, - "filename": "positive7.dockerfile" - } + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 2, + "filename": "positive1.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 3, + "filename": "positive1.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 4, + "filename": "positive1.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 2, + "filename": "positive2.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 3, + "filename": "positive2.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 4, + "filename": "positive2.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 2, + "filename": "positive3.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 2, + "filename": "positive4.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 3, + "filename": "positive4.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 3, + "filename": "positive5.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 2, + "filename": "positive5.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 3, + "filename": "positive6.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 2, + "filename": "positive6.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 3, + "filename": "positive7.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 2, + "filename": "positive7.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 2, + "filename": "positive8.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 3, + "filename": "positive8.dockerfile" + }, + { + "queryName": "APT-GET Missing Flags To Avoid Manual Input", + "severity": "LOW", + "line": 4, + "filename": "positive8.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/query.rego b/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/query.rego index c405fd7f93c..ff9b0a83bfa 100644 --- a/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/query.rego +++ b/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/query.rego @@ -15,9 +15,11 @@ CxPolicy[result] { regex.match("apt-get (-(-)?[a-zA-Z]+ *)*install", commandsSplit[j]) == true not avoidAdditionalPackages(commandsSplit[j]) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("'%s' uses '--no-install-recommends' flag to avoid installing additional packages", [resource.Original]), "keyActualValue": sprintf("'%s' does not use '--no-install-recommends' flag to avoid installing additional packages", [resource.Original]), @@ -37,9 +39,11 @@ CxPolicy[result] { not avoidAdditionalPackages(commands) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("'%s' uses '--no-install-recommends' flag to avoid installing additional packages", [resource.Original]), "keyActualValue": sprintf("'%s' does not use '--no-install-recommends' flag to avoid installing additional packages", [resource.Original]), diff --git a/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/test/negative2.dockerfile b/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/test/negative2.dockerfile new file mode 100644 index 00000000000..f9aafcc4b01 --- /dev/null +++ b/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/test/negative2.dockerfile @@ -0,0 +1,4 @@ +from node:12 +run apt-get --no-install-recommends install apt-utils +run ["apt-get", "apt::install-recommends=false", "install", "apt-utils"] + diff --git a/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/test/positive2.dockerfile b/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/test/positive2.dockerfile new file mode 100644 index 00000000000..216835726a1 --- /dev/null +++ b/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/test/positive2.dockerfile @@ -0,0 +1,3 @@ +from node:12 +run apt-get install apt-utils +run ["apt-get", "install", "apt-utils"] \ No newline at end of file diff --git a/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/test/positive_expected_result.json b/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/test/positive_expected_result.json index 224923adbc5..d8e2514a366 100644 --- a/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/test/positive_expected_result.json +++ b/assets/queries/dockerfile/apt_get_not_avoiding_additional_packages/test/positive_expected_result.json @@ -2,11 +2,25 @@ { "queryName": "APT-GET Not Avoiding Additional Packages", "severity": "INFO", - "line": 2 + "line": 2, + "fileName": "positive.dockerfile" }, { "queryName": "APT-GET Not Avoiding Additional Packages", "severity": "INFO", - "line": 3 + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "APT-GET Not Avoiding Additional Packages", + "severity": "INFO", + "line": 2, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "APT-GET Not Avoiding Additional Packages", + "severity": "INFO", + "line": 3, + "fileName": "positive2.dockerfile" } -] +] \ No newline at end of file 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 index 9e602aec2cd..a43dba6e428 100644 --- a/assets/queries/dockerfile/changing_default_shell_using_run_command/query.rego +++ b/assets/queries/dockerfile/changing_default_shell_using_run_command/query.rego @@ -1,6 +1,7 @@ package Cx import data.generic.common as common_lib +import data.generic.dockerfile as dockerLib shell_possibilities := { "/bin/bash", @@ -28,10 +29,12 @@ CxPolicy[result] { command_possibilities := {"mv", "chsh", "usermod", "ln"} command == command_possibilities[cp] + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "debug": sprintf("%s", [value[v]]), "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("{{%s}} should use the SHELL command to change the default shell", [resource.Original]), "keyActualValue": sprintf("{{%s}} uses the RUN command to change the default shell", [resource.Original]), @@ -46,9 +49,11 @@ CxPolicy[result] { command := run_values[0] contains(command, "powershell") + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("{{%s}} should use the SHELL command to change the default shell", [resource.Original]), "keyActualValue": sprintf("{{%s}} uses the RUN command to change the default shell", [resource.Original]), diff --git a/assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative5.dockerfile b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative5.dockerfile new file mode 100644 index 00000000000..ea91f7d74d7 --- /dev/null +++ b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/negative5.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/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/ +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/positive3.dockerfile b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/positive3.dockerfile new file mode 100644 index 00000000000..d3ce857b5f9 --- /dev/null +++ b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/positive3.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/positive_expected_result.json b/assets/queries/dockerfile/changing_default_shell_using_run_command/test/positive_expected_result.json index c9854941220..fc1d3dcbf58 100644 --- 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 @@ -10,5 +10,11 @@ "severity": "MEDIUM", "line": 5, "filename": "positive2.dockerfile" + }, + { + "queryName": "Changing Default Shell Using RUN Command", + "severity": "MEDIUM", + "line": 5, + "filename": "positive3.dockerfile" } -] +] \ No newline at end of file diff --git a/assets/queries/dockerfile/chown_flag_exists/query.rego b/assets/queries/dockerfile/chown_flag_exists/query.rego index 622837f5237..2acc6764e76 100644 --- a/assets/queries/dockerfile/chown_flag_exists/query.rego +++ b/assets/queries/dockerfile/chown_flag_exists/query.rego @@ -1,13 +1,16 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { resource := input.document[i].command[name] contains(resource[j].Flags[f], "--chown") + from_command := dockerLib.get_original_from_command(resource) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource[j].Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource[j].Original]), from_command.LineHint), "category": "Best Practices", "issueType": "IncorrectValue", "keyExpectedValue": "The 'Dockerfile' shouldn´t contain the 'chown' flag", diff --git a/assets/queries/dockerfile/chown_flag_exists/test/negative2.dockerfile b/assets/queries/dockerfile/chown_flag_exists/test/negative2.dockerfile new file mode 100644 index 00000000000..4edd7678daa --- /dev/null +++ b/assets/queries/dockerfile/chown_flag_exists/test/negative2.dockerfile @@ -0,0 +1,7 @@ +from python:3.7 +run pip install Flask==0.11.1 +run useradd -ms /bin/bash patrick +copy app /app +workdir /app +user patrick +cmd ["python", "app.py"] diff --git a/assets/queries/dockerfile/chown_flag_exists/test/positive2.dockerfile b/assets/queries/dockerfile/chown_flag_exists/test/positive2.dockerfile new file mode 100644 index 00000000000..7da67f57278 --- /dev/null +++ b/assets/queries/dockerfile/chown_flag_exists/test/positive2.dockerfile @@ -0,0 +1,7 @@ +from python:3.7 +run pip install Flask==0.11.1 +run useradd -ms /bin/bash patrick +copy --chown=patrick:patrick app /app +workdir /app +user patrick +cmd ["python", "app.py"] diff --git a/assets/queries/dockerfile/chown_flag_exists/test/positive_expected_result.json b/assets/queries/dockerfile/chown_flag_exists/test/positive_expected_result.json index 41cc05a4a3d..fa9e309eba9 100644 --- a/assets/queries/dockerfile/chown_flag_exists/test/positive_expected_result.json +++ b/assets/queries/dockerfile/chown_flag_exists/test/positive_expected_result.json @@ -1,7 +1,14 @@ [ - { - "queryName": "Chown Flag Exists", - "severity": "LOW", - "line": 4 - } -] + { + "queryName": "Chown Flag Exists", + "severity": "LOW", + "line": 4, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Chown Flag Exists", + "severity": "LOW", + "line": 4, + "fileName": "positive2.dockerfile" + } +] \ No newline at end of file diff --git a/assets/queries/dockerfile/copy_from_references_current_from_alias/query.rego b/assets/queries/dockerfile/copy_from_references_current_from_alias/query.rego index 533dcc77be4..4ad6e247cad 100644 --- a/assets/queries/dockerfile/copy_from_references_current_from_alias/query.rego +++ b/assets/queries/dockerfile/copy_from_references_current_from_alias/query.rego @@ -1,5 +1,7 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { resource := input.document[i].command[name][_] resource.Cmd == "copy" @@ -9,9 +11,11 @@ CxPolicy[result] { isAliasCurrentFromAlias(name, aux_split[1]) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "COPY --from should not reference the current FROM alias", "keyActualValue": "COPY --from references the current FROM alias", diff --git a/assets/queries/dockerfile/copy_from_references_current_from_alias/test/negative2.dockerfile b/assets/queries/dockerfile/copy_from_references_current_from_alias/test/negative2.dockerfile new file mode 100644 index 00000000000..c38778c6031 --- /dev/null +++ b/assets/queries/dockerfile/copy_from_references_current_from_alias/test/negative2.dockerfile @@ -0,0 +1,12 @@ +from golang:1.7.3 AS builder +workdir /go/src/github.com/foo/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 . + +# another dockerfile +from alpine:latest +run apk --no-cache add ca-certificates +workdir /root/ +copy --from=builder /go/src/github.com/foo/href-counter/app . +cmd ["./app"] diff --git a/assets/queries/dockerfile/copy_from_references_current_from_alias/test/positive2.dockerfile b/assets/queries/dockerfile/copy_from_references_current_from_alias/test/positive2.dockerfile new file mode 100644 index 00000000000..1ef6617540f --- /dev/null +++ b/assets/queries/dockerfile/copy_from_references_current_from_alias/test/positive2.dockerfile @@ -0,0 +1,3 @@ +from myimage:tag as dep +copy --from=dep /binary / +run dir c:\ \ No newline at end of file diff --git a/assets/queries/dockerfile/copy_from_references_current_from_alias/test/positive_expected_result.json b/assets/queries/dockerfile/copy_from_references_current_from_alias/test/positive_expected_result.json index 0a577a86177..9e540dc9751 100644 --- a/assets/queries/dockerfile/copy_from_references_current_from_alias/test/positive_expected_result.json +++ b/assets/queries/dockerfile/copy_from_references_current_from_alias/test/positive_expected_result.json @@ -1,7 +1,14 @@ [ - { - "queryName": "COPY '--from' References Current FROM Alias", - "severity": "LOW", - "line": 2 - } + { + "queryName": "COPY '--from' References Current FROM Alias", + "severity": "LOW", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "COPY '--from' References Current FROM Alias", + "severity": "LOW", + "line": 2, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/query.rego b/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/query.rego index c2ae55008f3..9734986d6df 100644 --- a/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/query.rego +++ b/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/query.rego @@ -1,5 +1,7 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { resource := input.document[i].command[name][_] @@ -12,9 +14,12 @@ CxPolicy[result] { not endswith(command[minus(numElems, 1)], "/") + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) + copy_command := substring(resource.Original, 0, 3) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.COPY={{%s}}", [name, resource.Value[0]]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.%s={{%s}}", [from_command.Value, name, copy_command, resource.Value[0]]), from_command.LineHint), "issueType": "IncorrectValue", #"MissingAttribute" / "RedundantAttribute" "keyExpectedValue": "When COPY command has more than two arguments, the last one should end with a slash", "keyActualValue": "COPY command has more than two arguments and the last one does not end with a slash", diff --git a/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/test/negative3.dockerfile b/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/test/negative3.dockerfile new file mode 100644 index 00000000000..1ed7dd5795b --- /dev/null +++ b/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/test/negative3.dockerfile @@ -0,0 +1,2 @@ +from node:carbon +copy package.json yarn.lock my_app/ diff --git a/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/test/positive2.dockerfile b/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/test/positive2.dockerfile new file mode 100644 index 00000000000..560ad97b040 --- /dev/null +++ b/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/test/positive2.dockerfile @@ -0,0 +1,2 @@ +from node:carbon2 +copy package.json yarn.lock my_app diff --git a/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/test/positive_expected_result.json b/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/test/positive_expected_result.json index 2774ad6013d..3d381bcd233 100644 --- a/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/test/positive_expected_result.json +++ b/assets/queries/dockerfile/copy_with_more_than_two_arguments_not_ending_with_slash/test/positive_expected_result.json @@ -1,8 +1,14 @@ [ - { - "queryName": "Copy With More Than Two Arguments Not Ending With Slash", - "severity": "LOW", - "fileName": "positive.dockerfile", - "line": 2 - } + { + "queryName": "Copy With More Than Two Arguments Not Ending With Slash", + "severity": "LOW", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Copy With More Than Two Arguments Not Ending With Slash", + "severity": "LOW", + "line": 2, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/curl_or_wget_instead_of_add/query.rego b/assets/queries/dockerfile/curl_or_wget_instead_of_add/query.rego index 7ebf5bccafa..b43f6a9d1f3 100644 --- a/assets/queries/dockerfile/curl_or_wget_instead_of_add/query.rego +++ b/assets/queries/dockerfile/curl_or_wget_instead_of_add/query.rego @@ -1,13 +1,17 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { resource := input.document[i].command[name][j] resource.Cmd == "add" httpRequestChecker(resource.Value) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("Should use 'curl' or 'wget' to download %s", [resource.Value[0]]), "keyActualValue": sprintf("'ADD' %s", [resource.Value[0]]), diff --git a/assets/queries/dockerfile/curl_or_wget_instead_of_add/test/negative3.dockerfile b/assets/queries/dockerfile/curl_or_wget_instead_of_add/test/negative3.dockerfile new file mode 100644 index 00000000000..8e78d88d5ae --- /dev/null +++ b/assets/queries/dockerfile/curl_or_wget_instead_of_add/test/negative3.dockerfile @@ -0,0 +1,5 @@ +from openjdk:10-jdk +run mkdir -p /usr/src/things \ + && curl -SL https://example.com/big.tar.xz \ + | tar -xJC /usr/src/things \ + && make -C /usr/src/things all diff --git a/assets/queries/dockerfile/curl_or_wget_instead_of_add/test/positive2.dockerfile b/assets/queries/dockerfile/curl_or_wget_instead_of_add/test/positive2.dockerfile new file mode 100644 index 00000000000..df5300c56c5 --- /dev/null +++ b/assets/queries/dockerfile/curl_or_wget_instead_of_add/test/positive2.dockerfile @@ -0,0 +1,5 @@ +from openjdk:10-jdk +volume /tmp +add https://example.com/big.tar.xz /usr/src/things/ +run tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things +run make -C /usr/src/things all diff --git a/assets/queries/dockerfile/curl_or_wget_instead_of_add/test/positive_expected_result.json b/assets/queries/dockerfile/curl_or_wget_instead_of_add/test/positive_expected_result.json index 914f34a3b1a..1bca3e91498 100644 --- a/assets/queries/dockerfile/curl_or_wget_instead_of_add/test/positive_expected_result.json +++ b/assets/queries/dockerfile/curl_or_wget_instead_of_add/test/positive_expected_result.json @@ -1,7 +1,14 @@ [ - { - "queryName": "Curl or Wget Instead of Add", - "severity": "LOW", - "line": 3 - } -] + { + "queryName": "Curl or Wget Instead of Add", + "severity": "LOW", + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Curl or Wget Instead of Add", + "severity": "LOW", + "line": 3, + "fileName": "positive2.dockerfile" + } +] \ No newline at end of file diff --git a/assets/queries/dockerfile/exposing_port_22/query.rego b/assets/queries/dockerfile/exposing_port_22/query.rego index c8f8bcd0a95..9095a9a343a 100644 --- a/assets/queries/dockerfile/exposing_port_22/query.rego +++ b/assets/queries/dockerfile/exposing_port_22/query.rego @@ -1,14 +1,18 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { command := input.document[i].command[name][_] command.Cmd == "expose" to_number(command.Value[_]) == 22 + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, command.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, command.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "'EXPOSE' shouldn't contain the port 22 ", "keyActualValue": "'EXPOSE' contains the port 22 ", diff --git a/assets/queries/dockerfile/exposing_port_22/test/negative2.dockerfile b/assets/queries/dockerfile/exposing_port_22/test/negative2.dockerfile new file mode 100644 index 00000000000..08e4bcb2593 --- /dev/null +++ b/assets/queries/dockerfile/exposing_port_22/test/negative2.dockerfile @@ -0,0 +1,4 @@ +from gliderlabs/alpine:3.3 +run apk --no-cache add nginx +expose 80 +cmd ["nginx", "-g", "daemon off;"] diff --git a/assets/queries/dockerfile/exposing_port_22/test/positive2.dockerfile b/assets/queries/dockerfile/exposing_port_22/test/positive2.dockerfile new file mode 100644 index 00000000000..328ffa1212b --- /dev/null +++ b/assets/queries/dockerfile/exposing_port_22/test/positive2.dockerfile @@ -0,0 +1,4 @@ +from gliderlabs/alpine:3.3 +run apk --no-cache add nginx +expose 3000 80 443 22 +cmd ["nginx", "-g", "daemon off;"] diff --git a/assets/queries/dockerfile/exposing_port_22/test/positive_expected_result.json b/assets/queries/dockerfile/exposing_port_22/test/positive_expected_result.json index 7c697fbb3e4..c5296736119 100644 --- a/assets/queries/dockerfile/exposing_port_22/test/positive_expected_result.json +++ b/assets/queries/dockerfile/exposing_port_22/test/positive_expected_result.json @@ -1,7 +1,14 @@ [ - { - "queryName": "Exposing Port 22 (SSH)", - "severity": "LOW", - "line": 3 - } -] + { + "queryName": "Exposing Port 22 (SSH)", + "severity": "LOW", + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Exposing Port 22 (SSH)", + "severity": "LOW", + "line": 3, + "fileName": "positive2.dockerfile" + } +] \ No newline at end of file diff --git a/assets/queries/dockerfile/gem_install_without_version/query.rego b/assets/queries/dockerfile/gem_install_without_version/query.rego index c4d76a64232..fad16c3ee4f 100644 --- a/assets/queries/dockerfile/gem_install_without_version/query.rego +++ b/assets/queries/dockerfile/gem_install_without_version/query.rego @@ -18,9 +18,11 @@ CxPolicy[result] { some j analyzePackages(j, packages[j], packages, length) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("%s is 'gem install :'", [resource.Original]), "keyActualValue": sprintf("%s is 'gem install ', you should use 'gem install :", [resource.Original]), @@ -40,9 +42,11 @@ CxPolicy[result] { regex.match("^[a-zA-Z]", resource.Value[j]) == true not dockerLib.withVersion(resource.Value[j]) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("%s is 'gem install :'", [resource.Original]), "keyActualValue": sprintf("%s is 'gem install ', you should use 'gem install :", [resource.Original]), diff --git a/assets/queries/dockerfile/gem_install_without_version/test/negative2.dockerfile b/assets/queries/dockerfile/gem_install_without_version/test/negative2.dockerfile new file mode 100644 index 00000000000..a0b8b9cabb9 --- /dev/null +++ b/assets/queries/dockerfile/gem_install_without_version/test/negative2.dockerfile @@ -0,0 +1,13 @@ +from alpine:3.5 +run apk add --update py2-pip +run gem install bundler:2.0.2 +run bundle install +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"] +env GRPC_VERSION 1.0.0 +run gem install grpc -v ${GRPC_RUBY_VERSION} +run gem install grpc:${GRPC_VERSION} grpc-tools:${GRPC_VERSION} diff --git a/assets/queries/dockerfile/gem_install_without_version/test/positive2.dockerfile b/assets/queries/dockerfile/gem_install_without_version/test/positive2.dockerfile new file mode 100644 index 00000000000..646f822efdb --- /dev/null +++ b/assets/queries/dockerfile/gem_install_without_version/test/positive2.dockerfile @@ -0,0 +1,12 @@ +from alpine:3.5 +run apk add --update py2-pip +run gem install bundler +run ["gem", "install", "blunder"] +run gem install grpc -v ${GRPC_RUBY_VERSION} blunder +run bundle install +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/gem_install_without_version/test/positive_expected_result.json b/assets/queries/dockerfile/gem_install_without_version/test/positive_expected_result.json index 9e0dc193dc6..d7bd07a860a 100644 --- a/assets/queries/dockerfile/gem_install_without_version/test/positive_expected_result.json +++ b/assets/queries/dockerfile/gem_install_without_version/test/positive_expected_result.json @@ -1,17 +1,38 @@ [ - { - "queryName": "Gem Install Without Version", - "severity": "MEDIUM", - "line": 3 - }, - { - "queryName": "Gem Install Without Version", - "severity": "MEDIUM", - "line": 4 - }, - { - "queryName": "Gem Install Without Version", - "severity": "MEDIUM", - "line": 5 - } -] + { + "queryName": "Gem Install Without Version", + "severity": "MEDIUM", + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Gem Install Without Version", + "severity": "MEDIUM", + "line": 4, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Gem Install Without Version", + "severity": "MEDIUM", + "line": 5, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Gem Install Without Version", + "severity": "MEDIUM", + "line": 3, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Gem Install Without Version", + "severity": "MEDIUM", + "line": 4, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Gem Install Without Version", + "severity": "MEDIUM", + "line": 5, + "fileName": "positive2.dockerfile" + } +] \ No newline at end of file diff --git a/assets/queries/dockerfile/healthcheck_instruction_missing/query.rego b/assets/queries/dockerfile/healthcheck_instruction_missing/query.rego index 7ac3f3bb857..c3fb25f1ceb 100644 --- a/assets/queries/dockerfile/healthcheck_instruction_missing/query.rego +++ b/assets/queries/dockerfile/healthcheck_instruction_missing/query.rego @@ -8,9 +8,10 @@ CxPolicy[result] { not contains(resource, "healthcheck") + from_command := dockerLib.get_original_from_command(resource) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}", [name]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}", [from_command.Value, name]), from_command.LineHint), "issueType": "MissingAttribute", "keyExpectedValue": "Dockerfile should contain instruction 'HEALTHCHECK'", "keyActualValue": "Dockerfile doesn't contain instruction 'HEALTHCHECK'", diff --git a/assets/queries/dockerfile/healthcheck_instruction_missing/test/negative3.dockerfile b/assets/queries/dockerfile/healthcheck_instruction_missing/test/negative3.dockerfile new file mode 100644 index 00000000000..0924f1fdf1d --- /dev/null +++ b/assets/queries/dockerfile/healthcheck_instruction_missing/test/negative3.dockerfile @@ -0,0 +1,8 @@ +from node:alpine +workdir /usr/src/app +copy package*.json ./ +run npm install +copy . . +expose 3000 +healthcheck CMD curl --fail http://localhost:3000 || exit 1 +cmd ["node","app.js"] \ No newline at end of file diff --git a/assets/queries/dockerfile/healthcheck_instruction_missing/test/positive3.dockerfile b/assets/queries/dockerfile/healthcheck_instruction_missing/test/positive3.dockerfile new file mode 100644 index 00000000000..ac28ce39a10 --- /dev/null +++ b/assets/queries/dockerfile/healthcheck_instruction_missing/test/positive3.dockerfile @@ -0,0 +1,7 @@ +from node:alpine +workdir /usr/src/app +copy package*.json ./ +run npm install +copy . . +expose 3000 +cmd ["node","app.js"] \ No newline at end of file diff --git a/assets/queries/dockerfile/healthcheck_instruction_missing/test/positive_expected_result.json b/assets/queries/dockerfile/healthcheck_instruction_missing/test/positive_expected_result.json index 3fff0a3f2f4..355b007dd53 100644 --- a/assets/queries/dockerfile/healthcheck_instruction_missing/test/positive_expected_result.json +++ b/assets/queries/dockerfile/healthcheck_instruction_missing/test/positive_expected_result.json @@ -10,5 +10,11 @@ "severity": "LOW", "line": 7, "fileName": "positive2.dockerfile" + }, + { + "queryName": "Healthcheck Instruction Missing", + "severity": "LOW", + "line": 1, + "fileName": "positive3.dockerfile" } -] +] \ No newline at end of file diff --git a/assets/queries/dockerfile/image_version_not_explicit/query.rego b/assets/queries/dockerfile/image_version_not_explicit/query.rego index 46c74eb9e80..95d2bf75bea 100644 --- a/assets/queries/dockerfile/image_version_not_explicit/query.rego +++ b/assets/queries/dockerfile/image_version_not_explicit/query.rego @@ -1,5 +1,7 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { resource := input.document[i].command[name][_] resource.Cmd == "from" @@ -7,9 +9,11 @@ CxPolicy[result] { versionNotExplicit(resource.Value,resource.EndLine) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}", [name]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}", [from_command.Value, name]), from_command.LineHint), "issueType": "MissingAttribute", "keyExpectedValue": sprintf("FROM %s:'version'", [resource.Value[0]]), "keyActualValue": sprintf("FROM %s'", [resource.Value[0]]), diff --git a/assets/queries/dockerfile/image_version_not_explicit/test/negative5.dockerfile b/assets/queries/dockerfile/image_version_not_explicit/test/negative5.dockerfile new file mode 100644 index 00000000000..3e0bf155135 --- /dev/null +++ b/assets/queries/dockerfile/image_version_not_explicit/test/negative5.dockerfile @@ -0,0 +1,11 @@ +from alpine:3.5 +run apk add --update py2-pip +run pip install --upgrade pip +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 +arg IMAGE=alpine:3.12 +from $IMAGE +cmd ["python", "/usr/src/app/app.py"] diff --git a/assets/queries/dockerfile/image_version_not_explicit/test/positive5.dockerfile b/assets/queries/dockerfile/image_version_not_explicit/test/positive5.dockerfile new file mode 100644 index 00000000000..d19a3511d99 --- /dev/null +++ b/assets/queries/dockerfile/image_version_not_explicit/test/positive5.dockerfile @@ -0,0 +1,9 @@ +from alpine +run apk add --update py2-pip +run pip install --upgrade pip +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"] \ No newline at end of file diff --git a/assets/queries/dockerfile/image_version_not_explicit/test/positive_expected_result.json b/assets/queries/dockerfile/image_version_not_explicit/test/positive_expected_result.json index a5cbb7933e2..2e04d53f46c 100644 --- a/assets/queries/dockerfile/image_version_not_explicit/test/positive_expected_result.json +++ b/assets/queries/dockerfile/image_version_not_explicit/test/positive_expected_result.json @@ -34,5 +34,11 @@ "severity": "MEDIUM", "fileName": "positive4.dockerfile", "line": 10 + }, + { + "queryName": "Image Version Not Explicit", + "severity": "MEDIUM", + "fileName": "positive5.dockerfile", + "line": 1 } -] +] \ No newline at end of file diff --git a/assets/queries/dockerfile/image_version_using_latest/query.rego b/assets/queries/dockerfile/image_version_using_latest/query.rego index cbce842332f..34bc06b3274 100644 --- a/assets/queries/dockerfile/image_version_using_latest/query.rego +++ b/assets/queries/dockerfile/image_version_using_latest/query.rego @@ -1,14 +1,18 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { resource := input.document[i].command[name][_] resource.Cmd == "from" not resource.Value[0] == "scratch" contains(resource.Value[0], ":latest") + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}", [name]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}", [from_command.Value, name]), from_command.LineHint), "issueType": "IncorrectValue", #"MissingAttribute" / "RedundantAttribute" "keyExpectedValue": sprintf("FROM %s:'version' where version should not be 'latest'", [resource.Value[0]]), "keyActualValue": sprintf("FROM %s'", [resource.Value[0]]), diff --git a/assets/queries/dockerfile/image_version_using_latest/test/negative2.dockerfile b/assets/queries/dockerfile/image_version_using_latest/test/negative2.dockerfile new file mode 100644 index 00000000000..e7dbf812104 --- /dev/null +++ b/assets/queries/dockerfile/image_version_using_latest/test/negative2.dockerfile @@ -0,0 +1,9 @@ +from alpine:3.5 +run apk add --update py2-pip +run pip install --upgrade pip +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"] \ No newline at end of file diff --git a/assets/queries/dockerfile/image_version_using_latest/test/positive2.dockerfile b/assets/queries/dockerfile/image_version_using_latest/test/positive2.dockerfile new file mode 100644 index 00000000000..d05cf997998 --- /dev/null +++ b/assets/queries/dockerfile/image_version_using_latest/test/positive2.dockerfile @@ -0,0 +1,9 @@ +from alpine:latest +run apk add --update py2-pip +run pip install --upgrade pip +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"] \ No newline at end of file diff --git a/assets/queries/dockerfile/image_version_using_latest/test/positive_expected_result.json b/assets/queries/dockerfile/image_version_using_latest/test/positive_expected_result.json index 54fb27a0982..3e6d61beed8 100644 --- a/assets/queries/dockerfile/image_version_using_latest/test/positive_expected_result.json +++ b/assets/queries/dockerfile/image_version_using_latest/test/positive_expected_result.json @@ -2,6 +2,13 @@ { "queryName": "Image Version Using 'latest'", "severity": "MEDIUM", - "line": 1 + "line": 1, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Image Version Using 'latest'", + "severity": "MEDIUM", + "line": 1, + "fileName": "positive2.dockerfile" } -] +] \ No newline at end of file diff --git a/assets/queries/dockerfile/last_user_is_root/query.rego b/assets/queries/dockerfile/last_user_is_root/query.rego index 28e2dca5a04..5326140d9f8 100644 --- a/assets/queries/dockerfile/last_user_is_root/query.rego +++ b/assets/queries/dockerfile/last_user_is_root/query.rego @@ -9,9 +9,10 @@ CxPolicy[result] { userCmd := [x | resource[j].Cmd == "user"; x := resource[j]] userCmd[minus(count(userCmd), 1)].Value[0] == "root" + from_command := dockerLib.get_original_from_command(resource) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, userCmd[minus(count(userCmd), 1)].Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, userCmd[minus(count(userCmd), 1)].Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "Last User shouldn't be root", "keyActualValue": "Last User is root", diff --git a/assets/queries/dockerfile/last_user_is_root/test/negative3.dockerfile b/assets/queries/dockerfile/last_user_is_root/test/negative3.dockerfile new file mode 100644 index 00000000000..b1a391b8d5d --- /dev/null +++ b/assets/queries/dockerfile/last_user_is_root/test/negative3.dockerfile @@ -0,0 +1,4 @@ +from alpine:2.6 +user root +run npm install +user guest \ No newline at end of file diff --git a/assets/queries/dockerfile/last_user_is_root/test/positive2.dockerfile b/assets/queries/dockerfile/last_user_is_root/test/positive2.dockerfile new file mode 100644 index 00000000000..7649a2f1ead --- /dev/null +++ b/assets/queries/dockerfile/last_user_is_root/test/positive2.dockerfile @@ -0,0 +1,3 @@ +from alpine:2.6 +user root +run npm install \ No newline at end of file diff --git a/assets/queries/dockerfile/last_user_is_root/test/positive_expected_result.json b/assets/queries/dockerfile/last_user_is_root/test/positive_expected_result.json index 751442ed373..4c836fb90dc 100644 --- a/assets/queries/dockerfile/last_user_is_root/test/positive_expected_result.json +++ b/assets/queries/dockerfile/last_user_is_root/test/positive_expected_result.json @@ -1,7 +1,14 @@ [ - { - "queryName": "Last User Is 'root'", - "severity": "HIGH", - "line": 2 - } + { + "queryName": "Last User Is 'root'", + "severity": "HIGH", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Last User Is 'root'", + "severity": "HIGH", + "line": 2, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/maintainer_instruction_being_used/query.rego b/assets/queries/dockerfile/maintainer_instruction_being_used/query.rego index 98973103c4f..a57b1b94b11 100644 --- a/assets/queries/dockerfile/maintainer_instruction_being_used/query.rego +++ b/assets/queries/dockerfile/maintainer_instruction_being_used/query.rego @@ -1,12 +1,17 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { resource := input.document[i].command[name][_] resource.Cmd == "maintainer" + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) + maintainer_command := substring(resource.Original, 0, 10) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.MAINTAINER={{%s}}", [name, resource.Value[0]]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.%s={{%s}}", [from_command.Value, name, maintainer_command, resource.Value[0]]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("Maintainer instruction being used in Label 'LABEL maintainer=%s'", [resource.Value[0]]), "keyActualValue": sprintf("Maintainer instruction not being used in Label 'MAINTAINER %s'", [resource.Value[0]]), diff --git a/assets/queries/dockerfile/maintainer_instruction_being_used/test/negative2.dockerfile b/assets/queries/dockerfile/maintainer_instruction_being_used/test/negative2.dockerfile new file mode 100644 index 00000000000..1678a5950ee --- /dev/null +++ b/assets/queries/dockerfile/maintainer_instruction_being_used/test/negative2.dockerfile @@ -0,0 +1,10 @@ +from alpine:3.5 +run apk add --update py2-pip +run pip install --upgrade pip +label maintainer="SvenDowideit@home.org.au" +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"] \ No newline at end of file diff --git a/assets/queries/dockerfile/maintainer_instruction_being_used/test/positive2.dockerfile b/assets/queries/dockerfile/maintainer_instruction_being_used/test/positive2.dockerfile new file mode 100644 index 00000000000..6c6bd02cc0f --- /dev/null +++ b/assets/queries/dockerfile/maintainer_instruction_being_used/test/positive2.dockerfile @@ -0,0 +1,10 @@ +from alpine:3.5 +run apk add --update py2-pip +run pip install --upgrade pip +maintainer "SvenDowideit@home.org.au" +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"] \ No newline at end of file diff --git a/assets/queries/dockerfile/maintainer_instruction_being_used/test/positive_expected_result.json b/assets/queries/dockerfile/maintainer_instruction_being_used/test/positive_expected_result.json index ae5d0a537f5..ee5d19ab48a 100644 --- a/assets/queries/dockerfile/maintainer_instruction_being_used/test/positive_expected_result.json +++ b/assets/queries/dockerfile/maintainer_instruction_being_used/test/positive_expected_result.json @@ -2,6 +2,13 @@ { "queryName": "MAINTAINER Instruction Being Used", "severity": "LOW", - "line": 4 + "line": 4, + "fileName": "positive.dockerfile" + }, + { + "queryName": "MAINTAINER Instruction Being Used", + "severity": "LOW", + "line": 4, + "fileName": "positive2.dockerfile" } -] +] \ No newline at end of file diff --git a/assets/queries/dockerfile/missing_dnf_clean_all/query.rego b/assets/queries/dockerfile/missing_dnf_clean_all/query.rego index 982d8117170..d41edc7b878 100644 --- a/assets/queries/dockerfile/missing_dnf_clean_all/query.rego +++ b/assets/queries/dockerfile/missing_dnf_clean_all/query.rego @@ -13,9 +13,12 @@ CxPolicy[result] { not containsDnfClean(input.document[i].command[name], resource._kics_line) not containsCleanAfterInstall(command) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) + run_command := substring(resource.Original, 0, 3) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.RUN={{%s}}", [name, resource.Value[0]]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.%s={{%s}}", [from_command.Value, name, run_command, resource.Value[0]]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "After installing a package with dnf, command 'dnf clean all' should run.", "keyActualValue": "Command `dnf clean all` is not being run after installing packages.", diff --git a/assets/queries/dockerfile/missing_dnf_clean_all/test/negative3.dockerfile b/assets/queries/dockerfile/missing_dnf_clean_all/test/negative3.dockerfile new file mode 100644 index 00000000000..c06cea0f7b1 --- /dev/null +++ b/assets/queries/dockerfile/missing_dnf_clean_all/test/negative3.dockerfile @@ -0,0 +1,7 @@ +from fedora:27 +run set -uex && \ + dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo && \ + sed -i 's/\$releasever/26/g' /etc/yum.repos.d/docker-ce.repo && \ + dnf install -vy docker-ce && \ + dnf clean all +healthcheck CMD curl --fail http://localhost:3000 || exit 1 diff --git a/assets/queries/dockerfile/missing_dnf_clean_all/test/positive2.dockerfile b/assets/queries/dockerfile/missing_dnf_clean_all/test/positive2.dockerfile new file mode 100644 index 00000000000..49a630da7c2 --- /dev/null +++ b/assets/queries/dockerfile/missing_dnf_clean_all/test/positive2.dockerfile @@ -0,0 +1,6 @@ +from fedora:27 +run set -uex && \ + dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo && \ + sed -i 's/\$releasever/26/g' /etc/yum.repos.d/docker-ce.repo && \ + dnf install -vy docker-ce +healthcheck CMD curl --fail http://localhost:3000 || exit 1 diff --git a/assets/queries/dockerfile/missing_dnf_clean_all/test/positive_expected_result.json b/assets/queries/dockerfile/missing_dnf_clean_all/test/positive_expected_result.json index 0c521996f67..c6d71e5c92c 100644 --- a/assets/queries/dockerfile/missing_dnf_clean_all/test/positive_expected_result.json +++ b/assets/queries/dockerfile/missing_dnf_clean_all/test/positive_expected_result.json @@ -1,7 +1,14 @@ [ - { - "queryName": "Missing Dnf Clean All", - "severity": "LOW", - "line": 2 - } + { + "queryName": "Missing Dnf Clean All", + "severity": "LOW", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Missing Dnf Clean All", + "severity": "LOW", + "line": 2, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/missing_flag_from_dnf_install/query.rego b/assets/queries/dockerfile/missing_flag_from_dnf_install/query.rego index f3f1f143c64..a77b56e8300 100644 --- a/assets/queries/dockerfile/missing_flag_from_dnf_install/query.rego +++ b/assets/queries/dockerfile/missing_flag_from_dnf_install/query.rego @@ -1,22 +1,25 @@ package Cx import data.generic.common as common_lib -import data.generic.dockerfile as docker_lib +import data.generic.dockerfile as dockerLib CxPolicy[result] { resource := input.document[i].command[name][cmd] resource.Cmd == "run" values := resource.Value[0] - commands = docker_lib.getCommands(values) + commands = dockerLib.getCommands(values) some k c := hasInstallCommandWithoutFlag(commands[k]) not hasYesFlag(c) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) + run_command := substring(resource.Original, 0, 3) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.RUN={{%s}}", [name, resource.Value[0]]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.%s={{%s}}", [from_command.Value, name, run_command, resource.Value[0]]), from_command.LineHint), "searchValue": trim_space(c), "issueType": "IncorrectValue", "keyExpectedValue": "When running `dnf install`, `-y` or `--assumeyes` switch should be set to avoid build failure ", diff --git a/assets/queries/dockerfile/missing_flag_from_dnf_install/test/negative5.dockerfile b/assets/queries/dockerfile/missing_flag_from_dnf_install/test/negative5.dockerfile new file mode 100644 index 00000000000..0446e79007d --- /dev/null +++ b/assets/queries/dockerfile/missing_flag_from_dnf_install/test/negative5.dockerfile @@ -0,0 +1,5 @@ +from fedora:27 +run set -uex && \ + dnf config-manager --set-enabled docker-ce-test && \ + dnf install -y docker-ce && \ + dnf clean all \ No newline at end of file diff --git a/assets/queries/dockerfile/missing_flag_from_dnf_install/test/positive5.dockerfile b/assets/queries/dockerfile/missing_flag_from_dnf_install/test/positive5.dockerfile new file mode 100644 index 00000000000..396ff2998bd --- /dev/null +++ b/assets/queries/dockerfile/missing_flag_from_dnf_install/test/positive5.dockerfile @@ -0,0 +1,11 @@ +from fedora:27 +run set -uex && \ + dnf config-manager --set-enabled docker-ce-test && \ + dnf install docker-ce && \ + dnf clean all + +from fedora:28 +run set -uex +run dnf config-manager --set-enabled docker-ce-test +run dnf in docker-ce +run dnf clean all \ No newline at end of file diff --git a/assets/queries/dockerfile/missing_flag_from_dnf_install/test/positive_expected_result.json b/assets/queries/dockerfile/missing_flag_from_dnf_install/test/positive_expected_result.json index 8ca30d102d6..f94db540134 100644 --- a/assets/queries/dockerfile/missing_flag_from_dnf_install/test/positive_expected_result.json +++ b/assets/queries/dockerfile/missing_flag_from_dnf_install/test/positive_expected_result.json @@ -1,38 +1,50 @@ [ - { - "queryName": "Missing Flag From Dnf Install", - "severity": "LOW", - "line": 2, - "fileName": "positive.dockerfile" - }, - { - "queryName": "Missing Flag From Dnf Install", - "severity": "LOW", - "line": 10, - "fileName": "positive.dockerfile" - }, - { - "queryName": "Missing Flag From Dnf Install", - "severity": "LOW", - "line": 2, - "fileName": "positive2.dockerfile" - }, - { - "queryName": "Missing Flag From Dnf Install", - "severity": "LOW", - "line": 10, - "fileName": "positive2.dockerfile" - }, - { - "queryName": "Missing Flag From Dnf Install", - "severity": "LOW", - "line": 2, - "fileName": "positive3.dockerfile" - }, - { - "queryName": "Missing Flag From Dnf Install", - "severity": "LOW", - "line": 21, - "fileName": "positive4.dockerfile" - } + { + "queryName": "Missing Flag From Dnf Install", + "severity": "LOW", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Missing Flag From Dnf Install", + "severity": "LOW", + "line": 10, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Missing Flag From Dnf Install", + "severity": "LOW", + "line": 2, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Missing Flag From Dnf Install", + "severity": "LOW", + "line": 10, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Missing Flag From Dnf Install", + "severity": "LOW", + "line": 2, + "fileName": "positive3.dockerfile" + }, + { + "queryName": "Missing Flag From Dnf Install", + "severity": "LOW", + "line": 21, + "fileName": "positive4.dockerfile" + }, + { + "queryName": "Missing Flag From Dnf Install", + "severity": "LOW", + "line": 2, + "fileName": "positive5.dockerfile" + }, + { + "queryName": "Missing Flag From Dnf Install", + "severity": "LOW", + "line": 10, + "fileName": "positive5.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/missing_user_instruction/query.rego b/assets/queries/dockerfile/missing_user_instruction/query.rego index 51913455708..be5594d9bf7 100644 --- a/assets/queries/dockerfile/missing_user_instruction/query.rego +++ b/assets/queries/dockerfile/missing_user_instruction/query.rego @@ -9,15 +9,18 @@ CxPolicy[result] { not name == "scratch" not has_user_instruction(resource) + from_command := dockerLib.get_original_from_command(resource) + result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}", [name]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}", [from_command.Value, name]), from_command.LineHint), "issueType": "MissingAttribute", "keyExpectedValue": "The 'Dockerfile' should contain the 'USER' instruction", - "keyActualValue": "The 'Dockerfile' does not contain any 'USER' instruction", + "keyActualValue": "The 'Dockerfile' does not contain any 'USER' instruction" } } has_user_instruction(resource) { + resource[_].Cmd == "user" } diff --git a/assets/queries/dockerfile/missing_user_instruction/test/negative4.dockerfile b/assets/queries/dockerfile/missing_user_instruction/test/negative4.dockerfile new file mode 100644 index 00000000000..d4bc4f7b01a --- /dev/null +++ b/assets/queries/dockerfile/missing_user_instruction/test/negative4.dockerfile @@ -0,0 +1,14 @@ +from python:2.7 +run pip install Flask==0.11.1 +run useradd -ms /bin/bash patrick +copy --chown=patrick:patrick app /app +workdir /app +user patrick +cmd ["python", "app.py"] + +from scratch +run pip install Flask==0.11.1 +run useradd -ms /bin/bash patrick +copy --chown=patrick:patrick app /app +workdir /app +cmd ["python", "app.py"] diff --git a/assets/queries/dockerfile/missing_user_instruction/test/positive3.dockerfile b/assets/queries/dockerfile/missing_user_instruction/test/positive3.dockerfile new file mode 100644 index 00000000000..81af6f6ec65 --- /dev/null +++ b/assets/queries/dockerfile/missing_user_instruction/test/positive3.dockerfile @@ -0,0 +1,6 @@ +from python:2.7 +run pip install Flask==0.11.1 +run useradd -ms /bin/bash patrick +copy --chown=patrick:patrick app /app +workdir /app +cmd ["python", "app.py"] diff --git a/assets/queries/dockerfile/missing_user_instruction/test/positive_expected_result.json b/assets/queries/dockerfile/missing_user_instruction/test/positive_expected_result.json index 8a0833e1de1..c6f89f7b14f 100644 --- a/assets/queries/dockerfile/missing_user_instruction/test/positive_expected_result.json +++ b/assets/queries/dockerfile/missing_user_instruction/test/positive_expected_result.json @@ -10,5 +10,11 @@ "severity": "HIGH", "line": 7, "fileName": "positive2.dockerfile" + }, + { + "queryName": "Missing User Instruction", + "severity": "HIGH", + "line": 1, + "fileName": "positive3.dockerfile" } ] diff --git a/assets/queries/dockerfile/missing_version_specification_in_dnf_install/query.rego b/assets/queries/dockerfile/missing_version_specification_in_dnf_install/query.rego index 0d50a1ebde0..aca877aee59 100644 --- a/assets/queries/dockerfile/missing_version_specification_in_dnf_install/query.rego +++ b/assets/queries/dockerfile/missing_version_specification_in_dnf_install/query.rego @@ -19,9 +19,11 @@ CxPolicy[result] { some j analyzePackages(j, packages[j], packages, length) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "Package version should be specified when using 'dnf install'", "keyActualValue": "Package version should be pinned when running ´dnf install´", @@ -42,9 +44,11 @@ CxPolicy[result] { regex.match("^[a-zA-Z]", resource.Value[j]) == true not dockerLib.withVersion(resource.Value[j]) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "Package version should be specified when using 'dnf install'", "keyActualValue": "Package version should be pinned when running ´dnf install´", diff --git a/assets/queries/dockerfile/missing_version_specification_in_dnf_install/test/negative2.dockerfile b/assets/queries/dockerfile/missing_version_specification_in_dnf_install/test/negative2.dockerfile new file mode 100644 index 00000000000..2e494c41782 --- /dev/null +++ b/assets/queries/dockerfile/missing_version_specification_in_dnf_install/test/negative2.dockerfile @@ -0,0 +1,6 @@ +from fedora:latest +run dnf -y update && dnf -y install httpd-2.24.2 && dnf clean all +run ["dnf", "install", "httpd-2.24.2"] +copy index.html /var/www/html/index.html +expose 80 +entrypoint /usr/sbin/httpd -DFOREGROUND diff --git a/assets/queries/dockerfile/missing_version_specification_in_dnf_install/test/positive2.dockerfile b/assets/queries/dockerfile/missing_version_specification_in_dnf_install/test/positive2.dockerfile new file mode 100644 index 00000000000..dba9ef1dee2 --- /dev/null +++ b/assets/queries/dockerfile/missing_version_specification_in_dnf_install/test/positive2.dockerfile @@ -0,0 +1,6 @@ +from fedora:latest +run dnf -y update && dnf -y install httpd && dnf clean all +run ["dnf", "install", "httpd"] +copy index.html /var/www/html/index.html +expose 80 +entrypoint /usr/sbin/httpd -DFOREGROUND diff --git a/assets/queries/dockerfile/missing_version_specification_in_dnf_install/test/positive_expected_result.json b/assets/queries/dockerfile/missing_version_specification_in_dnf_install/test/positive_expected_result.json index 53dca70b9fb..5e939155d07 100644 --- a/assets/queries/dockerfile/missing_version_specification_in_dnf_install/test/positive_expected_result.json +++ b/assets/queries/dockerfile/missing_version_specification_in_dnf_install/test/positive_expected_result.json @@ -1,12 +1,26 @@ [ - { - "queryName": "Missing Version Specification In dnf install", - "severity": "MEDIUM", - "line": 2 - }, - { - "queryName": "Missing Version Specification In dnf install", - "severity": "MEDIUM", - "line": 3 - } -] + { + "queryName": "Missing Version Specification In dnf install", + "severity": "MEDIUM", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Missing Version Specification In dnf install", + "severity": "MEDIUM", + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Missing Version Specification In dnf install", + "severity": "MEDIUM", + "line": 2, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Missing Version Specification In dnf install", + "severity": "MEDIUM", + "line": 3, + "fileName": "positive2.dockerfile" + } +] \ No newline at end of file diff --git a/assets/queries/dockerfile/missing_zypper_clean/query.rego b/assets/queries/dockerfile/missing_zypper_clean/query.rego index 9b19c044d0f..004a9c0d5e6 100644 --- a/assets/queries/dockerfile/missing_zypper_clean/query.rego +++ b/assets/queries/dockerfile/missing_zypper_clean/query.rego @@ -14,9 +14,10 @@ CxPolicy[result] { commandHasZypperUsage(command) not hasCleanAfterInstall(commands[img], c, j) + from_command := dockerLib.get_original_from_command(commands[img]) result := { "documentId": document.id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [img, commands[img][c].Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, img, commands[img][c].Original]), from_command.LineHint), "issueType": "MissingAttribute", "keyExpectedValue": "There should be a zypper clean after a zypper usage", "keyActualValue": sprintf("The command '%s' does not have a zypper clean after it", [commands[img][c].Value[j]]), diff --git a/assets/queries/dockerfile/missing_zypper_clean/test/negative3.dockerfile b/assets/queries/dockerfile/missing_zypper_clean/test/negative3.dockerfile new file mode 100644 index 00000000000..ba424b3e2bf --- /dev/null +++ b/assets/queries/dockerfile/missing_zypper_clean/test/negative3.dockerfile @@ -0,0 +1,3 @@ +from busybox:1.0 +run zypper install -y httpd=2.4 && zypper clean +healthcheck CMD curl --fail http://localhost:3000 || exit 1 diff --git a/assets/queries/dockerfile/missing_zypper_clean/test/positive2.dockerfile b/assets/queries/dockerfile/missing_zypper_clean/test/positive2.dockerfile new file mode 100644 index 00000000000..4e774713abf --- /dev/null +++ b/assets/queries/dockerfile/missing_zypper_clean/test/positive2.dockerfile @@ -0,0 +1,3 @@ +from busybox:1.0 +run zypper install +healthcheck CMD curl --fail http://localhost:3000 || exit 1 diff --git a/assets/queries/dockerfile/missing_zypper_clean/test/positive_expected_result.json b/assets/queries/dockerfile/missing_zypper_clean/test/positive_expected_result.json index 5570f022802..203ee10a67d 100644 --- a/assets/queries/dockerfile/missing_zypper_clean/test/positive_expected_result.json +++ b/assets/queries/dockerfile/missing_zypper_clean/test/positive_expected_result.json @@ -1,7 +1,14 @@ [ - { - "queryName": "Missing Zypper Clean", - "severity": "LOW", - "line": 2 - } + { + "queryName": "Missing Zypper Clean", + "severity": "LOW", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Missing Zypper Clean", + "severity": "LOW", + "line": 2, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/missing_zypper_non_interactive_switch/query.rego b/assets/queries/dockerfile/missing_zypper_non_interactive_switch/query.rego index 419ef8597a0..04bff7a8f30 100644 --- a/assets/queries/dockerfile/missing_zypper_non_interactive_switch/query.rego +++ b/assets/queries/dockerfile/missing_zypper_non_interactive_switch/query.rego @@ -14,9 +14,10 @@ CxPolicy[result] { commandHasZypperUsage(command) not commandHasNonInteractiveSwitch(command) + from_command := dockerLib.get_original_from_command(commands[img]) result := { "documentId": document.id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [img, commands[img][c].Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, img, commands[img][c].Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "zypper usages should have the non-interactive switch activated", "keyActualValue": sprintf("The command '%s' does not have the non-interactive switch activated (-y | --no-confirm)", [commands[img][c].Original]), diff --git a/assets/queries/dockerfile/missing_zypper_non_interactive_switch/test/negative3.dockerfile b/assets/queries/dockerfile/missing_zypper_non_interactive_switch/test/negative3.dockerfile new file mode 100644 index 00000000000..1c4879426a1 --- /dev/null +++ b/assets/queries/dockerfile/missing_zypper_non_interactive_switch/test/negative3.dockerfile @@ -0,0 +1,3 @@ +from busybox:1.0 +run zypper install -y httpd=2.4.46 && zypper clean +healthcheck CMD curl --fail http://localhost:3000 || exit 1 diff --git a/assets/queries/dockerfile/missing_zypper_non_interactive_switch/test/positive2.dockerfile b/assets/queries/dockerfile/missing_zypper_non_interactive_switch/test/positive2.dockerfile new file mode 100644 index 00000000000..5f770109502 --- /dev/null +++ b/assets/queries/dockerfile/missing_zypper_non_interactive_switch/test/positive2.dockerfile @@ -0,0 +1,3 @@ +from busybox:1.0 +run zypper install httpd && zypper clean +healthcheck CMD curl --fail http://localhost:3000 || exit 1 diff --git a/assets/queries/dockerfile/missing_zypper_non_interactive_switch/test/positive_expected_result.json b/assets/queries/dockerfile/missing_zypper_non_interactive_switch/test/positive_expected_result.json index fa3f05610c9..8a654e77bc9 100644 --- a/assets/queries/dockerfile/missing_zypper_non_interactive_switch/test/positive_expected_result.json +++ b/assets/queries/dockerfile/missing_zypper_non_interactive_switch/test/positive_expected_result.json @@ -2,6 +2,13 @@ { "queryName": "Missing Zypper Non-interactive Switch", "severity": "MEDIUM", - "line": 2 + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Missing Zypper Non-interactive Switch", + "severity": "MEDIUM", + "line": 2, + "fileName": "positive2.dockerfile" } -] +] \ No newline at end of file diff --git a/assets/queries/dockerfile/multiple_cmd_instructions_listed/query.rego b/assets/queries/dockerfile/multiple_cmd_instructions_listed/query.rego index 9976a3b449f..537abac872e 100644 --- a/assets/queries/dockerfile/multiple_cmd_instructions_listed/query.rego +++ b/assets/queries/dockerfile/multiple_cmd_instructions_listed/query.rego @@ -9,9 +9,10 @@ CxPolicy[result] { cmdInst := [x | resource[j].Cmd == "cmd"; x := resource[j]] count(cmdInst) > 1 + from_command := dockerLib.get_original_from_command(resource) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, cmdInst[0].Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, cmdInst[0].Original]), from_command.LineHint), "issueType": "RedundantAttribute", #"MissingAttribute" / "RedundantAttribute" "keyExpectedValue": "There should be only one CMD instruction", "keyActualValue": sprintf("There are %d CMD instructions", [count(cmdInst)]), diff --git a/assets/queries/dockerfile/multiple_cmd_instructions_listed/test/negative3.dockerfile b/assets/queries/dockerfile/multiple_cmd_instructions_listed/test/negative3.dockerfile new file mode 100644 index 00000000000..1db23282c4a --- /dev/null +++ b/assets/queries/dockerfile/multiple_cmd_instructions_listed/test/negative3.dockerfile @@ -0,0 +1,12 @@ +from golang:1.7.3 +workdir /go/src/github.com/foo/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 . +cmd ["./app"] + +from alpine:latest +run apk --no-cache add ca-certificates +workdir /root/ +copy --from=0 /go/src/github.com/foo/href-counter/app . +cmd ["./app"] \ No newline at end of file diff --git a/assets/queries/dockerfile/multiple_cmd_instructions_listed/test/positive2.dockerfile b/assets/queries/dockerfile/multiple_cmd_instructions_listed/test/positive2.dockerfile new file mode 100644 index 00000000000..38eef4b6c1b --- /dev/null +++ b/assets/queries/dockerfile/multiple_cmd_instructions_listed/test/positive2.dockerfile @@ -0,0 +1,12 @@ +from golang:1.7.3 +workdir /go/src/github.com/foo/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/foo/href-counter/app . +cmd ["./app"] +cmd ["./apps"] diff --git a/assets/queries/dockerfile/multiple_cmd_instructions_listed/test/positive_expected_result.json b/assets/queries/dockerfile/multiple_cmd_instructions_listed/test/positive_expected_result.json index 5110e6420af..ea8f2844cd3 100644 --- a/assets/queries/dockerfile/multiple_cmd_instructions_listed/test/positive_expected_result.json +++ b/assets/queries/dockerfile/multiple_cmd_instructions_listed/test/positive_expected_result.json @@ -1,8 +1,14 @@ [ - { - "queryName": "Multiple CMD Instructions Listed", - "severity": "LOW", - "line": 11, - "fileName": "positive.dockerfile" - } + { + "queryName": "Multiple CMD Instructions Listed", + "severity": "LOW", + "line": 11, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Multiple CMD Instructions Listed", + "severity": "LOW", + "line": 11, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/query.rego b/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/query.rego index bc821fe04fa..8ce7212e0c2 100644 --- a/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/query.rego +++ b/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/query.rego @@ -9,9 +9,10 @@ CxPolicy[result] { cmdInst := [x | resource[j].Cmd == "entrypoint"; x := resource[j]] count(cmdInst) > 1 + from_command := dockerLib.get_original_from_command(resource) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, cmdInst[0].Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, cmdInst[0].Original]), from_command.LineHint), "issueType": "RedundantAttribute", "keyExpectedValue": "There should be only one ENTRYPOINT instruction", "keyActualValue": sprintf("There are %d ENTRYPOINT instructions", [count(cmdInst)]), diff --git a/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/test/negative3.dockerfile b/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/test/negative3.dockerfile new file mode 100644 index 00000000000..87709ec452f --- /dev/null +++ b/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/test/negative3.dockerfile @@ -0,0 +1,12 @@ +from golang:1.7.3 +workdir /go/src/github.com/foo/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 . +entrypoint [ "/opt/app/run.sh", "--port", "8080" ] + +from alpine:latest +run apk --no-cache add ca-certificates +workdir /root/ +copy --from=0 /go/src/github.com/foo/href-counter/app . +entrypoint [ "/opt/app/run.sh", "--port", "8080" ] \ No newline at end of file diff --git a/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/test/positive2.dockerfile b/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/test/positive2.dockerfile new file mode 100644 index 00000000000..2a55b12e841 --- /dev/null +++ b/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/test/positive2.dockerfile @@ -0,0 +1,12 @@ +from golang:1.7.3 +workdir /go/src/github.com/foo/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/foo/href-counter/app . +entrypoint [ "/opt/app/run.sh", "--port", "8080" ] +entrypoint [ "/opt/app/run.sh", "--port", "8000" ] diff --git a/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/test/positive_expected_result.json b/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/test/positive_expected_result.json index c1c67a870ea..8096a6b8ad3 100644 --- a/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/test/positive_expected_result.json +++ b/assets/queries/dockerfile/multiple_entrypoint_instructions_listed/test/positive_expected_result.json @@ -1,8 +1,14 @@ [ - { - "queryName": "Multiple ENTRYPOINT Instructions Listed", - "severity": "LOW", - "line": 11, - "fileName": "positive.dockerfile" - } + { + "queryName": "Multiple ENTRYPOINT Instructions Listed", + "severity": "LOW", + "line": 11, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Multiple ENTRYPOINT Instructions Listed", + "severity": "LOW", + "line": 11, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/query.rego b/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/query.rego index 4c07f1660e2..0ad800a0e72 100644 --- a/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/query.rego +++ b/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/query.rego @@ -22,9 +22,10 @@ CxPolicy[result] { countCmdInst := count(lineCounter) countCmdInst > 0 + from_command := dockerLib.get_original_from_command(resource) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, lineCounter[0].Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, lineCounter[0].Original]), from_command.LineHint), "issueType": "RedundantAttribute", "keyExpectedValue": sprintf("There isn´t any %s instruction that could be grouped", [upperName]), "keyActualValue": sprintf("There are %s instructions that could be grouped", [upperName]), diff --git a/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/test/negative6.dockerfile b/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/test/negative6.dockerfile new file mode 100644 index 00000000000..5fb5e41e709 --- /dev/null +++ b/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/test/negative6.dockerfile @@ -0,0 +1,2 @@ +from ubuntu +run apt-get install wget && wget https://…/downloadedfile.tar && tar xvzf downloadedfile.tar && rm downloadedfile.tar && apt-get remove wget diff --git a/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/test/positive4.dockerfile b/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/test/positive4.dockerfile new file mode 100644 index 00000000000..8ffdb7017cc --- /dev/null +++ b/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/test/positive4.dockerfile @@ -0,0 +1,6 @@ +from ubuntu +run apt-get install -y wget +run wget https://…/downloadedfile.tar +run tar xvzf downloadedfile.tar +run rm downloadedfile.tar +run apt-get remove wget diff --git a/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/test/positive_expected_result.json b/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/test/positive_expected_result.json index 7474b74c429..0057ce393cc 100644 --- a/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/test/positive_expected_result.json +++ b/assets/queries/dockerfile/multiple_run_add_copy_instructions_listed/test/positive_expected_result.json @@ -1,20 +1,26 @@ [ - { - "queryName": "Multiple RUN, ADD, COPY, Instructions Listed", - "severity": "LOW", - "line": 2, - "fileName": "positive1.dockerfile" - }, - { - "queryName": "Multiple RUN, ADD, COPY, Instructions Listed", - "severity": "LOW", - "line": 2, - "fileName": "positive2.dockerfile" - }, - { - "queryName": "Multiple RUN, ADD, COPY, Instructions Listed", - "severity": "LOW", - "line": 2, - "fileName": "positive3.dockerfile" - } -] + { + "queryName": "Multiple RUN, ADD, COPY, Instructions Listed", + "severity": "LOW", + "line": 2, + "fileName": "positive1.dockerfile" + }, + { + "queryName": "Multiple RUN, ADD, COPY, Instructions Listed", + "severity": "LOW", + "line": 2, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Multiple RUN, ADD, COPY, Instructions Listed", + "severity": "LOW", + "line": 2, + "fileName": "positive3.dockerfile" + }, + { + "queryName": "Multiple RUN, ADD, COPY, Instructions Listed", + "severity": "LOW", + "line": 2, + "fileName": "positive4.dockerfile" + } +] \ No newline at end of file diff --git a/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/query.rego b/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/query.rego index 2ab522b1177..c6a5e4dd4fe 100644 --- a/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/query.rego +++ b/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/query.rego @@ -9,9 +9,11 @@ CxPolicy[result] { resource.Cmd == "cmd" resource.JSON == false + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("{{%s}} should be in the JSON Notation", [resource.Original]), "keyActualValue": sprintf("{{%s}} isn't in JSON Notation", [resource.Original]), @@ -25,9 +27,11 @@ CxPolicy[result] { resource.Cmd == "entrypoint" resource.JSON == false + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("{{%s}} should be in the JSON Notation", [resource.Original]), "keyActualValue": sprintf("{{%s}} isn't in JSON Notation", [resource.Original]), diff --git a/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/test/negative3.dockerfile b/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/test/negative3.dockerfile new file mode 100644 index 00000000000..a9084a1cfec --- /dev/null +++ b/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/test/negative3.dockerfile @@ -0,0 +1,11 @@ +from alpine:3.5 +run apk add --update py2-pip +run sudo yum install bundler +run yum install +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"] +entrypoint ["top", "-b"] \ No newline at end of file diff --git a/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/test/positive2.dockerfile b/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/test/positive2.dockerfile new file mode 100644 index 00000000000..217643dd8c2 --- /dev/null +++ b/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/test/positive2.dockerfile @@ -0,0 +1,11 @@ +from alpine:3.5 +run apk add --update py2-pip +run sudo yum install bundler +run yum install +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] +entrypoint [top, -b] \ No newline at end of file diff --git a/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/test/positive_expected_result.json b/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/test/positive_expected_result.json index 779bfea3ef6..2a3d11dfd6c 100644 --- a/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/test/positive_expected_result.json +++ b/assets/queries/dockerfile/not_using_json_in_cmd_and_entrypoint_arguments/test/positive_expected_result.json @@ -2,11 +2,25 @@ { "queryName": "Not Using JSON In CMD And ENTRYPOINT Arguments", "severity": "MEDIUM", - "line": 10 + "line": 10, + "fileName": "positive.dockerfile" }, { "queryName": "Not Using JSON In CMD And ENTRYPOINT Arguments", "severity": "MEDIUM", - "line": 11 + "line": 11, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Not Using JSON In CMD And ENTRYPOINT Arguments", + "severity": "MEDIUM", + "line": 10, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Not Using JSON In CMD And ENTRYPOINT Arguments", + "severity": "MEDIUM", + "line": 11, + "fileName": "positive2.dockerfile" } -] +] \ No newline at end of file diff --git a/assets/queries/dockerfile/npm_install_without_pinned_version/query.rego b/assets/queries/dockerfile/npm_install_without_pinned_version/query.rego index f5d472adda6..b5dceec999a 100644 --- a/assets/queries/dockerfile/npm_install_without_pinned_version/query.rego +++ b/assets/queries/dockerfile/npm_install_without_pinned_version/query.rego @@ -1,5 +1,7 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { runCmd := input.document[i].command[name][_] is_run_cmd(runCmd) @@ -21,9 +23,11 @@ CxPolicy[result] { token != "install" not valid_match(token) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, runCmd.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, runCmd.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("'%s' uses npm install with a pinned version", [runCmd.Original]), "keyActualValue": sprintf("'%s' does not uses npm install with a pinned version", [runCmd.Original]), diff --git a/assets/queries/dockerfile/npm_install_without_pinned_version/test/negative2.dockerfile b/assets/queries/dockerfile/npm_install_without_pinned_version/test/negative2.dockerfile new file mode 100644 index 00000000000..cb59f7ede0e --- /dev/null +++ b/assets/queries/dockerfile/npm_install_without_pinned_version/test/negative2.dockerfile @@ -0,0 +1,11 @@ +from node:12 +run npm install +run npm install sax@latest +run npm install sax@0.1.1 +run npm install sax@0.1.1 | grep fail && npm install sax@latest +run npm install git://github.com/npm/cli.git +run npm install git+ssh://git@github.com:npm/cli#semver:^5.0 +run npm install --production --no-cache +run npm config set registry && \ + npm install && \ + npx vite build --mode $VITE_MODE \ No newline at end of file diff --git a/assets/queries/dockerfile/npm_install_without_pinned_version/test/positive2.dockerfile b/assets/queries/dockerfile/npm_install_without_pinned_version/test/positive2.dockerfile new file mode 100644 index 00000000000..ea0b3b40465 --- /dev/null +++ b/assets/queries/dockerfile/npm_install_without_pinned_version/test/positive2.dockerfile @@ -0,0 +1,8 @@ +from node:12 +run npm install sax +run npm install sax --no-cache +run npm install sax | grep fail && npm install sax@latest +run npm install sax@latest | grep fail && npm install sax +run npm install sax | grep fail && npm install sax +run npm i -g @angular/cli +run ["npm","add","sax"] diff --git a/assets/queries/dockerfile/npm_install_without_pinned_version/test/positive_expected_result.json b/assets/queries/dockerfile/npm_install_without_pinned_version/test/positive_expected_result.json index ec6862cd11b..b65937068ba 100644 --- a/assets/queries/dockerfile/npm_install_without_pinned_version/test/positive_expected_result.json +++ b/assets/queries/dockerfile/npm_install_without_pinned_version/test/positive_expected_result.json @@ -1,44 +1,86 @@ [ - { - "queryName": "NPM Install Command Without Pinned Version", - "severity": "MEDIUM", - "line": 2, - "filename": "positive1.dockerfile" - }, - { - "queryName": "NPM Install Command Without Pinned Version", - "severity": "MEDIUM", - "line": 3, - "filename": "positive1.dockerfile" - }, - { - "queryName": "NPM Install Command Without Pinned Version", - "severity": "MEDIUM", - "line": 4, - "filename": "positive1.dockerfile" - }, - { - "queryName": "NPM Install Command Without Pinned Version", - "severity": "MEDIUM", - "line": 5, - "filename": "positive1.dockerfile" - }, - { - "queryName": "NPM Install Command Without Pinned Version", - "severity": "MEDIUM", - "line": 6, - "filename": "positive1.dockerfile" - }, - { - "queryName": "NPM Install Command Without Pinned Version", - "severity": "MEDIUM", - "line": 7, - "filename": "positive1.dockerfile" - }, - { - "queryName": "NPM Install Command Without Pinned Version", - "severity": "MEDIUM", - "line": 8, - "filename": "positive1.dockerfile" - } + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 2, + "filename": "positive1.dockerfile" + }, + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 3, + "filename": "positive1.dockerfile" + }, + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 4, + "filename": "positive1.dockerfile" + }, + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 5, + "filename": "positive1.dockerfile" + }, + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 6, + "filename": "positive1.dockerfile" + }, + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 7, + "filename": "positive1.dockerfile" + }, + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 8, + "filename": "positive1.dockerfile" + }, + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 2, + "filename": "positive2.dockerfile" + }, + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 3, + "filename": "positive2.dockerfile" + }, + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 4, + "filename": "positive2.dockerfile" + }, + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 5, + "filename": "positive2.dockerfile" + }, + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 6, + "filename": "positive2.dockerfile" + }, + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 7, + "filename": "positive2.dockerfile" + }, + { + "queryName": "NPM Install Command Without Pinned Version", + "severity": "MEDIUM", + "line": 8, + "filename": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/pip_install_keeping_cached_packages/query.rego b/assets/queries/dockerfile/pip_install_keeping_cached_packages/query.rego index 50eec9a5c4c..8e9e7f2952a 100644 --- a/assets/queries/dockerfile/pip_install_keeping_cached_packages/query.rego +++ b/assets/queries/dockerfile/pip_install_keeping_cached_packages/query.rego @@ -11,9 +11,11 @@ CxPolicy[result] { hasCacheFlag(values) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, values]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, values]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "The '--no-cache-dir' flag should be set when running 'pip/pip3 install'", "keyActualValue": "The '--no-cache-dir' flag isn't set when running 'pip/pip3 install'", @@ -30,9 +32,11 @@ CxPolicy[result] { not hasCacheFlagInList(resource.Value) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "The '--no-cache-dir' flag should be set when running 'pip/pip3 install'", "keyActualValue": "The '--no-cache-dir' flag isn't set when running 'pip/pip3 install'", diff --git a/assets/queries/dockerfile/pip_install_keeping_cached_packages/test/negative2.dockerfile b/assets/queries/dockerfile/pip_install_keeping_cached_packages/test/negative2.dockerfile new file mode 100644 index 00000000000..b8fe51dec35 --- /dev/null +++ b/assets/queries/dockerfile/pip_install_keeping_cached_packages/test/negative2.dockerfile @@ -0,0 +1,7 @@ +from python:3 +run pip install --no-cache-dir --upgrade pip && \ + pip install --no-cache-dir nibabel pydicom matplotlib pillow && \ + pip install --no-cache-dir med2image +run pip3 install --no-cache-dir requests=2.7.0 +run ["pip3", "install", "requests=2.7.0", "--no-cache-dir"] +cmd ["cat", "/etc/os-release"] diff --git a/assets/queries/dockerfile/pip_install_keeping_cached_packages/test/positive2.dockerfile b/assets/queries/dockerfile/pip_install_keeping_cached_packages/test/positive2.dockerfile new file mode 100644 index 00000000000..116a81d85ad --- /dev/null +++ b/assets/queries/dockerfile/pip_install_keeping_cached_packages/test/positive2.dockerfile @@ -0,0 +1,12 @@ +from python:3 +run pip install --upgrade pip && \ + pip install nibabel pydicom matplotlib pillow && \ + pip install med2image +cmd ["cat", "/etc/os-release"] + +from python:3.1 +run pip install --upgrade pip +run python -m pip install nibabel pydicom matplotlib pillow +run pip3 install requests=2.7.0 +run ["pip3", "install", "requests=2.7.0"] +cmd ["cat", "/etc/os-release"] diff --git a/assets/queries/dockerfile/pip_install_keeping_cached_packages/test/positive_expected_result.json b/assets/queries/dockerfile/pip_install_keeping_cached_packages/test/positive_expected_result.json index 727c06aeff6..01668bfd73b 100644 --- a/assets/queries/dockerfile/pip_install_keeping_cached_packages/test/positive_expected_result.json +++ b/assets/queries/dockerfile/pip_install_keeping_cached_packages/test/positive_expected_result.json @@ -1,27 +1,62 @@ [ - { - "queryName": "Pip install Keeping Cached Packages", - "severity": "LOW", - "line": 2 - }, - { - "queryName": "Pip install Keeping Cached Packages", - "severity": "LOW", - "line": 8 - }, - { - "queryName": "Pip install Keeping Cached Packages", - "severity": "LOW", - "line": 9 - }, - { - "queryName": "Pip install Keeping Cached Packages", - "severity": "LOW", - "line": 10 - }, - { - "queryName": "Pip install Keeping Cached Packages", - "severity": "LOW", - "line": 11 - } + { + "queryName": "Pip install Keeping Cached Packages", + "severity": "LOW", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Pip install Keeping Cached Packages", + "severity": "LOW", + "line": 8, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Pip install Keeping Cached Packages", + "severity": "LOW", + "line": 9, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Pip install Keeping Cached Packages", + "severity": "LOW", + "line": 10, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Pip install Keeping Cached Packages", + "severity": "LOW", + "line": 11, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Pip install Keeping Cached Packages", + "severity": "LOW", + "line": 2, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Pip install Keeping Cached Packages", + "severity": "LOW", + "line": 8, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Pip install Keeping Cached Packages", + "severity": "LOW", + "line": 9, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Pip install Keeping Cached Packages", + "severity": "LOW", + "line": 10, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Pip install Keeping Cached Packages", + "severity": "LOW", + "line": 11, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file 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 9a75bbd392b..10a13075da2 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 @@ -1,18 +1,23 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { resource := input.document[i].command[name][_] resource.Cmd == "run" - run_command := resource.Value[_] - values := split(run_command, " ") + run_command_value := resource.Value[_] + values := split(run_command_value, " ") trim_space(values[index]) == "cd" path := trim_space(values[index+1]) not is_full_path(path) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) + run_command := substring(resource.Original, 0, 3) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.RUN={{%s}}", [name, resource.Value[0]]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.%s={{%s}}", [from_command.Value, name, run_command, resource.Value[0]]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "Using WORKDIR to change directory", "keyActualValue": sprintf("RUN %s'", [resource.Value[0]]), diff --git a/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/negative3.dockerfile b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/negative3.dockerfile new file mode 100644 index 00000000000..cfd63dc6a9b --- /dev/null +++ b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/negative3.dockerfile @@ -0,0 +1,5 @@ +from nginx +env AUTHOR=Docker +workdir /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;' \ No newline at end of file diff --git a/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/positive2.dockerfile b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/positive2.dockerfile new file mode 100644 index 00000000000..8abcf679cba --- /dev/null +++ b/assets/queries/dockerfile/run_command_cd_instead_of_workdir/test/positive2.dockerfile @@ -0,0 +1,17 @@ +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 ../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 4cba6c72f3f..6c533054a7f 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 @@ -1,20 +1,38 @@ [ - { - "queryName": "RUN Instruction Using 'cd' Instead of WORKDIR", - "severity": "LOW", - "line": 3, - "fileName": "positive.dockerfile" - }, - { - "queryName": "RUN Instruction Using 'cd' Instead of WORKDIR", - "severity": "LOW", - "line": 9, - "fileName": "positive.dockerfile" - }, - { - "queryName": "RUN Instruction Using 'cd' Instead of WORKDIR", - "severity": "LOW", - "line": 15, - "fileName": "positive.dockerfile" - } + { + "queryName": "RUN Instruction Using 'cd' Instead of WORKDIR", + "severity": "LOW", + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "RUN Instruction Using 'cd' Instead of WORKDIR", + "severity": "LOW", + "line": 9, + "fileName": "positive.dockerfile" + }, + { + "queryName": "RUN Instruction Using 'cd' Instead of WORKDIR", + "severity": "LOW", + "line": 15, + "fileName": "positive.dockerfile" + }, + { + "queryName": "RUN Instruction Using 'cd' Instead of WORKDIR", + "severity": "LOW", + "line": 3, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "RUN Instruction Using 'cd' Instead of WORKDIR", + "severity": "LOW", + "line": 9, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "RUN Instruction Using 'cd' Instead of WORKDIR", + "severity": "LOW", + "line": 15, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/run_using_apt/query.rego b/assets/queries/dockerfile/run_using_apt/query.rego index b7f210a8805..ae5a81b193d 100644 --- a/assets/queries/dockerfile/run_using_apt/query.rego +++ b/assets/queries/dockerfile/run_using_apt/query.rego @@ -1,5 +1,7 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { document := input.document[i] commands = document.command @@ -9,9 +11,10 @@ CxPolicy[result] { some j contains(commands[img][c].Value[j], "apt ") + from_command := dockerLib.get_original_from_command(commands[img]) result := { "documentId": document.id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [img, commands[img][c].Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, img, commands[img][c].Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "RUN instructions should not use the 'apt' program", "keyActualValue": "RUN instruction is invoking the 'apt' program", diff --git a/assets/queries/dockerfile/run_using_apt/test/negative2.dockerfile b/assets/queries/dockerfile/run_using_apt/test/negative2.dockerfile new file mode 100644 index 00000000000..0fbb58df873 --- /dev/null +++ b/assets/queries/dockerfile/run_using_apt/test/negative2.dockerfile @@ -0,0 +1,3 @@ +from busybox:1.0 +run apt-get install curl +healthcheck CMD curl --fail http://localhost:3000 || exit 1 diff --git a/assets/queries/dockerfile/run_using_apt/test/positive2.dockerfile b/assets/queries/dockerfile/run_using_apt/test/positive2.dockerfile new file mode 100644 index 00000000000..2c507976add --- /dev/null +++ b/assets/queries/dockerfile/run_using_apt/test/positive2.dockerfile @@ -0,0 +1,3 @@ +from busybox:1.0 +run apt install curl +healthcheck CMD curl --fail http://localhost:3000 || exit 1 diff --git a/assets/queries/dockerfile/run_using_apt/test/positive_expected_result.json b/assets/queries/dockerfile/run_using_apt/test/positive_expected_result.json index c6a5e011847..fdf680af097 100644 --- a/assets/queries/dockerfile/run_using_apt/test/positive_expected_result.json +++ b/assets/queries/dockerfile/run_using_apt/test/positive_expected_result.json @@ -1,7 +1,14 @@ [ - { - "queryName": "Run Using apt", - "severity": "LOW", - "line": 2 - } + { + "queryName": "Run Using apt", + "severity": "LOW", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Run Using apt", + "severity": "LOW", + "line": 2, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/run_using_sudo/query.rego b/assets/queries/dockerfile/run_using_sudo/query.rego index 224e4913438..c8c7f78f602 100644 --- a/assets/queries/dockerfile/run_using_sudo/query.rego +++ b/assets/queries/dockerfile/run_using_sudo/query.rego @@ -9,9 +9,12 @@ CxPolicy[result] { hasSudo(resource.Value[0]) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) + run_command := substring(resource.Original, 0, 3) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.RUN={{%s}}", [name, resource.Value[0]]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.%s={{%s}}", [from_command.Value, name, run_command, resource.Value[0]]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "RUN instruction shouldn't contain sudo", "keyActualValue": "RUN instruction contains sudo", @@ -25,9 +28,12 @@ CxPolicy[result] { resource.Value[0] == "sudo" + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) + run_command := substring(resource.Original, 0, 3) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.RUN={{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.%s={{%s}}", [from_command.Value, name, run_command, resource.Value[0]]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "RUN instruction shouldn't contain sudo", "keyActualValue": "RUN instruction contains sudo", diff --git a/assets/queries/dockerfile/run_using_sudo/test/negative2.dockerfile b/assets/queries/dockerfile/run_using_sudo/test/negative2.dockerfile new file mode 100644 index 00000000000..7e5ee45d32a --- /dev/null +++ b/assets/queries/dockerfile/run_using_sudo/test/negative2.dockerfile @@ -0,0 +1,10 @@ +from alpine:3.5 +run apk add --update py2-pip +run pip install --upgrade pip +run apt-get install sudo +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/run_using_sudo/test/positive2.dockerfile b/assets/queries/dockerfile/run_using_sudo/test/positive2.dockerfile new file mode 100644 index 00000000000..650b345cb74 --- /dev/null +++ b/assets/queries/dockerfile/run_using_sudo/test/positive2.dockerfile @@ -0,0 +1,9 @@ +from alpine:3.5 +run apk add --update py2-pip +run sudo pip install --upgrade pip +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"] \ No newline at end of file diff --git a/assets/queries/dockerfile/run_using_sudo/test/positive_expected_result.json b/assets/queries/dockerfile/run_using_sudo/test/positive_expected_result.json index 581fa52051a..da03ae5536a 100644 --- a/assets/queries/dockerfile/run_using_sudo/test/positive_expected_result.json +++ b/assets/queries/dockerfile/run_using_sudo/test/positive_expected_result.json @@ -1,7 +1,14 @@ [ - { - "queryName": "Run Using Sudo", - "severity": "MEDIUM", - "line": 3 - } + { + "queryName": "Run Using Sudo", + "severity": "MEDIUM", + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Run Using Sudo", + "severity": "MEDIUM", + "line": 3, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/run_using_wget_and_curl/query.rego b/assets/queries/dockerfile/run_using_wget_and_curl/query.rego index 617376f946c..98479d6405d 100644 --- a/assets/queries/dockerfile/run_using_wget_and_curl/query.rego +++ b/assets/queries/dockerfile/run_using_wget_and_curl/query.rego @@ -11,9 +11,10 @@ CxPolicy[result] { count(curl) > 0 count(wget) > 0 + from_command := dockerLib.get_original_from_command(resource) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, curl[0]]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, curl[0]]), from_command.LineHint), "issueType": "RedundantAttribute", "keyExpectedValue": "Exclusively using 'wget' or 'curl'", "keyActualValue": "Using both 'wget' and 'curl'", diff --git a/assets/queries/dockerfile/run_using_wget_and_curl/test/negative2.dockerfile b/assets/queries/dockerfile/run_using_wget_and_curl/test/negative2.dockerfile new file mode 100644 index 00000000000..d48fe91f7c6 --- /dev/null +++ b/assets/queries/dockerfile/run_using_wget_and_curl/test/negative2.dockerfile @@ -0,0 +1,4 @@ +from debian +run curl http://google.com +run curl http://bing.com +run ["curl", "http://bing.com"] diff --git a/assets/queries/dockerfile/run_using_wget_and_curl/test/positive2.dockerfile b/assets/queries/dockerfile/run_using_wget_and_curl/test/positive2.dockerfile new file mode 100644 index 00000000000..a78eab5ff90 --- /dev/null +++ b/assets/queries/dockerfile/run_using_wget_and_curl/test/positive2.dockerfile @@ -0,0 +1,8 @@ +from debian +run wget http://google.com +run curl http://bing.com + +from baseImage +run wget http://test.com +run curl http://bing.com +run ["curl", "http://bing.com"] diff --git a/assets/queries/dockerfile/run_using_wget_and_curl/test/positive_expected_result.json b/assets/queries/dockerfile/run_using_wget_and_curl/test/positive_expected_result.json index 82340b752d6..704421a805d 100644 --- a/assets/queries/dockerfile/run_using_wget_and_curl/test/positive_expected_result.json +++ b/assets/queries/dockerfile/run_using_wget_and_curl/test/positive_expected_result.json @@ -1,17 +1,38 @@ [ - { - "queryName": "Run Using 'wget' and 'curl'", - "severity": "LOW", - "line": 3 - }, - { - "queryName": "Run Using 'wget' and 'curl'", - "severity": "LOW", - "line": 7 - }, - { - "queryName": "Run Using 'wget' and 'curl'", - "severity": "LOW", - "line": 8 - } + { + "queryName": "Run Using 'wget' and 'curl'", + "severity": "LOW", + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Run Using 'wget' and 'curl'", + "severity": "LOW", + "line": 7, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Run Using 'wget' and 'curl'", + "severity": "LOW", + "line": 8, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Run Using 'wget' and 'curl'", + "severity": "LOW", + "line": 3, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Run Using 'wget' and 'curl'", + "severity": "LOW", + "line": 7, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Run Using 'wget' and 'curl'", + "severity": "LOW", + "line": 8, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/run_utilities_and_posix_commands/query.rego b/assets/queries/dockerfile/run_utilities_and_posix_commands/query.rego index 887ccb0aae6..dbb6eae2c7d 100644 --- a/assets/queries/dockerfile/run_utilities_and_posix_commands/query.rego +++ b/assets/queries/dockerfile/run_utilities_and_posix_commands/query.rego @@ -1,14 +1,18 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { resource := input.document[i].command[name][_] resource.Cmd == "run" containsCommand(resource) == true + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "There should be no dangerous commands or utilities executed", "keyActualValue": sprintf("Run instruction is executing the %s command", [resource.Value[0]]), diff --git a/assets/queries/dockerfile/run_utilities_and_posix_commands/test/negative2.dockerfile b/assets/queries/dockerfile/run_utilities_and_posix_commands/test/negative2.dockerfile new file mode 100644 index 00000000000..d3aa7e141ae --- /dev/null +++ b/assets/queries/dockerfile/run_utilities_and_posix_commands/test/negative2.dockerfile @@ -0,0 +1,8 @@ +from ubuntu +run apt-get update && apt-get install -y x11vnc xvfb firefox +run mkdir ~/.vnc +run x11vnc -storepasswd 1234 ~/.vnc/passwd +run bash -c 'echo "firefox" >> /.bashrc' +run apt-get install nano vim +expose 5900 +cmd ["x11vnc", "-forever", "-usepw", "-create"] diff --git a/assets/queries/dockerfile/run_utilities_and_posix_commands/test/positive2.dockerfile b/assets/queries/dockerfile/run_utilities_and_posix_commands/test/positive2.dockerfile new file mode 100644 index 00000000000..3b72fdee3ee --- /dev/null +++ b/assets/queries/dockerfile/run_utilities_and_posix_commands/test/positive2.dockerfile @@ -0,0 +1,6 @@ +from golang:1.12.0-stretch +workdir /go +copy . /go +run top +run ["ps", "-d"] +cmd ["go", "run", "main.go"] diff --git a/assets/queries/dockerfile/run_utilities_and_posix_commands/test/positive_expected_result.json b/assets/queries/dockerfile/run_utilities_and_posix_commands/test/positive_expected_result.json index 9f366ada36a..1cccbe4fdac 100644 --- a/assets/queries/dockerfile/run_utilities_and_posix_commands/test/positive_expected_result.json +++ b/assets/queries/dockerfile/run_utilities_and_posix_commands/test/positive_expected_result.json @@ -1,12 +1,26 @@ [ - { - "queryName": "Run Utilities And POSIX Commands", - "severity": "INFO", - "line": 4 - }, - { - "queryName": "Run Utilities And POSIX Commands", - "severity": "INFO", - "line": 5 - } -] + { + "queryName": "Run Utilities And POSIX Commands", + "severity": "INFO", + "line": 4, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Run Utilities And POSIX Commands", + "severity": "INFO", + "line": 5, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Run Utilities And POSIX Commands", + "severity": "INFO", + "line": 4, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Run Utilities And POSIX Commands", + "severity": "INFO", + "line": 5, + "fileName": "positive2.dockerfile" + } +] \ No newline at end of file diff --git a/assets/queries/dockerfile/same_alias_in_different_froms/query.rego b/assets/queries/dockerfile/same_alias_in_different_froms/query.rego index 933f588f86f..10fd87a717c 100644 --- a/assets/queries/dockerfile/same_alias_in_different_froms/query.rego +++ b/assets/queries/dockerfile/same_alias_in_different_froms/query.rego @@ -1,5 +1,7 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { resource := input.document[i].command[name][com] resource.Cmd == "from" @@ -14,12 +16,13 @@ CxPolicy[result] { idx_2 := getIndex(aliasResource.Value) aliasResource.Value[idx_2] == nameAlias + from_command := dockerLib.get_original_from_command(input.document[i].command[name2]) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}", [aliasResource.Value[idx_2]]), - "issueType": "IncorrectValue", + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}", [from_command.Value, aliasResource.Value[idx_2]]), from_command.LineHint), + "issueType": "IncorrectValue", "keyExpectedValue": "Different FROM commands don't have the same alias defined", - "keyActualValue": sprintf("Different FROM commands with with the same alias '%s' defined", [aliasResource.Value[idx_2]]), + "keyActualValue": sprintf("Different FROM commands with the same alias '%s' defined", [aliasResource.Value[idx_2]]), } } diff --git a/assets/queries/dockerfile/same_alias_in_different_froms/test/negative2.dockerfile b/assets/queries/dockerfile/same_alias_in_different_froms/test/negative2.dockerfile new file mode 100644 index 00000000000..735a723879a --- /dev/null +++ b/assets/queries/dockerfile/same_alias_in_different_froms/test/negative2.dockerfile @@ -0,0 +1,5 @@ +from debian:jesse1 as build +run stuff + +from debian:jesse1 as another-alias +run more_stuff diff --git a/assets/queries/dockerfile/same_alias_in_different_froms/test/positive2.dockerfile b/assets/queries/dockerfile/same_alias_in_different_froms/test/positive2.dockerfile new file mode 100644 index 00000000000..1932587dc3d --- /dev/null +++ b/assets/queries/dockerfile/same_alias_in_different_froms/test/positive2.dockerfile @@ -0,0 +1,8 @@ +from baseImage +run Test + +from debian:jesse2 as build +run stuff + +from debian:jesse1 as build +run more_stuff diff --git a/assets/queries/dockerfile/same_alias_in_different_froms/test/positive_expected_result.json b/assets/queries/dockerfile/same_alias_in_different_froms/test/positive_expected_result.json index 9e65369181b..89041ad6f99 100644 --- a/assets/queries/dockerfile/same_alias_in_different_froms/test/positive_expected_result.json +++ b/assets/queries/dockerfile/same_alias_in_different_froms/test/positive_expected_result.json @@ -1,7 +1,26 @@ [ - { - "queryName": "Same Alias In Different Froms", - "severity": "LOW", - "line": 4 - } + { + "queryName": "Same Alias In Different Froms", + "severity": "LOW", + "line": 4, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Same Alias In Different Froms", + "severity": "LOW", + "line": 7, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Same Alias In Different Froms", + "severity": "LOW", + "line": 4, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Same Alias In Different Froms", + "severity": "LOW", + "line": 7, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/query.rego b/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/query.rego index 0f0ceedb0a7..4db8d8ed1ba 100644 --- a/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/query.rego +++ b/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/query.rego @@ -1,6 +1,7 @@ package Cx import data.generic.common as common_lib +import data.generic.dockerfile as dockerLib CxPolicy[result] { commands := input.document[i].command[name] @@ -19,9 +20,10 @@ CxPolicy[result] { not hasPipefail(commands, match.shell, j) + from_command := dockerLib.get_original_from_command(commands) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, runCmd.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, runCmd.Original]), from_command.LineHint), "searchValue": match.shell, "issueType": "MissingAttribute", "keyExpectedValue": sprintf("'%s' has pipefail option set for pipe command with shell %s.", [runCmd.Original, match.shell]), @@ -47,9 +49,10 @@ CxPolicy[result] { cmdFormatted := replace(runCmd.Original, "\"", "'") + from_command := dockerLib.get_original_from_command(commands) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, runCmd.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, runCmd.Original]), from_command.LineHint), "searchValue": match.shell, "issueType": "MissingAttribute", "keyExpectedValue": sprintf("'%s' has pipefail option set for pipe command with shell %s.", [cmdFormatted, match.shell]), diff --git a/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/test/negative2.dockerfile b/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/test/negative2.dockerfile new file mode 100644 index 00000000000..6a8d174d98d --- /dev/null +++ b/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/test/negative2.dockerfile @@ -0,0 +1,7 @@ +from node:12 +run pwsh SOME_CMD | SOME_OTHER_CMD +shell [ "zsh", "-o","pipefail" ] +run zsh ./some_output | ./some_script +shell [ "/bin/bash", "-o","pipefail" ] +run [ "/bin/bash", "./some_output", "./some_script" ] + diff --git a/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/test/positive2.dockerfile b/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/test/positive2.dockerfile new file mode 100644 index 00000000000..de691928f03 --- /dev/null +++ b/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/test/positive2.dockerfile @@ -0,0 +1,3 @@ +from node:12 +run zsh ./some_output | ./some_script +run [ "/bin/bash", "./some_output", "|", "./some_script" ] \ No newline at end of file diff --git a/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/test/positive_expected_result.json b/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/test/positive_expected_result.json index 66769b07386..5f7aab1d611 100644 --- a/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/test/positive_expected_result.json +++ b/assets/queries/dockerfile/shell_running_a_pipe_without_pipefail_flag/test/positive_expected_result.json @@ -1,12 +1,26 @@ [ - { - "queryName": "Shell Running A Pipe Without Pipefail Flag", - "severity": "LOW", - "line": 2 - }, - { - "queryName": "Shell Running A Pipe Without Pipefail Flag", - "severity": "LOW", - "line": 3 - } + { + "queryName": "Shell Running A Pipe Without Pipefail Flag", + "severity": "LOW", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Shell Running A Pipe Without Pipefail Flag", + "severity": "LOW", + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Shell Running A Pipe Without Pipefail Flag", + "severity": "LOW", + "line": 2, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Shell Running A Pipe Without Pipefail Flag", + "severity": "LOW", + "line": 3, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/unix_ports_out_of_range/query.rego b/assets/queries/dockerfile/unix_ports_out_of_range/query.rego index 5278813156e..b4b7ce1e1c4 100644 --- a/assets/queries/dockerfile/unix_ports_out_of_range/query.rego +++ b/assets/queries/dockerfile/unix_ports_out_of_range/query.rego @@ -1,14 +1,18 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { command := input.document[i].command[name][_] command.Cmd == "expose" containsPortOutOfRange(command.Value) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, command.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, command.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "'EXPOSE' should not contain ports out of range [0, 65535]", "keyActualValue": "'EXPOSE' contains ports out of range [0, 65535]", diff --git a/assets/queries/dockerfile/unix_ports_out_of_range/test/negative2.dockerfile b/assets/queries/dockerfile/unix_ports_out_of_range/test/negative2.dockerfile new file mode 100644 index 00000000000..a1d39b6ed5c --- /dev/null +++ b/assets/queries/dockerfile/unix_ports_out_of_range/test/negative2.dockerfile @@ -0,0 +1,4 @@ +from gliderlabs/alpine:3.3 +run apk --no-cache add nginx +expose 3000 80 443 22 +cmd ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/assets/queries/dockerfile/unix_ports_out_of_range/test/positive2.dockerfile b/assets/queries/dockerfile/unix_ports_out_of_range/test/positive2.dockerfile new file mode 100644 index 00000000000..0f1e9c0f7db --- /dev/null +++ b/assets/queries/dockerfile/unix_ports_out_of_range/test/positive2.dockerfile @@ -0,0 +1,4 @@ +from gliderlabs/alpine:3.3 +run apk --no-cache add nginx +expose 65536/tcp 80 443 22 +cmd ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/assets/queries/dockerfile/unix_ports_out_of_range/test/positive_expected_result.json b/assets/queries/dockerfile/unix_ports_out_of_range/test/positive_expected_result.json index 5d57ac73d0c..45a3ec6dce2 100644 --- a/assets/queries/dockerfile/unix_ports_out_of_range/test/positive_expected_result.json +++ b/assets/queries/dockerfile/unix_ports_out_of_range/test/positive_expected_result.json @@ -1,7 +1,14 @@ [ - { - "queryName": "UNIX Ports Out Of Range", - "severity": "INFO", - "line": 3 - } + { + "queryName": "UNIX Ports Out Of Range", + "severity": "INFO", + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "UNIX Ports Out Of Range", + "severity": "INFO", + "line": 3, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/unpinned_package_version_in_apk_add/query.rego b/assets/queries/dockerfile/unpinned_package_version_in_apk_add/query.rego index eed1cad8afc..fddee27a9f8 100644 --- a/assets/queries/dockerfile/unpinned_package_version_in_apk_add/query.rego +++ b/assets/queries/dockerfile/unpinned_package_version_in_apk_add/query.rego @@ -1,7 +1,7 @@ package Cx import data.generic.common as common_lib -import data.generic.dockerfile as docker_lib +import data.generic.dockerfile as dockerLib CxPolicy[result] { resource := input.document[i].command[name][cmd] @@ -15,16 +15,18 @@ CxPolicy[result] { apk := regex.find_n("apk (-(-)?[a-zA-Z]+ *)*add", commands_trim, -1) apk != null - packages = docker_lib.getPackages(commands_trim, apk) + packages = dockerLib.getPackages(commands_trim, apk) length := count(packages) some j analyzePackages(j, packages[j], packages, length) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "RUN instruction with 'apk add ' should use package pinning form 'apk add ='", "keyActualValue": sprintf("RUN instruction %s does not use package pinning form", [resource.Value[0]]), @@ -44,16 +46,18 @@ CxPolicy[result] { apk := regex.find_n("apk (-(-)?[a-zA-Z]+ *)*add", commands_trim, -1) apk != null - packages = docker_lib.getPackages(commands_trim, apk) + packages = dockerLib.getPackages(commands_trim, apk) length := count(packages) some j analyzePackages(j, packages[j], packages, length) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "RUN instruction with 'apk add ' should use package pinning form 'apk add ='", "keyActualValue": sprintf("RUN instruction %s does not use package pinning form", [resource.Value[0]]), @@ -72,16 +76,18 @@ CxPolicy[result] { apk := regex.find_n("apk (-(-)?[a-zA-Z]+ *)*add", commands, -1) apk != null - packages = docker_lib.getPackages(commands, apk) + packages = dockerLib.getPackages(commands, apk) length := count(packages) some j analyzePackages(j, packages[j], packages, length) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "RUN instruction with 'apk add ' should use package pinning form 'apk add ='", "keyActualValue": sprintf("RUN instruction %s does not use package pinning form", [resource.Value[0]]), @@ -95,17 +101,19 @@ CxPolicy[result] { count(resource.Value) > 1 - docker_lib.arrayContains(resource.Value, {"apk", "add"}) + dockerLib.arrayContains(resource.Value, {"apk", "add"}) resource.Value[j] != "apk" resource.Value[j] != "add" regex.match("^[a-zA-Z]", resource.Value[j]) - not docker_lib.withVersion(resource.Value[j]) + not dockerLib.withVersion(resource.Value[j]) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "searchValue": resource.Value[j], "issueType": "IncorrectValue", "keyExpectedValue": "RUN instruction with 'apk add ' should use package pinning form 'apk add ='", @@ -117,12 +125,12 @@ CxPolicy[result] { analyzePackages(j, currentPackage, packages, length) { j == length - 1 regex.match("^[a-zA-Z]", currentPackage) - not docker_lib.withVersion(currentPackage) + not dockerLib.withVersion(currentPackage) } analyzePackages(j, currentPackage, packages, length) { j != length - 1 regex.match("^[a-zA-Z]", currentPackage) packages[plus(j, 1)] != "-v" - not docker_lib.withVersion(currentPackage) + not dockerLib.withVersion(currentPackage) } diff --git a/assets/queries/dockerfile/unpinned_package_version_in_apk_add/test/negative3.dockerfile b/assets/queries/dockerfile/unpinned_package_version_in_apk_add/test/negative3.dockerfile new file mode 100644 index 00000000000..7e7f239eea8 --- /dev/null +++ b/assets/queries/dockerfile/unpinned_package_version_in_apk_add/test/negative3.dockerfile @@ -0,0 +1,20 @@ +from alpine:3.4 +run apk add --update py-pip=7.1.2-r0 +run sudo pip install --upgrade pip +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"] + +from alpine:3.1 +run apk add py-pip=7.1.2-r0 +run ["apk", "add", "py-pip=7.1.2-r0"] +run sudo pip install --upgrade pip +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/unpinned_package_version_in_apk_add/test/positive2.dockerfile b/assets/queries/dockerfile/unpinned_package_version_in_apk_add/test/positive2.dockerfile new file mode 100644 index 00000000000..d0356a93875 --- /dev/null +++ b/assets/queries/dockerfile/unpinned_package_version_in_apk_add/test/positive2.dockerfile @@ -0,0 +1,25 @@ +from alpine:3.9 +run apk add --update py-pip +run sudo pip install --upgrade pip +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 +env TEST="test" +cmd ["python", "/usr/src/app/app.py"] + +from alpine:3.7 +run apk add py-pip && apk add tea +run apk add py-pip \ + && rm -rf /tmp/* +run apk add --dir /dir libimagequant \ + && minidlna +run ["apk", "add", "py-pip"] +run sudo pip install --upgrade pip +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"] diff --git a/assets/queries/dockerfile/unpinned_package_version_in_apk_add/test/positive_expected_result.json b/assets/queries/dockerfile/unpinned_package_version_in_apk_add/test/positive_expected_result.json index 9f377d63c28..c1e87f5d252 100644 --- a/assets/queries/dockerfile/unpinned_package_version_in_apk_add/test/positive_expected_result.json +++ b/assets/queries/dockerfile/unpinned_package_version_in_apk_add/test/positive_expected_result.json @@ -1,27 +1,62 @@ [ - { - "queryName": "Unpinned Package Version in Apk Add", - "severity": "MEDIUM", - "line": 2 - }, - { - "queryName": "Unpinned Package Version in Apk Add", - "severity": "MEDIUM", - "line": 13 - }, - { - "queryName": "Unpinned Package Version in Apk Add", - "severity": "MEDIUM", - "line": 14 - }, - { - "queryName": "Unpinned Package Version in Apk Add", - "severity": "MEDIUM", - "line": 16 - }, - { - "queryName": "Unpinned Package Version in Apk Add", - "severity": "MEDIUM", - "line": 18 - } -] + { + "queryName": "Unpinned Package Version in Apk Add", + "severity": "MEDIUM", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Apk Add", + "severity": "MEDIUM", + "line": 13, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Apk Add", + "severity": "MEDIUM", + "line": 14, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Apk Add", + "severity": "MEDIUM", + "line": 16, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Apk Add", + "severity": "MEDIUM", + "line": 18, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Apk Add", + "severity": "MEDIUM", + "line": 2, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Apk Add", + "severity": "MEDIUM", + "line": 13, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Apk Add", + "severity": "MEDIUM", + "line": 14, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Apk Add", + "severity": "MEDIUM", + "line": 16, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Apk Add", + "severity": "MEDIUM", + "line": 18, + "fileName": "positive2.dockerfile" + } +] \ No newline at end of file diff --git a/assets/queries/dockerfile/unpinned_package_version_in_pip_install/query.rego b/assets/queries/dockerfile/unpinned_package_version_in_pip_install/query.rego index 268199179fb..53d41d0f659 100644 --- a/assets/queries/dockerfile/unpinned_package_version_in_pip_install/query.rego +++ b/assets/queries/dockerfile/unpinned_package_version_in_pip_install/query.rego @@ -23,9 +23,11 @@ CxPolicy[result] { some j analyzePackages(j, refactorPackages[j], packages, length) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "RUN instruction with 'pip/pip3 install ' should use package pinning form 'pip/pip3 install ='", "keyActualValue": sprintf("RUN instruction %s does not use package pinning form", [commands]), @@ -47,9 +49,11 @@ CxPolicy[result] { regex.match("^[a-zA-Z]", resource.Value[j]) == true not dockerLib.withVersion(resource.Value[j]) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": "RUN instruction with 'pip/pip3 install ' should use package pinning form 'pip/pip3 install ='", "keyActualValue": sprintf("RUN instruction %s does not use package pinning form", [resource.Value[j]]), diff --git a/assets/queries/dockerfile/unpinned_package_version_in_pip_install/test/negative4.dockerfile b/assets/queries/dockerfile/unpinned_package_version_in_pip_install/test/negative4.dockerfile new file mode 100644 index 00000000000..0ab18ef4643 --- /dev/null +++ b/assets/queries/dockerfile/unpinned_package_version_in_pip_install/test/negative4.dockerfile @@ -0,0 +1,20 @@ +from alpine:3.4 +run apk add --update py-pip=7.1.2-r0 +run sudo pip install --upgrade pip=20.3 connexion=2.7.0 +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"] + +from alpine:3.1 +run apk add py-pip=7.1.2-r0 +run sudo pip install --upgrade pip=20.3 connexion=2.7.0 +copy requirements.txt /usr/src/app/ +run pip install --no-cache-dir -r /usr/src/app/requirements.txt +run pip3 install requests=2.7.0 +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/unpinned_package_version_in_pip_install/test/positive2.dockerfile b/assets/queries/dockerfile/unpinned_package_version_in_pip_install/test/positive2.dockerfile new file mode 100644 index 00000000000..d60c4c736af --- /dev/null +++ b/assets/queries/dockerfile/unpinned_package_version_in_pip_install/test/positive2.dockerfile @@ -0,0 +1,22 @@ +from alpine:3.9 +run apk add --update py-pip=7.1.2-r0 +run pip install --user pip +run ["pip", "install", "connexion"] +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 +env TEST="test" +cmd ["python", "/usr/src/app/app.py"] + +from alpine:3.7 +run apk add --update py-pip=7.1.2-r0 +run pip install connexion +copy requirements.txt /usr/src/app/ +run pip install --no-cache-dir -r /usr/src/app/requirements.txt +run pip3 install requests +copy app.py /usr/src/app/ +copy templates/index.html /usr/src/app/templates/ +expose 5000 +cmd ["python"] diff --git a/assets/queries/dockerfile/unpinned_package_version_in_pip_install/test/positive_expected_result.json b/assets/queries/dockerfile/unpinned_package_version_in_pip_install/test/positive_expected_result.json index 4ffe50570bf..9c3e92bb647 100644 --- a/assets/queries/dockerfile/unpinned_package_version_in_pip_install/test/positive_expected_result.json +++ b/assets/queries/dockerfile/unpinned_package_version_in_pip_install/test/positive_expected_result.json @@ -1,26 +1,50 @@ [ - { - "queryName": "Unpinned Package Version in Pip Install", - "severity": "MEDIUM", - "line": 3, - "filename": "positive1.dockerfile" - }, - { - "queryName": "Unpinned Package Version in Pip Install", - "severity": "MEDIUM", - "line": 4, - "filename": "positive1.dockerfile" - }, - { - "queryName": "Unpinned Package Version in Pip Install", - "severity": "MEDIUM", - "line": 15, - "filename": "positive1.dockerfile" - }, - { - "queryName": "Unpinned Package Version in Pip Install", - "severity": "MEDIUM", - "line": 18, - "filename": "positive1.dockerfile" - } -] + { + "queryName": "Unpinned Package Version in Pip Install", + "severity": "MEDIUM", + "line": 3, + "filename": "positive1.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Pip Install", + "severity": "MEDIUM", + "line": 4, + "filename": "positive1.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Pip Install", + "severity": "MEDIUM", + "line": 15, + "filename": "positive1.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Pip Install", + "severity": "MEDIUM", + "line": 18, + "filename": "positive1.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Pip Install", + "severity": "MEDIUM", + "line": 3, + "filename": "positive2.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Pip Install", + "severity": "MEDIUM", + "line": 4, + "filename": "positive2.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Pip Install", + "severity": "MEDIUM", + "line": 15, + "filename": "positive2.dockerfile" + }, + { + "queryName": "Unpinned Package Version in Pip Install", + "severity": "MEDIUM", + "line": 18, + "filename": "positive2.dockerfile" + } +] \ No newline at end of file diff --git a/assets/queries/dockerfile/update_instruction_alone/query.rego b/assets/queries/dockerfile/update_instruction_alone/query.rego index 9bf0c5837c4..1436d2fd1da 100644 --- a/assets/queries/dockerfile/update_instruction_alone/query.rego +++ b/assets/queries/dockerfile/update_instruction_alone/query.rego @@ -1,5 +1,7 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { # Check if there is a command that runs install before update resource := input.document[i].command[name][_] @@ -20,9 +22,12 @@ CxPolicy[result] { not checkFollowedBy(update, install) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) + run_command := substring(resource.Original, 0, 3) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.RUN={{%s}}", [name, resource.Value[0]]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.%s={{%s}}", [from_command.Value, name, run_command, resource.Value[0]]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("Instruction 'RUN %s %s' should be followed by 'RUN %s %s' in the same 'RUN' statement", [packageManager, pkg_installer[packageManager], packageManager, pkg_updater[packageManager]]), "keyActualValue": sprintf("Instruction 'RUN %s %s' isn't followed by 'RUN %s %s in the same 'RUN' statement", [packageManager, pkg_installer[packageManager], packageManager, pkg_updater[packageManager]]), @@ -58,9 +63,12 @@ CxPolicy[result] { nextUpdate := [x | x := getDetail(nextCommandRefactor, pkg_updater[nextPackageManager][_]); count(x) > 0] count(nextUpdate) == 0 - result := { + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) + run_command := substring(resource.Original, 0, 3) + result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.RUN={{%s}}", [name, nextResource.Value[0]]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.%s={{%s}}", [from_command.Value, name, run_command, nextResource.Value[0]]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("Instruction 'RUN %s %s' should be combined with 'RUN %s %s' in the same 'RUN' statement", [nextPackageManager, pkg_installer[nextPackageManager], nextPackageManager, pkg_updater[nextPackageManager]]), "keyActualValue": sprintf("Instruction 'RUN %s %s' isn't combined with 'RUN %s %s in the same 'RUN' statement", [nextPackageManager, pkg_installer[nextPackageManager], nextPackageManager, pkg_updater[nextPackageManager]]), diff --git a/assets/queries/dockerfile/update_instruction_alone/test/negative12.dockerfile b/assets/queries/dockerfile/update_instruction_alone/test/negative12.dockerfile new file mode 100644 index 00000000000..ad75faf9819 --- /dev/null +++ b/assets/queries/dockerfile/update_instruction_alone/test/negative12.dockerfile @@ -0,0 +1,8 @@ +from ubuntu:18.04 +run apt-get update \ + && apt-get install -y --no-install-recommends mysql-client \ + && rm -rf /var/lib/apt/lists/* +run apk update \ + && apk add --no-cache git ca-certificates +run apk --update add easy-rsa +entrypoint ["mysql"] diff --git a/assets/queries/dockerfile/update_instruction_alone/test/positive8.dockerfile b/assets/queries/dockerfile/update_instruction_alone/test/positive8.dockerfile new file mode 100644 index 00000000000..4b49de8c58a --- /dev/null +++ b/assets/queries/dockerfile/update_instruction_alone/test/positive8.dockerfile @@ -0,0 +1,5 @@ +from alpine:latest +run apk update +run apk add nginx + +cmd ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/assets/queries/dockerfile/update_instruction_alone/test/positive_expected_result.json b/assets/queries/dockerfile/update_instruction_alone/test/positive_expected_result.json index 64b7e65cd1f..568f9337486 100644 --- a/assets/queries/dockerfile/update_instruction_alone/test/positive_expected_result.json +++ b/assets/queries/dockerfile/update_instruction_alone/test/positive_expected_result.json @@ -1,44 +1,50 @@ [ - { - "queryName": "Update Instruction Alone", - "severity": "LOW", - "line": 3, - "fileName": "positive1.dockerfile" - }, - { - "queryName": "Update Instruction Alone", - "severity": "LOW", - "line": 3, - "fileName": "positive2.dockerfile" - }, - { - "queryName": "Update Instruction Alone", - "severity": "LOW", - "line": 3, - "fileName": "positive3.dockerfile" - }, - { - "queryName": "Update Instruction Alone", - "severity": "LOW", - "line": 3, - "fileName": "positive4.dockerfile" - }, - { - "queryName": "Update Instruction Alone", - "severity": "LOW", - "line": 3, - "fileName": "positive5.dockerfile" - }, - { - "queryName": "Update Instruction Alone", - "severity": "LOW", - "line": 3, - "fileName": "positive6.dockerfile" - }, - { - "queryName": "Update Instruction Alone", - "severity": "LOW", - "line": 3, - "fileName": "positive7.dockerfile" - } + { + "queryName": "Update Instruction Alone", + "severity": "LOW", + "line": 3, + "fileName": "positive1.dockerfile" + }, + { + "queryName": "Update Instruction Alone", + "severity": "LOW", + "line": 3, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Update Instruction Alone", + "severity": "LOW", + "line": 3, + "fileName": "positive3.dockerfile" + }, + { + "queryName": "Update Instruction Alone", + "severity": "LOW", + "line": 3, + "fileName": "positive4.dockerfile" + }, + { + "queryName": "Update Instruction Alone", + "severity": "LOW", + "line": 3, + "fileName": "positive5.dockerfile" + }, + { + "queryName": "Update Instruction Alone", + "severity": "LOW", + "line": 3, + "fileName": "positive6.dockerfile" + }, + { + "queryName": "Update Instruction Alone", + "severity": "LOW", + "line": 3, + "fileName": "positive7.dockerfile" + }, + { + "queryName": "Update Instruction Alone", + "severity": "LOW", + "line": 3, + "fileName": "positive8.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/using_platform_with_from/query.rego b/assets/queries/dockerfile/using_platform_with_from/query.rego index 8e133aece1e..64b9fe67b15 100644 --- a/assets/queries/dockerfile/using_platform_with_from/query.rego +++ b/assets/queries/dockerfile/using_platform_with_from/query.rego @@ -1,6 +1,7 @@ package Cx import data.generic.common as common_lib +import data.generic.dockerfile as dockerLib CxPolicy[result] { resource := input.document[i].command[name][_] @@ -8,11 +9,13 @@ CxPolicy[result] { contains(resource.Flags[j], "--platform") contains(resource.Cmd, "from") + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", - "keyExpectedValue": sprintf("FROM={{%s}}.{{%s}} should not use the '--platform' flag", [name, resource.Original]), - "keyActualValue": sprintf("FROM={{%s}}.{{%s}} is using the '--platform' flag", [name, resource.Original]), + "keyExpectedValue": sprintf("%s={{%s}}.{{%s}} should not use the '--platform' flag", [from_command.Value, name, resource.Original]), + "keyActualValue": sprintf("%s={{%s}}.{{%s}} is using the '--platform' flag", [from_command.Value, name, resource.Original]), } } diff --git a/assets/queries/dockerfile/using_platform_with_from/test/negative2.dockerfile b/assets/queries/dockerfile/using_platform_with_from/test/negative2.dockerfile new file mode 100644 index 00000000000..04dc547bc08 --- /dev/null +++ b/assets/queries/dockerfile/using_platform_with_from/test/negative2.dockerfile @@ -0,0 +1,6 @@ +from alpine:3.5 +run apk add --update py2-pip +run pip install --upgrade pip +label maintainer="SvenDowideit@home.org.au" +copy requirements.txt /usr/src/app/ +from baseimage as baseimage-build diff --git a/assets/queries/dockerfile/using_platform_with_from/test/positive2.dockerfile b/assets/queries/dockerfile/using_platform_with_from/test/positive2.dockerfile new file mode 100644 index 00000000000..c0529ec2a65 --- /dev/null +++ b/assets/queries/dockerfile/using_platform_with_from/test/positive2.dockerfile @@ -0,0 +1,6 @@ +from alpine:3.5 +run apk add --update py2-pip +run pip install --upgrade pip +label maintainer="SvenDowideit@home.org.au" +copy requirements.txt /usr/src/app/ +from --platform=arm64 baseimage as baseimage-build diff --git a/assets/queries/dockerfile/using_platform_with_from/test/positive_expected_result.json b/assets/queries/dockerfile/using_platform_with_from/test/positive_expected_result.json index 17bce5638c8..21a2a3f1b72 100644 --- a/assets/queries/dockerfile/using_platform_with_from/test/positive_expected_result.json +++ b/assets/queries/dockerfile/using_platform_with_from/test/positive_expected_result.json @@ -1,8 +1,14 @@ [ - { - "queryName": "Using Platform Flag with FROM Command", - "severity": "INFO", - "line": 6, - "fileName": "positive1.dockerfile" - } + { + "queryName": "Using Platform Flag with FROM Command", + "severity": "INFO", + "line": 6, + "fileName": "positive1.dockerfile" + }, + { + "queryName": "Using Platform Flag with FROM Command", + "severity": "INFO", + "line": 6, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/using_unnamed_build_stages/query.rego b/assets/queries/dockerfile/using_unnamed_build_stages/query.rego index ff621dc15f8..b39850fa2d9 100644 --- a/assets/queries/dockerfile/using_unnamed_build_stages/query.rego +++ b/assets/queries/dockerfile/using_unnamed_build_stages/query.rego @@ -1,5 +1,7 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { commands := input.document[i].command[name][_] @@ -11,9 +13,11 @@ CxPolicy[result] { to_number(flag_split[1]) > -1 + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, commands.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, commands.Original]), from_command.LineHint), "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/using_unnamed_build_stages/test/negative2.dockerfile b/assets/queries/dockerfile/using_unnamed_build_stages/test/negative2.dockerfile new file mode 100644 index 00000000000..c38778c6031 --- /dev/null +++ b/assets/queries/dockerfile/using_unnamed_build_stages/test/negative2.dockerfile @@ -0,0 +1,12 @@ +from golang:1.7.3 AS builder +workdir /go/src/github.com/foo/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 . + +# another dockerfile +from alpine:latest +run apk --no-cache add ca-certificates +workdir /root/ +copy --from=builder /go/src/github.com/foo/href-counter/app . +cmd ["./app"] diff --git a/assets/queries/dockerfile/using_unnamed_build_stages/test/positive2.dockerfile b/assets/queries/dockerfile/using_unnamed_build_stages/test/positive2.dockerfile new file mode 100644 index 00000000000..b96bdaac1c7 --- /dev/null +++ b/assets/queries/dockerfile/using_unnamed_build_stages/test/positive2.dockerfile @@ -0,0 +1,11 @@ +from golang:1.16 +workdir /go/src/github.com/foo/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/foo/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 index d0e9eb1f3db..72b72f46fb2 100644 --- 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 @@ -4,5 +4,11 @@ "severity": "LOW", "line": 10, "filename": "positive1.dockerfile" + }, + { + "queryName": "Using Unnamed Build Stages", + "severity": "LOW", + "line": 10, + "filename": "positive2.dockerfile" } -] +] \ No newline at end of file diff --git a/assets/queries/dockerfile/workdir_path_not_absolute/query.rego b/assets/queries/dockerfile/workdir_path_not_absolute/query.rego index a6d8fbeabf3..def161b9769 100644 --- a/assets/queries/dockerfile/workdir_path_not_absolute/query.rego +++ b/assets/queries/dockerfile/workdir_path_not_absolute/query.rego @@ -1,13 +1,18 @@ package Cx +import data.generic.dockerfile as dockerLib + CxPolicy[result] { resource := input.document[i].command[name][_] resource.Cmd == "workdir" not regex.match("(^\"?/[A-z0-9-_+]*)|(^\"?[A-z0-9-_+]:\\\\.*)|(^\"?\\$[{}A-z0-9-_+].*)", resource.Value[0]) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) + workdir_command := substring(resource.Original, 0, 7) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.WORKDIR={{%s}}", [name, resource.Value[0]]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.%s={{%s}}", [from_command.Value, name, workdir_command, resource.Value[0]]), from_command.LineHint), "issueType": "IncorrectValue", #"MissingAttribute" / "RedundantAttribute" "keyExpectedValue": "'WORKDIR' Command has absolute path", "keyActualValue": "'WORKDIR' Command doesn't have absolute path", diff --git a/assets/queries/dockerfile/workdir_path_not_absolute/test/negative2.dockerfile b/assets/queries/dockerfile/workdir_path_not_absolute/test/negative2.dockerfile new file mode 100644 index 00000000000..aa660c1b737 --- /dev/null +++ b/assets/queries/dockerfile/workdir_path_not_absolute/test/negative2.dockerfile @@ -0,0 +1,17 @@ +from alpine:3.5 +run apk add --update py2-pip +run pip install --upgrade pip +workdir /path/to/workdir +workdir "/path/to/workdir" +workdir / +workdir c:\\windows +env DIRPATH=/path +env GLASSFISH_ARCHIVE glassfish5 +workdir $DIRPATH/$DIRNAME +workdir ${GLASSFISH_HOME}/bin +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/workdir_path_not_absolute/test/positive2.dockerfile b/assets/queries/dockerfile/workdir_path_not_absolute/test/positive2.dockerfile new file mode 100644 index 00000000000..bc3c52343fc --- /dev/null +++ b/assets/queries/dockerfile/workdir_path_not_absolute/test/positive2.dockerfile @@ -0,0 +1,11 @@ +from alpine:3.5 +run apk add --update py2-pip +run pip install --upgrade pip +workdir /path/to/workdir +workdir workdir +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"] \ No newline at end of file diff --git a/assets/queries/dockerfile/workdir_path_not_absolute/test/positive_expected_result.json b/assets/queries/dockerfile/workdir_path_not_absolute/test/positive_expected_result.json index ece07faaf7f..bd94ad4b78f 100644 --- a/assets/queries/dockerfile/workdir_path_not_absolute/test/positive_expected_result.json +++ b/assets/queries/dockerfile/workdir_path_not_absolute/test/positive_expected_result.json @@ -1,7 +1,14 @@ [ - { - "queryName": "WORKDIR Path Not Absolute", - "severity": "LOW", - "line": 5 - } + { + "queryName": "WORKDIR Path Not Absolute", + "severity": "LOW", + "line": 5, + "fileName": "positive.dockerfile" + }, + { + "queryName": "WORKDIR Path Not Absolute", + "severity": "LOW", + "line": 5, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/yum_clean_all_missing/query.rego b/assets/queries/dockerfile/yum_clean_all_missing/query.rego index 556f93f16b5..3ec1659dd4e 100644 --- a/assets/queries/dockerfile/yum_clean_all_missing/query.rego +++ b/assets/queries/dockerfile/yum_clean_all_missing/query.rego @@ -15,9 +15,11 @@ CxPolicy[result] { not containsCleanAfterYum(command) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("{{%s}} should have 'yum clean all' after 'yum install' command", [resource.Original]), "keyActualValue": sprintf("{{%s}} doesn't have 'yum clean all' after 'yum install' command", [resource.Original]), diff --git a/assets/queries/dockerfile/yum_clean_all_missing/test/negative3.dockerfile b/assets/queries/dockerfile/yum_clean_all_missing/test/negative3.dockerfile new file mode 100644 index 00000000000..a3fcb3e7a29 --- /dev/null +++ b/assets/queries/dockerfile/yum_clean_all_missing/test/negative3.dockerfile @@ -0,0 +1,14 @@ +from alpine:3.5 +run apk add --update py2-pip +run yum install \ + yum clean all +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"] + +from alpine:3.4 +run yum -y install \ + yum clean all diff --git a/assets/queries/dockerfile/yum_clean_all_missing/test/positive2.dockerfile b/assets/queries/dockerfile/yum_clean_all_missing/test/positive2.dockerfile new file mode 100644 index 00000000000..a38c8a8f68b --- /dev/null +++ b/assets/queries/dockerfile/yum_clean_all_missing/test/positive2.dockerfile @@ -0,0 +1,13 @@ +from alpine:3.5 +run apk add --update py2-pip +run yum install +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"] + +from alpine:3.4 +run yum clean all \ + yum -y install diff --git a/assets/queries/dockerfile/yum_clean_all_missing/test/positive_expected_result.json b/assets/queries/dockerfile/yum_clean_all_missing/test/positive_expected_result.json index f4e28bb33cf..100dd704469 100644 --- a/assets/queries/dockerfile/yum_clean_all_missing/test/positive_expected_result.json +++ b/assets/queries/dockerfile/yum_clean_all_missing/test/positive_expected_result.json @@ -1,8 +1,14 @@ [ - { - "queryName": "Yum Clean All Missing", - "severity": "LOW", - "line": 12, - "fileName": "positive.dockerfile" - } + { + "queryName": "Yum Clean All Missing", + "severity": "LOW", + "line": 12, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Yum Clean All Missing", + "severity": "LOW", + "line": 12, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/yum_install_allows_manual_input/query.rego b/assets/queries/dockerfile/yum_install_allows_manual_input/query.rego index 1c7fb4f21b6..179392addff 100644 --- a/assets/queries/dockerfile/yum_install_allows_manual_input/query.rego +++ b/assets/queries/dockerfile/yum_install_allows_manual_input/query.rego @@ -12,9 +12,11 @@ CxPolicy[result] { not avoidManualInput(command) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("{{%s}} should avoid manual input", [resource.Original]), "keyActualValue": sprintf("{{%s}} doesn't avoid manual input", [resource.Original]), @@ -30,9 +32,11 @@ CxPolicy[result] { not avoidManualInputInList(resource.Value) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("{{%s}} should avoid manual input", [resource.Original]), "keyActualValue": sprintf("{{%s}} doesn't avoid manual input", [resource.Original]), diff --git a/assets/queries/dockerfile/yum_install_allows_manual_input/test/negative2.dockerfile b/assets/queries/dockerfile/yum_install_allows_manual_input/test/negative2.dockerfile new file mode 100644 index 00000000000..6cb32a326e6 --- /dev/null +++ b/assets/queries/dockerfile/yum_install_allows_manual_input/test/negative2.dockerfile @@ -0,0 +1,9 @@ +from alpine:3.5 +run apk add --update py2-pip +run sudo yum install -y bundler +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"] \ No newline at end of file diff --git a/assets/queries/dockerfile/yum_install_allows_manual_input/test/positive2.dockerfile b/assets/queries/dockerfile/yum_install_allows_manual_input/test/positive2.dockerfile new file mode 100644 index 00000000000..6fe942813de --- /dev/null +++ b/assets/queries/dockerfile/yum_install_allows_manual_input/test/positive2.dockerfile @@ -0,0 +1,10 @@ +from alpine:3.5 +run apk add --update py2-pip +run sudo yum install bundler +run ["sudo yum", "install", "bundler"] +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/yum_install_allows_manual_input/test/positive_expected_result.json b/assets/queries/dockerfile/yum_install_allows_manual_input/test/positive_expected_result.json index c6fa582d3aa..ed762eee0d3 100644 --- a/assets/queries/dockerfile/yum_install_allows_manual_input/test/positive_expected_result.json +++ b/assets/queries/dockerfile/yum_install_allows_manual_input/test/positive_expected_result.json @@ -1,12 +1,26 @@ [ - { - "queryName": "Yum Install Allows Manual Input", - "severity": "LOW", - "line": 3 - }, - { - "queryName": "Yum Install Allows Manual Input", - "severity": "LOW", - "line": 4 - } + { + "queryName": "Yum Install Allows Manual Input", + "severity": "LOW", + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Yum Install Allows Manual Input", + "severity": "LOW", + "line": 4, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Yum Install Allows Manual Input", + "severity": "LOW", + "line": 3, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Yum Install Allows Manual Input", + "severity": "LOW", + "line": 4, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/assets/queries/dockerfile/yum_install_without_version/query.rego b/assets/queries/dockerfile/yum_install_without_version/query.rego index fbc9c0bf295..f4c510183a3 100644 --- a/assets/queries/dockerfile/yum_install_without_version/query.rego +++ b/assets/queries/dockerfile/yum_install_without_version/query.rego @@ -1,7 +1,7 @@ package Cx import data.generic.common as common_lib -import data.generic.dockerfile as docker_lib +import data.generic.dockerfile as dockerLib CxPolicy[result] { resource := input.document[i].command[name][cmd] @@ -13,15 +13,17 @@ CxPolicy[result] { yum := regex.find_n("yum (-(-)?[a-zA-Z]+ *)*(group|local)?install", commands, -1) yum != null - packages = docker_lib.getPackages(commands, yum) + packages = dockerLib.getPackages(commands, yum) length := count(packages) some j analyzePackages(j, packages[j], packages, length) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "searchValue": packages[j], "issueType": "IncorrectValue", "keyExpectedValue": "The package version should always be specified when using yum install", @@ -36,16 +38,18 @@ CxPolicy[result] { count(resource.Value) > 1 - docker_lib.arrayContains(resource.Value, {"yum", "install"}) + dockerLib.arrayContains(resource.Value, {"yum", "install"}) resource.Value[j] != "install" resource.Value[j] != "yum" regex.match("^[a-zA-Z]", resource.Value[j]) == true - not docker_lib.withVersion(resource.Value[j]) + not dockerLib.withVersion(resource.Value[j]) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "searchValue": resource.Value[j], "issueType": "IncorrectValue", "keyExpectedValue": "The package version should always be specified when using yum install", @@ -57,12 +61,12 @@ CxPolicy[result] { analyzePackages(j, currentPackage, packages, length) { j == length - 1 regex.match("^[a-zA-Z]", currentPackage) == true - not docker_lib.withVersion(currentPackage) + not dockerLib.withVersion(currentPackage) } analyzePackages(j, currentPackage, packages, length) { j != length - 1 regex.match("^[a-zA-Z]", currentPackage) == true packages[plus(j, 1)] != "-v" - not docker_lib.withVersion(currentPackage) + not dockerLib.withVersion(currentPackage) } diff --git a/assets/queries/dockerfile/yum_install_without_version/test/negative2.dockerfile b/assets/queries/dockerfile/yum_install_without_version/test/negative2.dockerfile new file mode 100644 index 00000000000..ba8e3557b85 --- /dev/null +++ b/assets/queries/dockerfile/yum_install_without_version/test/negative2.dockerfile @@ -0,0 +1,8 @@ +from opensuse/leap:15.2 +run yum install -y httpd-2.24.2 && yum clean all +healthcheck CMD curl --fail http://localhost:3000 || exit 1 + + +from opensuse/leap:15.3 +env RETHINKDB_PACKAGE_VERSION 2.4.0~0trusty +run yum install -y rethinkdb-$RETHINKDB_PACKAGE_VERSION && yum clean all diff --git a/assets/queries/dockerfile/yum_install_without_version/test/positive2.dockerfile b/assets/queries/dockerfile/yum_install_without_version/test/positive2.dockerfile new file mode 100644 index 00000000000..7cc1025defd --- /dev/null +++ b/assets/queries/dockerfile/yum_install_without_version/test/positive2.dockerfile @@ -0,0 +1,4 @@ +from opensuse/leap:15.2 +run yum install -y httpd && yum clean all +run ["yum", "install", "httpd"] +healthcheck CMD curl --fail http://localhost:3000 || exit 1 diff --git a/assets/queries/dockerfile/yum_install_without_version/test/positive_expected_result.json b/assets/queries/dockerfile/yum_install_without_version/test/positive_expected_result.json index 2ed431a5849..b7670737f6c 100644 --- a/assets/queries/dockerfile/yum_install_without_version/test/positive_expected_result.json +++ b/assets/queries/dockerfile/yum_install_without_version/test/positive_expected_result.json @@ -1,12 +1,26 @@ [ - { - "queryName": "Yum install Without Version", - "severity": "MEDIUM", - "line": 2 - }, - { - "queryName": "Yum install Without Version", - "severity": "MEDIUM", - "line": 3 - } -] + { + "queryName": "Yum install Without Version", + "severity": "MEDIUM", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Yum install Without Version", + "severity": "MEDIUM", + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Yum install Without Version", + "severity": "MEDIUM", + "line": 2, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Yum install Without Version", + "severity": "MEDIUM", + "line": 3, + "fileName": "positive2.dockerfile" + } +] \ No newline at end of file diff --git a/assets/queries/dockerfile/zypper_install_without_version/query.rego b/assets/queries/dockerfile/zypper_install_without_version/query.rego index 92ed90a266b..0847a43667c 100644 --- a/assets/queries/dockerfile/zypper_install_without_version/query.rego +++ b/assets/queries/dockerfile/zypper_install_without_version/query.rego @@ -1,7 +1,7 @@ package Cx import data.generic.common as common_lib -import data.generic.dockerfile as docker_lib +import data.generic.dockerfile as dockerLib CxPolicy[result] { resource := input.document[i].command[name][cmd] @@ -13,15 +13,17 @@ CxPolicy[result] { zypper := regex.find_n("zypper (-(-)?[a-zA-Z]+ *)*in(stall)?", commands, -1) zypper != null - packages = docker_lib.getPackages(commands, zypper) + packages = dockerLib.getPackages(commands, zypper) length := count(packages) some j analyzePackages(j, packages[j], packages, length) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "searchValue": packages[j], "issueType": "IncorrectValue", "keyExpectedValue": "The package version should always be specified when using zypper install", @@ -36,16 +38,18 @@ CxPolicy[result] { count(resource.Value) > 1 - docker_lib.arrayContains(resource.Value, {"zypper", "install"}) + dockerLib.arrayContains(resource.Value, {"zypper", "install"}) resource.Value[j] != "install" resource.Value[j] != "zypper" regex.match("^[a-zA-Z]", resource.Value[j]) == true - not docker_lib.withVersion(resource.Value[j]) + not dockerLib.withVersion(resource.Value[j]) + stage := input.document[i].command[name] + from_command := dockerLib.get_original_from_command(stage) result := { "documentId": input.document[i].id, - "searchKey": sprintf("FROM={{%s}}.{{%s}}", [name, resource.Original]), + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.{{%s}}", [from_command.Value, name, resource.Original]), from_command.LineHint), "searchValue": resource.Value[j], "issueType": "IncorrectValue", "keyExpectedValue": "The package version should always be specified when using zypper install", @@ -57,12 +61,12 @@ CxPolicy[result] { analyzePackages(j, currentPackage, packages, length) { j == length - 1 regex.match("^[a-zA-Z]", currentPackage) == true - not docker_lib.withVersion(currentPackage) + not dockerLib.withVersion(currentPackage) } analyzePackages(j, currentPackage, packages, length) { j != length - 1 regex.match("^[a-zA-Z]", currentPackage) == true packages[plus(j, 1)] != "-v" - not docker_lib.withVersion(currentPackage) + not dockerLib.withVersion(currentPackage) } diff --git a/assets/queries/dockerfile/zypper_install_without_version/test/negative2.dockerfile b/assets/queries/dockerfile/zypper_install_without_version/test/negative2.dockerfile new file mode 100644 index 00000000000..d9fbcfc5dbb --- /dev/null +++ b/assets/queries/dockerfile/zypper_install_without_version/test/negative2.dockerfile @@ -0,0 +1,3 @@ +from opensuse/leap:15.2 +run zypper install -y httpd=2.4.46 && zypper clean +healthcheck CMD curl --fail http://localhost:3000 || exit 1 diff --git a/assets/queries/dockerfile/zypper_install_without_version/test/positive2.dockerfile b/assets/queries/dockerfile/zypper_install_without_version/test/positive2.dockerfile new file mode 100644 index 00000000000..8a17fc54f81 --- /dev/null +++ b/assets/queries/dockerfile/zypper_install_without_version/test/positive2.dockerfile @@ -0,0 +1,4 @@ +from opensuse/leap:15.2 +run zypper install -y httpd && zypper clean +run ["zypper", "install", "http"] +healthcheck CMD curl --fail http://localhost:3000 || exit 1 diff --git a/assets/queries/dockerfile/zypper_install_without_version/test/positive_expected_result.json b/assets/queries/dockerfile/zypper_install_without_version/test/positive_expected_result.json index 7d64d6a1109..5ffc570d5da 100644 --- a/assets/queries/dockerfile/zypper_install_without_version/test/positive_expected_result.json +++ b/assets/queries/dockerfile/zypper_install_without_version/test/positive_expected_result.json @@ -1,12 +1,26 @@ [ - { - "queryName": "Zypper Install Without Version", - "severity": "LOW", - "line": 2 - }, - { - "queryName": "Zypper Install Without Version", - "severity": "LOW", - "line": 3 - } + { + "queryName": "Zypper Install Without Version", + "severity": "LOW", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Zypper Install Without Version", + "severity": "LOW", + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Zypper Install Without Version", + "severity": "LOW", + "line": 2, + "fileName": "positive2.dockerfile" + }, + { + "queryName": "Zypper Install Without Version", + "severity": "LOW", + "line": 3, + "fileName": "positive2.dockerfile" + } ] \ No newline at end of file diff --git a/docs/creating-queries.md b/docs/creating-queries.md index 5471685e7ba..66de29ced9b 100644 --- a/docs/creating-queries.md +++ b/docs/creating-queries.md @@ -462,9 +462,50 @@ Examples: build_search_line(path, ["son"]) ``` -##### Ansible Inventory +#### 🚨 Platform Specific Guidelines 🚨 + +##### Ⓐ Ansible Inventory "searchLine" To create a `searchLine` query in Rego for this case, you need to think of the path as if you were dealing with a YAML/JSON file. This way, the query will be capable of locating vulnerabilities in all three types of Ansible host files. +--- + +##### 🐳 Dockerfile "searchKey" + +Dockerfile queries use a dedicated searchKey format and two helper functions from the `dockerfile.rego` library (`import data.generic.dockerfile as dockerLib`). + +Basic format: + +``` +FROM={{}}.={{}} +``` + +Where the first segment identifies the build stage by its FROM image, and subsequent segments identify the target instruction. For example: + +``` +FROM={{alpine:3.14}}.RUN={{apk update && apk add curl}} +``` + +Helper functions: + + - `dockerLib.get_original_from_command(stage)` — returns an object with `Value` (the literal `"FROM"` string preserving the original casing) and `LineHint` (the line number hint derived from the FROM instruction, used to tell the detector where to start searching). Use this instead of hardcoding `"FROM"`. + - `dockerLib.add_line_hint(searchKey, lineHint)` — appends a `^` suffix to the searchKey that tells the detector where to start searching in the file. The line hint is stripped before reaching the final results. + +Typical usage in a query: + +```rego +stage := input.document[i].command[name] +from_command := dockerLib.get_original_from_command(stage) + +result := { + "documentId": input.document[i].id, + "searchKey": dockerLib.add_line_hint(sprintf("%s={{%s}}.%s={{%s}}", [from_command.Value, name, run_command, commands]), from_command.LineHint), + ... +} +``` + +This produces a searchKey like: `FROM={{alpine:3.14}}.RUN={{apk update && apk add curl}}^2` + + #### Allowing users to overwrite query data Starting on v1.3.5, KICS started to support custom data overwriting on queries. This can be useful if users want to provide their own dataset or if users have different datasets for multiple environments. This can be supported easily following some steps: diff --git a/docs/platforms.md b/docs/platforms.md index bc4a6c7de7d..302faa278de 100644 --- a/docs/platforms.md +++ b/docs/platforms.md @@ -86,7 +86,9 @@ Note that KICS recognizes this technology as Azure Resource Manager (for queries ## Docker -KICS supports scanning Docker files with any name (but with no extension) and files with `.dockerfile` extension. +KICS supports scanning Dockerfile configurations with any name (but with no extension) and files matched by either name (`Dockerfile`, `Dockerfile.`), extension (`.dockerfile`,`.ubi8`,`.debian`), or by location inside directories named `docker`, `dockerfile`, or `dockerfiles`, where all text files are verified for a valid configuration regardless of extension. + +Note that every check is matched case-insensitively with the exception of the `.ubi8` and `.debian` extensions. ## Docker Compose diff --git a/e2e/fixtures/E2E_CLI_106_PAYLOAD.json b/e2e/fixtures/E2E_CLI_106_PAYLOAD.json new file mode 100644 index 00000000000..845d13c6cdd --- /dev/null +++ b/e2e/fixtures/E2E_CLI_106_PAYLOAD.json @@ -0,0 +1,2541 @@ +{ + "document": [ + { + "args": [], + "command": { + "openjdk:10-jdk": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM openjdk:10-jdk", + "SubCmd": "", + "Value": [ + "openjdk:10-jdk" + ], + "_kics_line": 1 + }, + { + "Cmd": "volume", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "VOLUME /tmp", + "SubCmd": "", + "Value": [ + "/tmp" + ], + "_kics_line": 2 + }, + { + "Cmd": "add", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "ADD http://source.file/package.file.tar.gz /temp", + "SubCmd": "", + "Value": [ + "http://source.file/package.file.tar.gz", + "/temp" + ], + "_kics_line": 3 + }, + { + "Cmd": "run", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "RUN tar -xjf /temp/package.file.tar.gz", + "SubCmd": "", + "Value": [ + "tar -xjf /temp/package.file.tar.gz" + ], + "_kics_line": 4 + }, + { + "Cmd": "arg", + "EndLine": 5, + "Flags": [], + "JSON": false, + "Original": "ARG JAR_FILE", + "SubCmd": "", + "Value": [ + "JAR_FILE" + ], + "_kics_line": 5 + }, + { + "Cmd": "add", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "ADD ${JAR_FILE} app.jar", + "SubCmd": "", + "Value": [ + "${JAR_FILE}", + "app.jar" + ], + "_kics_line": 6 + }, + { + "Cmd": "entrypoint", + "EndLine": 7, + "Flags": [], + "JSON": true, + "Original": "ENTRYPOINT [\"java\",\"-Djava.security.egd=file:/dev/./urandom\",\"-jar\",\"/app.jar\"]", + "SubCmd": "", + "Value": [ + "java", + "-Djava.security.egd=file:/dev/./urandom", + "-jar", + "/app.jar" + ], + "_kics_line": 7 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "ubuntu:latestnightly": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM ubuntu:latestnightly", + "SubCmd": "", + "Value": [ + "ubuntu:latestnightly" + ], + "_kics_line": 1 + }, + { + "Cmd": "volume", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "VOLUME /tmp", + "SubCmd": "", + "Value": [ + "/tmp" + ], + "_kics_line": 2 + }, + { + "Cmd": "entrypoint", + "EndLine": 3, + "Flags": [], + "JSON": true, + "Original": "ENTRYPOINT [\"java\",\"-Djava.security.egd=file:/dev/./urandom\",\"-jar\",\"/app.jar\"]", + "SubCmd": "", + "Value": [ + "java", + "-Djava.security.egd=file:/dev/./urandom", + "-jar", + "/app.jar" + ], + "_kics_line": 3 + } + ], + "ubuntu:latestnightly(1)": [ + { + "Cmd": "from", + "EndLine": 5, + "Flags": [], + "JSON": false, + "Original": "FROM ubuntu:latestnightly", + "SubCmd": "", + "Value": [ + "ubuntu:latestnightly" + ], + "_kics_line": 5 + }, + { + "Cmd": "volume", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "VOLUME /tmp", + "SubCmd": "", + "Value": [ + "/tmp" + ], + "_kics_line": 6 + }, + { + "Cmd": "add", + "EndLine": 7, + "Flags": [], + "JSON": false, + "Original": "ADD http://source.file/package.file.tar.gz /temp", + "SubCmd": "", + "Value": [ + "http://source.file/package.file.tar.gz", + "/temp" + ], + "_kics_line": 7 + }, + { + "Cmd": "run", + "EndLine": 8, + "Flags": [], + "JSON": false, + "Original": "RUN tar -xjf /temp/package.file.tar.gz", + "SubCmd": "", + "Value": [ + "tar -xjf /temp/package.file.tar.gz" + ], + "_kics_line": 8 + }, + { + "Cmd": "arg", + "EndLine": 9, + "Flags": [], + "JSON": false, + "Original": "ARG JAR_FILE", + "SubCmd": "", + "Value": [ + "JAR_FILE" + ], + "_kics_line": 9 + }, + { + "Cmd": "add", + "EndLine": 10, + "Flags": [], + "JSON": false, + "Original": "ADD ${JAR_FILE} app.jar", + "SubCmd": "", + "Value": [ + "${JAR_FILE}", + "app.jar" + ], + "_kics_line": 10 + }, + { + "Cmd": "entrypoint", + "EndLine": 11, + "Flags": [], + "JSON": true, + "Original": "ENTRYPOINT [\"java\",\"-Djava.security.egd=file:/dev/./urandom\",\"-jar\",\"/app.jar\"]", + "SubCmd": "", + "Value": [ + "java", + "-Djava.security.egd=file:/dev/./urandom", + "-jar", + "/app.jar" + ], + "_kics_line": 11 + } + ], + "ubuntu:latestnightly(2)": [ + { + "Cmd": "from", + "EndLine": 13, + "Flags": [], + "JSON": false, + "Original": "FROM ubuntu:latestnightly", + "SubCmd": "", + "Value": [ + "ubuntu:latestnightly" + ], + "_kics_line": 13 + } + ], + "ubuntu:latestnightly(3)": [ + { + "Cmd": "from", + "EndLine": 14, + "Flags": [], + "JSON": false, + "Original": "FROM ubuntu:latestnightly", + "SubCmd": "", + "Value": [ + "ubuntu:latestnightly" + ], + "_kics_line": 14 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 13, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 13 + }, + { + "Cmd": "copy", + "EndLine": 15, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 15 + }, + { + "Cmd": "healthcheck", + "EndLine": 17, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 17 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "ARG VERSION=1.0", + "SubCmd": "", + "Value": [ + "VERSION=1.0" + ], + "_kics_line": 1 + }, + { + "Cmd": "arg", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "ARG BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 2 + } + ], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 4 + }, + { + "Cmd": "copy", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 6 + }, + { + "Cmd": "healthcheck", + "EndLine": 8, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 8 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "ARG VERSION=1.0", + "SubCmd": "", + "Value": [ + "VERSION=1.0" + ], + "_kics_line": 1 + }, + { + "Cmd": "arg", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "ARG BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 2 + } + ], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 4 + }, + { + "Cmd": "copy", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 6 + }, + { + "Cmd": "healthcheck", + "EndLine": 8, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 8 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "ARG VERSION=1.0", + "SubCmd": "", + "Value": [ + "VERSION=1.0" + ], + "_kics_line": 1 + }, + { + "Cmd": "arg", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "ARG BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 2 + } + ], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 4 + }, + { + "Cmd": "copy", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 6 + }, + { + "Cmd": "healthcheck", + "EndLine": 8, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 8 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "ARG BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 1 + }, + { + "Cmd": "arg", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "ARG JAR_FILE", + "SubCmd": "", + "Value": [ + "JAR_FILE" + ], + "_kics_line": 4 + } + ], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 6 + }, + { + "Cmd": "copy", + "EndLine": 8, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 8 + }, + { + "Cmd": "healthcheck", + "EndLine": 10, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 10 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "ARG BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 2 + } + ], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 6 + }, + { + "Cmd": "copy", + "EndLine": 8, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 8 + }, + { + "Cmd": "healthcheck", + "EndLine": 10, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 10 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "ARG BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 1 + } + ], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 4 + }, + { + "Cmd": "copy", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "COPY .. .", + "SubCmd": "", + "Value": [ + "..", + "." + ], + "_kics_line": 6 + }, + { + "Cmd": "healthcheck", + "EndLine": 8, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 8 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "ARG BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 1 + } + ], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 3 + }, + { + "Cmd": "copy", + "EndLine": 5, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 5 + }, + { + "Cmd": "healthcheck", + "EndLine": 7, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 7 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "alpine:3.19 as builder": [ + { + "Cmd": "from", + "EndLine": 13, + "Flags": [], + "JSON": false, + "Original": "from alpine:3.19 as builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "as", + "builder" + ], + "_kics_line": 13 + }, + { + "Cmd": "copy", + "EndLine": 15, + "Flags": [], + "JSON": false, + "Original": "copy . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 15 + }, + { + "Cmd": "healthcheck", + "EndLine": 17, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "cmd", + "executable" + ], + "_kics_line": 17 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "arg VERSION=1.0", + "SubCmd": "", + "Value": [ + "VERSION=1.0" + ], + "_kics_line": 1 + }, + { + "Cmd": "arg", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "arg BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 2 + } + ], + "command": { + "alpine:3.19 as builder": [ + { + "Cmd": "from", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "from alpine:3.19 as builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "as", + "builder" + ], + "_kics_line": 4 + }, + { + "Cmd": "copy", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "copy . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 6 + }, + { + "Cmd": "healthcheck", + "EndLine": 8, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "cmd", + "executable" + ], + "_kics_line": 8 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "arg VERSION=1.0", + "SubCmd": "", + "Value": [ + "VERSION=1.0" + ], + "_kics_line": 1 + }, + { + "Cmd": "arg", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "arg BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 2 + } + ], + "command": { + "alpine:3.19 as builder": [ + { + "Cmd": "from", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "from alpine:3.19 as builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "as", + "builder" + ], + "_kics_line": 4 + }, + { + "Cmd": "copy", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "copy . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 6 + }, + { + "Cmd": "healthcheck", + "EndLine": 8, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "cmd", + "executable" + ], + "_kics_line": 8 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "arg VERSION=1.0", + "SubCmd": "", + "Value": [ + "VERSION=1.0" + ], + "_kics_line": 1 + }, + { + "Cmd": "arg", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "arg BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 2 + } + ], + "command": { + "alpine:3.19 as builder": [ + { + "Cmd": "from", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "from alpine:3.19 as builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "as", + "builder" + ], + "_kics_line": 4 + }, + { + "Cmd": "copy", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "copy . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 6 + }, + { + "Cmd": "healthcheck", + "EndLine": 8, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "cmd", + "executable" + ], + "_kics_line": 8 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "arg BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 1 + }, + { + "Cmd": "arg", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "arg JAR_FILE", + "SubCmd": "", + "Value": [ + "JAR_FILE" + ], + "_kics_line": 4 + } + ], + "command": { + "alpine:3.19 as builder": [ + { + "Cmd": "from", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "from alpine:3.19 as builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "as", + "builder" + ], + "_kics_line": 6 + }, + { + "Cmd": "copy", + "EndLine": 8, + "Flags": [], + "JSON": false, + "Original": "copy . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 8 + }, + { + "Cmd": "healthcheck", + "EndLine": 10, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "cmd", + "executable" + ], + "_kics_line": 10 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "arg BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 2 + } + ], + "command": { + "alpine:3.19 as builder": [ + { + "Cmd": "from", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "from alpine:3.19 as builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "as", + "builder" + ], + "_kics_line": 6 + }, + { + "Cmd": "copy", + "EndLine": 8, + "Flags": [], + "JSON": false, + "Original": "copy . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 8 + }, + { + "Cmd": "healthcheck", + "EndLine": 10, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "cmd", + "executable" + ], + "_kics_line": 10 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "arg BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 1 + } + ], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "from alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 4 + }, + { + "Cmd": "copy", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "copy .. .", + "SubCmd": "", + "Value": [ + "..", + "." + ], + "_kics_line": 6 + }, + { + "Cmd": "healthcheck", + "EndLine": 8, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "cmd", + "executable" + ], + "_kics_line": 8 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "arg BASE_IMAGE=ubuntu:22.04", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=ubuntu:22.04" + ], + "_kics_line": 1 + } + ], + "command": { + "alpine:3.19 as builder": [ + { + "Cmd": "from", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "from alpine:3.19 as builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "as", + "builder" + ], + "_kics_line": 3 + }, + { + "Cmd": "copy", + "EndLine": 5, + "Flags": [], + "JSON": false, + "Original": "copy . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 5 + }, + { + "Cmd": "healthcheck", + "EndLine": 7, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "cmd", + "executable" + ], + "_kics_line": 7 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "alpine:latest": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:latest", + "SubCmd": "", + "Value": [ + "alpine:latest" + ], + "_kics_line": 1 + }, + { + "Cmd": "copy", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "COPY {{ file_path }} /test", + "SubCmd": "", + "Value": [ + "{{", + "file_path", + "}}", + "/test" + ], + "_kics_line": 3 + }, + { + "Cmd": "run", + "EndLine": 5, + "Flags": [], + "JSON": false, + "Original": "RUN echo \"failure\"", + "SubCmd": "", + "Value": [ + "echo \"failure\"" + ], + "_kics_line": 5 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "ARG BASE_IMAGE=alpine:latest", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=alpine:latest" + ], + "_kics_line": 1 + } + ], + "command": { + "${BASE_IMAGE}": [ + { + "Cmd": "from", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "FROM ${BASE_IMAGE}", + "SubCmd": "", + "Value": [ + "alpine:latest" + ], + "_kics_line": 2 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "bighostnameusedasasample.example.com:4903/team/my-app:2.0": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM bighostnameusedasasample.example.com:4903/team/my-app:2.0", + "SubCmd": "", + "Value": [ + "bighostnameusedasasample.example.com:4903/team/my-app:2.0" + ], + "_kics_line": 1 + }, + { + "Cmd": "run", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "RUN echo \"hello\"", + "SubCmd": "", + "Value": [ + "echo \"hello\"" + ], + "_kics_line": 2 + }, + { + "Cmd": "add", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "ADD ${JAR_FILE} app.jar", + "SubCmd": "", + "Value": [ + "${JAR_FILE}", + "app.jar" + ], + "_kics_line": 3 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "alpine:latest": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:latest", + "SubCmd": "", + "Value": [ + "alpine:latest" + ], + "_kics_line": 1 + }, + { + "Cmd": "copy", + "EndLine": 2, + "Flags": [ + "--from=builder" + ], + "JSON": false, + "Original": "COPY --from=builder /src/bin /usr/local/bin/", + "SubCmd": "", + "Value": [ + "/src/bin", + "/usr/local/bin/" + ], + "_kics_line": 2 + }, + { + "Cmd": "cmd", + "EndLine": 3, + "Flags": [], + "JSON": true, + "Original": "CMD [\"/usr/local/bin/app\"]", + "SubCmd": "", + "Value": [ + "/usr/local/bin/app" + ], + "_kics_line": 3 + }, + { + "Cmd": "add", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "ADD ${JAR_FILE} app.jar", + "SubCmd": "", + "Value": [ + "${JAR_FILE}", + "app.jar" + ], + "_kics_line": 4 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "ARG BASE_IMAGE=alpine:latest", + "SubCmd": "", + "Value": [ + "BASE_IMAGE=alpine:latest" + ], + "_kics_line": 1 + } + ], + "command": { + "${BASE_IMAGE}": [ + { + "Cmd": "from", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "FROM ${BASE_IMAGE}", + "SubCmd": "", + "Value": [ + "alpine:latest" + ], + "_kics_line": 2 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a", + "SubCmd": "", + "Value": [ + "alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a" + ], + "_kics_line": 1 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "192.168.1.100:5000/team/image:v1": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM 192.168.1.100:5000/team/image:v1", + "SubCmd": "", + "Value": [ + "192.168.1.100:5000/team/image:v1" + ], + "_kics_line": 1 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "alpine:3.18 AS base": [ + { + "Cmd": "from", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.18 AS base", + "SubCmd": "", + "Value": [ + "alpine:3.18", + "AS", + "base" + ], + "_kics_line": 1 + }, + { + "Cmd": "volume", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "VOLUME /tmp", + "SubCmd": "", + "Value": [ + "/tmp" + ], + "_kics_line": 4 + }, + { + "Cmd": "add", + "EndLine": 5, + "Flags": [], + "JSON": false, + "Original": "ADD http://source.file/package.file.tar.gz /temp", + "SubCmd": "", + "Value": [ + "http://source.file/package.file.tar.gz", + "/temp" + ], + "_kics_line": 5 + }, + { + "Cmd": "run", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "RUN tar -xjf /temp/package.file.tar.gz", + "SubCmd": "", + "Value": [ + "tar -xjf /temp/package.file.tar.gz" + ], + "_kics_line": 6 + }, + { + "Cmd": "arg", + "EndLine": 7, + "Flags": [], + "JSON": false, + "Original": "ARG JAR_FILE", + "SubCmd": "", + "Value": [ + "JAR_FILE" + ], + "_kics_line": 7 + }, + { + "Cmd": "add", + "EndLine": 8, + "Flags": [], + "JSON": false, + "Original": "ADD ${JAR_FILE} app.jar", + "SubCmd": "", + "Value": [ + "${JAR_FILE}", + "app.jar" + ], + "_kics_line": 8 + }, + { + "Cmd": "entrypoint", + "EndLine": 9, + "Flags": [], + "JSON": true, + "Original": "ENTRYPOINT [\"java\",\"-Djava.security.egd=file:/dev/./urandom\",\"-jar\",\"/app.jar\"]", + "SubCmd": "", + "Value": [ + "java", + "-Djava.security.egd=file:/dev/./urandom", + "-jar", + "/app.jar" + ], + "_kics_line": 9 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "--platform=linux/amd64 alpine:latest": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [ + "--platform=linux/amd64" + ], + "JSON": false, + "Original": "FROM --platform=linux/amd64 alpine:latest", + "SubCmd": "", + "Value": [ + "alpine:latest" + ], + "_kics_line": 1 + }, + { + "Cmd": "copy", + "EndLine": 2, + "Flags": [ + "--from=builder" + ], + "JSON": false, + "Original": "COPY --from=builder /src/bin /usr/local/bin/", + "SubCmd": "", + "Value": [ + "/src/bin", + "/usr/local/bin/" + ], + "_kics_line": 2 + }, + { + "Cmd": "cmd", + "EndLine": 3, + "Flags": [], + "JSON": true, + "Original": "CMD [\"/usr/local/bin/app\"]", + "SubCmd": "", + "Value": [ + "/usr/local/bin/app" + ], + "_kics_line": 3 + }, + { + "Cmd": "add", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "ADD ${JAR_FILE} app.jar", + "SubCmd": "", + "Value": [ + "${JAR_FILE}", + "app.jar" + ], + "_kics_line": 4 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "--platform=linux/amd64 ubuntu:latest AS builder": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [ + "--platform=linux/amd64" + ], + "JSON": false, + "Original": "FROM --platform=linux/amd64 ubuntu:latest AS builder", + "SubCmd": "", + "Value": [ + "ubuntu:latest", + "AS", + "builder" + ], + "_kics_line": 1 + }, + { + "Cmd": "run", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "RUN apt-get update && apt-get install -y gcc", + "SubCmd": "", + "Value": [ + "apt-get update && apt-get install -y gcc" + ], + "_kics_line": 2 + }, + { + "Cmd": "copy", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "COPY . /src", + "SubCmd": "", + "Value": [ + ".", + "/src" + ], + "_kics_line": 3 + }, + { + "Cmd": "run", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "RUN make /src", + "SubCmd": "", + "Value": [ + "make /src" + ], + "_kics_line": 4 + }, + { + "Cmd": "add", + "EndLine": 5, + "Flags": [], + "JSON": false, + "Original": "ADD ${JAR_FILE} app.jar", + "SubCmd": "", + "Value": [ + "${JAR_FILE}", + "app.jar" + ], + "_kics_line": 5 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "--platform=linux/arm64 scratch": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [ + "--platform=linux/arm64" + ], + "JSON": false, + "Original": "FROM --platform=linux/arm64 scratch", + "SubCmd": "", + "Value": [ + "scratch" + ], + "_kics_line": 1 + }, + { + "Cmd": "copy", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "COPY myapp /", + "SubCmd": "", + "Value": [ + "myapp", + "/" + ], + "_kics_line": 2 + }, + { + "Cmd": "entrypoint", + "EndLine": 3, + "Flags": [], + "JSON": true, + "Original": "ENTRYPOINT [\"/myapp\"]", + "SubCmd": "", + "Value": [ + "/myapp" + ], + "_kics_line": 3 + }, + { + "Cmd": "add", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "ADD ${JAR_FILE} app.jar", + "SubCmd": "", + "Value": [ + "${JAR_FILE}", + "app.jar" + ], + "_kics_line": 4 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [ + { + "Cmd": "arg", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "ARG BUILDPLATFORM", + "SubCmd": "", + "Value": [ + "BUILDPLATFORM" + ], + "_kics_line": 1 + } + ], + "command": { + "--platform=$BUILDPLATFORM golang:1.21": [ + { + "Cmd": "from", + "EndLine": 2, + "Flags": [ + "--platform=$BUILDPLATFORM" + ], + "JSON": false, + "Original": "FROM --platform=$BUILDPLATFORM golang:1.21", + "SubCmd": "", + "Value": [ + "golang:1.21" + ], + "_kics_line": 2 + }, + { + "Cmd": "workdir", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "WORKDIR /src", + "SubCmd": "", + "Value": [ + "/src" + ], + "_kics_line": 3 + }, + { + "Cmd": "copy", + "EndLine": 4, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 4 + }, + { + "Cmd": "run", + "EndLine": 5, + "Flags": [], + "JSON": false, + "Original": "RUN go build -o /app", + "SubCmd": "", + "Value": [ + "go build -o /app" + ], + "_kics_line": 5 + }, + { + "Cmd": "cmd", + "EndLine": 6, + "Flags": [], + "JSON": true, + "Original": "CMD [\"/app\"]", + "SubCmd": "", + "Value": [ + "/app" + ], + "_kics_line": 6 + }, + { + "Cmd": "add", + "EndLine": 7, + "Flags": [], + "JSON": false, + "Original": "ADD ${JAR_FILE} app.jar", + "SubCmd": "", + "Value": [ + "${JAR_FILE}", + "app.jar" + ], + "_kics_line": 7 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "example.com:5000/team/my-app:2.0": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM example.com:5000/team/my-app:2.0", + "SubCmd": "", + "Value": [ + "example.com:5000/team/my-app:2.0" + ], + "_kics_line": 1 + }, + { + "Cmd": "run", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "RUN echo \"hello\"", + "SubCmd": "", + "Value": [ + "echo \"hello\"" + ], + "_kics_line": 2 + }, + { + "Cmd": "add", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "ADD ${JAR_FILE} app.jar", + "SubCmd": "", + "Value": [ + "${JAR_FILE}", + "app.jar" + ], + "_kics_line": 3 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "openjdk:10-jdk": [ + { + "Cmd": "from", + "EndLine": 5, + "Flags": [], + "JSON": false, + "Original": "FROM openjdk:10-jdk", + "SubCmd": "", + "Value": [ + "openjdk:10-jdk" + ], + "_kics_line": 5 + }, + { + "Cmd": "volume", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "VOLUME /tmp", + "SubCmd": "", + "Value": [ + "/tmp" + ], + "_kics_line": 6 + }, + { + "Cmd": "add", + "EndLine": 7, + "Flags": [], + "JSON": false, + "Original": "ADD http://source.file/package.file.tar.gz /temp", + "SubCmd": "", + "Value": [ + "http://source.file/package.file.tar.gz", + "/temp" + ], + "_kics_line": 7 + }, + { + "Cmd": "run", + "EndLine": 8, + "Flags": [], + "JSON": false, + "Original": "RUN tar -xjf /temp/package.file.tar.gz", + "SubCmd": "", + "Value": [ + "tar -xjf /temp/package.file.tar.gz" + ], + "_kics_line": 8 + }, + { + "Cmd": "arg", + "EndLine": 9, + "Flags": [], + "JSON": false, + "Original": "ARG JAR_FILE", + "SubCmd": "", + "Value": [ + "JAR_FILE" + ], + "_kics_line": 9 + }, + { + "Cmd": "add", + "EndLine": 10, + "Flags": [], + "JSON": false, + "Original": "ADD ${JAR_FILE} app.jar", + "SubCmd": "", + "Value": [ + "${JAR_FILE}", + "app.jar" + ], + "_kics_line": 10 + }, + { + "Cmd": "entrypoint", + "EndLine": 11, + "Flags": [], + "JSON": true, + "Original": "ENTRYPOINT [\"java\",\"-Djava.security.egd=file:/dev/./urandom\",\"-jar\",\"/app.jar\"]", + "SubCmd": "", + "Value": [ + "java", + "-Djava.security.egd=file:/dev/./urandom", + "-jar", + "/app.jar" + ], + "_kics_line": 11 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 1 + }, + { + "Cmd": "copy", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 3 + }, + { + "Cmd": "healthcheck", + "EndLine": 5, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 5 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 1 + }, + { + "Cmd": "copy", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 3 + }, + { + "Cmd": "healthcheck", + "EndLine": 5, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 5 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 1 + }, + { + "Cmd": "copy", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 3 + }, + { + "Cmd": "healthcheck", + "EndLine": 5, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 5 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 1 + }, + { + "Cmd": "copy", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 3 + }, + { + "Cmd": "healthcheck", + "EndLine": 5, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 5 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 1 + }, + { + "Cmd": "copy", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 3 + }, + { + "Cmd": "healthcheck", + "EndLine": 5, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 5 + } + ] + }, + "file": "file", + "id": "0" + }, + { + "args": [], + "command": { + "alpine:3.19 AS builder": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM alpine:3.19 AS builder", + "SubCmd": "", + "Value": [ + "alpine:3.19", + "AS", + "builder" + ], + "_kics_line": 1 + }, + { + "Cmd": "copy", + "EndLine": 3, + "Flags": [], + "JSON": false, + "Original": "COPY . .", + "SubCmd": "", + "Value": [ + ".", + "." + ], + "_kics_line": 3 + }, + { + "Cmd": "healthcheck", + "EndLine": 5, + "Flags": [ + "--interval=30s", + "--timeout=30s", + "--start-period=5s", + "--retries=3" + ], + "JSON": true, + "Original": "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", + "SubCmd": "", + "Value": [ + "CMD", + "executable" + ], + "_kics_line": 5 + } + ] + }, + "file": "file", + "id": "0" + } + ] +} diff --git a/e2e/fixtures/E2E_CLI_106_RESULT.json b/e2e/fixtures/E2E_CLI_106_RESULT.json new file mode 100644 index 00000000000..2e4eb58d8ac --- /dev/null +++ b/e2e/fixtures/E2E_CLI_106_RESULT.json @@ -0,0 +1,1114 @@ +{ + "kics_version": "development", + "files_scanned": 38, + "lines_scanned": 278, + "files_parsed": 38, + "lines_parsed": 267, + "lines_ignored": 11, + "files_failed_to_scan": 0, + "queries_total": 48, + "queries_failed_to_execute": 1, + "queries_failed_to_compute_similarity_id": 0, + "scan_id": "console", + "severity_counters": { + "CRITICAL": 0, + "HIGH": 38, + "INFO": 6, + "LOW": 20, + "MEDIUM": 22, + "TRACE": 0 + }, + "total_counter": 86, + "total_bom_resources": 0, + "start": "2026-04-22T11:52:42.7735722+01:00", + "end": "2026-04-22T11:52:44.496561+01:00", + "paths": [ + "/path/test/fixtures/dockerfile", + "/path/test/fixtures/negative_dockerfile" + ], + "queries": [ + { + "query_name": "Missing User Instruction", + "query_id": "fd54f200-402c-4333-a5a4-36ef6709af2f", + "query_url": "https://docs.docker.com/engine/reference/builder/#user", + "severity": "HIGH", + "platform": "Dockerfile", + "cwe": "250", + "risk_score": "7.7", + "cloud_provider": "COMMON", + "category": "Build Process", + "experimental": false, + "description": "Always set a user in the runtime stage of your Dockerfile. Without it, the container defaults to root, even if earlier build stages define a user.", + "description_id": "eb49caf6", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/multiline_from_statement", + "similarity_id": "af265ea3757c481a3f200faa7949ddb6fe1b0527c191d95ef81be533b12a55ca", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.18 AS base}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "3a12e5dfa0766d9df64e7c2ea00321aae6756bfe37a36ecff1da59755e0f9d36", + "line": 14, + "issue_type": "MissingAttribute", + "search_key": "FROM={{ubuntu:latestnightly(3)}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/any_name/file_2.DOCKERfile", + "similarity_id": "29858cfa69a98973cc1ae10f84e66267240bd630126eba2ba15e58a7aa2dd54d", + "line": 4, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/big_indent_from", + "similarity_id": "fd5fe33f391e08e2d4d4fe8058f5e93a75c6cd8424ea137b9944b8d871d5c37e", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:latest}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/test_folder_names_case/Docker/any_file.txt", + "similarity_id": "6da391b0e3e24d85f72b3ace5db0569be32ef11e6f9a433b138ae4e0b004df58", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/use_of_host", + "similarity_id": "98a9086dd512b76196866fa544fda9b79cdbe49097e0781edb284da60286a356", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{example.com:5000/team/my-app:2.0}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/any_name/Dockerfile.something", + "similarity_id": "b41a39fe06c21fc69fbd6e8f7b3e2c44e8d0d7a8e2b0e0c251f5d6a174e031ee", + "line": 4, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/any_name/DOCKERfile.txt", + "similarity_id": "5663f110b46dbc0378ff0540fc4a54700c80197a1ced862564f987d4f2e7116d", + "line": 13, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/base_image_reference", + "similarity_id": "d2716fd01d8e6d44b30749e106a2bd40ce4cb0b673ec7e62a4df8ca982c0582e", + "line": 2, + "issue_type": "MissingAttribute", + "search_key": "FROM={{${BASE_IMAGE}}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/case_insensitive_tests/file_2.DOCKERfile", + "similarity_id": "fbc4d6daf991234501dbcba5df28a58b2de1983ffbcc8b18d2f4546c295dfa39", + "line": 4, + "issue_type": "MissingAttribute", + "search_key": "from={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/case_insensitive_tests/Dockerfile.something", + "similarity_id": "58a26cafd4b62b89183a99f03c581a511654a67074c333f95c0481af8816450e", + "line": 4, + "issue_type": "MissingAttribute", + "search_key": "from={{alpine:3.19 as builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/case_insensitive_tests/random_name", + "similarity_id": "9ca22b0eb1ead0048eef0d5aba185858c316469892ded249c7c261720f293370", + "line": 3, + "issue_type": "MissingAttribute", + "search_key": "from={{alpine:3.19 as builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/test_folder_names/dockerfile/any_file.txt", + "similarity_id": "c2e7f0c0c566a723ff253f4a95e837749faba964ec008551c1f87a7faa476110", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/with_excluded_comments", + "similarity_id": "7635feaa0695981bc88dd51b213bc0763acd55575fae3d0fe196fe6b2d727bc4", + "line": 5, + "issue_type": "MissingAttribute", + "search_key": "FROM={{openjdk:10-jdk}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/case_insensitive_tests/dockerFILE", + "similarity_id": "bda0e263debb4ef181a93c60d35e0f28a57ca27f9f19b0a550a2a219c7fb56b6", + "line": 6, + "issue_type": "MissingAttribute", + "search_key": "from={{alpine:3.19 as builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/host_ip_address", + "similarity_id": "9f206d8845b08c15965c3cf00b7db7d9ee679c281d14cb5f2d414065a423b8a3", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{192.168.1.100:5000/team/image:v1}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_with_arg_reference", + "similarity_id": "68909c9c7a5f9979df2102bd5e7a21930d1b036b25710875c5cd76f52539490d", + "line": 2, + "issue_type": "MissingAttribute", + "search_key": "FROM={{--platform=$BUILDPLATFORM golang:1.21}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/case_insensitive_tests/DOCKERfile.txt", + "similarity_id": "a1bc54f29a5cd60b430490ab4ddf040dde52e24f1a9c81544e3aae9e21704fe7", + "line": 13, + "issue_type": "MissingAttribute", + "search_key": "from={{alpine:3.19 as builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag_and_alias", + "similarity_id": "d6c1ff98af764022d3ff3dce0b91cb19fb8dce099e87c506ddd542176b9444c1", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{--platform=linux/amd64 ubuntu:latest AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag_scratch", + "similarity_id": "f7e67be39f384d20f8816eff63ab5538f5990b6bae86ddd244478d9cb89f7b65", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{--platform=linux/arm64 scratch}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/corrupted_dockerfile", + "similarity_id": "558c83370b9fc9e230035e00ff7b5302cd64c16f700e73c830579947e250a381", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:latest}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/commands_indented", + "similarity_id": "0cd6617349fda5c8b8a9a1f3a2d35161133350a3e3617586a260ba4b53648950", + "line": 2, + "issue_type": "MissingAttribute", + "search_key": "FROM={{${BASE_IMAGE}}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/test_folder_names_case/Dockerfile/any_file.txt", + "similarity_id": "47d6f707c904f56fe3ca1cc7bce1d2e0ae41d421da983110a5c15fc7e48105df", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/any_name/dockerFILE", + "similarity_id": "e97a5ec241eb063c5757aed13a666c8126e4375ac9aed300cdc72d4ae883dfdc", + "line": 6, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-example", + "similarity_id": "aeaf42752011d846797cea09ce1a0eb5457673c67b0fb16914a0c639a253e5c7", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{openjdk:10-jdk}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/big_host_name", + "similarity_id": "357d434e6436d7fc32420359985019be12903ca92c03f50369e95a19b96e9d97", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{bighostnameusedasasample.example.com:4903/team/my-app:2.0}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/case_insensitive_tests/file.Dockerfile", + "similarity_id": "5f4fd65d6c624e63abde45a2c7fbfc4a3c64ab9e7b7bc95ddde9b861818aedfc", + "line": 6, + "issue_type": "MissingAttribute", + "search_key": "from={{alpine:3.19 as builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/case_insensitive_tests/any_name.debian", + "similarity_id": "3bcbe17d4e87888fabeadb6871fac6370dec66a162d8c94b3afed2327a3ecc11", + "line": 4, + "issue_type": "MissingAttribute", + "search_key": "from={{alpine:3.19 as builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/any_name/random_name", + "similarity_id": "ee3531797486eec98e3dd28ec8cc5f7f6f00743d1cf79cd47f6859df87026f59", + "line": 3, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag", + "similarity_id": "574ad8efea37036e772f5f133327ee6d82456fcfa268e0a01f942dd36d84a30d", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{--platform=linux/amd64 alpine:latest}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/test_folder_names_case/Dockerfiles/any_file.txt", + "similarity_id": "b58c4c4ed6c88b82fdf62608154342a31a2de95eaae39716ff4f6ccf1a5bcdda", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/any_name/file.Dockerfile", + "similarity_id": "1d972910b640dfb968ab630847182b4a19f44b78aeeaa0ef93c96c7e27aa8b6a", + "line": 6, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/test_folder_names/docker/any_file.txt", + "similarity_id": "9d78b93c92fe63c29dec006a12993b74dc6c6fbf29ae295ff7c6e19136657e2d", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/full_sha_digest", + "similarity_id": "bfb661b85dd88d16e0c74c1c07972ecf9ae95203607e762f0faad91a3a02c94a", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/any_name/any_name.ubi8", + "similarity_id": "4d64348b27180d867de9cf04a51db582786ea6622adb94fb54fdbac03b284769", + "line": 4, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/test_folder_names/dockerfiles/any_file.txt", + "similarity_id": "e150676345e87674484ea970ca810125007b743069646ac448feaba242b7211f", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/case_insensitive_tests/any_name.ubi8", + "similarity_id": "99145bb5bb5996f2d9518769bbebb143a6edff8c1b9866b9de64b2b6fba667e5", + "line": 4, + "issue_type": "MissingAttribute", + "search_key": "from={{alpine:3.19 as builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + }, + { + "file_name": "path/test/fixtures/dockerfile/any_name/any_name.debian", + "similarity_id": "ef335c394fbaebc802c99ba59b1b3ec830043ac020b711efc7cf497752b73429", + "line": 4, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.19 AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + } + ] + }, + { + "query_name": "Add Instead of Copy", + "query_id": "9513a694-aa0d-41d8-be61-3271e056f36b", + "query_url": "https://docs.docker.com/engine/reference/builder/#add", + "severity": "MEDIUM", + "platform": "Dockerfile", + "cwe": "610", + "risk_score": "5.2", + "category": "Supply-Chain", + "experimental": false, + "description": "Using ADD to load external installation scripts could lead to an evil web server leveraging this and loading a malicious script.", + "description_id": "0aedd324", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/with_excluded_comments", + "similarity_id": "58376185618893478741c810195c3194b9947675f6855e3b36ac4788fee9c97c", + "line": 10, + "issue_type": "IncorrectValue", + "search_key": "FROM={{openjdk:10-jdk}}.{{ADD ${JAR_FILE} app.jar}}", + "search_line": -1, + "search_value": "", + "expected_value": "'COPY' ${JAR_FILE}", + "actual_value": "'ADD' ${JAR_FILE}" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "2c8f808eeabb29c31b940c9d5d7526fe997c8c2d155857c1c462030cffd8c366", + "line": 10, + "issue_type": "IncorrectValue", + "search_key": "FROM={{ubuntu:latestnightly(1)}}.{{ADD ${JAR_FILE} app.jar}}", + "search_line": -1, + "search_value": "", + "expected_value": "'COPY' ${JAR_FILE}", + "actual_value": "'ADD' ${JAR_FILE}" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/big_host_name", + "similarity_id": "715dceeefda701d5bbe85ec0915d7a322c7685d2efb3a265d9701f8a62e16042", + "line": 3, + "issue_type": "IncorrectValue", + "search_key": "FROM={{bighostnameusedasasample.example.com:4903/team/my-app:2.0}}.{{ADD ${JAR_FILE} app.jar}}", + "search_line": -1, + "search_value": "", + "expected_value": "'COPY' ${JAR_FILE}", + "actual_value": "'ADD' ${JAR_FILE}" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag", + "similarity_id": "7a1036d28f16660bc2d10439a80109abd807dfef90aae78130092d8a395b6868", + "line": 4, + "issue_type": "IncorrectValue", + "search_key": "FROM={{--platform=linux/amd64 alpine:latest}}.{{ADD ${JAR_FILE} app.jar}}", + "search_line": -1, + "search_value": "", + "expected_value": "'COPY' ${JAR_FILE}", + "actual_value": "'ADD' ${JAR_FILE}" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/big_indent_from", + "similarity_id": "3dbca73c5f5b0d65cb9d6f2e1748ddde903c80ec9e97848b8d6d23a8554320b9", + "line": 4, + "issue_type": "IncorrectValue", + "search_key": "FROM={{alpine:latest}}.{{ADD ${JAR_FILE} app.jar}}", + "search_line": -1, + "search_value": "", + "expected_value": "'COPY' ${JAR_FILE}", + "actual_value": "'ADD' ${JAR_FILE}" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/multiline_from_statement", + "similarity_id": "65fa5eb4dc035a375e7de6745c524a241571a42543ac1e1e50ab6b97992e36e7", + "line": 8, + "issue_type": "IncorrectValue", + "search_key": "FROM={{alpine:3.18 AS base}}.{{ADD ${JAR_FILE} app.jar}}", + "search_line": -1, + "search_value": "", + "expected_value": "'COPY' ${JAR_FILE}", + "actual_value": "'ADD' ${JAR_FILE}" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_with_arg_reference", + "similarity_id": "a6fec2d84a496642691bb3bb5e77cb5fdd04755e956063e42804d5072abb2281", + "line": 7, + "issue_type": "IncorrectValue", + "search_key": "FROM={{--platform=$BUILDPLATFORM golang:1.21}}.{{ADD ${JAR_FILE} app.jar}}", + "search_line": -1, + "search_value": "", + "expected_value": "'COPY' ${JAR_FILE}", + "actual_value": "'ADD' ${JAR_FILE}" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/use_of_host", + "similarity_id": "46bf1a187c7c8aa95cc825281d886f4f34560a3f48210f81bbc1ed7c0b0ebab6", + "line": 3, + "issue_type": "IncorrectValue", + "search_key": "FROM={{example.com:5000/team/my-app:2.0}}.{{ADD ${JAR_FILE} app.jar}}", + "search_line": -1, + "search_value": "", + "expected_value": "'COPY' ${JAR_FILE}", + "actual_value": "'ADD' ${JAR_FILE}" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag_scratch", + "similarity_id": "b293e7dc91ace6a7c4fec199190cdf5805dede7f6f499ede4f8401b32c6b0d95", + "line": 4, + "issue_type": "IncorrectValue", + "search_key": "FROM={{--platform=linux/arm64 scratch}}.{{ADD ${JAR_FILE} app.jar}}", + "search_line": -1, + "search_value": "", + "expected_value": "'COPY' ${JAR_FILE}", + "actual_value": "'ADD' ${JAR_FILE}" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-example", + "similarity_id": "9d6bb1f4ca1093d79890b1b24b00dbb2e8fa60ca0df6b2ba391db348256eec6f", + "line": 6, + "issue_type": "IncorrectValue", + "search_key": "FROM={{openjdk:10-jdk}}.{{ADD ${JAR_FILE} app.jar}}", + "search_line": -1, + "search_value": "", + "expected_value": "'COPY' ${JAR_FILE}", + "actual_value": "'ADD' ${JAR_FILE}" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag_and_alias", + "similarity_id": "331f21aa559609154c76603d67a2ea7ae94f1fe97c9c8614dba0424eb43da6bf", + "line": 5, + "issue_type": "IncorrectValue", + "search_key": "FROM={{--platform=linux/amd64 ubuntu:latest AS builder}}.{{ADD ${JAR_FILE} app.jar}}", + "search_line": -1, + "search_value": "", + "expected_value": "'COPY' ${JAR_FILE}", + "actual_value": "'ADD' ${JAR_FILE}" + } + ] + }, + { + "query_name": "Apt Get Install Pin Version Not Defined", + "query_id": "965a08d7-ef86-4f14-8792-4a3b2098937e", + "query_url": "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/", + "severity": "MEDIUM", + "platform": "Dockerfile", + "cwe": "1357", + "risk_score": "5.7", + "category": "Supply-Chain", + "experimental": false, + "description": "When installing a package, its pin version should be defined", + "description_id": "e0e1edad", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag_and_alias", + "similarity_id": "f6fd7d36e1105623f456a64c19447ba1688c981aac4d7ae8724c57c8437461fb", + "line": 2, + "issue_type": "MissingAttribute", + "search_key": "FROM={{--platform=linux/amd64 ubuntu:latest AS builder}}.RUN={{apt-get update && apt-get install -y gcc}}", + "search_line": -1, + "search_value": "gcc", + "expected_value": "Package 'gcc' has version defined", + "actual_value": "Package 'gcc' does not have version defined" + } + ] + }, + { + "query_name": "Image Version Using 'latest'", + "query_id": "f45ea400-6bbe-4501-9fc7-1c3d75c32067", + "query_url": "https://docs.docker.com/develop/dev-best-practices/", + "severity": "MEDIUM", + "platform": "Dockerfile", + "cwe": "1357", + "risk_score": "5.1", + "category": "Best Practices", + "experimental": false, + "description": "When building images, always tag them with useful tags which codify version information, intended destination (prod or test, for instance), stability, or other information that is useful when deploying the application in different environments. Do not rely on the automatically-created latest tag", + "description_id": "22f535ec", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "c9903f97a9c6b1d61827bd33319d69199388b1c8f2723062fcdffeaa4114e609", + "line": 5, + "issue_type": "IncorrectValue", + "search_key": "FROM={{ubuntu:latestnightly(1)}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM ubuntu:latestnightly:'version' where version should not be 'latest'", + "actual_value": "FROM ubuntu:latestnightly'" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "d46445b62bae41ab265d7e79bca6c132018c33fd09d4ea48c354edc19628e83c", + "line": 14, + "issue_type": "IncorrectValue", + "search_key": "FROM={{ubuntu:latestnightly(3)}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM ubuntu:latestnightly:'version' where version should not be 'latest'", + "actual_value": "FROM ubuntu:latestnightly'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag_and_alias", + "similarity_id": "eddc0cae749b1b03806a495198d58e2675acdf908cd8d039dfd8743339069808", + "line": 1, + "issue_type": "IncorrectValue", + "search_key": "FROM={{--platform=linux/amd64 ubuntu:latest AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM ubuntu:latest:'version' where version should not be 'latest'", + "actual_value": "FROM ubuntu:latest'" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "2293ffe73033c89194caa88cbeb6155961a38f987f4ec5da36603522770c38f7", + "line": 1, + "issue_type": "IncorrectValue", + "search_key": "FROM={{ubuntu:latestnightly}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM ubuntu:latestnightly:'version' where version should not be 'latest'", + "actual_value": "FROM ubuntu:latestnightly'" + }, + { + "file_name": "path/test/fixtures/dockerfile/corrupted_dockerfile", + "similarity_id": "b8c6f58c6b52c4155b70475008be34bcf7ca39a15378ca1828e657a75ba907f3", + "line": 1, + "issue_type": "IncorrectValue", + "search_key": "FROM={{alpine:latest}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM alpine:latest:'version' where version should not be 'latest'", + "actual_value": "FROM alpine:latest'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag", + "similarity_id": "49639e49bb52319bd968d4a82dcc022a74bd93f58db8badc3757f4f416158e78", + "line": 1, + "issue_type": "IncorrectValue", + "search_key": "FROM={{--platform=linux/amd64 alpine:latest}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM alpine:latest:'version' where version should not be 'latest'", + "actual_value": "FROM alpine:latest'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/commands_indented", + "similarity_id": "9e14c38135b0625af308eb7ed536813f8e353739f4263fc3accfb91b0b8865bd", + "line": 2, + "issue_type": "IncorrectValue", + "search_key": "FROM={{${BASE_IMAGE}}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM alpine:latest:'version' where version should not be 'latest'", + "actual_value": "FROM alpine:latest'" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "c4486197745bf07a12ebe07d539eea97e4fb169a394676d2775028cdc4d1bcd3", + "line": 13, + "issue_type": "IncorrectValue", + "search_key": "FROM={{ubuntu:latestnightly(2)}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM ubuntu:latestnightly:'version' where version should not be 'latest'", + "actual_value": "FROM ubuntu:latestnightly'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/base_image_reference", + "similarity_id": "2b585700aa5471374491ee55cb32bd19043a996c4b527ef7968f21e217aa2d83", + "line": 2, + "issue_type": "IncorrectValue", + "search_key": "FROM={{${BASE_IMAGE}}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM alpine:latest:'version' where version should not be 'latest'", + "actual_value": "FROM alpine:latest'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/big_indent_from", + "similarity_id": "152040fb0f74af479251b64b3f3e04cddc103fceebe34abc81c8402b33b5791e", + "line": 1, + "issue_type": "IncorrectValue", + "search_key": "FROM={{alpine:latest}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM alpine:latest:'version' where version should not be 'latest'", + "actual_value": "FROM alpine:latest'" + } + ] + }, + { + "query_name": "Curl or Wget Instead of Add", + "query_id": "4b410d24-1cbe-4430-a632-62c9a931cf1c", + "query_url": "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/", + "severity": "LOW", + "platform": "Dockerfile", + "cwe": "610", + "risk_score": "2.8", + "category": "Best Practices", + "experimental": false, + "description": "Use of Curl or Wget should be done instead of Add to fetch packages from remote URLs due to the use of Add being strongly discouraged", + "description_id": "29e8216b", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/with_excluded_comments", + "similarity_id": "b7b5e6e04d1eedc9ce08a3b3cc6182d6c77f41539c44825f6b065a9b911a9541", + "line": 7, + "issue_type": "IncorrectValue", + "search_key": "FROM={{openjdk:10-jdk}}.{{ADD http://source.file/package.file.tar.gz /temp}}", + "search_line": -1, + "search_value": "", + "expected_value": "Should use 'curl' or 'wget' to download http://source.file/package.file.tar.gz", + "actual_value": "'ADD' http://source.file/package.file.tar.gz" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/multiline_from_statement", + "similarity_id": "b859a2050fd4032304ab5aa47a5ec7e7be60998e29f82c531d23ca123d31bbc0", + "line": 5, + "issue_type": "IncorrectValue", + "search_key": "FROM={{alpine:3.18 AS base}}.{{ADD http://source.file/package.file.tar.gz /temp}}", + "search_line": -1, + "search_value": "", + "expected_value": "Should use 'curl' or 'wget' to download http://source.file/package.file.tar.gz", + "actual_value": "'ADD' http://source.file/package.file.tar.gz" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-example", + "similarity_id": "37ebb20d72a17217823809f4bbf670db1167d627157c42c0b4dd9b063e30b5bd", + "line": 3, + "issue_type": "IncorrectValue", + "search_key": "FROM={{openjdk:10-jdk}}.{{ADD http://source.file/package.file.tar.gz /temp}}", + "search_line": -1, + "search_value": "", + "expected_value": "Should use 'curl' or 'wget' to download http://source.file/package.file.tar.gz", + "actual_value": "'ADD' http://source.file/package.file.tar.gz" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "553c7b4eeea7e969b88c30c34e9b7e97dc16963e731d1888fec90bb55f00e35a", + "line": 7, + "issue_type": "IncorrectValue", + "search_key": "FROM={{ubuntu:latestnightly(1)}}.{{ADD http://source.file/package.file.tar.gz /temp}}", + "search_line": -1, + "search_value": "", + "expected_value": "Should use 'curl' or 'wget' to download http://source.file/package.file.tar.gz", + "actual_value": "'ADD' http://source.file/package.file.tar.gz" + } + ] + }, + { + "query_name": "Healthcheck Instruction Missing", + "query_id": "b03a748a-542d-44f4-bb86-9199ab4fd2d5", + "query_url": "https://docs.docker.com/engine/reference/builder/#healthcheck", + "severity": "LOW", + "platform": "Dockerfile", + "cwe": "710", + "risk_score": "3.6", + "category": "Insecure Configurations", + "experimental": false, + "description": "Ensure that HEALTHCHECK is being used. The HEALTHCHECK instruction tells Docker how to test a container to check that it is still working", + "description_id": "426121ee", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/with_excluded_comments", + "similarity_id": "22833bad92ed958c49f1684b045a6c42d3b6551b87be013ada097a30a35b3121", + "line": 5, + "issue_type": "MissingAttribute", + "search_key": "FROM={{openjdk:10-jdk}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_with_arg_reference", + "similarity_id": "2b6ecbffb0ae35559c7e7fd068707a6774060cfdb0b6aad4e30c27f6f2201a2e", + "line": 2, + "issue_type": "MissingAttribute", + "search_key": "FROM={{--platform=$BUILDPLATFORM golang:1.21}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/big_indent_from", + "similarity_id": "6606bcfea791ee1653816d7fd3d11dda7df4f96cb35ef1557fa3556b0fdca764", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:latest}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/big_host_name", + "similarity_id": "0fbe6b1fc068e59c4519a7f0191bf3c66bcd48426bb400f8a17ada2e0db7f373", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{bighostnameusedasasample.example.com:4903/team/my-app:2.0}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/multiline_from_statement", + "similarity_id": "60e999ea86c199e26ee55024580e3e69d262b5c83092a25da9eddd321c7c2d21", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:3.18 AS base}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "7ce21db0493aefb24ea17710effc041ad227a72e7a1fe7cda7b1a18183a2c13a", + "line": 14, + "issue_type": "MissingAttribute", + "search_key": "FROM={{ubuntu:latestnightly(3)}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag_scratch", + "similarity_id": "1413f0f83a852273b3346b226059394439ada151ef2e23c654bf26fa3a2de923", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{--platform=linux/arm64 scratch}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/base_image_reference", + "similarity_id": "f89c7daf1bda819bc9f8c88ba0537cc09d73eabc47d2fb265289c7b60d6318c2", + "line": 2, + "issue_type": "MissingAttribute", + "search_key": "FROM={{${BASE_IMAGE}}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag", + "similarity_id": "1d75c91d6f47b6b0e00a70fb3aba1461133e91c394cd42a0378a30020a772c33", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{--platform=linux/amd64 alpine:latest}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/use_of_host", + "similarity_id": "e295d381f5cf7b496d9557d229dace321cf50a10cf6f35a47733c05d16cec1b4", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{example.com:5000/team/my-app:2.0}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-example", + "similarity_id": "4d0420e48f4c7d991ed6694980266d5b7313da8abb2e29b2dd777ce7c6f6251d", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{openjdk:10-jdk}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/corrupted_dockerfile", + "similarity_id": "ae470ca681b82da606c6080acf7ea93906066db785bf47e2372ef7b342f43f7e", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine:latest}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag_and_alias", + "similarity_id": "84c42fce04501e4cab82857ba0eac27193fbf52554ddf85aed92c9e50b1a55cc", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{--platform=linux/amd64 ubuntu:latest AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/commands_indented", + "similarity_id": "04ab9fbe080f52e2e5951e52d1377606b73d01ab60a8640d8fc5824e199d9d34", + "line": 2, + "issue_type": "MissingAttribute", + "search_key": "FROM={{${BASE_IMAGE}}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/full_sha_digest", + "similarity_id": "bac31787e6c7d3a127e2936f11ef5dadc6b477d8db1a10ed9e5d50c1c688d619", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/host_ip_address", + "similarity_id": "7549d9922711401d3f5a77b06fe02dbec6baf8f6488c483160b3adc5e27002de", + "line": 1, + "issue_type": "MissingAttribute", + "search_key": "FROM={{192.168.1.100:5000/team/image:v1}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + } + ] + }, + { + "query_name": "APT-GET Not Avoiding Additional Packages", + "query_id": "7384dfb2-fcd1-4fbf-91cd-6c44c318c33c", + "query_url": "https://docs.docker.com/engine/reference/builder/#run", + "severity": "INFO", + "platform": "Dockerfile", + "cwe": "710", + "risk_score": "0.0", + "category": "Supply-Chain", + "experimental": false, + "description": "Check if any apt-get installs don't use '--no-install-recommends' flag to avoid installing additional packages.", + "description_id": "2e92d18c", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag_and_alias", + "similarity_id": "9e2783060abf1251b3d7a3d12355870c24afc059e8755e7794806b710f95967e", + "line": 2, + "issue_type": "IncorrectValue", + "search_key": "FROM={{--platform=linux/amd64 ubuntu:latest AS builder}}.{{RUN apt-get update && apt-get install -y gcc}}", + "search_line": -1, + "search_value": "", + "expected_value": "'RUN apt-get update && apt-get install -y gcc' uses '--no-install-recommends' flag to avoid installing additional packages", + "actual_value": "'RUN apt-get update && apt-get install -y gcc' does not use '--no-install-recommends' flag to avoid installing additional packages" + } + ] + }, + { + "query_name": "Apt Get Install Lists Were Not Deleted", + "query_id": "df746b39-6564-4fed-bf85-e9c44382303c", + "query_url": "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/", + "severity": "INFO", + "platform": "Dockerfile", + "cwe": "459", + "risk_score": "0.0", + "category": "Supply-Chain", + "experimental": false, + "description": "After using apt-get install, it is needed to delete apt-get lists", + "description_id": "4236a50c", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag_and_alias", + "similarity_id": "589b8ca20e5ccc137ddd4f428f5217d55df9621929f33a87b75c724f6a93239f", + "line": 2, + "issue_type": "IncorrectValue", + "search_key": "FROM={{--platform=linux/amd64 ubuntu:latest AS builder}}.RUN={{apt-get update && apt-get install -y gcc}}", + "search_line": -1, + "search_value": "", + "expected_value": "After using apt-get install, the apt-get lists should be deleted", + "actual_value": "After using apt-get install, the apt-get lists were not deleted" + } + ] + }, + { + "query_name": "Using Platform Flag with FROM Command", + "query_id": "b16e8501-ef3c-44e1-a543-a093238099c9", + "query_url": "https://docs.docker.com/engine/reference/builder/#from", + "severity": "INFO", + "platform": "Dockerfile", + "cwe": "695", + "risk_score": "0.0", + "cloud_provider": "COMMON", + "category": "Best Practices", + "experimental": false, + "description": "'FROM' instruction should not use the flag '--platform'", + "description_id": "5bd0baab", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag", + "similarity_id": "d0502e1abfdc65c9d3caa7d9526e8289097c38bf09f81bc3e58a8391f18d9093", + "line": 1, + "issue_type": "IncorrectValue", + "search_key": "FROM={{--platform=linux/amd64 alpine:latest}}.{{FROM --platform=linux/amd64 alpine:latest}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM={{--platform=linux/amd64 alpine:latest}}.{{FROM --platform=linux/amd64 alpine:latest}} should not use the '--platform' flag", + "actual_value": "FROM={{--platform=linux/amd64 alpine:latest}}.{{FROM --platform=linux/amd64 alpine:latest}} is using the '--platform' flag" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_with_arg_reference", + "similarity_id": "6ddfbe87de63e9678da90c68dcb202f4e4f2e9bc1d4af42f3d2bdd9a9984cd65", + "line": 2, + "issue_type": "IncorrectValue", + "search_key": "FROM={{--platform=$BUILDPLATFORM golang:1.21}}.{{FROM --platform=$BUILDPLATFORM golang:1.21}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM={{--platform=$BUILDPLATFORM golang:1.21}}.{{FROM --platform=$BUILDPLATFORM golang:1.21}} should not use the '--platform' flag", + "actual_value": "FROM={{--platform=$BUILDPLATFORM golang:1.21}}.{{FROM --platform=$BUILDPLATFORM golang:1.21}} is using the '--platform' flag" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag_and_alias", + "similarity_id": "9b809e69a6609234e47deb34e1c6c519bb5a76364d8bbf6c1df962b9f7eebdcf", + "line": 1, + "issue_type": "IncorrectValue", + "search_key": "FROM={{--platform=linux/amd64 ubuntu:latest AS builder}}.{{FROM --platform=linux/amd64 ubuntu:latest AS builder}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM={{--platform=linux/amd64 ubuntu:latest AS builder}}.{{FROM --platform=linux/amd64 ubuntu:latest AS builder}} should not use the '--platform' flag", + "actual_value": "FROM={{--platform=linux/amd64 ubuntu:latest AS builder}}.{{FROM --platform=linux/amd64 ubuntu:latest AS builder}} is using the '--platform' flag" + }, + { + "file_name": "path/test/fixtures/dockerfile/should_generate_payload/platform_flag_scratch", + "similarity_id": "aa0029fe9eeb17144a5fd68b1017848f3bf0ca1fe083f9aacea7c4b6ca22b8cd", + "line": 1, + "issue_type": "IncorrectValue", + "search_key": "FROM={{--platform=linux/arm64 scratch}}.{{FROM --platform=linux/arm64 scratch}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM={{--platform=linux/arm64 scratch}}.{{FROM --platform=linux/arm64 scratch}} should not use the '--platform' flag", + "actual_value": "FROM={{--platform=linux/arm64 scratch}}.{{FROM --platform=linux/arm64 scratch}} is using the '--platform' flag" + } + ] + } + ] +} diff --git a/e2e/fixtures/E2E_CLI_107_PAYLOAD.json b/e2e/fixtures/E2E_CLI_107_PAYLOAD.json new file mode 100644 index 00000000000..405e897ca42 --- /dev/null +++ b/e2e/fixtures/E2E_CLI_107_PAYLOAD.json @@ -0,0 +1,171 @@ +{ + "document": [ + { + "args": [], + "command": { + "ubuntu:latestnightly": [ + { + "Cmd": "from", + "EndLine": 1, + "Flags": [], + "JSON": false, + "Original": "FROM ubuntu:latestnightly", + "SubCmd": "", + "Value": [ + "ubuntu:latestnightly" + ], + "_kics_line": 1 + }, + { + "Cmd": "volume", + "EndLine": 2, + "Flags": [], + "JSON": false, + "Original": "VOLUME /tmp", + "SubCmd": "", + "Value": [ + "/tmp" + ], + "_kics_line": 2 + }, + { + "Cmd": "entrypoint", + "EndLine": 3, + "Flags": [], + "JSON": true, + "Original": "ENTRYPOINT [\"java\",\"-Djava.security.egd=file:/dev/./urandom\",\"-jar\",\"/app.jar\"]", + "SubCmd": "", + "Value": [ + "java", + "-Djava.security.egd=file:/dev/./urandom", + "-jar", + "/app.jar" + ], + "_kics_line": 3 + } + ], + "ubuntu:latestnightly(1)": [ + { + "Cmd": "from", + "EndLine": 5, + "Flags": [], + "JSON": false, + "Original": "FROM ubuntu:latestnightly", + "SubCmd": "", + "Value": [ + "ubuntu:latestnightly" + ], + "_kics_line": 5 + }, + { + "Cmd": "volume", + "EndLine": 6, + "Flags": [], + "JSON": false, + "Original": "VOLUME /tmp", + "SubCmd": "", + "Value": [ + "/tmp" + ], + "_kics_line": 6 + }, + { + "Cmd": "add", + "EndLine": 7, + "Flags": [], + "JSON": false, + "Original": "ADD http://source.file/package.file.tar.gz /temp", + "SubCmd": "", + "Value": [ + "http://source.file/package.file.tar.gz", + "/temp" + ], + "_kics_line": 7 + }, + { + "Cmd": "run", + "EndLine": 8, + "Flags": [], + "JSON": false, + "Original": "RUN tar -xjf /temp/package.file.tar.gz", + "SubCmd": "", + "Value": [ + "tar -xjf /temp/package.file.tar.gz" + ], + "_kics_line": 8 + }, + { + "Cmd": "arg", + "EndLine": 9, + "Flags": [], + "JSON": false, + "Original": "ARG JAR_FILE", + "SubCmd": "", + "Value": [ + "JAR_FILE" + ], + "_kics_line": 9 + }, + { + "Cmd": "add", + "EndLine": 10, + "Flags": [], + "JSON": false, + "Original": "ADD ${JAR_FILE} app.jar", + "SubCmd": "", + "Value": [ + "${JAR_FILE}", + "app.jar" + ], + "_kics_line": 10 + }, + { + "Cmd": "entrypoint", + "EndLine": 11, + "Flags": [], + "JSON": true, + "Original": "ENTRYPOINT [\"java\",\"-Djava.security.egd=file:/dev/./urandom\",\"-jar\",\"/app.jar\"]", + "SubCmd": "", + "Value": [ + "java", + "-Djava.security.egd=file:/dev/./urandom", + "-jar", + "/app.jar" + ], + "_kics_line": 11 + } + ], + "ubuntu:latestnightly(2)": [ + { + "Cmd": "from", + "EndLine": 13, + "Flags": [], + "JSON": false, + "Original": "FROM ubuntu:latestnightly", + "SubCmd": "", + "Value": [ + "ubuntu:latestnightly" + ], + "_kics_line": 13 + } + ], + "ubuntu:latestnightly(3)": [ + { + "Cmd": "from", + "EndLine": 14, + "Flags": [], + "JSON": false, + "Original": "FROM ubuntu:latestnightly", + "SubCmd": "", + "Value": [ + "ubuntu:latestnightly" + ], + "_kics_line": 14 + } + ] + }, + "file": "file", + "id": "0" + } + ] +} diff --git a/e2e/fixtures/E2E_CLI_107_RESULT.json b/e2e/fixtures/E2E_CLI_107_RESULT.json new file mode 100644 index 00000000000..92b1be4bd20 --- /dev/null +++ b/e2e/fixtures/E2E_CLI_107_RESULT.json @@ -0,0 +1,194 @@ +{ + "kics_version": "development", + "files_scanned": 1, + "lines_scanned": 15, + "files_parsed": 1, + "lines_parsed": 15, + "lines_ignored": 0, + "files_failed_to_scan": 0, + "queries_total": 48, + "queries_failed_to_execute": 0, + "queries_failed_to_compute_similarity_id": 0, + "scan_id": "console", + "severity_counters": { + "CRITICAL": 0, + "HIGH": 1, + "INFO": 0, + "LOW": 2, + "MEDIUM": 5, + "TRACE": 0 + }, + "total_counter": 8, + "total_bom_resources": 0, + "start": "2026-04-22T12:30:41.5288415+01:00", + "end": "2026-04-22T12:30:43.3332084+01:00", + "paths": [ + "/path/test/fixtures/dockerfile/Dockerfile-multistage" + ], + "queries": [ + { + "query_name": "Missing User Instruction", + "query_id": "fd54f200-402c-4333-a5a4-36ef6709af2f", + "query_url": "https://docs.docker.com/engine/reference/builder/#user", + "severity": "HIGH", + "platform": "Dockerfile", + "cwe": "250", + "risk_score": "7.7", + "cloud_provider": "COMMON", + "category": "Build Process", + "experimental": false, + "description": "Always set a user in the runtime stage of your Dockerfile. Without it, the container defaults to root, even if earlier build stages define a user.", + "description_id": "eb49caf6", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "10ce4cfcc91d053ee1b926b632f05f3c744c73044a629a8ac5416a73d6ba80da", + "line": 14, + "issue_type": "MissingAttribute", + "search_key": "FROM={{ubuntu:latestnightly(3)}}", + "search_line": -1, + "search_value": "", + "expected_value": "The 'Dockerfile' should contain the 'USER' instruction", + "actual_value": "The 'Dockerfile' does not contain any 'USER' instruction" + } + ] + }, + { + "query_name": "Add Instead of Copy", + "query_id": "9513a694-aa0d-41d8-be61-3271e056f36b", + "query_url": "https://docs.docker.com/engine/reference/builder/#add", + "severity": "MEDIUM", + "platform": "Dockerfile", + "cwe": "610", + "risk_score": "5.2", + "category": "Supply-Chain", + "experimental": false, + "description": "Using ADD to load external installation scripts could lead to an evil web server leveraging this and loading a malicious script.", + "description_id": "0aedd324", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "8a77052e7fe9677bd371c5a652763f6d4edcb6ef3ece7b2574a37de1e532870c", + "line": 10, + "issue_type": "IncorrectValue", + "search_key": "FROM={{ubuntu:latestnightly(1)}}.{{ADD ${JAR_FILE} app.jar}}", + "search_line": -1, + "search_value": "", + "expected_value": "'COPY' ${JAR_FILE}", + "actual_value": "'ADD' ${JAR_FILE}" + } + ] + }, + { + "query_name": "Image Version Using 'latest'", + "query_id": "f45ea400-6bbe-4501-9fc7-1c3d75c32067", + "query_url": "https://docs.docker.com/develop/dev-best-practices/", + "severity": "MEDIUM", + "platform": "Dockerfile", + "cwe": "1357", + "risk_score": "5.1", + "category": "Best Practices", + "experimental": false, + "description": "When building images, always tag them with useful tags which codify version information, intended destination (prod or test, for instance), stability, or other information that is useful when deploying the application in different environments. Do not rely on the automatically-created latest tag", + "description_id": "22f535ec", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "7cd0ad90e5f32a85978dbfa12788d4098d089c58464d0206b3686a24c0401a0f", + "line": 5, + "issue_type": "IncorrectValue", + "search_key": "FROM={{ubuntu:latestnightly(1)}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM ubuntu:latestnightly:'version' where version should not be 'latest'", + "actual_value": "FROM ubuntu:latestnightly'" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "35196abdb4d1bc998a888b4f84a2c27ff21cc3ef0c98866f921313a59e871c2f", + "line": 13, + "issue_type": "IncorrectValue", + "search_key": "FROM={{ubuntu:latestnightly(2)}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM ubuntu:latestnightly:'version' where version should not be 'latest'", + "actual_value": "FROM ubuntu:latestnightly'" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "3ce5bdec335929d052c1cd553ea0f09716eca37667944c5f2703da9e92b1a50c", + "line": 14, + "issue_type": "IncorrectValue", + "search_key": "FROM={{ubuntu:latestnightly(3)}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM ubuntu:latestnightly:'version' where version should not be 'latest'", + "actual_value": "FROM ubuntu:latestnightly'" + }, + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "61497abefc29818ee73a40c7bd0c69f67c83e20279fbd89fbe69e7f6eac4e71c", + "line": 1, + "issue_type": "IncorrectValue", + "search_key": "FROM={{ubuntu:latestnightly}}", + "search_line": -1, + "search_value": "", + "expected_value": "FROM ubuntu:latestnightly:'version' where version should not be 'latest'", + "actual_value": "FROM ubuntu:latestnightly'" + } + ] + }, + { + "query_name": "Curl or Wget Instead of Add", + "query_id": "4b410d24-1cbe-4430-a632-62c9a931cf1c", + "query_url": "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/", + "severity": "LOW", + "platform": "Dockerfile", + "cwe": "610", + "risk_score": "2.8", + "category": "Best Practices", + "experimental": false, + "description": "Use of Curl or Wget should be done instead of Add to fetch packages from remote URLs due to the use of Add being strongly discouraged", + "description_id": "29e8216b", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "dfcf010141323091aa3e4594c429c4fa9c9c9e3ac1baa0bc553772e6cc5efafc", + "line": 7, + "issue_type": "IncorrectValue", + "search_key": "FROM={{ubuntu:latestnightly(1)}}.{{ADD http://source.file/package.file.tar.gz /temp}}", + "search_line": -1, + "search_value": "", + "expected_value": "Should use 'curl' or 'wget' to download http://source.file/package.file.tar.gz", + "actual_value": "'ADD' http://source.file/package.file.tar.gz" + } + ] + }, + { + "query_name": "Healthcheck Instruction Missing", + "query_id": "b03a748a-542d-44f4-bb86-9199ab4fd2d5", + "query_url": "https://docs.docker.com/engine/reference/builder/#healthcheck", + "severity": "LOW", + "platform": "Dockerfile", + "cwe": "710", + "risk_score": "3.6", + "category": "Insecure Configurations", + "experimental": false, + "description": "Ensure that HEALTHCHECK is being used. The HEALTHCHECK instruction tells Docker how to test a container to check that it is still working", + "description_id": "426121ee", + "files": [ + { + "file_name": "path/test/fixtures/dockerfile/Dockerfile-multistage", + "similarity_id": "ab1403dc6252c1a8affa62b3642775a987231a5e7144b78bdce4c4747376ef51", + "line": 14, + "issue_type": "MissingAttribute", + "search_key": "FROM={{ubuntu:latestnightly(3)}}", + "search_line": -1, + "search_value": "", + "expected_value": "Dockerfile should contain instruction 'HEALTHCHECK'", + "actual_value": "Dockerfile doesn't contain instruction 'HEALTHCHECK'" + } + ] + } + ] +} diff --git a/e2e/testcases/e2e-cli-075_ansible_host_detected.go b/e2e/testcases/e2e-cli-075_ansible_host_detected.go index 241bf3a7d21..b261a1d32ea 100644 --- a/e2e/testcases/e2e-cli-075_ansible_host_detected.go +++ b/e2e/testcases/e2e-cli-075_ansible_host_detected.go @@ -4,7 +4,7 @@ package testcases // should perform the scan successfully detect ansible and return result 40 func init() { //nolint testSample := TestCase{ - Name: "should perform a valid scan and and detect ansible [E2E-CLI-075]", + Name: "should perform a valid scan and detect ansible [E2E-CLI-075]", Args: args{ Args: []cmdArgs{ []string{"scan", "-o", "/path/e2e/output", diff --git a/e2e/testcases/e2e-cli-106_valid_dockerfile_detected.go b/e2e/testcases/e2e-cli-106_valid_dockerfile_detected.go new file mode 100644 index 00000000000..14577ced017 --- /dev/null +++ b/e2e/testcases/e2e-cli-106_valid_dockerfile_detected.go @@ -0,0 +1,31 @@ +package testcases + +// E2E-CLI-106 - KICS scan +// should perform the scan successfully detecting all valid dockerfile files and return result 50 +func init() { //nolint + testSample := TestCase{ + Name: "should perform a valid scan with all dockerfile files parsed [E2E-CLI-106]", + Args: args{ + Args: []cmdArgs{ + []string{"scan", "-o", "/path/e2e/output", + "--output-name", "E2E_CLI_106_RESULT", + "-p", "/path/test/fixtures/dockerfile", + "-p", "/path/test/fixtures/negative_dockerfile", + "--payload-path", "/path/e2e/output/E2E_CLI_106_PAYLOAD.json", + }, + }, + ExpectedResult: []ResultsValidation{ + { + ResultsFile: "E2E_CLI_106_RESULT", + ResultsFormats: []string{"json"}, + }, + }, + ExpectedPayload: []string{ + "E2E_CLI_106_PAYLOAD.json", + }, + }, + WantStatus: []int{50}, + } + + Tests = append(Tests, testSample) +} diff --git a/e2e/testcases/e2e-cli-107_dockerfile_multistage_scan.go b/e2e/testcases/e2e-cli-107_dockerfile_multistage_scan.go new file mode 100644 index 00000000000..4e80cbadfa8 --- /dev/null +++ b/e2e/testcases/e2e-cli-107_dockerfile_multistage_scan.go @@ -0,0 +1,31 @@ +package testcases + +// E2E-CLI-107 - KICS scan +// should perform the scan successfully detecting all dockerfile vulnerabilities on sample with 2 "FROM" +// statements on a single image +func init() { //nolint + testSample := TestCase{ + Name: "should perform a valid scan on dockerfile multistage sample [E2E-CLI-107]", + Args: args{ + Args: []cmdArgs{ + []string{"scan", "-o", "/path/e2e/output", + "--output-name", "E2E_CLI_107_RESULT", + "-p", "/path/test/fixtures/dockerfile/Dockerfile-multistage", + "--payload-path", "/path/e2e/output/E2E_CLI_107_PAYLOAD.json", + }, + }, + ExpectedResult: []ResultsValidation{ + { + ResultsFile: "E2E_CLI_107_RESULT", + ResultsFormats: []string{"json"}, + }, + }, + ExpectedPayload: []string{ + "E2E_CLI_107_PAYLOAD.json", + }, + }, + WantStatus: []int{50}, + } + + Tests = append(Tests, testSample) +} diff --git a/pkg/analyzer/analyzer.go b/pkg/analyzer/analyzer.go index 83ac9f8a359..10dfc5ac138 100644 --- a/pkg/analyzer/analyzer.go +++ b/pkg/analyzer/analyzer.go @@ -98,22 +98,20 @@ var ( listKeywordsGoogleDeployment = []string{"resources"} armRegexTypes = []string{"blueprint", "templateArtifact", "roleAssignmentArtifact", "policyAssignmentArtifact"} possibleFileTypes = map[string]bool{ - ".yml": true, - ".yaml": true, - ".json": true, - ".dockerfile": true, - "Dockerfile": true, - "possibleDockerfile": true, - ".debian": true, - ".ubi8": true, - ".tf": true, - "tfvars": true, - ".proto": true, - ".sh": true, - ".cfg": true, - ".conf": true, - ".ini": true, - ".bicep": true, + ".yml": true, + ".yaml": true, + ".json": true, + ".dockerfile": true, + ".debian": true, + ".ubi8": true, + ".tf": true, + "tfvars": true, + ".proto": true, + ".sh": true, + ".cfg": true, + ".conf": true, + ".ini": true, + ".bicep": true, } supportedRegexes = map[string][]string{ "azureresourcemanager": append(armRegexTypes, arm), @@ -152,9 +150,16 @@ type analyzerInfo struct { typesFlag []string excludeTypesFlag []string filePath string + fileExt string fallbackMinifiedFileLOC int } +// fileExtInfo contains file path and detected extension +type fileExtInfo struct { + path string + ext string +} + // fileTypeInfo contains file path, detected platform type, and LOC count type fileTypeInfo struct { filePath string @@ -328,7 +333,7 @@ func Analyze(a *Analyzer) (model.AnalyzedPaths, error) { FileStats: make(map[string]model.FileStatistics), } - var files []string + var files []fileExtInfo var wg sync.WaitGroup // results is the channel shared by the workers that contains the types found results := make(chan string) @@ -348,18 +353,27 @@ func Analyze(a *Analyzer) (model.AnalyzedPaths, error) { return err } + fileData, errFile := os.Stat(path) + if errFile != nil { + return nil + } + + if fileData.IsDir() { + return nil + } + + trimmedPath := strings.ReplaceAll(path, a.Paths[0], filepath.Base(a.Paths[0])) + ignoreFiles = a.checkIgnore(info.Size(), hasGitIgnoreFile, gitIgnore, path, trimmedPath, ignoreFiles) + ext, errExt := utils.GetExtension(path) if errExt == nil { - trimmedPath := strings.ReplaceAll(path, a.Paths[0], filepath.Base(a.Paths[0])) - ignoreFiles = a.checkIgnore(info.Size(), hasGitIgnoreFile, gitIgnore, path, trimmedPath, ignoreFiles) - if isConfigFile(path, defaultConfigFiles) { projectConfigFiles = append(projectConfigFiles, path) a.Exc = append(a.Exc, path) } if _, ok := possibleFileTypes[ext]; ok && !isExcludedFile(path, a.Exc) { - files = append(files, path) + files = append(files, fileExtInfo{path, ext}) } } return nil @@ -380,7 +394,8 @@ func Analyze(a *Analyzer) (model.AnalyzedPaths, error) { a := &analyzerInfo{ typesFlag: a.Types, excludeTypesFlag: a.ExcludeTypes, - filePath: file, + filePath: file.path, + fileExt: file.ext, fallbackMinifiedFileLOC: a.FallbackMinifiedFileLOC, } go a.worker(results, unwanted, locCount, fileInfo, &wg) @@ -429,86 +444,50 @@ func (a *analyzerInfo) worker( //nolint: gocyclo wg.Done() }() - ext, errExt := utils.GetExtension(a.filePath) - if errExt == nil { - linesCount, _ := utils.LineCounter(a.filePath, a.fallbackMinifiedFileLOC) - - switch ext { - // Dockerfile (direct identification) - case ".dockerfile", "Dockerfile": - if a.isAvailableType(dockerfile) { - results <- dockerfile - locCount <- linesCount - fileInfo <- fileTypeInfo{filePath: a.filePath, fileType: dockerfile, locCount: linesCount} - } - // Dockerfile (indirect identification) - case "possibleDockerfile", ".ubi8", ".debian": - if a.isAvailableType(dockerfile) && isDockerfile(a.filePath) { - results <- dockerfile - locCount <- linesCount - fileInfo <- fileTypeInfo{filePath: a.filePath, fileType: dockerfile, locCount: linesCount} - } else { - unwanted <- a.filePath - } - // Terraform - case ".tf", "tfvars": - if a.isAvailableType(terraform) { - results <- terraform - locCount <- linesCount - fileInfo <- fileTypeInfo{filePath: a.filePath, fileType: terraform, locCount: linesCount} - } - // Bicep - case ".bicep": - if a.isAvailableType(bicep) { - results <- arm - locCount <- linesCount - fileInfo <- fileTypeInfo{filePath: a.filePath, fileType: arm, locCount: linesCount} - } - // GRPC - case ".proto": - if a.isAvailableType(grpc) { - results <- grpc - locCount <- linesCount - fileInfo <- fileTypeInfo{filePath: a.filePath, fileType: grpc, locCount: linesCount} - } - // It could be Ansible Config or Ansible Inventory - case ".cfg", ".conf", ".ini": - if a.isAvailableType(ansible) { - results <- ansible - locCount <- linesCount - fileInfo <- fileTypeInfo{filePath: a.filePath, fileType: ansible, locCount: linesCount} - } - /* It could be Ansible, Buildah, CICD, CloudFormation, Crossplane, OpenAPI, Azure Resource Manager - Docker Compose, Knative, Kubernetes, Pulumi, ServerlessFW or Google Deployment Manager. - We also have FHIR's case which will be ignored since it's not a platform file.*/ - case yaml, yml, json, sh: - a.checkContent(results, unwanted, locCount, fileInfo, linesCount, ext) - } - } -} - -func isDockerfile(path string) bool { - content, err := os.ReadFile(filepath.Clean(path)) - if err != nil { - log.Error().Msgf("failed to analyze file: %s", err) - return false - } - - regexes := []*regexp.Regexp{ - regexp.MustCompile(`\s*FROM\s*`), - regexp.MustCompile(`\s*RUN\s*`), - } - - check := true + linesCount, _ := utils.LineCounter(a.filePath, a.fallbackMinifiedFileLOC) - for _, regex := range regexes { - if !regex.Match(content) { - check = false - break + switch a.fileExt { + // Dockerfile + case ".dockerfile": + if a.isAvailableType(dockerfile) { + results <- dockerfile + locCount <- linesCount + fileInfo <- fileTypeInfo{filePath: a.filePath, fileType: dockerfile, locCount: linesCount} + } + // Terraform + case ".tf", "tfvars": + if a.isAvailableType(terraform) { + results <- terraform + locCount <- linesCount + fileInfo <- fileTypeInfo{filePath: a.filePath, fileType: terraform, locCount: linesCount} + } + // Bicep + case ".bicep": + if a.isAvailableType(bicep) { + results <- arm + locCount <- linesCount + fileInfo <- fileTypeInfo{filePath: a.filePath, fileType: arm, locCount: linesCount} } + // GRPC + case ".proto": + if a.isAvailableType(grpc) { + results <- grpc + locCount <- linesCount + fileInfo <- fileTypeInfo{filePath: a.filePath, fileType: grpc, locCount: linesCount} + } + // It could be Ansible Config or Ansible Inventory + case ".cfg", ".conf", ".ini": + if a.isAvailableType(ansible) { + results <- ansible + locCount <- linesCount + fileInfo <- fileTypeInfo{filePath: a.filePath, fileType: ansible, locCount: linesCount} + } + /* It could be Ansible, Buildah, CICD, CloudFormation, Crossplane, OpenAPI, Azure Resource Manager + Docker Compose, Knative, Kubernetes, Pulumi, ServerlessFW or Google Deployment Manager. + We also have FHIR's case which will be ignored since it's not a platform file.*/ + case yaml, yml, json, sh: + a.checkContent(results, unwanted, locCount, fileInfo, linesCount, a.fileExt) } - - return check } // overrides k8s match when all regexes pass for azureresourcemanager key and extension is set to json diff --git a/pkg/analyzer/analyzer_test.go b/pkg/analyzer/analyzer_test.go index ea7adcf7f69..cce93881918 100644 --- a/pkg/analyzer/analyzer_test.go +++ b/pkg/analyzer/analyzer_test.go @@ -151,7 +151,6 @@ func TestAnalyzer_Analyze(t *testing.T) { wantExclude: []string{ filepath.FromSlash("../../test/fixtures/gitignore/positive.dockerfile"), filepath.FromSlash("../../test/fixtures/gitignore/secrets.tf"), - filepath.FromSlash("../../test/fixtures/gitignore/gitignore"), }, typesFromFlag: []string{""}, excludeTypesFromFlag: []string{""}, @@ -167,7 +166,7 @@ func TestAnalyzer_Analyze(t *testing.T) { filepath.FromSlash("../../test/fixtures/gitignore"), }, wantTypes: []string{"dockerfile", "kubernetes", "terraform"}, - wantExclude: []string{filepath.FromSlash("../../test/fixtures/gitignore/gitignore")}, + wantExclude: []string{}, typesFromFlag: []string{""}, excludeTypesFromFlag: []string{""}, wantLOC: 42, diff --git a/pkg/detector/docker/docker_detect.go b/pkg/detector/docker/docker_detect.go index 5b6a7ea7231..103f5318460 100644 --- a/pkg/detector/docker/docker_detect.go +++ b/pkg/detector/docker/docker_detect.go @@ -35,6 +35,11 @@ func (d DetectKindLine) DetectLine(file *model.FileMetadata, searchKey string, ResolvedFiles: make(map[string]model.ResolvedFileSplit), } + searchKey, startLine := extractLineHint(searchKey) + if startLine > 0 { + det.CurrentLine = startLine + } + var extractedString [][]string extractedString = detector.GetBracketValues(searchKey, extractedString, "") sKey := searchKey @@ -42,6 +47,8 @@ func (d DetectKindLine) DetectLine(file *model.FileMetadata, searchKey string, sKey = strings.ReplaceAll(sKey, str[0], `{{`+strconv.Itoa(idx)+`}}`) } + extractedString[0][1], _, _ = strings.Cut(extractedString[0][1], "(") + unchangedText := make([]string, len(*file.LinesOriginalData)) copy(unchangedText, *file.LinesOriginalData) @@ -72,6 +79,14 @@ func (d DetectKindLine) DetectLine(file *model.FileMetadata, searchKey string, } } +func extractLineHint(value string) (trimmedValue string, endLine int) { + idx := strings.LastIndex(value, "^") + if n, err := strconv.Atoi(value[idx+len("^"):]); err == nil { + return value[:idx], n + } + return value, 0 +} + func prepareDockerFileLines(text []string) []string { for idx, key := range text { if !commentRegex.MatchString(key) { diff --git a/pkg/detector/docker/docker_detect_test.go b/pkg/detector/docker/docker_detect_test.go index 5492ea32d62..b9b7cf87de8 100644 --- a/pkg/detector/docker/docker_detect_test.go +++ b/pkg/detector/docker/docker_detect_test.go @@ -49,6 +49,25 @@ RUN apk update \ && rm -rf /var/cache/apk/* ENTRYPOINT ["kubectl"]` +var OriginalData4 = `FROM openjdk:10-jdk +VOLUME /tmp +ADD http://source.file/package.file.tar.gz /temp +RUN tar -xjf /temp/package.file.tar.gz \ + && make -C /tmp/package.file \ + && rm /tmp/ package.file.tar.gz +ARG JAR_FILE +ADD ${JAR_FILE} apps.jar + +FROM openjdk:10-jdk +VOLUME /tmp +ADD http://source.file/package.file.tar.gz /temp +RUN tar -xjf /temp/package.file.tar.gz \ + && make -C /tmp/package.file \ + && rm /tmp/ package.file.tar.gz +ARG JAR_FILE +ADD ${JAR_FILE} apps.jar +` + // TestDetectDockerLine tests the functions [DetectDockerLine()] and all the methods called by them func TestDetectDockerLine(t *testing.T) { //nolint testCases := []struct { @@ -101,7 +120,7 @@ func TestDetectDockerLine(t *testing.T) { //nolint }, }, }, - searchKey: "FROM=openjdk:11-jdk.{{ADD ${JAR_FILE} apps.jar}}", + searchKey: "FROM={{openjdk:11-jdk}}.{{ADD ${JAR_FILE} apps.jar}}", file: &model.FileMetadata{ ScanID: "Test3", ID: "Test3", @@ -137,6 +156,33 @@ func TestDetectDockerLine(t *testing.T) { //nolint LinesOriginalData: utils.SplitLines(OriginalData3), }, }, + { + expected: model.VulnerabilityLines{ + Line: 17, + VulnLines: &[]model.CodeLine{ + { + Position: 16, + Line: "ARG JAR_FILE", + }, + { + Position: 17, + Line: "ADD ${JAR_FILE} apps.jar", + }, + { + Position: 18, + Line: "", + }, + }, + }, + searchKey: "FROM={{openjdk:10-jdk(1)}}.{{ADD ${JAR_FILE} apps.jar}}^8", + file: &model.FileMetadata{ + ScanID: "Test4", + ID: "Test4", + Kind: model.KindDOCKER, + OriginalData: OriginalData4, + LinesOriginalData: utils.SplitLines(OriginalData4), + }, + }, } for i, testCase := range testCases { diff --git a/pkg/engine/provider/filesystem_test.go b/pkg/engine/provider/filesystem_test.go index c20163c3765..d0f2169d787 100644 --- a/pkg/engine/provider/filesystem_test.go +++ b/pkg/engine/provider/filesystem_test.go @@ -128,7 +128,7 @@ func TestFileSystemSourceProvider_GetSources(t *testing.T) { //nolint { name: "get_sources_file", fields: fields{ - paths: []string{"assets/queries/dockerfile/add_instead_of_copy/test/positive.dockerfile"}, + paths: []string{"assets/queries/dockerfile/add_instead_of_copy/test/positive1.dockerfile"}, excludes: map[string][]os.FileInfo{}, }, args: args{ diff --git a/pkg/engine/vulnerability_builder.go b/pkg/engine/vulnerability_builder.go index a1a9a64659d..166d7dea5ed 100644 --- a/pkg/engine/vulnerability_builder.go +++ b/pkg/engine/vulnerability_builder.go @@ -125,6 +125,12 @@ var DefaultVulnerabilityBuilder = func(ctx *QueryContext, searchKey, _ = modifyVulSearchKeyReference(intDoc, searchKey, vulsSplit) vObj["searchKey"] = searchKey linesVulne = detector.DetectLine(&file, searchKey, &logWithFields) + if file.Kind == model.KindDOCKER { + if idx := strings.LastIndex(searchKey, "^"); idx != -1 { + searchKey = searchKey[:idx] + initialSearchKeyValue = searchKey + } + } } else { logWithFields.Error().Msg("Saving result. failed to detect line") } diff --git a/pkg/engine/vulnerability_builder_test.go b/pkg/engine/vulnerability_builder_test.go index cb34ba9b135..5d04bef3117 100644 --- a/pkg/engine/vulnerability_builder_test.go +++ b/pkg/engine/vulnerability_builder_test.go @@ -814,6 +814,56 @@ func TestEngine_calculate(t *testing.T) { //nolint } } +// TestDockerSearchKeyLineHintRemoval tests that the line hint (^suffix) is stripped +// from the searchKey +func TestDockerSearchKeyLineHintRemoval(t *testing.T) { + insDetector := detector.NewDetectLine(3). + Add(docker.DetectKindLine{}, model.KindDOCKER) + + tt := struct { + name string + searchKey string + wantSearchKey string + }{ + name: "searchKey with line hint is stripped", + searchKey: "command.alpine:3.14.run^3", + wantSearchKey: "command.alpine:3.14.run", + } + + t.Run(tt.name, func(t *testing.T) { + args := vbArgs{ + tracker: &tracker.CITracker{}, + ctx: &QueryContext{ + scanID: "ScanID", + Query: &PreparedQuery{ + Metadata: model.QueryMetadata{ + Metadata: map[string]interface{}{ + "id": "test-docker-query", + "severity": model.SeverityHigh, + "issueType": "IncorrectValue", + "searchKey": tt.searchKey, + }, + Query: "TestDockerQuery", + }, + }, + Files: map[string]model.FileMetadata{ + "dockerFileID": { + Kind: model.KindDOCKER, + LinesOriginalData: &[]string{}, + }, + }, + }, + v: map[string]interface{}{ + "documentId": "dockerFileID", + }, + } + + got, err := DefaultVulnerabilityBuilder(args.ctx, args.tracker, args.v, insDetector, false, false, map[string]TransitionQueryInfo{}) + require.NoError(t, err) + require.Equal(t, tt.wantSearchKey, got.SearchKey) + }) +} + func TestSanitize(t *testing.T) { tests := []struct { searchKey string diff --git a/pkg/parser/docker/parser.go b/pkg/parser/docker/parser.go index 7f97835b07e..548edcd586b 100644 --- a/pkg/parser/docker/parser.go +++ b/pkg/parser/docker/parser.go @@ -50,6 +50,7 @@ func (p *Parser) Parse(_ string, fileContent []byte) ([]model.Document, []int, e fromValue := "" from := make(map[string][]Command) + fromCount := make(map[string]int) arguments := make([]Command, 0) ignoreStruct := newIgnore() @@ -59,7 +60,12 @@ func (p *Parser) Parse(_ string, fileContent []byte) ([]model.Document, []int, e for _, child := range parsed.AST.Children { child.Value = strings.ToLower(child.Value) if child.Value == "from" { - fromValue = strings.TrimPrefix(child.Original, "FROM ") + fromValue = child.Original[5:] + fromValue = strings.TrimSpace(fromValue) + fromCount[fromValue]++ + if fromCount[fromValue] > 1 { + fromValue = fmt.Sprintf("%s(%d)", fromValue, fromCount[fromValue]-1) + } } if ignoreStruct.getIgnoreComments(child) { @@ -133,7 +139,7 @@ func (p *Parser) GetKind() model.FileKind { // SupportedExtensions returns Dockerfile extensions func (p *Parser) SupportedExtensions() []string { - return []string{"Dockerfile", ".dockerfile", ".ubi8", ".debian", "possibleDockerfile"} + return []string{".dockerfile"} } // SupportedTypes returns types supported by this parser, which are dockerfile diff --git a/pkg/parser/docker/parser_test.go b/pkg/parser/docker/parser_test.go index 3f6d6076ba9..aeff4a0838e 100644 --- a/pkg/parser/docker/parser_test.go +++ b/pkg/parser/docker/parser_test.go @@ -17,7 +17,7 @@ func TestParser_GetKind(t *testing.T) { // TestParser_SupportedExtensions tests the functions [SupportedExtensions()] and all the methods called by them func TestParser_SupportedExtensions(t *testing.T) { p := &Parser{} - require.Equal(t, []string{"Dockerfile", ".dockerfile", ".ubi8", ".debian", "possibleDockerfile"}, p.SupportedExtensions()) + require.Equal(t, []string{".dockerfile"}, p.SupportedExtensions()) } // TestParser_SupportedExtensions tests the functions [SupportedTypes()] and all the methods called by them @@ -216,6 +216,26 @@ ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] } } +// TestParser_Parse_DuplicateFROM tests that two FROM statements referencing the same image +// produce two distinct command groups in the parsed output +func TestParser_Parse_DuplicateFROM(t *testing.T) { + p := &Parser{} + sample := `FROM alpine:3.14 +RUN echo "stage1" +FROM alpine:3.14 +RUN echo "stage2" +` + doc, _, err := p.Parse("Dockerfile", []byte(sample)) + require.NoError(t, err) + require.Len(t, doc, 1) + + commands := doc[0]["command"].(map[string]interface{}) + + require.Len(t, commands, 2) + require.Contains(t, commands, "alpine:3.14") + require.Contains(t, commands, "alpine:3.14(1)") +} + func TestParser_GetResolvedFiles(t *testing.T) { tests := []struct { name string @@ -235,3 +255,46 @@ func TestParser_GetResolvedFiles(t *testing.T) { }) } } + +// TestParser_Parse_CaseInsensitive tests that the parser handles Dockerfile commands +// in a case-insensitive manner +func TestParser_Parse_CaseInsensitive(t *testing.T) { + p := &Parser{} + // baseline sample + upper := ` +FROM alpine:3.18 +RUN echo "hello" +` + lower := ` +from alpine:3.18 +run echo "hello" +` + mixed := ` +fRoM alpine:3.18 +rUn echo "hello" +` + + docUpper, _, err := p.Parse("Dockerfile", []byte(upper)) + require.NoError(t, err) + require.Len(t, docUpper, 1) + cmdsUpper := docUpper[0]["command"].(map[string]interface{})["alpine:3.18"].([]interface{}) + + docLower, _, err := p.Parse("Dockerfile", []byte(lower)) + require.NoError(t, err) + require.Len(t, docLower, 1) + cmdsLower := docLower[0]["command"].(map[string]interface{})["alpine:3.18"].([]interface{}) + require.Len(t, cmdsUpper, len(cmdsLower)) + + docMixed, _, err := p.Parse("Dockerfile", []byte(mixed)) + require.NoError(t, err) + require.Len(t, docMixed, 1) + cmdsMixed := docMixed[0]["command"].(map[string]interface{})["alpine:3.18"].([]interface{}) + require.Len(t, cmdsUpper, len(cmdsMixed)) + + for i := range cmdsUpper { + require.Equal(t, cmdsUpper[i].(map[string]interface{})["Cmd"], cmdsMixed[i].(map[string]interface{})["Cmd"]) + require.Equal(t, cmdsUpper[i].(map[string]interface{})["Value"], cmdsMixed[i].(map[string]interface{})["Value"]) + require.Equal(t, cmdsUpper[i].(map[string]interface{})["Cmd"], cmdsLower[i].(map[string]interface{})["Cmd"]) + require.Equal(t, cmdsUpper[i].(map[string]interface{})["Value"], cmdsLower[i].(map[string]interface{})["Value"]) + } +} diff --git a/pkg/parser/parser_test.go b/pkg/parser/parser_test.go index 73d1f4d44b7..0eff1ce94ea 100644 --- a/pkg/parser/parser_test.go +++ b/pkg/parser/parser_test.go @@ -94,7 +94,6 @@ func TestParser_SupportedExtensions(t *testing.T) { require.Contains(t, extensions, ".tf") require.Contains(t, extensions, ".yaml") require.Contains(t, extensions, ".dockerfile") - require.Contains(t, extensions, "Dockerfile") } func initilizeBuilder() []*Parser { diff --git a/pkg/remediation/scan.go b/pkg/remediation/scan.go index e48f5648ee0..ffd04fc5656 100644 --- a/pkg/remediation/scan.go +++ b/pkg/remediation/scan.go @@ -95,7 +95,7 @@ func getPayload(filePath string, content []byte, openAPIResolveReferences bool, var err error switch ext { - case ".dockerfile", "Dockerfile", "possibleDockerfile", ".ubi8", ".debian": + case ".dockerfile": p, err = parser.NewBuilder().Add(&dockerParser.Parser{}).Build([]string{""}, []string{""}) case terraformExtension: diff --git a/pkg/utils/get_extension.go b/pkg/utils/get_extension.go index cfc9bc48861..827058d0f81 100644 --- a/pkg/utils/get_extension.go +++ b/pkg/utils/get_extension.go @@ -2,21 +2,36 @@ package utils import ( "bufio" - "bytes" "fmt" "os" "path/filepath" + "regexp" "strings" + "github.com/Checkmarx/kics/v2/internal/constants" "github.com/rs/zerolog/log" - "golang.org/x/tools/godoc/util" ) +const ( + extDockerfile = ".dockerfile" + dockerFromPattern = `(?i)^from\s+` + pythonImportPattern = `(?i)from\s+\S+\s+import\s+\S+` + emailPattern = `(?i)from\s*(:)?\s*[\w\-.]+@([\w\-]+\.)+[\w\-]{2,4}` + capitalizedAliasPattern = `^(?i:FROM)\s+\S+\s+(?i:AS)\s+[A-Z]` + dockerfileIllegalCharacters = `["'` + "`" + `()\[\],;|&?*^%!~<>]` +) + +var dockerFrom = regexp.MustCompile(dockerFromPattern) + +var falsePositiveFROMPatterns = []*regexp.Regexp{ + regexp.MustCompile(pythonImportPattern), + regexp.MustCompile(emailPattern), + regexp.MustCompile(capitalizedAliasPattern), + regexp.MustCompile(dockerfileIllegalCharacters), +} + // GetExtension gets the extension of a file path func GetExtension(path string) (string, error) { - targets := []string{"Dockerfile", "tfvars"} - - // Get file information fileInfo, err := os.Stat(path) if err != nil { return "", fmt.Errorf("file %s not found", path) @@ -26,35 +41,49 @@ func GetExtension(path string) (string, error) { return "", fmt.Errorf("the path %s is a directory", path) } + if ext, ok := isDockerfileExtension(path); ok { + return ext, nil + } + ext := filepath.Ext(path) - if ext == "" { - base := filepath.Base(path) + switch ext { + case ".ubi8", ".debian": + if readPossibleDockerFile(path) { + return extDockerfile, nil + } + case "": + if filepath.Base(path) == "tfvars" { + return ".tfvars", nil + } + if readPossibleDockerFile(path) { + return extDockerfile, nil + } + } + return ext, nil +} - if Contains(base, targets) { - ext = base - } else { - isText, err := isTextFile(path) +func isDockerfileExtension(path string) (string, bool) { + base := filepath.Base(path) - if err != nil { - return "", err - } + lower := strings.ToLower(base) + if lower == constants.AvailablePlatforms["Dockerfile"] || strings.HasPrefix(lower, "dockerfile.") { + return extDockerfile, true + } - if isText { - if readPossibleDockerFile(path) { - ext = "possibleDockerfile" - } - } - } + if strings.EqualFold(filepath.Ext(path), extDockerfile) { + return extDockerfile, true } - return ext, nil + dir := strings.ToLower(filepath.Base(filepath.Dir(path))) + if (dir == "docker" || dir == constants.AvailablePlatforms["Dockerfile"] || dir == "dockerfiles") && readPossibleDockerFile(path) { + return extDockerfile, true + } + + return "", false } func readPossibleDockerFile(path string) bool { path = filepath.Clean(path) - if strings.HasSuffix(path, "gitignore") { - return true - } file, err := os.Open(path) if err != nil { return false @@ -68,37 +97,21 @@ func readPossibleDockerFile(path string) bool { scanner := bufio.NewScanner(file) // Read lines from the file for scanner.Scan() { - if strings.HasPrefix(scanner.Text(), "FROM") { - return true - } else if strings.HasPrefix(scanner.Text(), "#") { + line := strings.TrimSpace(scanner.Text()) + if strings.HasPrefix(line, "#") || strings.HasPrefix(strings.ToLower(line), "arg") || line == "" { continue } else { - return false + return dockerFrom.MatchString(line) && !matchesAny(falsePositiveFROMPatterns, line) } } return false } -func isTextFile(path string) (bool, error) { - info, err := os.Stat(path) - if err != nil { - log.Error().Msgf("failed to get file info: %s", err) - return false, err - } - - if info.IsDir() { - return false, nil - } - - content, err := os.ReadFile(filepath.Clean(path)) - if err != nil { - log.Error().Msgf("failed to analyze file: %s", err) - return false, err +func matchesAny(patterns []*regexp.Regexp, s string) bool { + for _, p := range patterns { + if p.MatchString(s) { + return true + } } - - content = bytes.ReplaceAll(content, []byte("\r"), []byte("")) - - isText := util.IsText(content) - - return isText, nil + return false } diff --git a/pkg/utils/get_extension_test.go b/pkg/utils/get_extension_test.go index 73f5955effa..37df89de556 100644 --- a/pkg/utils/get_extension_test.go +++ b/pkg/utils/get_extension_test.go @@ -18,22 +18,106 @@ func TestGetExtension(t *testing.T) { }{ { name: "Get extension from a file named as Dockerfile and without extension defined ('Dockerfile')", - want: "Dockerfile", + want: ".dockerfile", filePath: "../../Dockerfile", toCreate: false, err: nil, }, { - name: "Get extension from a file not named as Dockerfile and without extension defined ('Dockerfile-example')", - want: "possibleDockerfile", - filePath: "../../test/fixtures/dockerfile/Dockerfile-example", + name: "Get extension from a file named as dockerFILE and without extension defined ('dockerFILE')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/any_name/dockerFILE", toCreate: false, err: nil, }, { - name: "Get extension from a file with extension defined ('positive.tf')", - want: ".tf", - filePath: "../../test/fixtures/all_auth_users_get_read_access/test/positive.tf", + name: "Get extension from a file not named 'dockerfile' with extension defined as Dockerfile ('file.Dockerfile')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/any_name/file.Dockerfile", + toCreate: false, + err: nil, + }, + { + name: "Get extension from a file not named 'dockerfile' with extension defined as DOCKERfile ('file_2.DOCKERfile')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/any_name/file_2.DOCKERfile", + toCreate: false, + err: nil, + }, + { + name: "Get extension from a file named 'Dockerfile' with any extension defined ('Dockerfile.something')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/any_name/Dockerfile.something", + toCreate: false, + err: nil, + }, + { + name: "Get extension from a file named 'DOCKERfile' with any extension defined ('DOCKERfile.txt')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/any_name/DOCKERfile.txt", + toCreate: false, + err: nil, + }, + { + name: "Get extension from a file not named as Dockerfile and without a relevant extension defined ('any_file.txt'), should detect due to parent folder 'docker'", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/test_folder_names/docker/any_file.txt", + toCreate: false, + err: nil, + }, + { + name: "Get extension from a file not named as Dockerfile and without a relevant extension defined ('any_file.txt'), should detect due to parent folder 'Docker'", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/test_folder_names_case/Docker/any_file.txt", + toCreate: false, + err: nil, + }, + { + name: "Get extension from a file not named as Dockerfile and without a relevant extension defined ('any_file.txt'), should detect due to parent folder 'dockerfile'", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/test_folder_names/dockerfile/any_file.txt", + toCreate: false, + err: nil, + }, + { + name: "Get extension from a file not named as Dockerfile and without a relevant extension defined ('any_file.txt'), should detect due to parent folder 'Dockerfile'", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/test_folder_names_case/Dockerfile/any_file.txt", + toCreate: false, + err: nil, + }, + { + name: "Get extension from a file not named as Dockerfile and without a relevant extension defined ('any_file.txt'), should detect due to parent folder 'dockerfiles'", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/test_folder_names/dockerfiles/any_file.txt", + toCreate: false, + err: nil, + }, + { + name: "Get extension from a file not named as Dockerfile and without a relevant extension defined ('any_file.txt'), should detect due to parent folder 'Dockerfiles'", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/test_folder_names_case/Dockerfiles/any_file.txt", + toCreate: false, + err: nil, + }, + { + name: "Get extension from a file not named as Dockerfile and without extension defined ('random_name'), due to parent folder scan will identify dockerfile syntax regardless", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/any_name/random_name", + toCreate: false, + err: nil, + }, + { + name: "Get extension from a valid text file with dockerfile syntax and '.ubi8' extension ('any_name.ubi8')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/any_name/any_name.ubi8", + toCreate: false, + err: nil, + }, + { + name: "Get extension from a valid text file with dockerfile syntax and '.debian' extension ('any_name.debian')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/any_name/any_name.debian", toCreate: false, err: nil, }, @@ -44,6 +128,27 @@ func TestGetExtension(t *testing.T) { toCreate: false, err: nil, }, + { + name: "Get literal extension from a file not named as Dockerfile and with extension that is not .dockerfile,.ubi8 or .debian, regardless of text syntax", + want: ".txt", + filePath: "../../test/fixtures/negative_dockerfile/not_dockerfile.txt", + toCreate: false, + err: nil, + }, + { + name: "Get literal extension from a valid text file with '.ubi8' extension that lacks relevant dockerfile syntax('any_name.ubi8')", + want: ".ubi8", + filePath: "../../test/fixtures/negative_dockerfile/not_dockerfile.ubi8", + toCreate: false, + err: nil, + }, + { + name: "Get literal extension from a valid text file with '.debian' extension that lacks relevant dockerfile syntax('any_name.debian')", + want: ".debian", + filePath: "../../test/fixtures/negative_dockerfile/not_dockerfile.debian", + toCreate: false, + err: nil, + }, { name: "Get error when analyze a folder", want: "", @@ -51,6 +156,69 @@ func TestGetExtension(t *testing.T) { toCreate: true, err: fmt.Errorf("the path %s is a directory", "../../test/fixtures/for_test_folder"), }, + { + name: "Get extension from a file with extension defined ('positive.tf')", + want: ".tf", + filePath: "../../test/fixtures/all_auth_users_get_read_access/test/positive.tf", + toCreate: false, + err: nil, + }, + { + name: "(Case_insensitive_tests) -- Get extension from a file named as dockerFILE and without extension defined ('dockerFILE')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/case_insensitive_tests/dockerFILE", + toCreate: false, + err: nil, + }, + { + name: "(Case_insensitive_tests) -- Get extension from a file not named 'dockerfile' with extension defined as Dockerfile ('file.Dockerfile')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/case_insensitive_tests/file.Dockerfile", + toCreate: false, + err: nil, + }, + { + name: "(Case_insensitive_tests) -- Get extension from a file not named 'dockerfile' with extension defined as DOCKERfile ('file_2.DOCKERfile')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/case_insensitive_tests/file_2.DOCKERfile", + toCreate: false, + err: nil, + }, + { + name: "(Case_insensitive_tests) -- Get extension from a file named 'Dockerfile' with any extension defined ('Dockerfile.something')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/case_insensitive_tests/Dockerfile.something", + toCreate: false, + err: nil, + }, + { + name: "(Case_insensitive_tests) -- Get extension from a file named 'DOCKERfile' with any extension defined ('DOCKERfile.txt')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/case_insensitive_tests/DOCKERfile.txt", + toCreate: false, + err: nil, + }, + { + name: "(Case_insensitive_tests) -- Get extension from a file not named as Dockerfile and without extension defined ('random_name'), due to parent folder scan will identify dockerfile syntax regardless", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/case_insensitive_tests/random_name", + toCreate: false, + err: nil, + }, + { + name: "(Case_insensitive_tests) -- Get extension from a valid text file with dockerfile syntax and '.ubi8' extension ('any_name.ubi8')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/case_insensitive_tests/any_name.ubi8", + toCreate: false, + err: nil, + }, + { + name: "(Case_insensitive_tests) -- Get extension from a valid text file with dockerfile syntax and '.debian' extension ('any_name.debian')", + want: ".dockerfile", + filePath: "../../test/fixtures/dockerfile/case_insensitive_tests/any_name.debian", + toCreate: false, + err: nil, + }, } for _, test := range tests { diff --git a/test/fixtures/dockerfile/Dockerfile-example b/test/fixtures/dockerfile/Dockerfile-example index 4bd67e4f18f..d7d7935b60b 100644 --- a/test/fixtures/dockerfile/Dockerfile-example +++ b/test/fixtures/dockerfile/Dockerfile-example @@ -1,7 +1,7 @@ FROM openjdk:10-jdk VOLUME /tmp ADD http://source.file/package.file.tar.gz /temp -RUN tar -xjf /temp/package.file.tar.gz +RUN tar -xjf /temp/package.file.tar.gz ARG JAR_FILE ADD ${JAR_FILE} app.jar ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] diff --git a/test/fixtures/dockerfile/Dockerfile-multistage b/test/fixtures/dockerfile/Dockerfile-multistage new file mode 100644 index 00000000000..553875db8ba --- /dev/null +++ b/test/fixtures/dockerfile/Dockerfile-multistage @@ -0,0 +1,14 @@ +FROM ubuntu:latestnightly +VOLUME /tmp +ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] + +FROM ubuntu:latestnightly +VOLUME /tmp +ADD http://source.file/package.file.tar.gz /temp +RUN tar -xjf /temp/package.file.tar.gz +ARG JAR_FILE +ADD ${JAR_FILE} app.jar +ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] + +FROM ubuntu:latestnightly +FROM ubuntu:latestnightly diff --git a/test/fixtures/dockerfile/any_name/DOCKERfile.txt b/test/fixtures/dockerfile/any_name/DOCKERfile.txt new file mode 100644 index 00000000000..6ff5c5c2694 --- /dev/null +++ b/test/fixtures/dockerfile/any_name/DOCKERfile.txt @@ -0,0 +1,17 @@ + + + + + + + + + + + + +FROM alpine:3.19 AS builder + +COPY . . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/any_name/Dockerfile.something b/test/fixtures/dockerfile/any_name/Dockerfile.something new file mode 100644 index 00000000000..9b120bfb8ab --- /dev/null +++ b/test/fixtures/dockerfile/any_name/Dockerfile.something @@ -0,0 +1,8 @@ +ARG VERSION=1.0 +ARG BASE_IMAGE=ubuntu:22.04 + +FROM alpine:3.19 AS builder + +COPY . . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/any_name/any_name.debian b/test/fixtures/dockerfile/any_name/any_name.debian new file mode 100644 index 00000000000..9b120bfb8ab --- /dev/null +++ b/test/fixtures/dockerfile/any_name/any_name.debian @@ -0,0 +1,8 @@ +ARG VERSION=1.0 +ARG BASE_IMAGE=ubuntu:22.04 + +FROM alpine:3.19 AS builder + +COPY . . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/any_name/any_name.ubi8 b/test/fixtures/dockerfile/any_name/any_name.ubi8 new file mode 100644 index 00000000000..9b120bfb8ab --- /dev/null +++ b/test/fixtures/dockerfile/any_name/any_name.ubi8 @@ -0,0 +1,8 @@ +ARG VERSION=1.0 +ARG BASE_IMAGE=ubuntu:22.04 + +FROM alpine:3.19 AS builder + +COPY . . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/any_name/dockerFILE b/test/fixtures/dockerfile/any_name/dockerFILE new file mode 100644 index 00000000000..151a7d85c3b --- /dev/null +++ b/test/fixtures/dockerfile/any_name/dockerFILE @@ -0,0 +1,10 @@ +ARG BASE_IMAGE=ubuntu:22.04 + +# Comments between arg +ARG JAR_FILE + +FROM alpine:3.19 AS builder + +COPY . . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/any_name/file.Dockerfile b/test/fixtures/dockerfile/any_name/file.Dockerfile new file mode 100644 index 00000000000..991a057479f --- /dev/null +++ b/test/fixtures/dockerfile/any_name/file.Dockerfile @@ -0,0 +1,10 @@ +# Comments before arg +ARG BASE_IMAGE=ubuntu:22.04 + +# Comments after arg + +FROM alpine:3.19 AS builder + +COPY . . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] diff --git a/test/fixtures/dockerfile/any_name/file_2.DOCKERfile b/test/fixtures/dockerfile/any_name/file_2.DOCKERfile new file mode 100644 index 00000000000..589ac77f479 --- /dev/null +++ b/test/fixtures/dockerfile/any_name/file_2.DOCKERfile @@ -0,0 +1,8 @@ +ARG BASE_IMAGE=ubuntu:22.04 +# Comments before FROM + +FROM alpine:3.19 AS builder + +COPY .. . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] diff --git a/test/fixtures/dockerfile/any_name/random_name b/test/fixtures/dockerfile/any_name/random_name new file mode 100644 index 00000000000..ca2ebdfb132 --- /dev/null +++ b/test/fixtures/dockerfile/any_name/random_name @@ -0,0 +1,7 @@ +ARG BASE_IMAGE=ubuntu:22.04 + +FROM alpine:3.19 AS builder + +COPY . . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/case_insensitive_tests/DOCKERfile.txt b/test/fixtures/dockerfile/case_insensitive_tests/DOCKERfile.txt new file mode 100644 index 00000000000..453f5147d38 --- /dev/null +++ b/test/fixtures/dockerfile/case_insensitive_tests/DOCKERfile.txt @@ -0,0 +1,17 @@ + + + + + + + + + + + + +from alpine:3.19 as builder + +copy . . + +healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/case_insensitive_tests/Dockerfile.something b/test/fixtures/dockerfile/case_insensitive_tests/Dockerfile.something new file mode 100644 index 00000000000..104b1d85e89 --- /dev/null +++ b/test/fixtures/dockerfile/case_insensitive_tests/Dockerfile.something @@ -0,0 +1,8 @@ +arg VERSION=1.0 +arg BASE_IMAGE=ubuntu:22.04 + +from alpine:3.19 as builder + +copy . . + +healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/case_insensitive_tests/any_name.debian b/test/fixtures/dockerfile/case_insensitive_tests/any_name.debian new file mode 100644 index 00000000000..104b1d85e89 --- /dev/null +++ b/test/fixtures/dockerfile/case_insensitive_tests/any_name.debian @@ -0,0 +1,8 @@ +arg VERSION=1.0 +arg BASE_IMAGE=ubuntu:22.04 + +from alpine:3.19 as builder + +copy . . + +healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/case_insensitive_tests/any_name.ubi8 b/test/fixtures/dockerfile/case_insensitive_tests/any_name.ubi8 new file mode 100644 index 00000000000..104b1d85e89 --- /dev/null +++ b/test/fixtures/dockerfile/case_insensitive_tests/any_name.ubi8 @@ -0,0 +1,8 @@ +arg VERSION=1.0 +arg BASE_IMAGE=ubuntu:22.04 + +from alpine:3.19 as builder + +copy . . + +healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/case_insensitive_tests/dockerFILE b/test/fixtures/dockerfile/case_insensitive_tests/dockerFILE new file mode 100644 index 00000000000..a9b4c423e2c --- /dev/null +++ b/test/fixtures/dockerfile/case_insensitive_tests/dockerFILE @@ -0,0 +1,10 @@ +arg BASE_IMAGE=ubuntu:22.04 + +# Comments between arg +arg JAR_FILE + +from alpine:3.19 as builder + +copy . . + +healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/case_insensitive_tests/file.Dockerfile b/test/fixtures/dockerfile/case_insensitive_tests/file.Dockerfile new file mode 100644 index 00000000000..66464c06378 --- /dev/null +++ b/test/fixtures/dockerfile/case_insensitive_tests/file.Dockerfile @@ -0,0 +1,10 @@ +# Comments before arg +arg BASE_IMAGE=ubuntu:22.04 + +# Comments after arg + +from alpine:3.19 as builder + +copy . . + +healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ "executable" ] diff --git a/test/fixtures/dockerfile/case_insensitive_tests/file_2.DOCKERfile b/test/fixtures/dockerfile/case_insensitive_tests/file_2.DOCKERfile new file mode 100644 index 00000000000..b09209f5e55 --- /dev/null +++ b/test/fixtures/dockerfile/case_insensitive_tests/file_2.DOCKERfile @@ -0,0 +1,8 @@ +arg BASE_IMAGE=ubuntu:22.04 +# Comments before from + +from alpine:3.19 AS builder + +copy .. . + +healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ "executable" ] diff --git a/test/fixtures/dockerfile/case_insensitive_tests/random_name b/test/fixtures/dockerfile/case_insensitive_tests/random_name new file mode 100644 index 00000000000..424b06c294c --- /dev/null +++ b/test/fixtures/dockerfile/case_insensitive_tests/random_name @@ -0,0 +1,7 @@ +arg BASE_IMAGE=ubuntu:22.04 + +from alpine:3.19 as builder + +copy . . + +healthcheck --interval=30s --timeout=30s --start-period=5s --retries=3 cmd [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/should_generate_payload/base_image_reference b/test/fixtures/dockerfile/should_generate_payload/base_image_reference new file mode 100644 index 00000000000..9bd70f9f19d --- /dev/null +++ b/test/fixtures/dockerfile/should_generate_payload/base_image_reference @@ -0,0 +1,2 @@ +ARG BASE_IMAGE=alpine:latest +FROM ${BASE_IMAGE} \ No newline at end of file diff --git a/test/fixtures/dockerfile/should_generate_payload/big_host_name b/test/fixtures/dockerfile/should_generate_payload/big_host_name new file mode 100644 index 00000000000..f48911c6989 --- /dev/null +++ b/test/fixtures/dockerfile/should_generate_payload/big_host_name @@ -0,0 +1,3 @@ +FROM bighostnameusedasasample.example.com:4903/team/my-app:2.0 +RUN echo "hello" +ADD ${JAR_FILE} app.jar \ No newline at end of file diff --git a/test/fixtures/dockerfile/should_generate_payload/big_indent_from b/test/fixtures/dockerfile/should_generate_payload/big_indent_from new file mode 100644 index 00000000000..d33baab3c25 --- /dev/null +++ b/test/fixtures/dockerfile/should_generate_payload/big_indent_from @@ -0,0 +1,4 @@ + FROM alpine:latest +COPY --from=builder /src/bin /usr/local/bin/ +CMD ["/usr/local/bin/app"] +ADD ${JAR_FILE} app.jar diff --git a/test/fixtures/dockerfile/should_generate_payload/commands_indented b/test/fixtures/dockerfile/should_generate_payload/commands_indented new file mode 100644 index 00000000000..04930146ee9 --- /dev/null +++ b/test/fixtures/dockerfile/should_generate_payload/commands_indented @@ -0,0 +1,2 @@ + ARG BASE_IMAGE=alpine:latest + FROM ${BASE_IMAGE} \ No newline at end of file diff --git a/test/fixtures/dockerfile/should_generate_payload/full_sha_digest b/test/fixtures/dockerfile/should_generate_payload/full_sha_digest new file mode 100644 index 00000000000..ca2a195b08e --- /dev/null +++ b/test/fixtures/dockerfile/should_generate_payload/full_sha_digest @@ -0,0 +1,3 @@ +FROM alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a + + diff --git a/test/fixtures/dockerfile/should_generate_payload/host_ip_address b/test/fixtures/dockerfile/should_generate_payload/host_ip_address new file mode 100644 index 00000000000..431590a8d2d --- /dev/null +++ b/test/fixtures/dockerfile/should_generate_payload/host_ip_address @@ -0,0 +1 @@ +FROM 192.168.1.100:5000/team/image:v1 \ No newline at end of file diff --git a/test/fixtures/dockerfile/should_generate_payload/multiline_from_statement b/test/fixtures/dockerfile/should_generate_payload/multiline_from_statement new file mode 100644 index 00000000000..6795be694c4 --- /dev/null +++ b/test/fixtures/dockerfile/should_generate_payload/multiline_from_statement @@ -0,0 +1,9 @@ +FROM \ + alpine:3.18 \ + AS base + VOLUME /tmp + ADD http://source.file/package.file.tar.gz /temp + RUN tar -xjf /temp/package.file.tar.gz + ARG JAR_FILE + ADD ${JAR_FILE} app.jar + ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] \ No newline at end of file diff --git a/test/fixtures/dockerfile/should_generate_payload/platform_flag b/test/fixtures/dockerfile/should_generate_payload/platform_flag new file mode 100644 index 00000000000..17b821aaf81 --- /dev/null +++ b/test/fixtures/dockerfile/should_generate_payload/platform_flag @@ -0,0 +1,4 @@ +FROM --platform=linux/amd64 alpine:latest +COPY --from=builder /src/bin /usr/local/bin/ +CMD ["/usr/local/bin/app"] +ADD ${JAR_FILE} app.jar diff --git a/test/fixtures/dockerfile/should_generate_payload/platform_flag_and_alias b/test/fixtures/dockerfile/should_generate_payload/platform_flag_and_alias new file mode 100644 index 00000000000..05995cd46b0 --- /dev/null +++ b/test/fixtures/dockerfile/should_generate_payload/platform_flag_and_alias @@ -0,0 +1,5 @@ +FROM --platform=linux/amd64 ubuntu:latest AS builder +RUN apt-get update && apt-get install -y gcc +COPY . /src +RUN make /src +ADD ${JAR_FILE} app.jar \ No newline at end of file diff --git a/test/fixtures/dockerfile/should_generate_payload/platform_flag_scratch b/test/fixtures/dockerfile/should_generate_payload/platform_flag_scratch new file mode 100644 index 00000000000..e01804be298 --- /dev/null +++ b/test/fixtures/dockerfile/should_generate_payload/platform_flag_scratch @@ -0,0 +1,4 @@ +FROM --platform=linux/arm64 scratch +COPY myapp / +ENTRYPOINT ["/myapp"] +ADD ${JAR_FILE} app.jar diff --git a/test/fixtures/dockerfile/should_generate_payload/platform_with_arg_reference b/test/fixtures/dockerfile/should_generate_payload/platform_with_arg_reference new file mode 100644 index 00000000000..41c3caeb340 --- /dev/null +++ b/test/fixtures/dockerfile/should_generate_payload/platform_with_arg_reference @@ -0,0 +1,7 @@ +ARG BUILDPLATFORM +FROM --platform=$BUILDPLATFORM golang:1.21 +WORKDIR /src +COPY . . +RUN go build -o /app +CMD ["/app"] +ADD ${JAR_FILE} app.jar \ No newline at end of file diff --git a/test/fixtures/dockerfile/should_generate_payload/use_of_host b/test/fixtures/dockerfile/should_generate_payload/use_of_host new file mode 100644 index 00000000000..82ce75f9502 --- /dev/null +++ b/test/fixtures/dockerfile/should_generate_payload/use_of_host @@ -0,0 +1,3 @@ +FROM example.com:5000/team/my-app:2.0 +RUN echo "hello" +ADD ${JAR_FILE} app.jar \ No newline at end of file diff --git a/test/fixtures/dockerfile/should_generate_payload/with_excluded_comments b/test/fixtures/dockerfile/should_generate_payload/with_excluded_comments new file mode 100644 index 00000000000..9cc045be133 --- /dev/null +++ b/test/fixtures/dockerfile/should_generate_payload/with_excluded_comments @@ -0,0 +1,11 @@ +# Maintainer: from user@example.com. +# from pyhtonSample export dockerfile +# FROM (SELECT * FROM t) AS sub + +FROM openjdk:10-jdk +VOLUME /tmp +ADD http://source.file/package.file.tar.gz /temp +RUN tar -xjf /temp/package.file.tar.gz +ARG JAR_FILE +ADD ${JAR_FILE} app.jar +ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] diff --git a/test/fixtures/dockerfile/test_folder_names/docker/any_file.txt b/test/fixtures/dockerfile/test_folder_names/docker/any_file.txt new file mode 100644 index 00000000000..83acf398c06 --- /dev/null +++ b/test/fixtures/dockerfile/test_folder_names/docker/any_file.txt @@ -0,0 +1,5 @@ +FROM alpine:3.19 AS builder + +COPY . . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/test_folder_names/dockerfile/any_file.txt b/test/fixtures/dockerfile/test_folder_names/dockerfile/any_file.txt new file mode 100644 index 00000000000..83acf398c06 --- /dev/null +++ b/test/fixtures/dockerfile/test_folder_names/dockerfile/any_file.txt @@ -0,0 +1,5 @@ +FROM alpine:3.19 AS builder + +COPY . . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/test_folder_names/dockerfiles/any_file.txt b/test/fixtures/dockerfile/test_folder_names/dockerfiles/any_file.txt new file mode 100644 index 00000000000..83acf398c06 --- /dev/null +++ b/test/fixtures/dockerfile/test_folder_names/dockerfiles/any_file.txt @@ -0,0 +1,5 @@ +FROM alpine:3.19 AS builder + +COPY . . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/test_folder_names_case/Docker/any_file.txt b/test/fixtures/dockerfile/test_folder_names_case/Docker/any_file.txt new file mode 100644 index 00000000000..83acf398c06 --- /dev/null +++ b/test/fixtures/dockerfile/test_folder_names_case/Docker/any_file.txt @@ -0,0 +1,5 @@ +FROM alpine:3.19 AS builder + +COPY . . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/test_folder_names_case/Dockerfile/any_file.txt b/test/fixtures/dockerfile/test_folder_names_case/Dockerfile/any_file.txt new file mode 100644 index 00000000000..83acf398c06 --- /dev/null +++ b/test/fixtures/dockerfile/test_folder_names_case/Dockerfile/any_file.txt @@ -0,0 +1,5 @@ +FROM alpine:3.19 AS builder + +COPY . . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/dockerfile/test_folder_names_case/Dockerfiles/any_file.txt b/test/fixtures/dockerfile/test_folder_names_case/Dockerfiles/any_file.txt new file mode 100644 index 00000000000..83acf398c06 --- /dev/null +++ b/test/fixtures/dockerfile/test_folder_names_case/Dockerfiles/any_file.txt @@ -0,0 +1,5 @@ +FROM alpine:3.19 AS builder + +COPY . . + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "executable" ] \ No newline at end of file diff --git a/test/fixtures/negative_dockerfile/not_dockerfile.debian b/test/fixtures/negative_dockerfile/not_dockerfile.debian new file mode 100644 index 00000000000..d9e8b4e17a4 --- /dev/null +++ b/test/fixtures/negative_dockerfile/not_dockerfile.debian @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Hello, World!") +} \ No newline at end of file diff --git a/test/fixtures/negative_dockerfile/not_dockerfile.txt b/test/fixtures/negative_dockerfile/not_dockerfile.txt new file mode 100644 index 00000000000..847945a42b0 --- /dev/null +++ b/test/fixtures/negative_dockerfile/not_dockerfile.txt @@ -0,0 +1,3 @@ +# should not flag since name is not "dockerfile" and extension is not .dockerfile, .ubi8 or .debian (.txt) + +FROM alpine:3.19 AS builder \ No newline at end of file diff --git a/test/fixtures/negative_dockerfile/not_dockerfile.ubi8 b/test/fixtures/negative_dockerfile/not_dockerfile.ubi8 new file mode 100644 index 00000000000..3d781ec4ba6 --- /dev/null +++ b/test/fixtures/negative_dockerfile/not_dockerfile.ubi8 @@ -0,0 +1,5 @@ +public class HelloWorld { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } +} \ No newline at end of file diff --git a/test/fixtures/negative_dockerfile/should_not_generate_payload/invalid_syntax b/test/fixtures/negative_dockerfile/should_not_generate_payload/invalid_syntax new file mode 100644 index 00000000000..3558d624ac5 --- /dev/null +++ b/test/fixtures/negative_dockerfile/should_not_generate_payload/invalid_syntax @@ -0,0 +1,3 @@ +# Uppercase character at start of alias is not allowed +FROM alpine AS My!Stage + diff --git a/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_email b/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_email new file mode 100644 index 00000000000..6e97e688a7b --- /dev/null +++ b/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_email @@ -0,0 +1,6 @@ +From user@example.com +# "@" is not allowed +Subject: Test email +Date: Thu, 1 Jan 2024 00:00:00 +0000 + +This is the body of the email. diff --git a/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_email_2 b/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_email_2 new file mode 100644 index 00000000000..48712c0550c --- /dev/null +++ b/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_email_2 @@ -0,0 +1,3 @@ +From: jdoe@company.co.uk Mon Jan 1 12:00:00 2024 +Return-Path: +Received: from mail.example.com \ No newline at end of file diff --git a/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_python b/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_python new file mode 100644 index 00000000000..04d8bf2ca65 --- /dev/null +++ b/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_python @@ -0,0 +1,6 @@ +from urllib import request + +def main(): + print("Dockerfile sample") + + diff --git a/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_python_2 b/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_python_2 new file mode 100644 index 00000000000..af4bfedc0de --- /dev/null +++ b/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_python_2 @@ -0,0 +1,4 @@ +from ...grandparent import other + +def main(): + print("Dockerfile sample") \ No newline at end of file diff --git a/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_sparql b/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_sparql new file mode 100644 index 00000000000..157cddd825b --- /dev/null +++ b/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_sparql @@ -0,0 +1,5 @@ +# SPARQL query with named graph +FROM +# "<" and ">" are not allowed +SELECT ?subject ?predicate ?object +WHERE { ?subject ?predicate ?object } diff --git a/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_sql b/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_sql new file mode 100644 index 00000000000..95c0b246f22 --- /dev/null +++ b/test/fixtures/negative_dockerfile/should_not_generate_payload/negative_sql @@ -0,0 +1,6 @@ +# SQL with subquery alias +FROM (SELECT * FROM t) AS sub +# Note the double FROM +# 1 - "(" and "*" are not not alowed +# 2 - anything other than whitespaces before a FROM is not allowed + ")" is not alowed +WHERE sub.status = 'active' \ No newline at end of file