Repository: bridgecrewio/checkov

Issue:
Error while checking Dockerfile USER set with env variable
**Describe the bug**
The **checkov** CLI return an error when analyzing a Dockerfile with USER set according to an ENV variable. 

**To Reproduce**
Steps to reproduce the behavior:
1. Get this snippet :
```Dockerfile
FROM python:alpine

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt

ENV USERNAME=app
RUN addgroup -S ${USERNAME} && adduser -s /sbin/nologin -S ${USERNAME} -G ${USERNAME} && chown -R ${USERNAME} /app
USER ${USERNAME}

COPY --chown=${USERNAME} script.py .

CMD python3 script.py
```
2. Run cli command  'checkov -f Dockerfile'
3. See error

**Expected behavior**
No error.

**Screenshots**
<img width="750" alt="" src="https://user-images.githubusercontent.com/44492274/115271564-c380b080-a13d-11eb-9c4d-cb086e3bd9fd.png">

**Desktop (please complete the following information):**
 - OS: macOS Big Sur 11.2.3
 - Checkov Version 2.0.55


Discussion:
Thanks for reporting the error, I took a look into my code and realized, how we test the docker checks is actually incorrect and that's why this error slipped through 🙈  

Resolving patch:
diff --git a/checkov/dockerfile/checks/MaintainerExists.py b/checkov/dockerfile/checks/MaintainerExists.py
--- a/checkov/dockerfile/checks/MaintainerExists.py
+++ b/checkov/dockerfile/checks/MaintainerExists.py
@@ -11,10 +11,7 @@ def __init__(self):
         super().__init__(name=name, id=id, categories=categories, supported_instructions=supported_instructions)
 
     def scan_entity_conf(self, conf):
-        for instruction, content in conf.items():
-            if instruction == "MAINTAINER":
-                return CheckResult.FAILED, content[0]
-        return CheckResult.PASSED, None
+        return CheckResult.FAILED, conf[0]
 
 
 check = MaintainerExists()
diff --git a/checkov/dockerfile/checks/RootUser.py b/checkov/dockerfile/checks/RootUser.py
--- a/checkov/dockerfile/checks/RootUser.py
+++ b/checkov/dockerfile/checks/RootUser.py
@@ -11,16 +11,11 @@ def __init__(self):
         super().__init__(name=name, id=id, categories=categories, supported_instructions=supported_instructions)
 
     def scan_entity_conf(self, conf):
-        contents = conf.get("USER")
+        last_user = conf[-1]
+        if last_user["value"] == "root":
+            return CheckResult.FAILED, last_user
 
-        if contents:
-            last_user = contents[-1]
-            if last_user["value"] == "root":
-                return CheckResult.FAILED, last_user
-
-            return CheckResult.PASSED, last_user
-
-        return CheckResult.UNKNOWN, None
+        return CheckResult.PASSED, last_user
 
 
 check = RootUser()


Test patch:
diff --git a/tests/dockerfile/checks/example_MaintainerExists/failure/Dockerfile b/tests/dockerfile/checks/example_MaintainerExists/failure/Dockerfile
new file mode 100644
--- /dev/null
+++ b/tests/dockerfile/checks/example_MaintainerExists/failure/Dockerfile
@@ -0,0 +1,3 @@
+FROM  base
+
+MAINTAINER checkov
diff --git a/tests/dockerfile/checks/example_RootUser/failure/Dockerfile b/tests/dockerfile/checks/example_RootUser/failure/Dockerfile
new file mode 100644
--- /dev/null
+++ b/tests/dockerfile/checks/example_RootUser/failure/Dockerfile
@@ -0,0 +1,3 @@
+FROM base
+
+USER root
diff --git a/tests/dockerfile/checks/example_RootUser/success/Dockerfile b/tests/dockerfile/checks/example_RootUser/success/Dockerfile
new file mode 100644
--- /dev/null
+++ b/tests/dockerfile/checks/example_RootUser/success/Dockerfile
@@ -0,0 +1,6 @@
+FROM base
+
+USER root
+COPY test.sh /test.sh
+
+USER checkov
diff --git a/tests/dockerfile/checks/test_MaintainerExists.py b/tests/dockerfile/checks/test_MaintainerExists.py
--- a/tests/dockerfile/checks/test_MaintainerExists.py
+++ b/tests/dockerfile/checks/test_MaintainerExists.py
@@ -1,34 +1,31 @@
+import os
 import unittest
 
