Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement uploading SARIF results for OpenScanHub #2621

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions packit_service/worker/handlers/open_scan_hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
# SPDX-License-Identifier: MIT

import logging
from typing import Union
from typing import Optional, Union

from ogr.services.github import GithubProject
from packit.config import (
JobType,
)
Expand Down Expand Up @@ -47,6 +48,7 @@ def __init__(self, **kwargs):
self.event: Union[OpenScanHubTaskFinishedEvent | OpenScanHubTaskStartedEvent] = (
self.data.to_event()
)
self._openscanhub_helper: Optional[OpenScanHubHelper] = None

@staticmethod
def get_checkers() -> tuple[type[Checker], ...]:
Expand All @@ -68,6 +70,12 @@ def get_helper(self) -> OpenScanHubHelper:
build=self.event.build,
)

@property
def openscanhub_helper(self):
if not self._openscanhub_helper:
self._openscanhub_helper = self.get_helper()
return self._openscanhub_helper

def check_scan_and_build(self):
task_id = self.data.event_dict["task_id"]
if not self.event.scan or not self.event.build:
Expand Down Expand Up @@ -114,6 +122,12 @@ def run(self) -> TaskResults:
self.event.scan.set_issues_added_url(self.event.issues_added_url)
self.event.scan.set_issues_fixed_url(self.event.issues_fixed_url)
self.event.scan.set_scan_results_url(self.event.scan_results_url)
if isinstance(self.project, GithubProject):
issues_sarif = self.openscanhub_helper.get_sarif_to_upload(
self.event.issues_added_url
)
if issues_sarif:
self.openscanhub_helper.upload_sarif(issues_sarif)
else:
state = BaseCommitStatus.neutral
description = f"Scan in OpenScanHub is finished in a {self.event.status} state."
Expand All @@ -123,7 +137,7 @@ def run(self) -> TaskResults:
else:
self.event.scan.set_status(OSHScanStatus.failed)

self.get_helper().report(
self.openscanhub_helper.report(
state=state,
description=description,
url=self.event.scan.url,
Expand Down Expand Up @@ -154,7 +168,7 @@ def run(self) -> TaskResults:
description = "Scan in OpenScanHub has started."
self.event.scan.set_status(OSHScanStatus.running)

self.get_helper().report(
self.openscanhub_helper.report(
state=state,
description=description,
url=self.event.scan.url,
Expand Down
70 changes: 69 additions & 1 deletion packit_service/worker/helpers/open_scan_hub.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

import base64
import gzip
import json
import logging
import re
Expand All @@ -10,11 +11,13 @@
from pathlib import Path
from typing import Optional

from ogr.services.github import GithubProject
from packit.config import (
JobConfig,
JobConfigTriggerType,
JobType,
)
from packit.utils import run_command

from packit_service.constants import (
OPEN_SCAN_HUB_FEATURE_DESCRIPTION,
Expand Down Expand Up @@ -271,3 +274,68 @@ def download_srpm(srpm_model: SRPMBuildModel) -> Optional[Path]:
return None

return base_srpm_path, srpm_path

@staticmethod
def get_sarif_to_upload(url: str) -> Optional[str]:
"""
Fetch the file content, convert to SARIF using csgrep,
compress and encode it so that it can be uploaded to GitHub.
"""
with tempfile.TemporaryDirectory() as directory:
path = Path(directory).joinpath(basename(url))
if not download_file(url, path):
logger.info(f"Downloading of file {url} was not successful.")
return None

# run `csgrep` to convert to SARIF format
result = run_command(["csgrep", "--mode=sarif", str(path)], fail=False, output=True)

if not result.success:
logger.info(f"Conversion to SARIF was not successful: {result.stderr}")
return None

logger.info("Conversion to SARIF was successful, about to compress and encode.")

try:
# TODO replace csmock with OpenScanHub/ [Packit] OpenScanHub where needed
# so that this name is displayed later in GitHub UI
sarif_data = result.stdout.encode("utf-8")
compressed_data = gzip.compress(sarif_data)
base64_encoded_data = base64.b64encode(compressed_data).decode("utf-8")

logger.info("SARIF file successfully compressed and encoded.")
return base64_encoded_data

except Exception as e:
logger.error(f"An error occurred during compression and encoding: {e}")
return None

def upload_sarif(self, data: str):
"""
Upload the encoded SARIF to GitHub.
"""
if self.copr_build_helper.job_build.merge_pr_in_ci:
# TODO this is not really correct and we need to discuss it
commit_sha = self.copr_build_helper.pull_request_object.merge_commit_sha
ref = f"refs/pull/{self.copr_build_helper.pr_id}/merge"
else:
commit_sha = self.copr_build_helper.db_project_event.commit_sha
ref = f"refs/pull/{self.copr_build_helper.pr_id}/head"

# there is no PyGithub support yet, API docs:
# https://docs.github.com/en/rest/code-scanning/code-scanning?
# apiVersion=2022-11-28#upload-an-analysis-as-sarif-data--parameters
payload = {
"commit_sha": commit_sha,
"ref": ref,
"sarif": data,
}
if not isinstance(self.copr_build_helper.project, GithubProject):
return

pygithub_repo = self.copr_build_helper.project.github_repo
pygithub_repo._requester.requestJsonAndCheck(
"POST",
f"{pygithub_repo.url}/code-scanning/sarifs",
input=payload,
)
2 changes: 2 additions & 0 deletions tests/unit/test_open_scan_hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ def test_handle_scan_task_finished(
"0.7.5-1.20241007054606793155.pr405.23.g829aafd6/scan-results.js?format=raw"
),
}
flexmock(OpenScanHubHelper).should_receive("get_sarif_to_upload").and_return(flexmock())
flexmock(OpenScanHubHelper).should_receive("upload_sarif")
elif scan_status == OpenScanHubTaskFinishedEvent.Status.cancel:
state = BaseCommitStatus.neutral
description = f"Scan in OpenScanHub is finished in a {scan_status} state."
Expand Down
Loading