Skip to content

Commit 07dbd0a

Browse files
committed
feat: add Dockerfile analysis for build command detection
Changes: -Function find_dockerfile_from_job: handles finding Dockerfile inside workflow in 2 cases of workflow jobs: -run and -uses. -Simple DockerNode class, so far it stores mainly the dockerfile path retrieved from workflow -Parsing Dockerfile using dockerfile-parse and RUN instruction commands using bashparser.py -Parsing and storing build commands found in Dockerfiles Signed-off-by: Achraf Maghous <achraf.maghous@oracle.com>
1 parent 6a712af commit 07dbd0a

File tree

5 files changed

+374
-13
lines changed

5 files changed

+374
-13
lines changed

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ dependencies = [
3838
"problog >= 2.2.6,<3.0.0",
3939
"cryptography >=44.0.0,<45.0.0",
4040
"semgrep == 1.113.0",
41+
"dockerfile-parse >= 2.0.1"
4142
]
4243
keywords = []
4344
# https://pypi.org/classifiers/
@@ -79,6 +80,7 @@ dev = [
7980
"pylint >=3.0.3,<4.0.0",
8081
"cyclonedx-bom >=4.0.0,<5.0.0",
8182
"types-beautifulsoup4 >= 4.12.0,<5.0.0",
83+
"types-dockerfile-parse >= 2.0.0"
8284
]
8385
docs = [
8486
"sphinx >=8.0.0,<9.0.0",

src/macaron/slsa_analyzer/checks/build_script_check.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,15 @@ def run_check(self, ctx: AnalyzeContext) -> CheckResultData:
107107
# we parse bash scripts that are reachable through CI only.
108108
result_tables: list[CheckFacts] = []
109109
ci_services = ctx.dynamic_data["ci_services"]
110+
110111
for tool in build_tools:
111112
for ci_info in ci_services:
112113
ci_service: BaseCIService = ci_info["service"]
113114
# Checking if a CI service is discovered for this repo.
114115
if isinstance(ci_service, NoneCIService):
115116
continue
117+
118+
# Process regular workflow build commands
116119
try:
117120
for build_command in ci_service.get_build_tool_commands(
118121
callgraph=ci_info["callgraph"], build_tool=tool
@@ -148,6 +151,38 @@ def run_check(self, ctx: AnalyzeContext) -> CheckResultData:
148151
except CallGraphError as error:
149152
logger.debug(error)
150153

154+
# Process Docker build commands if the CI service has the method
155+
if hasattr(ci_service, "get_docker_build_commands"):
156+
try:
157+
for build_command in ci_service.get_docker_build_commands(
158+
callgraph=ci_info["callgraph"], build_tool=tool
159+
):
160+
logger.debug("Processing Docker build command %s", build_command)
161+
# For Dockerfile, link to the Dockerfile itself
162+
relative_path = os.path.relpath(build_command["ci_path"], ctx.component.repository.fs_path)
163+
trigger_link = ci_service.api_client.get_file_link(
164+
ctx.component.repository.full_name,
165+
ctx.component.repository.commit_sha,
166+
relative_path,
167+
)
168+
logger.debug("Trigger link for Docker build command: %s", trigger_link)
169+
170+
result_tables.append(
171+
BuildScriptFacts(
172+
build_tool_name=tool.name,
173+
ci_service_name=ci_service.name,
174+
build_trigger=trigger_link,
175+
language=build_command["language"],
176+
language_distributions=None,
177+
language_versions=None,
178+
language_url=None,
179+
build_tool_command=tool.serialize_to_json(build_command["command"]),
180+
confidence=Confidence.HIGH,
181+
)
182+
)
183+
except CallGraphError as error:
184+
logger.debug(error)
185+
151186
return CheckResultData(result_tables=result_tables, result_type=CheckResultType.PASSED)
152187

153188

src/macaron/slsa_analyzer/ci_service/base_ci_service.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,24 @@ def get_third_party_configurations(self) -> list[str]:
280280
"""
281281
return []
282282

283+
def get_docker_build_commands(self, callgraph: CallGraph, build_tool: BaseBuildTool) -> Iterable[BuildToolCommand]:
284+
"""
285+
Traverse the callgraph and find all the reachable Docker build commands.
286+
287+
Parameters
288+
----------
289+
callgraph: CallGraph
290+
The callgraph reachable from the CI workflows.
291+
292+
Yields
293+
------
294+
BuildToolCommand
295+
The object that contains the Docker build command as well useful contextual information.
296+
"""
297+
# By default we assume that there is no Docker build command available for a CI service.
298+
# Each CI service should override this method if a Docker build command is generated for it.
299+
raise CallGraphError("There is no Docker build command for this CI service.")
300+
283301

284302
class NoneCIService(BaseCIService):
285303
"""This class can be used to initialize an empty CI service."""

0 commit comments

Comments
 (0)