From 6c85f85021005c8d89eca1762b9ad0e8c001151f Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Tue, 5 Dec 2023 17:03:00 +0200 Subject: [PATCH 01/19] feat(OX_4913) add azure template --- oxeye-scan/azure.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 oxeye-scan/azure.yaml diff --git a/oxeye-scan/azure.yaml b/oxeye-scan/azure.yaml new file mode 100644 index 0000000..4ad87f5 --- /dev/null +++ b/oxeye-scan/azure.yaml @@ -0,0 +1,4 @@ +steps: +- bash: | + echo "Hello from a different Azure DevOps Organization!" + displayName: 'Hello from Beta' \ No newline at end of file From 464cb721439b899592865e644fa52af2f715c5ba Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Wed, 6 Dec 2023 09:33:55 +0200 Subject: [PATCH 02/19] add scm_scan --- images/Dockerfile.oxeye-scan | 2 +- images/entrypoint-oxeye-scan.sh | 8 +- images/scm_scan.py | 642 ++++++++++++++++++++++++++++++++ 3 files changed, 647 insertions(+), 5 deletions(-) create mode 100644 images/scm_scan.py diff --git a/images/Dockerfile.oxeye-scan b/images/Dockerfile.oxeye-scan index 127dbf4..8685364 100644 --- a/images/Dockerfile.oxeye-scan +++ b/images/Dockerfile.oxeye-scan @@ -37,5 +37,5 @@ RUN rm -rf /var/cache/apk/* COPY --from=oxctl_builder /app/oxctl /app/ COPY entrypoint-oxeye-scan.sh /entrypoint.sh - +COPY scm_scan.py /app/scm_scan.py ENTRYPOINT ["/entrypoint.sh"] diff --git a/images/entrypoint-oxeye-scan.sh b/images/entrypoint-oxeye-scan.sh index e7ad8e8..b472f29 100755 --- a/images/entrypoint-oxeye-scan.sh +++ b/images/entrypoint-oxeye-scan.sh @@ -44,10 +44,10 @@ else fi # Download Script -curl -s -o /app/scm_scan.py --location "https://${host}/api/scm/script?provider=${cicd_tool}" \ ---header "Content-Type: application/json" \ ---header "Accept: application/octet-stream" \ ---header "Authorization: Bearer ${bearerToken}" +# curl -s -o /app/scm_scan.py --location "https://${host}/api/scm/script?provider=${cicd_tool}" \ +# --header "Content-Type: application/json" \ +# --header "Accept: application/octet-stream" \ +# --header "Authorization: Bearer ${bearerToken}" # RUN SCM Scan Script python /app/scm_scan.py --host $host --repo-token $token --client-id $client_id --secret $secret --workspace-id $workspace_id --release $release --excludes "$excludes" diff --git a/images/scm_scan.py b/images/scm_scan.py new file mode 100644 index 0000000..87346b8 --- /dev/null +++ b/images/scm_scan.py @@ -0,0 +1,642 @@ +import argparse +import json +import logging +import os +import re +import shlex +import stat +import subprocess +import sys +import uuid +from enum import Enum +from typing import Any, Dict, Optional, TypedDict + +import pydantic +import requests +from git import Repo +from pydantic import BaseModel + +logging.basicConfig( + stream=sys.stdout, + level=logging.INFO, + format="%(asctime)s %(message)s %(levelname)s %(pathname)s:%(lineno)s %(funcName)s", + datefmt="%m/%d/%Y %I:%M:%S %p", +) + +logger = logging.getLogger(__name__) + + +class LightzPreSignedURLs(BaseModel): + script: str + config: str + + +class Provider(Enum): + BitBucket = "bitbucket" + GitHub = "github" + GitLab = "gitlab" + Jenkins = "jenkins" + + +REMOTE_URL_REGEX = re.compile( + r"(git@|https://)(?P.*)(:|/)(?P.*)/(?P.*?)(.git|$)" +) + +# Setup Parameters +class RepositoryParameters(TypedDict): + provider: str + server_url: str + organization: str + name: str + description: str + license: str + branch: str + languages: str + run_id: str + workdir: str + arch: str + + +# GENERAL +SCM_LOG_LOCATION = "/tmp/scm.log" +LIGHTZ_AIO_LOG_LOCATION = "/tmp/lightz-aio.log" + +# Syft Parameters +SYFT_VERSION = "v0.70.0" +SYFT_INSTALL_FILE_URL = ( + f"https://raw.githubusercontent.com/anchore/syft/main/install.sh" +) +SYFT_INSTALL_FILE_LOCAL = f"/tmp/install_syft.sh" +SYFT_INSTALL_FILE_CMD = f"{SYFT_INSTALL_FILE_LOCAL} -b /usr/local/bin {SYFT_VERSION}" +SYFT_SBOM_FILE = f"/app/oxeye.sbom" + +# Lightz Parameters +LIGHTZ_VERSION = "v1.0.3" +LIGHTZ_BINARY_LOCAL = f"/app/lightz-aio" +LIGHTZ_CONFIG_LOCAL = f"/app/lightz-aio.yaml" +LIGHTZ_SARIF_FILE = f"/app/oxeye.sarif" + + +def parse_arguments() -> argparse.Namespace: + argument_parser = argparse.ArgumentParser() + + argument_parser.add_argument("--host", required=True, type=str) + argument_parser.add_argument("--repo-token", required=True, type=str) + argument_parser.add_argument("--client-id", required=True, type=str) + argument_parser.add_argument("--secret", required=True, type=str) + argument_parser.add_argument("--workspace-id", required=True, type=str) + argument_parser.add_argument("--release", required=True, type=str) + argument_parser.add_argument("--excludes", required=False, type=str) + + return argument_parser.parse_args() + + +def get_repository_data( + url: str, headers: Optional[Dict[str, str]] = None +) -> Optional[Dict[Any, Any]]: + try: + response = requests.get(url, headers=headers) + response.raise_for_status() + except (requests.exceptions.HTTPError, requests.exceptions.RequestException) as err: + logger.exception(f"Failed getting repository data {err=} {url=} {headers=}") + return None + + return response.json() + + +def setup_github( + github_api_url: str, github_repository: str, repo_token: str +) -> Optional[RepositoryParameters]: + provider = Provider.GitHub.value + headers = {"Authorization": f"token {repo_token}"} + repository_data_url = f"{github_api_url}/repos/{github_repository}" + if not ( + repository_data := get_repository_data(url=repository_data_url, headers=headers) + ): + return None + if not ( + languages := get_repository_data( + url=f"{repository_data_url}/languages", headers=headers + ) + ): + return None + if not (server_url := os.getenv("GITHUB_SERVER_URL")): + return None + if not (organization := os.getenv("GITHUB_REPOSITORY_OWNER")): + return None + if not (repo := os.getenv("GITHUB_REPOSITORY")): + return None + if not (branch := os.getenv("GITHUB_REF_NAME")): + return None + if not (workdir := os.getenv("GITHUB_WORKSPACE")): + return None + if not (arch := os.getenv("RUNNER_ARCH")): + return None + + description = repository_data.get("description", "None") + license = repository_data.get("license", "None") + run_id = str(uuid.uuid4()) + + return RepositoryParameters( + provider=provider, + server_url=server_url, + organization=organization, + name=repo.removeprefix(f"{organization}/"), + description=description if description else "None", + license=license if license else "None", + branch=branch, + languages=json.dumps(languages), + run_id=run_id, + workdir=workdir, + arch=arch, + ) + + +def setup_gitlab( + gitlab_api_url: str, gitlab_project_id: str, repo_token: str +) -> Optional[RepositoryParameters]: + provider = Provider.GitLab.value + if not ( + repository_data := get_repository_data( + url=f"{gitlab_api_url}/projects/{gitlab_project_id}?private_token={repo_token}" + ) + ): + return None + if not ( + languages := get_repository_data( + url=f"{gitlab_api_url}/projects/{gitlab_project_id}/languages?private_token={repo_token}" + ) + ): + return None + if not (server_url := os.getenv("CI_SERVER_URL")): + return None + if not (organization := os.getenv("CI_PROJECT_NAMESPACE")): + return None + if not (repo := os.getenv("CI_PROJECT_NAME")): + return None + if ci_commit_branch := os.getenv("CI_COMMIT_BRANCH"): + branch = ci_commit_branch + elif ci_commit_ref_name := os.getenv("CI_COMMIT_REF_NAME"): + branch = ci_commit_ref_name + else: + return None + if not (workdir := os.getenv("CI_PROJECT_DIR")): + return None + if not (arch := os.getenv("CI_RUNNER_EXECUTABLE_ARCH")): + return None + description = repository_data.get("description", "None") + license = repository_data.get("license", "None") + run_id = str(uuid.uuid4()) + + return RepositoryParameters( + provider=provider, + server_url=server_url, + organization=organization, + name=repo.removeprefix(f"{organization}/"), + description=description if description else "None", + license=license if license else "None", + branch=branch, + languages=json.dumps(languages), + run_id=run_id, + workdir=workdir, + arch=arch, + ) + + +def setup_jenkins() -> Optional[RepositoryParameters]: + provider = Provider.Jenkins.value + if not (workdir := os.getenv("WORKSPACE")): + logger.error(f"Could not get workspace") + return None + if (repo := Repo(workdir)).bare: + logger.error(f"Could not get repo") + return None + if not (origin := repo.remotes["origin"]): + logger.error(f"Could not get origin") + return None + if len(urls := list(origin.urls)) < 1: + logger.error(f"Could not get origin urls") + return None + match_url = REMOTE_URL_REGEX.search(urls[0]) + if not match_url: + logger.error(f"Could not parse origin url") + return None + server_url = f"https://{match_url.group('server_url')}" + organization = match_url.group("organization") + name = match_url.group("name") + run_id = str(uuid.uuid4()) + + return RepositoryParameters( + provider=provider, + server_url=server_url, + organization=organization, + name=name, + description="None", + license="None", + branch="branch", + languages="{}", + run_id=run_id, + workdir=workdir, + arch="", + ) + + +def setup(repo_token: str) -> Optional[RepositoryParameters]: + if (github_api_url := os.getenv("GITHUB_API_URL")) and ( + github_repository := os.getenv("GITHUB_REPOSITORY") + ): + return setup_github( + github_api_url=github_api_url, + github_repository=github_repository, + repo_token=repo_token, + ) + elif (gitlab_api_url := os.getenv("CI_API_V4_URL")) and ( + gitlab_project_id := os.getenv("CI_PROJECT_ID") + ): + return setup_gitlab( + gitlab_api_url=gitlab_api_url, + gitlab_project_id=gitlab_project_id, + repo_token=repo_token, + ) + elif "JENKINS_URL" in os.environ: + return setup_jenkins() + + logger.error(f"Error - could not determine environment. aborting...") + return None + + +def get_authorization_token(host: str, client_id: str, secret: str) -> str: + auth_url = f"https://{host}/api/auth/api-token" + + try: + response = requests.post( + auth_url, json={"clientId": client_id, "secret": secret} + ) + response.raise_for_status() + except (requests.exceptions.HTTPError, requests.exceptions.RequestException) as err: + logger.exception(f"Failed getting authorization token {err=} {auth_url=}") + raise + + return str(response.text) + + +def get_lightz_presigned_urls( + host: str, auth_token: str, arch: str, version: str +) -> LightzPreSignedURLs: + url = f"https://{host}/api/scm/lightz?arch={arch}&version={version}" + + headers = {"Authorization": f"Bearer {auth_token}"} + try: + response = requests.get(url, headers=headers) + response.raise_for_status() + return LightzPreSignedURLs.model_validate(response.json()) + except (requests.exceptions.HTTPError, requests.exceptions.RequestException) as err: + logger.exception(f"Failed getting pre-signed urls {err=} {url=}") + raise + except pydantic.ValidationError as err: + logger.exception(f"Failed parsing pre-signed urls response{err=} {url=}") + raise + + +def download_file(url: str, local_filename: str) -> None: + try: + response = requests.get(url, stream=True) + response.raise_for_status() + except (requests.exceptions.HTTPError, requests.exceptions.RequestException) as err: + logger.exception(f"Failed downloading file {err=} {url=}") + raise + + try: + with open(local_filename, "wb") as file: + try: + for chunk in response.iter_content(chunk_size=8192): + file.write(chunk) + except (IOError, OSError): + logger.exception(f"Error writing to file {local_filename}") + raise + except (FileNotFoundError, PermissionError, OSError): + logger.exception(f"Error opening file {local_filename}") + raise + + try: + os.chmod(local_filename, stat.S_IXUSR) + except: + logger.exception(f"Could not chmod {local_filename}") + raise + + +def run_shell_cmd_with_log( + cmd: str, err_msg: str, log_location: str = SCM_LOG_LOCATION +) -> None: + try: + with open(log_location, "a") as log_file: + try: + subprocess.run( + cmd, + shell=True, + check=True, + stderr=log_file, + stdout=log_file, + ) + + except (subprocess.SubprocessError, subprocess.CalledProcessError): + logger.exception(f"{err_msg} cmd={cmd}") + raise + except (FileNotFoundError, PermissionError, OSError): + logger.exception(f"Error opening file {log_location}") + raise + + +def run_shell_cmd_with_result( + cmd: str, err_msg: str, log_location: str = SCM_LOG_LOCATION +) -> str: + try: + with open(log_location, "a") as log_file: + try: + result = subprocess.run( + cmd, + shell=True, + check=True, + stderr=log_file, + stdout=subprocess.PIPE, + ) + + except (subprocess.SubprocessError, subprocess.CalledProcessError): + logger.exception(f"{err_msg} cmd={cmd}") + raise + except (FileNotFoundError, PermissionError, OSError): + logger.exception(f"Error opening file {log_location}") + raise + + return result.stdout.decode() + + +def release_start( + host: str, + client_id: str, + secret: str, + workspace_id: str, + release: str, +) -> None: + oxctl_cmd = ( + f"/app/oxctl release start " + f"--host {host} " + f"--client-id {client_id} " + f"--secret {secret} " + f"-a {workspace_id} " + f"-t {release} " + ) + run_shell_cmd_with_log(oxctl_cmd, "Failed Start Release") + + +def upload_result( + upload_type: str, + host: str, + client_id: str, + secret: str, + workspace_id: str, + release: str, + repo_params: RepositoryParameters, + file: str, +) -> None: + oxctl_cmd = ( + f"/app/oxctl upload {upload_type} " + f"--host {host} " + f"--client-id {client_id} " + f"--secret {secret} " + f"--workspace {workspace_id} " + f"--repository-provider '{repo_params['provider']}' " + f"--repository-server-url '{repo_params['server_url']}' " + f"--repository-organization '{repo_params['organization']}' " + f"--repository-name '{repo_params['name']}' " + f"--repository-description '{repo_params['description']}' " + f"--repository-license '{repo_params['license']}' " + f"--repository-branch '{repo_params['branch']}' " + f"--repository-languages '{repo_params['languages']}' " + f"--repository-run-id '{repo_params['run_id']}' " + f"--repository-release-tag '{release}' " + f"--file {file} " + ) + run_shell_cmd_with_log(oxctl_cmd, "Failed Uploading Result") + + +def upload_file( + host: str, + client_id: str, + secret: str, + workspace_id: str, + release: str, + repo_params: RepositoryParameters, + entity_type: str, + upload_file: str, +) -> None: + oxctl_cmd = ( + f"/app/oxctl uploadURL " + f"--host {host} " + f"--client-id {client_id} " + f"--secret {secret} " + f"--workspace {workspace_id} " + f"--repository-provider '{repo_params['provider']}' " + f"--repository-server-url '{repo_params['server_url']}' " + f"--repository-organization '{repo_params['organization']}' " + f"--repository-name '{repo_params['name']}' " + f"--repository-description '{repo_params['description']}' " + f"--repository-license '{repo_params['license']}' " + f"--repository-branch '{repo_params['branch']}' " + f"--repository-languages '{repo_params['languages']}' " + f"--repository-run-id '{repo_params['run_id']}' " + f"--repository-release-tag '{release}' " + f"--entity-type '{entity_type}' " + ) + pre_signed_url = run_shell_cmd_with_result( + oxctl_cmd, + "Failed Uploading Result", + ) + + try: + with open(upload_file, "rb") as file: + try: + requests.put(pre_signed_url, data=file) + except ( + requests.exceptions.HTTPError, + requests.exceptions.RequestException, + ) as err: + logger.exception( + f"Failed to upload file {pre_signed_url=} {err=}", + ) + except (FileNotFoundError, PermissionError, OSError): + logger.exception(f"Error opening upload_file: {upload_file}") + raise + + +def download_syft() -> None: + download_file(SYFT_INSTALL_FILE_URL, SYFT_INSTALL_FILE_LOCAL) + run_shell_cmd_with_log( + SYFT_INSTALL_FILE_CMD, + "Failed installing Package Scanner", + ) + + +def execute_syft(repo_params: RepositoryParameters, output_file: str) -> None: + syft_cmd = f"cd {repo_params['workdir']};" f"syft . -q -o spdx-json={output_file} " + run_shell_cmd_with_log(syft_cmd, "Failed Running Package Scanner") + + +def run_syft( + host: str, + client_id: str, + secret: str, + workspace_id: str, + release: str, + repo_params: RepositoryParameters, +) -> None: + download_syft() + execute_syft( + repo_params=repo_params, + output_file=SYFT_SBOM_FILE, + ) + upload_file( + host=host, + client_id=client_id, + secret=secret, + workspace_id=workspace_id, + release=release, + repo_params=repo_params, + entity_type="CodeScanSyftSPDX", + upload_file=SYFT_SBOM_FILE, + ) + + +def download_lightz( + host: str, client_id: str, secret: str, repo_params: RepositoryParameters +) -> None: + auth_token = get_authorization_token(host, client_id, secret) + + pre_signed_urls: LightzPreSignedURLs = get_lightz_presigned_urls( + host=host, + auth_token=auth_token, + arch=repo_params["arch"], + version=LIGHTZ_VERSION, + ) + + download_file(pre_signed_urls.script, LIGHTZ_BINARY_LOCAL) + download_file(pre_signed_urls.config, LIGHTZ_CONFIG_LOCAL) + + +def execute_lightz( + repo_params: RepositoryParameters, + output_file: str, + excludes: str, +) -> None: + logger.info(f"cwd = {os.getcwd}") + lightz_cmd = ( + f"cd {repo_params['workdir']};" + f"{LIGHTZ_BINARY_LOCAL} " + f"--config {LIGHTZ_CONFIG_LOCAL} " + f"--output {output_file} " + f"{' '.join(['--exclude ' + shlex.quote(exclude) for exclude in shlex.split(excludes)])} " + f"--target . " + ) + run_shell_cmd_with_log( + cmd=lightz_cmd, + err_msg="Failed Running Source Code Scan", + log_location=LIGHTZ_AIO_LOG_LOCATION, + ) + + +def run_lightz( + host: str, + client_id: str, + secret: str, + workspace_id: str, + release: str, + repo_params: RepositoryParameters, + excludes: str, +) -> None: + download_lightz( + host=host, client_id=client_id, secret=secret, repo_params=repo_params + ) + + execute_lightz( + repo_params=repo_params, + output_file=LIGHTZ_SARIF_FILE, + excludes=excludes, + ) + + upload_file( + host=host, + client_id=client_id, + secret=secret, + workspace_id=workspace_id, + release=release, + repo_params=repo_params, + entity_type="CodeScanSemgrep", + upload_file=LIGHTZ_SARIF_FILE, + ) + + +def main() -> None: + arguments: argparse.Namespace = parse_arguments() + repo_params: Optional[RepositoryParameters] = setup(repo_token=arguments.repo_token) + if not repo_params: + sys.exit(1) + try: + release_start( + host=arguments.host, + client_id=arguments.client_id, + secret=arguments.secret, + workspace_id=arguments.workspace_id, + release=arguments.release, + ) + except: + sys.exit(2) + try: + run_syft( + host=arguments.host, + client_id=arguments.client_id, + secret=arguments.secret, + workspace_id=arguments.workspace_id, + release=arguments.release, + repo_params=repo_params, + ) + except: + sys.exit(3) + try: + run_lightz( + host=arguments.host, + client_id=arguments.client_id, + secret=arguments.secret, + workspace_id=arguments.workspace_id, + release=arguments.release, + repo_params=repo_params, + excludes=arguments.excludes, + ) + except: + # Upload Lightz AIO Log + upload_file( + host=arguments.host, + client_id=arguments.client_id, + secret=arguments.secret, + workspace_id=arguments.workspace_id, + release=arguments.release, + repo_params=repo_params, + entity_type="LightzAioLogs", + upload_file=LIGHTZ_AIO_LOG_LOCATION, + ) + sys.exit(4) + + # Upload Lightz AIO Log + upload_file( + host=arguments.host, + client_id=arguments.client_id, + secret=arguments.secret, + workspace_id=arguments.workspace_id, + release=arguments.release, + repo_params=repo_params, + entity_type="LightzAioLogs", + upload_file=LIGHTZ_AIO_LOG_LOCATION, + ) + + +if __name__ == "__main__": + main() From 355031f9b172cdcdc7cd88558e67e2a94a56b9a5 Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Wed, 6 Dec 2023 12:06:32 +0200 Subject: [PATCH 03/19] add setup_azure --- images/entrypoint-oxeye-scan.sh | 4 +- images/scm_scan.py | 91 +++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/images/entrypoint-oxeye-scan.sh b/images/entrypoint-oxeye-scan.sh index b472f29..1c5f0b4 100755 --- a/images/entrypoint-oxeye-scan.sh +++ b/images/entrypoint-oxeye-scan.sh @@ -34,9 +34,9 @@ elif [ -n "$CI_API_V4_URL" ]; then cicd_tool="gitlab" elif [ -n "$JENKINS_URL" ]; then cicd_tool="jenkins" -elif [ -n "$BUILD_REPOSITORY_URI"]; then +elif [ -n "$BUILD_REPOSITORY_LOCALPATH"]; then cicd_tool="azure" -elif [ -n "$BITBUCKET_PROJECT_UUID"]; then +elif [ -n "$BITBUCKET_CLONE_DIR"]; then cicd_tool="bitbucket" else echo "Error - could not determine environment. aborting..." diff --git a/images/scm_scan.py b/images/scm_scan.py index 87346b8..682e5e1 100644 --- a/images/scm_scan.py +++ b/images/scm_scan.py @@ -36,12 +36,17 @@ class Provider(Enum): GitHub = "github" GitLab = "gitlab" Jenkins = "jenkins" + Azure ="azure" REMOTE_URL_REGEX = re.compile( r"(git@|https://)(?P.*)(:|/)(?P.*)/(?P.*?)(.git|$)" ) +# git@github.com:ox-eye/github-actions.git +# https://github.com/ox-eye/oxeye-rulez +# https://ox-eye@dev.azure.com/ox-eye/cloud-native-lab/_git/cloud-native-lab + # Setup Parameters class RepositoryParameters(TypedDict): provider: str @@ -241,6 +246,88 @@ def setup_jenkins() -> Optional[RepositoryParameters]: ) +def setup_azure() -> Optional[RepositoryParameters]: + provider = Provider.Azure.value + if not (workdir := os.getenv("BUILD_REPOSITORY_LOCALPATH")): + logger.error(f"Could not get repository localpath") + return None + if (repo := Repo(workdir)).bare: + logger.error(f"Could not get repo") + return None + if not (origin := repo.remotes["origin"]): + logger.error(f"Could not get origin") + return None + if len(urls := list(origin.urls)) < 1: + logger.error(f"Could not get origin urls") + return None + match_url = REMOTE_URL_REGEX.search(urls[0]) + if not match_url: + logger.error(f"Could not parse origin url") + return None + server_url = f"https://{match_url.group('server_url')}" + organization = match_url.group("organization") + name = match_url.group("name") + run_id = str(uuid.uuid4()) + + logger.info(f"{urls[0]=} {server_url=} {organization=} {name=}") + sys.exit(0) + + return RepositoryParameters( + provider=provider, + server_url=server_url, + organization=organization, + name=name, + description="None", + license="None", + branch="branch", + languages="{}", + run_id=run_id, + workdir=workdir, + arch="", + ) + + +def setup_bitbucket() -> Optional[RepositoryParameters]: + provider = Provider.Azure.value + if not (workdir := os.getenv("BITBUCKET_CLONE_DIR")): + logger.error(f"Could not get repository localpath") + return None + if (repo := Repo(workdir)).bare: + logger.error(f"Could not get repo") + return None + if not (origin := repo.remotes["origin"]): + logger.error(f"Could not get origin") + return None + if len(urls := list(origin.urls)) < 1: + logger.error(f"Could not get origin urls") + return None + match_url = REMOTE_URL_REGEX.search(urls[0]) + if not match_url: + logger.error(f"Could not parse origin url") + return None + server_url = f"https://{match_url.group('server_url')}" + organization = match_url.group("organization") + name = match_url.group("name") + run_id = str(uuid.uuid4()) + + logger.info(f"{urls[0]=} {server_url=} {organization=} {name=}") + sys.exit(0) + + return RepositoryParameters( + provider=provider, + server_url=server_url, + organization=organization, + name=name, + description="None", + license="None", + branch="branch", + languages="{}", + run_id=run_id, + workdir=workdir, + arch="", + ) + + def setup(repo_token: str) -> Optional[RepositoryParameters]: if (github_api_url := os.getenv("GITHUB_API_URL")) and ( github_repository := os.getenv("GITHUB_REPOSITORY") @@ -260,6 +347,10 @@ def setup(repo_token: str) -> Optional[RepositoryParameters]: ) elif "JENKINS_URL" in os.environ: return setup_jenkins() + elif "BUILD_REPOSITORY_LOCALPATH" in os.environ: + return setup_azure() + elif "BITBUCKET_CLONE_DIR" in os.environ: + return setup_bitbucket() logger.error(f"Error - could not determine environment. aborting...") return None From c9d7c7c85e7a075734d494eb3294fef8395b599f Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Wed, 6 Dec 2023 12:18:37 +0200 Subject: [PATCH 04/19] config git safe directory --- images/entrypoint-oxeye-scan.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/images/entrypoint-oxeye-scan.sh b/images/entrypoint-oxeye-scan.sh index 1c5f0b4..53acef4 100755 --- a/images/entrypoint-oxeye-scan.sh +++ b/images/entrypoint-oxeye-scan.sh @@ -35,6 +35,7 @@ elif [ -n "$CI_API_V4_URL" ]; then elif [ -n "$JENKINS_URL" ]; then cicd_tool="jenkins" elif [ -n "$BUILD_REPOSITORY_LOCALPATH"]; then + git config --global --add safe.directory "*" cicd_tool="azure" elif [ -n "$BITBUCKET_CLONE_DIR"]; then cicd_tool="bitbucket" From 78b52f510dd46c0e8f941d00da9de0e0a969c99d Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Wed, 6 Dec 2023 15:42:25 +0200 Subject: [PATCH 05/19] check get remote --- images/entrypoint-oxeye-scan.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/images/entrypoint-oxeye-scan.sh b/images/entrypoint-oxeye-scan.sh index 53acef4..512dbce 100755 --- a/images/entrypoint-oxeye-scan.sh +++ b/images/entrypoint-oxeye-scan.sh @@ -36,6 +36,8 @@ elif [ -n "$JENKINS_URL" ]; then cicd_tool="jenkins" elif [ -n "$BUILD_REPOSITORY_LOCALPATH"]; then git config --global --add safe.directory "*" + git remote get-url origin + exit 0 cicd_tool="azure" elif [ -n "$BITBUCKET_CLONE_DIR"]; then cicd_tool="bitbucket" From a3a5d0282cd72be124c9b4291a3df289a4db11c1 Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Wed, 6 Dec 2023 15:57:06 +0200 Subject: [PATCH 06/19] fix if statement --- images/entrypoint-oxeye-scan.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/entrypoint-oxeye-scan.sh b/images/entrypoint-oxeye-scan.sh index 512dbce..4d6da5f 100755 --- a/images/entrypoint-oxeye-scan.sh +++ b/images/entrypoint-oxeye-scan.sh @@ -34,12 +34,12 @@ elif [ -n "$CI_API_V4_URL" ]; then cicd_tool="gitlab" elif [ -n "$JENKINS_URL" ]; then cicd_tool="jenkins" -elif [ -n "$BUILD_REPOSITORY_LOCALPATH"]; then +elif [ -n "$BUILD_REPOSITORY_LOCALPATH" ]; then git config --global --add safe.directory "*" git remote get-url origin exit 0 cicd_tool="azure" -elif [ -n "$BITBUCKET_CLONE_DIR"]; then +elif [ -n "$BITBUCKET_CLONE_DIR" ]; then cicd_tool="bitbucket" else echo "Error - could not determine environment. aborting..." From 3d6b6d6686425dc0b395246be6500a49a8f8d7d3 Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Wed, 6 Dec 2023 16:00:12 +0200 Subject: [PATCH 07/19] add test --- images/entrypoint-oxeye-scan.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/images/entrypoint-oxeye-scan.sh b/images/entrypoint-oxeye-scan.sh index 4d6da5f..01b1515 100755 --- a/images/entrypoint-oxeye-scan.sh +++ b/images/entrypoint-oxeye-scan.sh @@ -36,6 +36,7 @@ elif [ -n "$JENKINS_URL" ]; then cicd_tool="jenkins" elif [ -n "$BUILD_REPOSITORY_LOCALPATH" ]; then git config --global --add safe.directory "*" + cd $BUILD_REPOSITORY_LOCALPATH git remote get-url origin exit 0 cicd_tool="azure" From dd7e36074e2426af6e76c14362d4441b7424dd8e Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Wed, 6 Dec 2023 16:07:13 +0200 Subject: [PATCH 08/19] remove debug --- images/entrypoint-oxeye-scan.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/images/entrypoint-oxeye-scan.sh b/images/entrypoint-oxeye-scan.sh index 01b1515..bbeceae 100755 --- a/images/entrypoint-oxeye-scan.sh +++ b/images/entrypoint-oxeye-scan.sh @@ -36,9 +36,6 @@ elif [ -n "$JENKINS_URL" ]; then cicd_tool="jenkins" elif [ -n "$BUILD_REPOSITORY_LOCALPATH" ]; then git config --global --add safe.directory "*" - cd $BUILD_REPOSITORY_LOCALPATH - git remote get-url origin - exit 0 cicd_tool="azure" elif [ -n "$BITBUCKET_CLONE_DIR" ]; then cicd_tool="bitbucket" From 9d30e6c19a4e2170c192cd77e00e5675304880c3 Mon Sep 17 00:00:00 2001 From: joeyhavia Date: Wed, 6 Dec 2023 16:13:44 +0200 Subject: [PATCH 09/19] add logger error --- images/scm_scan.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/images/scm_scan.py b/images/scm_scan.py index 682e5e1..0783c7b 100644 --- a/images/scm_scan.py +++ b/images/scm_scan.py @@ -292,19 +292,23 @@ def setup_bitbucket() -> Optional[RepositoryParameters]: if not (workdir := os.getenv("BITBUCKET_CLONE_DIR")): logger.error(f"Could not get repository localpath") return None + if (repo := Repo(workdir)).bare: logger.error(f"Could not get repo") return None + if not (origin := repo.remotes["origin"]): logger.error(f"Could not get origin") return None + if len(urls := list(origin.urls)) < 1: logger.error(f"Could not get origin urls") return None - match_url = REMOTE_URL_REGEX.search(urls[0]) - if not match_url: - logger.error(f"Could not parse origin url") + + if not (match_url := REMOTE_URL_REGEX.search(urls[0])): + logger.error(f"Could not parse origin url",extra={"url": match_url}) return None + server_url = f"https://{match_url.group('server_url')}" organization = match_url.group("organization") name = match_url.group("name") From 716f2dd18d24e4f86ea093e86e0fc0d85bbc1dc6 Mon Sep 17 00:00:00 2001 From: joeyhavia Date: Wed, 6 Dec 2023 16:17:32 +0200 Subject: [PATCH 10/19] add logger error --- images/scm_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/scm_scan.py b/images/scm_scan.py index 0783c7b..36e47fe 100644 --- a/images/scm_scan.py +++ b/images/scm_scan.py @@ -306,7 +306,7 @@ def setup_bitbucket() -> Optional[RepositoryParameters]: return None if not (match_url := REMOTE_URL_REGEX.search(urls[0])): - logger.error(f"Could not parse origin url",extra={"url": match_url}) + logger.error(f"Could not parse origin url {match_url}") return None server_url = f"https://{match_url.group('server_url')}" From 8d579d3f3da8bf3a289f146aeb29e06e86beb726 Mon Sep 17 00:00:00 2001 From: joeyhavia Date: Wed, 6 Dec 2023 16:21:55 +0200 Subject: [PATCH 11/19] add logger error --- images/scm_scan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/scm_scan.py b/images/scm_scan.py index 36e47fe..459225d 100644 --- a/images/scm_scan.py +++ b/images/scm_scan.py @@ -305,6 +305,8 @@ def setup_bitbucket() -> Optional[RepositoryParameters]: logger.error(f"Could not get origin urls") return None + logger.info(f"{repo=} {origin=} {urls=}") + if not (match_url := REMOTE_URL_REGEX.search(urls[0])): logger.error(f"Could not parse origin url {match_url}") return None @@ -315,8 +317,6 @@ def setup_bitbucket() -> Optional[RepositoryParameters]: run_id = str(uuid.uuid4()) logger.info(f"{urls[0]=} {server_url=} {organization=} {name=}") - sys.exit(0) - return RepositoryParameters( provider=provider, server_url=server_url, From cce0ef0b0f6bed2a2317b419b5a4284e7e24b7f4 Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Wed, 6 Dec 2023 16:33:25 +0200 Subject: [PATCH 12/19] update regex --- images/scm_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/scm_scan.py b/images/scm_scan.py index 682e5e1..54f1ed5 100644 --- a/images/scm_scan.py +++ b/images/scm_scan.py @@ -40,7 +40,7 @@ class Provider(Enum): REMOTE_URL_REGEX = re.compile( - r"(git@|https://)(?P.*)(:|/)(?P.*)/(?P.*?)(.git|$)" + r"(git@|https://)(?P.*?)(:|/)(?P.*)/(?P.*?)(.git|$)" ) # git@github.com:ox-eye/github-actions.git From 79dd6c47b2c77bc942a147cbf1d57ecdef209475 Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Wed, 6 Dec 2023 16:37:51 +0200 Subject: [PATCH 13/19] remove debug 2 --- images/scm_scan.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/images/scm_scan.py b/images/scm_scan.py index 95b9eeb..a7deb1c 100644 --- a/images/scm_scan.py +++ b/images/scm_scan.py @@ -269,9 +269,6 @@ def setup_azure() -> Optional[RepositoryParameters]: name = match_url.group("name") run_id = str(uuid.uuid4()) - logger.info(f"{urls[0]=} {server_url=} {organization=} {name=}") - sys.exit(0) - return RepositoryParameters( provider=provider, server_url=server_url, From efe152acb93e634c6bd8b7bfa42165235c326b95 Mon Sep 17 00:00:00 2001 From: joeyhavia Date: Wed, 6 Dec 2023 16:38:49 +0200 Subject: [PATCH 14/19] fix regex to support both http and https --- images/scm_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/scm_scan.py b/images/scm_scan.py index 459225d..cd8d78d 100644 --- a/images/scm_scan.py +++ b/images/scm_scan.py @@ -40,7 +40,7 @@ class Provider(Enum): REMOTE_URL_REGEX = re.compile( - r"(git@|https://)(?P.*)(:|/)(?P.*)/(?P.*?)(.git|$)" + r"(git@|https?://)(?P.*)(:|/)(?P.*)/(?P.*?)(.git|$)" ) # git@github.com:ox-eye/github-actions.git From 80f824d91799a81b34edd62440cdce5929ff02db Mon Sep 17 00:00:00 2001 From: joeyhavia Date: Wed, 6 Dec 2023 16:40:04 +0200 Subject: [PATCH 15/19] fix regex to support both http and https --- images/scm_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/scm_scan.py b/images/scm_scan.py index a7deb1c..2218116 100644 --- a/images/scm_scan.py +++ b/images/scm_scan.py @@ -40,7 +40,7 @@ class Provider(Enum): REMOTE_URL_REGEX = re.compile( - r"(git@|https://)(?P.*?)(:|/)(?P.*)/(?P.*?)(.git|$)" + r"(git@|https?://)(?P.*?)(:|/)(?P.*)/(?P.*?)(.git|$)" ) # git@github.com:ox-eye/github-actions.git From ac139122e1e3008c14096acbd349c910746830c3 Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Wed, 6 Dec 2023 17:06:36 +0200 Subject: [PATCH 16/19] fix provider --- images/scm_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/scm_scan.py b/images/scm_scan.py index a7deb1c..237be3a 100644 --- a/images/scm_scan.py +++ b/images/scm_scan.py @@ -285,7 +285,7 @@ def setup_azure() -> Optional[RepositoryParameters]: def setup_bitbucket() -> Optional[RepositoryParameters]: - provider = Provider.Azure.value + provider = Provider.BitBucket.value if not (workdir := os.getenv("BITBUCKET_CLONE_DIR")): logger.error(f"Could not get repository localpath") return None From 29406d188a6d3487a658d9a58123b4fcf1b93e1a Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Thu, 7 Dec 2023 11:29:45 +0200 Subject: [PATCH 17/19] udpate regex --- images/scm_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/scm_scan.py b/images/scm_scan.py index 1409260..7988a91 100644 --- a/images/scm_scan.py +++ b/images/scm_scan.py @@ -40,7 +40,7 @@ class Provider(Enum): REMOTE_URL_REGEX = re.compile( - r"(git@|https?://)(?P.*?)(:|/)(?P.*)/(?P.*?)(.git|$)" + r"(git@|https?://)(?P.*?)(:|/)(?P.*?)/(?P.*?)(\.git|$)" ) # git@github.com:ox-eye/github-actions.git From 0f5d5f18d3d6fca38fc6b9a6cfa9a2f03d94cfdc Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Thu, 7 Dec 2023 12:43:06 +0200 Subject: [PATCH 18/19] remove azure template --- images/scm_scan.py | 734 ------------------------------------------ oxeye-scan/azure.yaml | 4 - 2 files changed, 738 deletions(-) delete mode 100644 images/scm_scan.py delete mode 100644 oxeye-scan/azure.yaml diff --git a/images/scm_scan.py b/images/scm_scan.py deleted file mode 100644 index 7988a91..0000000 --- a/images/scm_scan.py +++ /dev/null @@ -1,734 +0,0 @@ -import argparse -import json -import logging -import os -import re -import shlex -import stat -import subprocess -import sys -import uuid -from enum import Enum -from typing import Any, Dict, Optional, TypedDict - -import pydantic -import requests -from git import Repo -from pydantic import BaseModel - -logging.basicConfig( - stream=sys.stdout, - level=logging.INFO, - format="%(asctime)s %(message)s %(levelname)s %(pathname)s:%(lineno)s %(funcName)s", - datefmt="%m/%d/%Y %I:%M:%S %p", -) - -logger = logging.getLogger(__name__) - - -class LightzPreSignedURLs(BaseModel): - script: str - config: str - - -class Provider(Enum): - BitBucket = "bitbucket" - GitHub = "github" - GitLab = "gitlab" - Jenkins = "jenkins" - Azure ="azure" - - -REMOTE_URL_REGEX = re.compile( - r"(git@|https?://)(?P.*?)(:|/)(?P.*?)/(?P.*?)(\.git|$)" -) - -# git@github.com:ox-eye/github-actions.git -# https://github.com/ox-eye/oxeye-rulez -# https://ox-eye@dev.azure.com/ox-eye/cloud-native-lab/_git/cloud-native-lab - -# Setup Parameters -class RepositoryParameters(TypedDict): - provider: str - server_url: str - organization: str - name: str - description: str - license: str - branch: str - languages: str - run_id: str - workdir: str - arch: str - - -# GENERAL -SCM_LOG_LOCATION = "/tmp/scm.log" -LIGHTZ_AIO_LOG_LOCATION = "/tmp/lightz-aio.log" - -# Syft Parameters -SYFT_VERSION = "v0.70.0" -SYFT_INSTALL_FILE_URL = ( - f"https://raw.githubusercontent.com/anchore/syft/main/install.sh" -) -SYFT_INSTALL_FILE_LOCAL = f"/tmp/install_syft.sh" -SYFT_INSTALL_FILE_CMD = f"{SYFT_INSTALL_FILE_LOCAL} -b /usr/local/bin {SYFT_VERSION}" -SYFT_SBOM_FILE = f"/app/oxeye.sbom" - -# Lightz Parameters -LIGHTZ_VERSION = "v1.0.3" -LIGHTZ_BINARY_LOCAL = f"/app/lightz-aio" -LIGHTZ_CONFIG_LOCAL = f"/app/lightz-aio.yaml" -LIGHTZ_SARIF_FILE = f"/app/oxeye.sarif" - - -def parse_arguments() -> argparse.Namespace: - argument_parser = argparse.ArgumentParser() - - argument_parser.add_argument("--host", required=True, type=str) - argument_parser.add_argument("--repo-token", required=True, type=str) - argument_parser.add_argument("--client-id", required=True, type=str) - argument_parser.add_argument("--secret", required=True, type=str) - argument_parser.add_argument("--workspace-id", required=True, type=str) - argument_parser.add_argument("--release", required=True, type=str) - argument_parser.add_argument("--excludes", required=False, type=str) - - return argument_parser.parse_args() - - -def get_repository_data( - url: str, headers: Optional[Dict[str, str]] = None -) -> Optional[Dict[Any, Any]]: - try: - response = requests.get(url, headers=headers) - response.raise_for_status() - except (requests.exceptions.HTTPError, requests.exceptions.RequestException) as err: - logger.exception(f"Failed getting repository data {err=} {url=} {headers=}") - return None - - return response.json() - - -def setup_github( - github_api_url: str, github_repository: str, repo_token: str -) -> Optional[RepositoryParameters]: - provider = Provider.GitHub.value - headers = {"Authorization": f"token {repo_token}"} - repository_data_url = f"{github_api_url}/repos/{github_repository}" - if not ( - repository_data := get_repository_data(url=repository_data_url, headers=headers) - ): - return None - if not ( - languages := get_repository_data( - url=f"{repository_data_url}/languages", headers=headers - ) - ): - return None - if not (server_url := os.getenv("GITHUB_SERVER_URL")): - return None - if not (organization := os.getenv("GITHUB_REPOSITORY_OWNER")): - return None - if not (repo := os.getenv("GITHUB_REPOSITORY")): - return None - if not (branch := os.getenv("GITHUB_REF_NAME")): - return None - if not (workdir := os.getenv("GITHUB_WORKSPACE")): - return None - if not (arch := os.getenv("RUNNER_ARCH")): - return None - - description = repository_data.get("description", "None") - license = repository_data.get("license", "None") - run_id = str(uuid.uuid4()) - - return RepositoryParameters( - provider=provider, - server_url=server_url, - organization=organization, - name=repo.removeprefix(f"{organization}/"), - description=description if description else "None", - license=license if license else "None", - branch=branch, - languages=json.dumps(languages), - run_id=run_id, - workdir=workdir, - arch=arch, - ) - - -def setup_gitlab( - gitlab_api_url: str, gitlab_project_id: str, repo_token: str -) -> Optional[RepositoryParameters]: - provider = Provider.GitLab.value - if not ( - repository_data := get_repository_data( - url=f"{gitlab_api_url}/projects/{gitlab_project_id}?private_token={repo_token}" - ) - ): - return None - if not ( - languages := get_repository_data( - url=f"{gitlab_api_url}/projects/{gitlab_project_id}/languages?private_token={repo_token}" - ) - ): - return None - if not (server_url := os.getenv("CI_SERVER_URL")): - return None - if not (organization := os.getenv("CI_PROJECT_NAMESPACE")): - return None - if not (repo := os.getenv("CI_PROJECT_NAME")): - return None - if ci_commit_branch := os.getenv("CI_COMMIT_BRANCH"): - branch = ci_commit_branch - elif ci_commit_ref_name := os.getenv("CI_COMMIT_REF_NAME"): - branch = ci_commit_ref_name - else: - return None - if not (workdir := os.getenv("CI_PROJECT_DIR")): - return None - if not (arch := os.getenv("CI_RUNNER_EXECUTABLE_ARCH")): - return None - description = repository_data.get("description", "None") - license = repository_data.get("license", "None") - run_id = str(uuid.uuid4()) - - return RepositoryParameters( - provider=provider, - server_url=server_url, - organization=organization, - name=repo.removeprefix(f"{organization}/"), - description=description if description else "None", - license=license if license else "None", - branch=branch, - languages=json.dumps(languages), - run_id=run_id, - workdir=workdir, - arch=arch, - ) - - -def setup_jenkins() -> Optional[RepositoryParameters]: - provider = Provider.Jenkins.value - if not (workdir := os.getenv("WORKSPACE")): - logger.error(f"Could not get workspace") - return None - if (repo := Repo(workdir)).bare: - logger.error(f"Could not get repo") - return None - if not (origin := repo.remotes["origin"]): - logger.error(f"Could not get origin") - return None - if len(urls := list(origin.urls)) < 1: - logger.error(f"Could not get origin urls") - return None - match_url = REMOTE_URL_REGEX.search(urls[0]) - if not match_url: - logger.error(f"Could not parse origin url") - return None - server_url = f"https://{match_url.group('server_url')}" - organization = match_url.group("organization") - name = match_url.group("name") - run_id = str(uuid.uuid4()) - - return RepositoryParameters( - provider=provider, - server_url=server_url, - organization=organization, - name=name, - description="None", - license="None", - branch="branch", - languages="{}", - run_id=run_id, - workdir=workdir, - arch="", - ) - - -def setup_azure() -> Optional[RepositoryParameters]: - provider = Provider.Azure.value - if not (workdir := os.getenv("BUILD_REPOSITORY_LOCALPATH")): - logger.error(f"Could not get repository localpath") - return None - if (repo := Repo(workdir)).bare: - logger.error(f"Could not get repo") - return None - if not (origin := repo.remotes["origin"]): - logger.error(f"Could not get origin") - return None - if len(urls := list(origin.urls)) < 1: - logger.error(f"Could not get origin urls") - return None - match_url = REMOTE_URL_REGEX.search(urls[0]) - if not match_url: - logger.error(f"Could not parse origin url") - return None - server_url = f"https://{match_url.group('server_url')}" - organization = match_url.group("organization") - name = match_url.group("name") - run_id = str(uuid.uuid4()) - - return RepositoryParameters( - provider=provider, - server_url=server_url, - organization=organization, - name=name, - description="None", - license="None", - branch="branch", - languages="{}", - run_id=run_id, - workdir=workdir, - arch="", - ) - - -def setup_bitbucket() -> Optional[RepositoryParameters]: - provider = Provider.BitBucket.value - if not (workdir := os.getenv("BITBUCKET_CLONE_DIR")): - logger.error(f"Could not get repository localpath") - return None - - if (repo := Repo(workdir)).bare: - logger.error(f"Could not get repo") - return None - - if not (origin := repo.remotes["origin"]): - logger.error(f"Could not get origin") - return None - - if len(urls := list(origin.urls)) < 1: - logger.error(f"Could not get origin urls") - return None - - logger.info(f"{repo=} {origin=} {urls=}") - - if not (match_url := REMOTE_URL_REGEX.search(urls[0])): - logger.error(f"Could not parse origin url {match_url}") - return None - - server_url = f"https://{match_url.group('server_url')}" - organization = match_url.group("organization") - name = match_url.group("name") - run_id = str(uuid.uuid4()) - - logger.info(f"{urls[0]=} {server_url=} {organization=} {name=}") - return RepositoryParameters( - provider=provider, - server_url=server_url, - organization=organization, - name=name, - description="None", - license="None", - branch="branch", - languages="{}", - run_id=run_id, - workdir=workdir, - arch="", - ) - - -def setup(repo_token: str) -> Optional[RepositoryParameters]: - if (github_api_url := os.getenv("GITHUB_API_URL")) and ( - github_repository := os.getenv("GITHUB_REPOSITORY") - ): - return setup_github( - github_api_url=github_api_url, - github_repository=github_repository, - repo_token=repo_token, - ) - elif (gitlab_api_url := os.getenv("CI_API_V4_URL")) and ( - gitlab_project_id := os.getenv("CI_PROJECT_ID") - ): - return setup_gitlab( - gitlab_api_url=gitlab_api_url, - gitlab_project_id=gitlab_project_id, - repo_token=repo_token, - ) - elif "JENKINS_URL" in os.environ: - return setup_jenkins() - elif "BUILD_REPOSITORY_LOCALPATH" in os.environ: - return setup_azure() - elif "BITBUCKET_CLONE_DIR" in os.environ: - return setup_bitbucket() - - logger.error(f"Error - could not determine environment. aborting...") - return None - - -def get_authorization_token(host: str, client_id: str, secret: str) -> str: - auth_url = f"https://{host}/api/auth/api-token" - - try: - response = requests.post( - auth_url, json={"clientId": client_id, "secret": secret} - ) - response.raise_for_status() - except (requests.exceptions.HTTPError, requests.exceptions.RequestException) as err: - logger.exception(f"Failed getting authorization token {err=} {auth_url=}") - raise - - return str(response.text) - - -def get_lightz_presigned_urls( - host: str, auth_token: str, arch: str, version: str -) -> LightzPreSignedURLs: - url = f"https://{host}/api/scm/lightz?arch={arch}&version={version}" - - headers = {"Authorization": f"Bearer {auth_token}"} - try: - response = requests.get(url, headers=headers) - response.raise_for_status() - return LightzPreSignedURLs.model_validate(response.json()) - except (requests.exceptions.HTTPError, requests.exceptions.RequestException) as err: - logger.exception(f"Failed getting pre-signed urls {err=} {url=}") - raise - except pydantic.ValidationError as err: - logger.exception(f"Failed parsing pre-signed urls response{err=} {url=}") - raise - - -def download_file(url: str, local_filename: str) -> None: - try: - response = requests.get(url, stream=True) - response.raise_for_status() - except (requests.exceptions.HTTPError, requests.exceptions.RequestException) as err: - logger.exception(f"Failed downloading file {err=} {url=}") - raise - - try: - with open(local_filename, "wb") as file: - try: - for chunk in response.iter_content(chunk_size=8192): - file.write(chunk) - except (IOError, OSError): - logger.exception(f"Error writing to file {local_filename}") - raise - except (FileNotFoundError, PermissionError, OSError): - logger.exception(f"Error opening file {local_filename}") - raise - - try: - os.chmod(local_filename, stat.S_IXUSR) - except: - logger.exception(f"Could not chmod {local_filename}") - raise - - -def run_shell_cmd_with_log( - cmd: str, err_msg: str, log_location: str = SCM_LOG_LOCATION -) -> None: - try: - with open(log_location, "a") as log_file: - try: - subprocess.run( - cmd, - shell=True, - check=True, - stderr=log_file, - stdout=log_file, - ) - - except (subprocess.SubprocessError, subprocess.CalledProcessError): - logger.exception(f"{err_msg} cmd={cmd}") - raise - except (FileNotFoundError, PermissionError, OSError): - logger.exception(f"Error opening file {log_location}") - raise - - -def run_shell_cmd_with_result( - cmd: str, err_msg: str, log_location: str = SCM_LOG_LOCATION -) -> str: - try: - with open(log_location, "a") as log_file: - try: - result = subprocess.run( - cmd, - shell=True, - check=True, - stderr=log_file, - stdout=subprocess.PIPE, - ) - - except (subprocess.SubprocessError, subprocess.CalledProcessError): - logger.exception(f"{err_msg} cmd={cmd}") - raise - except (FileNotFoundError, PermissionError, OSError): - logger.exception(f"Error opening file {log_location}") - raise - - return result.stdout.decode() - - -def release_start( - host: str, - client_id: str, - secret: str, - workspace_id: str, - release: str, -) -> None: - oxctl_cmd = ( - f"/app/oxctl release start " - f"--host {host} " - f"--client-id {client_id} " - f"--secret {secret} " - f"-a {workspace_id} " - f"-t {release} " - ) - run_shell_cmd_with_log(oxctl_cmd, "Failed Start Release") - - -def upload_result( - upload_type: str, - host: str, - client_id: str, - secret: str, - workspace_id: str, - release: str, - repo_params: RepositoryParameters, - file: str, -) -> None: - oxctl_cmd = ( - f"/app/oxctl upload {upload_type} " - f"--host {host} " - f"--client-id {client_id} " - f"--secret {secret} " - f"--workspace {workspace_id} " - f"--repository-provider '{repo_params['provider']}' " - f"--repository-server-url '{repo_params['server_url']}' " - f"--repository-organization '{repo_params['organization']}' " - f"--repository-name '{repo_params['name']}' " - f"--repository-description '{repo_params['description']}' " - f"--repository-license '{repo_params['license']}' " - f"--repository-branch '{repo_params['branch']}' " - f"--repository-languages '{repo_params['languages']}' " - f"--repository-run-id '{repo_params['run_id']}' " - f"--repository-release-tag '{release}' " - f"--file {file} " - ) - run_shell_cmd_with_log(oxctl_cmd, "Failed Uploading Result") - - -def upload_file( - host: str, - client_id: str, - secret: str, - workspace_id: str, - release: str, - repo_params: RepositoryParameters, - entity_type: str, - upload_file: str, -) -> None: - oxctl_cmd = ( - f"/app/oxctl uploadURL " - f"--host {host} " - f"--client-id {client_id} " - f"--secret {secret} " - f"--workspace {workspace_id} " - f"--repository-provider '{repo_params['provider']}' " - f"--repository-server-url '{repo_params['server_url']}' " - f"--repository-organization '{repo_params['organization']}' " - f"--repository-name '{repo_params['name']}' " - f"--repository-description '{repo_params['description']}' " - f"--repository-license '{repo_params['license']}' " - f"--repository-branch '{repo_params['branch']}' " - f"--repository-languages '{repo_params['languages']}' " - f"--repository-run-id '{repo_params['run_id']}' " - f"--repository-release-tag '{release}' " - f"--entity-type '{entity_type}' " - ) - pre_signed_url = run_shell_cmd_with_result( - oxctl_cmd, - "Failed Uploading Result", - ) - - try: - with open(upload_file, "rb") as file: - try: - requests.put(pre_signed_url, data=file) - except ( - requests.exceptions.HTTPError, - requests.exceptions.RequestException, - ) as err: - logger.exception( - f"Failed to upload file {pre_signed_url=} {err=}", - ) - except (FileNotFoundError, PermissionError, OSError): - logger.exception(f"Error opening upload_file: {upload_file}") - raise - - -def download_syft() -> None: - download_file(SYFT_INSTALL_FILE_URL, SYFT_INSTALL_FILE_LOCAL) - run_shell_cmd_with_log( - SYFT_INSTALL_FILE_CMD, - "Failed installing Package Scanner", - ) - - -def execute_syft(repo_params: RepositoryParameters, output_file: str) -> None: - syft_cmd = f"cd {repo_params['workdir']};" f"syft . -q -o spdx-json={output_file} " - run_shell_cmd_with_log(syft_cmd, "Failed Running Package Scanner") - - -def run_syft( - host: str, - client_id: str, - secret: str, - workspace_id: str, - release: str, - repo_params: RepositoryParameters, -) -> None: - download_syft() - execute_syft( - repo_params=repo_params, - output_file=SYFT_SBOM_FILE, - ) - upload_file( - host=host, - client_id=client_id, - secret=secret, - workspace_id=workspace_id, - release=release, - repo_params=repo_params, - entity_type="CodeScanSyftSPDX", - upload_file=SYFT_SBOM_FILE, - ) - - -def download_lightz( - host: str, client_id: str, secret: str, repo_params: RepositoryParameters -) -> None: - auth_token = get_authorization_token(host, client_id, secret) - - pre_signed_urls: LightzPreSignedURLs = get_lightz_presigned_urls( - host=host, - auth_token=auth_token, - arch=repo_params["arch"], - version=LIGHTZ_VERSION, - ) - - download_file(pre_signed_urls.script, LIGHTZ_BINARY_LOCAL) - download_file(pre_signed_urls.config, LIGHTZ_CONFIG_LOCAL) - - -def execute_lightz( - repo_params: RepositoryParameters, - output_file: str, - excludes: str, -) -> None: - logger.info(f"cwd = {os.getcwd}") - lightz_cmd = ( - f"cd {repo_params['workdir']};" - f"{LIGHTZ_BINARY_LOCAL} " - f"--config {LIGHTZ_CONFIG_LOCAL} " - f"--output {output_file} " - f"{' '.join(['--exclude ' + shlex.quote(exclude) for exclude in shlex.split(excludes)])} " - f"--target . " - ) - run_shell_cmd_with_log( - cmd=lightz_cmd, - err_msg="Failed Running Source Code Scan", - log_location=LIGHTZ_AIO_LOG_LOCATION, - ) - - -def run_lightz( - host: str, - client_id: str, - secret: str, - workspace_id: str, - release: str, - repo_params: RepositoryParameters, - excludes: str, -) -> None: - download_lightz( - host=host, client_id=client_id, secret=secret, repo_params=repo_params - ) - - execute_lightz( - repo_params=repo_params, - output_file=LIGHTZ_SARIF_FILE, - excludes=excludes, - ) - - upload_file( - host=host, - client_id=client_id, - secret=secret, - workspace_id=workspace_id, - release=release, - repo_params=repo_params, - entity_type="CodeScanSemgrep", - upload_file=LIGHTZ_SARIF_FILE, - ) - - -def main() -> None: - arguments: argparse.Namespace = parse_arguments() - repo_params: Optional[RepositoryParameters] = setup(repo_token=arguments.repo_token) - if not repo_params: - sys.exit(1) - try: - release_start( - host=arguments.host, - client_id=arguments.client_id, - secret=arguments.secret, - workspace_id=arguments.workspace_id, - release=arguments.release, - ) - except: - sys.exit(2) - try: - run_syft( - host=arguments.host, - client_id=arguments.client_id, - secret=arguments.secret, - workspace_id=arguments.workspace_id, - release=arguments.release, - repo_params=repo_params, - ) - except: - sys.exit(3) - try: - run_lightz( - host=arguments.host, - client_id=arguments.client_id, - secret=arguments.secret, - workspace_id=arguments.workspace_id, - release=arguments.release, - repo_params=repo_params, - excludes=arguments.excludes, - ) - except: - # Upload Lightz AIO Log - upload_file( - host=arguments.host, - client_id=arguments.client_id, - secret=arguments.secret, - workspace_id=arguments.workspace_id, - release=arguments.release, - repo_params=repo_params, - entity_type="LightzAioLogs", - upload_file=LIGHTZ_AIO_LOG_LOCATION, - ) - sys.exit(4) - - # Upload Lightz AIO Log - upload_file( - host=arguments.host, - client_id=arguments.client_id, - secret=arguments.secret, - workspace_id=arguments.workspace_id, - release=arguments.release, - repo_params=repo_params, - entity_type="LightzAioLogs", - upload_file=LIGHTZ_AIO_LOG_LOCATION, - ) - - -if __name__ == "__main__": - main() diff --git a/oxeye-scan/azure.yaml b/oxeye-scan/azure.yaml deleted file mode 100644 index 4ad87f5..0000000 --- a/oxeye-scan/azure.yaml +++ /dev/null @@ -1,4 +0,0 @@ -steps: -- bash: | - echo "Hello from a different Azure DevOps Organization!" - displayName: 'Hello from Beta' \ No newline at end of file From 09f17f147e1b3b4a4905ccce75f807df6bdb90f3 Mon Sep 17 00:00:00 2001 From: Schmil Monderer Date: Thu, 7 Dec 2023 12:43:45 +0200 Subject: [PATCH 19/19] remove scm_scan --- images/Dockerfile.oxeye-scan | 2 +- images/entrypoint-oxeye-scan.sh | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/images/Dockerfile.oxeye-scan b/images/Dockerfile.oxeye-scan index 8685364..127dbf4 100644 --- a/images/Dockerfile.oxeye-scan +++ b/images/Dockerfile.oxeye-scan @@ -37,5 +37,5 @@ RUN rm -rf /var/cache/apk/* COPY --from=oxctl_builder /app/oxctl /app/ COPY entrypoint-oxeye-scan.sh /entrypoint.sh -COPY scm_scan.py /app/scm_scan.py + ENTRYPOINT ["/entrypoint.sh"] diff --git a/images/entrypoint-oxeye-scan.sh b/images/entrypoint-oxeye-scan.sh index 21d2555..62bbb0f 100755 --- a/images/entrypoint-oxeye-scan.sh +++ b/images/entrypoint-oxeye-scan.sh @@ -50,10 +50,10 @@ else fi # Download Script -# curl -s -o /app/scm_scan.py --location "https://${host}/api/scm/script?provider=${cicd_tool}" \ -# --header "Content-Type: application/json" \ -# --header "Accept: application/octet-stream" \ -# --header "Authorization: Bearer ${bearerToken}" +curl -s -o /app/scm_scan.py --location "https://${host}/api/scm/script?provider=${cicd_tool}" \ +--header "Content-Type: application/json" \ +--header "Accept: application/octet-stream" \ +--header "Authorization: Bearer ${bearerToken}" # RUN SCM Scan Script python /app/scm_scan.py --host $host --repo-token $token --client-id $client_id --secret $secret --workspace-id $workspace_id --release $release --excludes "$excludes"