{"id":51226763,"url":"https://github.com/cartyc/cve-comparision","last_synced_at":"2026-06-28T12:30:45.488Z","repository":{"id":366947702,"uuid":"1278570886","full_name":"cartyc/cve-comparision","owner":"cartyc","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-24T01:36:43.000Z","size":48,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-24T02:23:35.476Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cartyc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-23T23:29:07.000Z","updated_at":"2026-06-24T01:34:49.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cartyc/cve-comparision","commit_stats":null,"previous_names":["cartyc/cve-comparision"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/cartyc/cve-comparision","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cartyc%2Fcve-comparision","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cartyc%2Fcve-comparision/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cartyc%2Fcve-comparision/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cartyc%2Fcve-comparision/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cartyc","download_url":"https://codeload.github.com/cartyc/cve-comparision/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cartyc%2Fcve-comparision/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34889047,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2026-06-28T12:30:44.839Z","updated_at":"2026-06-28T12:30:45.475Z","avatar_url":"https://github.com/cartyc.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Chainguard Remediated Libraries — Demo\n\nTwo identical applications (Java + Python), each built four ways — on a UBI 10 base\nand a Chainguard base, with unremediated and remediated library sets. Scan them with\ngrype to separate the CVE delta from the **base image** versus from **Chainguard's\nremediated libraries**.\n\n## Contents\n\n- [Structure](#structure)\n- [What each app does](#what-each-app-does)\n- [Scan results](#scan-results-grype-jun-2026)\n- [Key CVEs eliminated](#key-cves-eliminated)\n- [Prerequisites](#prerequisites)\n- [Credentials](#credentials)\n- [Run the demo](#run-the-demo)\n- [CI — build, sign, push to GHCR](#ci--build-sign-push-to-ghcr)\n- [License](#license)\n\n---\n\n## Structure\n\nEach app builds in four variants — two base images (UBI 10 and Chainguard) × two\nlibrary sets (unremediated and remediated):\n\n```\nremediation-demo/\n  java/\n    pom-unremediated.xml         # Spring Boot 2.7.0 / spring-web 5.3.20 (vulnerable)\n    pom-remediated.xml           # Spring Boot 2.7.0 / spring-web 5.3.39-0.cgr.4 (remediated)\n    Dockerfile.unremediated      # UBI 10: build on openjdk-21, run on ubi-minimal + JRE\n    Dockerfile.remediated        # UBI 10 + java-remediated libraries\n    Dockerfile.unremediated-cgr  # Chainguard jdk (ARG CGR_ORG): cgr.dev/\u003corg\u003e/jdk\n    Dockerfile.remediated-cgr    # Chainguard jdk + java-remediated libraries\n    settings-remediated.xml.template\n    src/...\n  python/\n    requirements-unremediated.txt      # PyPI, pinned vulnerable versions\n    requirements-remediated.txt        # libraries.cgr.dev/python-remediated/\n    Dockerfile.unremediated            # UBI 10: ubi-minimal + python3 (3.12)\n    Dockerfile.remediated              # UBI 10 + python-remediated libraries\n    Dockerfile.unremediated-cgr        # Chainguard python:3.12 (ARG CGR_ORG)\n    Dockerfile.remediated-cgr          # Chainguard python:3.12 + python-remediated libraries\n    app.py\n  scan.sh\n  cve-report.html              # rendered side-by-side CVE comparison\n```\n\n\u003e `scan.sh` builds the two UBI variants per language (tagged `demo-\u003clang\u003e-unremediated`\n\u003e / `demo-\u003clang\u003e-remediated`). The `-cgr` Chainguard variants are built directly with\n\u003e `docker build` — see each Dockerfile's header comment for the exact command.\n\n---\n\n## What each app does\n\n**Java** — Spring Boot REST API exercising spring-web's `UriComponentsBuilder` and JSON handling:\n- `GET  /health`              liveness check\n- `GET  /link?base=\u0026path=`   build a URI via `UriComponentsBuilder` (exercises CVE-2024-22243)\n- `POST /people`              accept and echo a list of `Person` objects (JSON via spring-web)\n- `GET  /greet/{name}`        simple greeting\n\n**Python** — FastAPI app exercising three remediated libraries:\n- `GET  /health`              liveness check\n- `POST /token`               generate a signed JWT (`pyjwt`)\n- `GET  /fetch?url=...`       proxy an HTTP GET (`urllib3`)\n- `POST /upload`              parse a multipart file upload (`python-multipart`)\n\n---\n\n## Scan results (grype, Jun 2026)\n\nCVEs are split by layer: **app** = application libraries (java-archive / pip), **OS** =\nbase-image packages. See `cve-report.html` for the full severity breakdown.\n\n### Java — Spring Boot 2.7.0\n\n| Image | Runtime base | App | OS | Total |\n|---|---|---|---|---|\n| unremediated `:ubi10` | ubi-minimal + JRE | 77 | 151 | **228** |\n| remediated `:ubi10` | ubi-minimal + JRE | 66 | 151 | **217** |\n| unremediated `:cgr` | cgr jdk:openjdk-17 | 77 | 0 | **77** |\n| **remediated `:cgr`** | cgr jdk:openjdk-17 | 66 | 0 | **66** |\n\n### Python — FastAPI / PyJWT / urllib3 / python-multipart\n\n| Image | Runtime base | App | OS | Total |\n|---|---|---|---|---|\n| unremediated `:ubi10` | ubi-minimal + python3 (3.12) | 33 | 168 | **201** |\n| remediated `:ubi10` | ubi-minimal + python3 (3.12) | 27 | 168 | **195** |\n| unremediated `:cgr` | cgr python:3.12 | 29 | 14 | **43** |\n| **remediated `:cgr`** | cgr python:3.12 | 23 | 14 | **37** |\n\nBoth `:cgr` images use the org-pinned `python:3.12` tag (Python 3.12.13, matching UBI)\nand the same library pins as their UBI counterparts, so UBI-vs-Chainguard is apples-to-apples.\n\n### Reading these numbers\n\n- **Base image is the dominant lever.** Moving Java from UBI 10 to the Chainguard JDK\n  drops all 151 OS CVEs (228 → 77). Library remediation removes a further 11 (→ 66).\n- **Grype false-positives on `+cgr` versions.** Grype does not recognise the\n  `+cgr.N` local version label as a patched backport, so remediated packages are still\n  matched against upstream advisories. The app-layer reduction shown here therefore\n  *understates* the true remediation — the backported fixes are present in the packages.\n- **Python remediation is a clean app-layer cut.** With the same base, the OS layer is\n  identical between unremediated and remediated, so remediation shows a pure app-layer\n  drop (−6 on both bases: 33→27 on UBI, 29→23 on cgr; High 16→11).\n- **Python `:cgr` uses the org-pinned `python:3.12` image**, which is far more aggressively\n  patched than the public `:latest` (OS layer 14, vs ~99 on `:latest` 3.14). Pinned tags\n  need org access — pass `--build-arg CGR_ORG=\u003cyour-org\u003e`.\n\n## Key CVEs eliminated\n\n### Java — spring-web 5.3.20 → 5.3.39-0.cgr.4\n| Advisory | Severity | Description |\n|---|---|---|\n| GHSA-4wrc-f8pq-fpqp | Critical | Spring URL parsing bypass |\n| GHSA-7phw-cxx7-q9vq | Critical | Spring WebMVC open redirect |\n| GHSA-g5vr-rgqm-vf78 | High | Spring path traversal |\n| GHSA-ccgv-vj62-xf9h | High | Spring web header injection |\n| GHSA-hgjh-9rj2-g67j | High | Spring path traversal via URL |\n| GHSA-2wrp-6fg6-hmc5 | High | Spring open redirect bypass |\n\n### Python — vulnerable PyPI pins → libraries.cgr.dev/python-remediated/\n| Library | Unremediated | Remediated | Top CVEs eliminated |\n|---|---|---|---|\n| PyJWT | 2.3.0 | 2.3.0+cgr.2 | GHSA-ffqj-6fqr-9h24 (High), GHSA-xgmm-8j9v-c9wx (High) |\n| urllib3 | 1.26.8 | 1.26.18+cgr.1 | GHSA-v845-jxx5-vc9f (High), GHSA-qccp-gfcp-xxvc (High) |\n| python-multipart | 0.0.6 | 0.0.6+cgr.1 | GHSA-wp53-j4wj-2cfg (High), GHSA-2jv5-9r88-3w3p (High) |\n\n---\n\n## Prerequisites\n\n- Docker with BuildKit (`export DOCKER_BUILDKIT=1`)\n- [grype](https://github.com/anchore/grype)\n- Chainguard pull tokens (see below)\n\n---\n\n## Credentials\n\n### Java remediated\n```bash\nchainctl auth pull-token --repository=java --parent=\u003cyour-org\u003e\n\nexport CHAINGUARD_JAVA_IDENTITY_ID=\u003cusername\u003e\nexport CHAINGUARD_JAVA_TOKEN=\u003cpassword\u003e\n```\n\n### Python remediated\n```bash\nchainctl auth pull-token --repository=python --parent=\u003cyour-org\u003e\n\nexport CHAINGUARD_PYTHON_IDENTITY_ID=\u003cusername\u003e   # replace any \"/\" with \"_\"\nexport CHAINGUARD_PYTHON_TOKEN=\u003cpassword\u003e\n```\n\n\u003e Credentials are never baked into image layers. The token (password) is passed\n\u003e via a BuildKit `--mount=type=secret`; the username is passed as a `--build-arg`.\n\u003e Both are short-lived — treat them as ephemeral and never commit them.\n\n---\n\n## Run the demo\n\n```bash\nchmod +x scan.sh\n\n./scan.sh java    # Java only\n./scan.sh python  # Python only\n./scan.sh all     # Both\n```\n\n---\n\n## CI — build, sign, push to GHCR\n\n[`.github/workflows/build-sign-push.yml`](.github/workflows/build-sign-push.yml) builds the\nimages, pushes them to `ghcr.io/\u003cowner\u003e/\u003crepo\u003e/\u003cname\u003e:\u003ctag\u003e`, and signs each with\n**cosign keyless** (GitHub OIDC + Sigstore — no signing keys to manage). It runs on pushes\nto `main` and via manual dispatch.\n\nEach build also generates an **SBOM + SLSA provenance** attestation\n(`--sbom --provenance=mode=max`), scans the image with **grype** and uploads SARIF to\nGitHub **code scanning** (informational — the unremediated images are intentionally\nvulnerable), and (for Node) runs the malware-feed gate. Node deps install from a\ncommitted **lockfile** (`npm ci`); Python deps install from committed, fully-hashed\nlockfiles (`pip install --require-hashes -r requirements-*.lock`).\n\nA single Chainguard **assumable identity** (assumed via GitHub OIDC with\n`chainguard-dev/setup-chainctl`) provides everything credentialed: private `:cgr`\nbase-image pulls **and** freshly minted library pull tokens for the remediated builds\n(`chainctl auth pull-token create`). No long-lived registry or library secrets are\nstored. Any image needing the identity is **skipped, not failed** when it's unset — so\nthe two public unremediated UBI images build even on forks.\n\n### Hardened GitHub Actions\n\nThe third-party actions in the workflow are pulled from\n[`chainguard-actions`](https://github.com/chainguard-actions) — hardened, drop-in\nrebuilds with identical inputs/outputs. Each is built from source, has every internal\n`uses:`/image reference pinned to an immutable SHA, and ships a `HARDENING.md` report.\nWe pin each by **commit SHA** (defence against tag-repointing attacks like the 2025\n`tj-actions` compromise). `chainguard-dev/setup-chainctl` is Chainguard's own\nfirst-party action and is used directly.\n\n| In use (SHA-pinned) | Replaces | Version | Hardening findings fixed |\n|---|---|---|---|\n| `chainguard-actions/actions-checkout` | `actions/checkout` | v4.3.1 | none (rebuilt; internal refs SHA-pinned) |\n| `chainguard-actions/docker-setup-buildx-action` | `docker/setup-buildx-action` | v4.1.0 | none |\n| `chainguard-actions/docker-login-action` | `docker/login-action` | v4.2.0 | none |\n| `chainguard-actions/sigstore-cosign-installer` | `sigstore/cosign-installer` | v4.1.2 | **2 (High)** — see below |\n\nPer its `HARDENING.md`, the cosign-installer mirror fixed two high-severity issues in\nthe upstream `action.yml`:\n- **`github-env-injection`** — the user-controlled `install-dir` input was written to\n  `$GITHUB_PATH` without newline sanitization (Linux/macOS and Windows steps), allowing\n  arbitrary `PATH` entries to be injected.\n- **`script-injection`** — unquoted shell expansions of `${{ runner.os }}` / `${{ runner.arch }}`\n  in `case` statements, vulnerable to shell-metacharacter injection.\n\nConfigure under **Settings → Secrets and variables → Actions → Variables**:\n\n| Variable | Value | Used by |\n|---|---|---|\n| `CGR_ORG` | your org, e.g. `example.com` | all `:cgr` images + library token minting |\n| `CGR_IDENTITY` | UIDP of the assumable identity | every credentialed image (6 of 8) |\n\nCreate the identity scoped to this repo's `main` branch and grant it **registry pull**\n(for `:cgr` base images) **and library pull-token create** (for the remediated builds);\nsee the workflow header for details.\n\n**Optional JFrog mirror.** If `JFROG_USERNAME` / `JFROG_PASSWORD` secrets are set, each\nimage is also mirrored (by digest, no rebuild) to\n`chainguardlibraries.jfrog.io/carty-demo-images/\u003cname\u003e:\u003ctag\u003e` and signed there too. The\nmirror is skipped — not failed — when those secrets are absent.\n\n**Malware check (Node).** The Node images run [`node/malware-check.sh`](node/malware-check.sh)\nagainst the built image: it enumerates installed npm dependencies and cross-references\nthem against the Chainguard Libraries malware feed\n(`libraries.cgr.dev/javascript/-/api/malware`), failing the build if any package is\nflagged at its exact installed version. Requires the identity's org to be entitled to\nthe `javascript` ecosystem (same as the remediated Node builds).\n\n### Verify the signed images\n\nEach image is signed keyless, so verification asserts it was built by this repo's\nworkflow (no key distribution). The packages are private by default — authenticate to\nGHCR first (skip if the packages are made public):\n\n```bash\necho \"$GITHUB_TOKEN\" | docker login ghcr.io -u \u003cyour-username\u003e --password-stdin\n# or: gh auth token | docker login ghcr.io -u \u003cyour-username\u003e --password-stdin\n```\n\nVerify a single image:\n\n```bash\ncosign verify ghcr.io/cartyc/cve-comparision/python-remediated:cgr \\\n  --certificate-identity-regexp \"^https://github.com/cartyc/cve-comparision/.github/workflows/build-sign-push.yml@refs/heads/main$\" \\\n  --certificate-oidc-issuer https://token.actions.githubusercontent.com \\\n  -o text\n```\n\nVerify all eight at once:\n\n```bash\nREPO=ghcr.io/cartyc/cve-comparision\nfor img in java-unremediated java-remediated python-unremediated python-remediated; do\n  for tag in ubi10 cgr; do\n    echo \"== $img:$tag ==\"\n    cosign verify \"$REPO/$img:$tag\" \\\n      --certificate-identity-regexp \"^https://github.com/cartyc/cve-comparision/.github/workflows/build-sign-push.yml@refs/heads/main$\" \\\n      --certificate-oidc-issuer https://token.actions.githubusercontent.com \\\n      \u003e/dev/null \u0026\u0026 echo \"OK\" || echo \"FAILED\"\n  done\ndone\n```\n\nThe `--certificate-identity-regexp` pins the signer to this workflow on `main`; the\n`--certificate-oidc-issuer` pins it to GitHub's OIDC provider. Both must match or\nverification fails.\n\n---\n\n## License\n\nLicensed under the Apache License, Version 2.0. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcartyc%2Fcve-comparision","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcartyc%2Fcve-comparision","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcartyc%2Fcve-comparision/lists"}