-from dockerfile_parse import DockerfileParser
-
-from checkov.common.models.enums import CheckResult
 from checkov.dockerfile.checks.MaintainerExists import check
-from checkov.dockerfile.parser import dfp_group_by_instructions
+from checkov.dockerfile.runner import Runner
+from checkov.runner_filter import RunnerFilter
 
 
 class TestMaintainerExists(unittest.TestCase):
-    def test_failure(self):
-        dfp = DockerfileParser()
+    def test(self):
+        runner = Runner()
+        current_dir = os.path.dirname(os.path.realpath(__file__))
 
-        dfp.content = """
-        From  base
-        MAINTAINER checkov
-        """
+        test_files_dir = current_dir + "/example_MaintainerExists"
+        report = runner.run(root_folder=test_files_dir, runner_filter=RunnerFilter(checks=[check.id]))
+        summary = report.get_summary()
 
-        conf = dfp_group_by_instructions(dfp)[0]
-        scan_result = check.scan_entity_conf(conf)
+        failing_resources = {"/failure/Dockerfile.MAINTAINER"}
 
-        self.assertEqual((CheckResult.FAILED), scan_result[0])
+        failed_check_resources = set([c.resource for c in report.failed_checks])
 
-    def test_success(self):
-        dfp = DockerfileParser()
+        self.assertEqual(summary["passed"], 0)
+        self.assertEqual(summary["failed"], 1)
+        self.assertEqual(summary["skipped"], 0)
+        self.assertEqual(summary["parsing_errors"], 0)
 
-        dfp.content = """\
-        From  base
-        """
+        self.assertEqual(failing_resources, failed_check_resources)
 
-        conf = dfp_group_by_instructions(dfp)[0]
-        scan_result = check.scan_entity_conf(conf)
 
-        self.assertEqual((CheckResult.PASSED, None), scan_result)
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tests/dockerfile/checks/test_RootUser.py b/tests/dockerfile/checks/test_RootUser.py
--- a/tests/dockerfile/checks/test_RootUser.py
+++ b/tests/dockerfile/checks/test_RootUser.py
@@ -1,39 +1,34 @@
+import os
 import unittest
 
-from dockerfile_parse import DockerfileParser
-
-from checkov.common.models.enums import CheckResult
 from checkov.dockerfile.checks.RootUser import check
-from checkov.dockerfile.parser import dfp_group_by_instructions
+from checkov.dockerfile.runner import Runner
+from checkov.runner_filter import RunnerFilter
 
 
-class TestUserExists(unittest.TestCase):
-    def test_failure(self):
-        dfp = DockerfileParser()
+class TestRootUser(unittest.TestCase):
+    def test(self):
+        runner = Runner()
+        current_dir = os.path.dirname(os.path.realpath(__file__))
 
-        dfp.content = """
-        FROM base
-        USER root
-        """
+        test_files_dir = current_dir + "/example_RootUser"
+        report = runner.run(root_folder=test_files_dir, runner_filter=RunnerFilter(checks=[check.id]))
+        summary = report.get_summary()
 
-        conf = dfp_group_by_instructions(dfp)[0]
-        scan_result = check.scan_entity_conf(conf)
+        passing_resources = {"/success/Dockerfile.USER"}
+        failing_resources = {"/failure/Dockerfile.USER"}
 
-        self.assertEqual(CheckResult.FAILED, scan_result[0])
-        self.assertEqual("root", scan_result[1]["value"])
+        passed_check_resources = set([c.resource for c in report.passed_checks])
+        failed_check_resources = set([c.resource for c in report.failed_checks])
 
-    def test_success(self):
-        dfp = DockerfileParser()
+        self.assertEqual(summary["passed"], 1)
+        self.assertEqual(summary["failed"], 1)
+        self.assertEqual(summary["skipped"], 0)
+        self.assertEqual(summary["parsing_errors"], 0)
 
-        dfp.content = """
-        FROM base
-        USER root
-        COPY test.sh /test.sh
-        USER checkov
-        """
+        self.assertEqual(passing_resources, passed_check_resources)
+        self.assertEqual(failing_resources, failed_check_resources)
 
-        conf = dfp_group_by_instructions(dfp)[0]
-        scan_result = check.scan_entity_conf(conf)
 
-        self.assertEqual(CheckResult.PASSED, scan_result[0])
-        self.assertEqual("checkov", scan_result[1]["value"])
+if __name__ == "__main__":
+    unittest.main()