{"id":19259050,"url":"https://github.com/r3drun3/immunize","last_synced_at":"2025-04-21T16:30:38.623Z","repository":{"id":215651460,"uuid":"739411971","full_name":"R3DRUN3/immunize","owner":"R3DRUN3","description":"Pipeline for patching CVEs in container images 💉📦","archived":true,"fork":false,"pushed_at":"2024-02-08T08:06:31.000Z","size":999,"stargazers_count":20,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-21T10:03:10.870Z","etag":null,"topics":["container-image","copacetic","cve","docker","in-toto","oci","security-automation","security-tools","supply-chain-security","vulnerability"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/R3DRUN3.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2024-01-05T14:00:12.000Z","updated_at":"2025-02-14T13:57:14.000Z","dependencies_parsed_at":"2024-02-06T12:27:41.820Z","dependency_job_id":"50d0862d-8543-4d27-a6c1-c7f1428b4e63","html_url":"https://github.com/R3DRUN3/immunize","commit_stats":null,"previous_names":["r3drun3/immunize"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/R3DRUN3%2Fimmunize","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/R3DRUN3%2Fimmunize/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/R3DRUN3%2Fimmunize/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/R3DRUN3%2Fimmunize/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/R3DRUN3","download_url":"https://codeload.github.com/R3DRUN3/immunize/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250090637,"owners_count":21373222,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["container-image","copacetic","cve","docker","in-toto","oci","security-automation","security-tools","supply-chain-security","vulnerability"],"created_at":"2024-11-09T19:15:18.323Z","updated_at":"2025-04-21T16:30:38.288Z","avatar_url":"https://github.com/R3DRUN3.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# IMMUNIZE\n\n[![patch](https://github.com/R3DRUN3/immunize/actions/workflows/patch.yaml/badge.svg)](https://github.com/R3DRUN3/immunize/actions/workflows/patch.yaml)  \n\u003cimg src=\"images/immunize-logo.png\" alt=\"logo\" width=\"150\" height=\"130\"/\u003e\n\nPipeline for patching vulnerable container images 💉📦\n\n## Abstract\nThe present is a repository containing a [Github action](https://github.com/features/actions) to patch  \nvulnerable container images with [copacetic](https://github.com/project-copacetic/copacetic) and attest them with [cosign](https://docs.sigstore.dev/signing/quickstart/).  \n\n\u003e [!Note]\n\u003e The patched images can be found [here](https://github.com/R3DRUN3?tab=packages\u0026repo_name=immunize).  \n\n## Instructions\n\nThe pipeline is triggered upon a push to the repo (any branch).   \nThe corresponding action is configured in the `.github/workflows/patch.yaml` file.  \nSpecifically, the list of container images to patch is specified within the strategy as follows:\n\n```yaml\nimages: ['docker.io/library/nginx:1.21.6', 'docker.io/openpolicyagent/opa:0.46.0']\n```  \nFollowing is an high-level description of the pipeline jobs and steps:  \n## Immunize Job\n### Overview:\n\nThis job is triggered on every push event (excluding changes to README.md) and focuses on scanning and immunizing Docker images for security vulnerabilities.\n### Steps: \n1. **Install Cosign:**  \n   - Install *Cosign* on the runner environment  \n1. **Set up Docker Buildx:**  \n   - Uses the `docker/setup-buildx-action` to set up Docker Buildx for multi-platform builds. \n1. **Generate Trivy Report:**  \n   - Utilizes the `aquasecurity/trivy-action` to scan specified Docker images for OS vulnerabilities and generates a JSON report. \n1. **Check Vuln Count:**  \n   - Parses the Trivy report using `jq` to count the number of vulnerabilities and outputs the count to the GitHub environment. \n1. **Set Tag:** \n   - Extracts the tag from the Docker image reference and appends \"-immunized\" to create a new tag. Sets this new tag in the GitHub environment. \n1. **Copa Action:**  \n   - Conditionally executes the `project-copacetic/copa-action` if vulnerabilities are found.\n   - Utilizes Copa to apply security patches to the Docker image, generating a patched image and a detailed report.  \n1. **Log into ghcr:**  \n   - Logs into GitHub Container Registry (ghcr.io) using the `docker/login-action` with the GitHub token. \n1. **Tag Image for GHCR:** \n   - Tags the patched Docker image and prepares it for pushing to GitHub Container Registry.  \n1. **Docker Push Patched Image:**\n   - Pushes the patched Docker image to GitHub Container Registry for storage and distribution.  \n1. **Produce Image SBOM:**\n   - Produce a *Software Bill of Material* using the `anchore/sbom-action` for the pushed image.  \n1. **Sign image with Cosign:**  \n   - Sign the pushed image with *Cosign*  \n1. **Attest the Image with SBOM**:\n   - Attest the image using the SBOM as a predicate via cosign.\n\n\n## Send-Mail-Report Job\n### Overview:\n\nThis job is dependent on the completion of the `Immunize` job and is responsible for sending an email report.  \nIf you dont need this job you can comment it out in the pipeline manifest.  \n### Steps: \n1. **Checkout Repository:** \n   - Checks out the repository to access necessary files and scripts. \n2. **Send Mail Report:**  \n   - Executes a Python script (`send_mail_report.py`) located in the repository, sending a report via email.  \n   - Configures email recipient addresses, sender address, and password using github action secrets.  \n3. **Report Example**:  \n   - ![report](images/report.png)\n\n\u003cbr /\u003e\n\n\n\nTo perform image pulls, authentication is not required; however, GitHub may prompt for a token if the API call limit is exceeded.  \nIn such instances, please refer to the instructions provided [here](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-with-a-personal-access-token-classic) to configure an access token.  \nSubsequently, proceed to log in as follows:\n\n```console\nexport CR_PAT=YOUR_TOKEN \\\n\u0026\u0026 echo $CR_PAT | docker login ghcr.io -u USERNAME --password-stdin\n\nLogin Succeeded\n```   \n\n## Verify Patching\n\n\u003e[!Warning]\n\u003e Please be aware that *Copacetic* focuses on rectifying vulnerabilities within the operating system's libraries in the corresponding image layer, rather than addressing application dependencies.  \n\nTo assess the effectiveness of patching, you may conduct a scan using [Trivy](https://github.com/aquasecurity/trivy) initially on one of the original images:  \n```console\ntrivy image docker.io/openpolicyagent/opa:0.46.0\n```  \n\nOutput for OS CVEs:  \n```console   \nTotal: 41 (UNKNOWN: 0, LOW: 11, MEDIUM: 21, HIGH: 9, CRITICAL: 0)\n```  \n\nAnd then on the immunized version of that same image:  \n```console\ntrivy image ghcr.io/r3drun3/immunize/docker.io/openpolicyagent/opa:0.46.0-immunized\n```  \n\nOutput for OS CVEs:  \n```console   \nTotal: 18 (UNKNOWN: 0, LOW: 11, MEDIUM: 7, HIGH: 0, CRITICAL: 0)\n```  \n\nAs you can see the latest has way less CVEs than the former!  \n\nThe benefit becomes even more apparent when patching old images.  \nFor instance, observe the difference between this:  \n```console\ntrivy image python:3.5.10-slim\n\nTotal: 307 (UNKNOWN: 8, LOW: 101, MEDIUM: 81, HIGH: 87, CRITICAL: 30)\n```   \n\nand the immunized one:  \n```console  \ntrivy image ghcr.io/r3drun3/immunize/docker.io/library/python:3.5.10-slim-immunized\n\nTotal: 148 (UNKNOWN: 0, LOW: 97, MEDIUM: 26, HIGH: 23, CRITICAL: 2)\n```  \n\nCritical vulnerabilities have been reduced by ~93%!  \n\n\u003e [!Warning]\n\u003e Even though the benefits are more apparent in older images, it is always advisable to prioritize new images and the most recent tags whenever possible.  \n\n\n\n\n## Verify Image Signatures and Attestations\nAll the patched OCI images produced by the pipeline are signed with [cosign](https://github.com/sigstore/cosign).  \nIn order to verify the signature, adapt the following command for the desired image:  \n```console\ncosign verify --key cosign/cosign.pub ghcr.io/r3drun3/immunize/docker.io/library/node:18.17.1-slim-immunized\n```   \n\nOutput:  \n```json\n\nVerification for ghcr.io/r3drun3/immunize/docker.io/library/node:18.17.1-slim-immunized --\nThe following checks were performed on each of these signatures:\n  - The cosign claims were validated\n  - Existence of the claims in the transparency log was verified offline\n  - The signatures were verified against the specified public key\n\n[\n   {\"critical\":\n      {\"identity\":{\"docker-reference\":\"ghcr.io/r3drun3/immunize/docker.io/library/node\"},\n      \"image\":\n      {\"docker-manifest-digest\":\"sha256:19940c59087a363148b44c56447186d97d6afbc2165727b2d0a2ea0ce43b69fd\"},\n      \"type\":\"cosign container image signature\"},\n      \"optional\":\n         {\"Bundle\":\n         {\"SignedEntryTimestamp\":\"MEUCIHo1Jja4t0+OPDYqHo/B/p7HUtP+/i8ZD+fu6Rb57Lw9AiEA7N1i7JDiIvRxu9QtYOrrS8Y+AeekHMWNE3p7GJAbHAs=\",\n         \"Payload\":     {\"body\":\"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2MmM1NWZhNGQxMjE5YTk5ZWJhMjkzY2E0YzNiNmFiN2Y1Y2QxNmE5YjFmMmY2OWVhNDlmM2NkZDhkYzg4ODcwIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUMxenUwajdZejVLUWpwYU5sTnkvRkpUT3FQZ0k4RHcrbVR6Z2s4R2JjV1lnSWhBTlBaTzQ3TFNvcW82MGJYWXd4aWo1SkFDVmxpZjZpdmpTNlVaRlJMMHdpMyIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCUVZVSk1TVU1nUzBWWkxTMHRMUzBLVFVacmQwVjNXVWhMYjFwSmVtb3dRMEZSV1VsTGIxcEplbW93UkVGUlkwUlJaMEZGTWxwdllrWlVTWFI1VDFodllqbHdTM053VWpCaFJGTmhXR3BXYWdwRVJYQTRZbkpFYzJ0Q05rOXVUVlY0TjBkUlJXSnNSREpTUkVKQ2JWQTFWRUZMZG5Od1lYa3ljM2x3TkZvck5YTXlWalk1ZGxNNFQwdG5QVDBLTFMwdExTMUZUa1FnVUZWQ1RFbERJRXRGV1MwdExTMHRDZz09In19fX0=\",\n   \"integratedTime\":1705317423,\n   \"logIndex\":63825695,\n   \"logID\":\"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d\"}\n     }\n    }\n   }\n]\n```   \n\nThe pipeline, for every image, produces an SBOM in [SPDX](https://spdx.github.io/spdx-spec/v2.3/) format and creates an [in-toto attestation](https://docs.sigstore.dev/verifying/attestation/) for the image using that artifact as a predicate.  \n\n\u003e [!Note]\n\u003e You can learn the difference between SBOMs and Attestations [here](https://edu.chainguard.dev/open-source/sbom/sboms-and-attestations).\n\n\nIn order to verify the image attestation with cosign, use the following command (adapt for the desired image):  \n```console\nexport IMAGE=ghcr.io/r3drun3/immunize/docker.io/openpolicyagent/opa:0.46.0-immunized\ncosign verify-attestation --type spdx --key ./cosign/cosign.pub $IMAGE\n```   \n\nThe above command verifies and returns the uploaded artifact data in *base64* format.  \nWe can decode it to query the artifact (in this case, the SBOM file):  \n```console\ncosign verify-attestation --type spdx --key ./cosign/cosign.pub $IMAGE | jq -r .payload | base64 -D | jq .\n```   \nThe SPDX sbom is the `predicate` property in the previous command output:  \n\n\nOutput Sample:  \n```json\nVerification for ghcr.io/r3drun3/immunize/docker.io/openpolicyagent/opa:0.46.0-immunized --\nThe following checks were performed on each of these signatures:\n  - The cosign claims were validated\n  - Existence of the claims in the transparency log was verified offline\n  - The signatures were verified against the specified public key\n{\n  \"_type\": \"https://in-toto.io/Statement/v0.1\",\n  \"predicateType\": \"https://spdx.dev/Document\",\n  \"subject\": [\n    {\n      \"name\": \"ghcr.io/r3drun3/immunize/docker.io/openpolicyagent/opa\",\n      \"digest\": {\n        \"sha256\": \"45de0d4de2ef8590ccdb97ee817d70ba6bb3efb70aabd4abe74a3a6facff24ea\"\n      }\n    }\n  ],\n  \"predicate\": \"[SPDX-SBOM-HERE]\"\n}\n```   \n\n\nIn order to better understand the previous output, take a look at the [in-toto attestation framework spec](https://github.com/in-toto/attestation/blob/main/spec/README.md#in-toto-attestation-framework-spec).  \nIn ordert to understand how in-toto verifies an attestation, take a look at the [in-toto validation model](https://github.com/in-toto/attestation/blob/main/docs/validation.md).  \nIf you want to learn how to enforce the SBOM attestations on your kubernetes cluster take a look at [this repo](https://github.com/R3DRUN3/cyberhall/tree/main/k8s-security/k8s-sigstore).  \n\nIf you want to produce a single json file for the sbom in SPDX format, you can use the following command:  \n```console\ncosign verify-attestation --type spdx --key ./cosign/cosign.pub $IMAGE | jq -r .payload | base64 -D | jq -r '.predicate | fromjson' \u003e final-spdx.json\n```   \n\n\nStarting from that command you can then use [open source tools](https://spdx.dev/use/tools/open-source-tools/) or [this website](https://tools.spdx.org/app/) to do validation and other operations on the SBOM.  \n\nFor example, let's say I want to validate the SBOM for the `ghcr.io/r3drun3/immunize/docker.io/library/python:3.5.10-slim-immunized` image.  \nI will start by obtaining the SBOM in Json format:  \n\n```console\nexport IMAGE=ghcr.io/r3drun3/immunize/docker.io/library/python:3.5.10-slim-immunized\ncosign verify-attestation --type spdx --key ./cosign/cosign.pub $IMAGE | jq -r .payload | base64 -D | jq -r '.predicate | fromjson' \u003e final-spdx.json\n```   \n\nThen I will head to the aforementioned website, select *validate* and upload my json file:  \n![valid](images/valid-sbom.png)  \n\n\nIf I want to produce an XLSX spreadsheet, I will select *convert* on the website, enter the format, upload the file and then download the produced spreadsheet:  \n![valid](images/spreadsheet.png)  \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fr3drun3%2Fimmunize","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fr3drun3%2Fimmunize","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fr3drun3%2Fimmunize/lists"}