From c01ad7a02b15423575b80e71f5df79d1ccaa46bb Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Wed, 7 Oct 2026 11:56:34 +0200 Subject: [PATCH] fix: removed all non secret dependency proxy content --- src/nix-tests/nixmd.mts | 20 +++++++++++ .../dependency-proxy/ci-runners.mdx | 2 +- .../how-to-guides/dependency-proxy/index.mdx | 16 +++++---- .../dependency-proxy/setup-composer-proxy.mdx | 12 ++++--- .../dependency-proxy/setup-debian-proxy.mdx | 14 ++++---- .../dependency-proxy/setup-go-proxy.mdx | 6 ++-- .../dependency-proxy/setup-maven-proxy.mdx | 12 ++++--- .../dependency-proxy/setup-npm-proxy.mdx | 10 +++--- .../dependency-proxy/setup-oci-proxy.mdx | 36 ++++++++----------- .../dependency-proxy/setup-pypi-proxy.mdx | 8 +++-- 10 files changed, 82 insertions(+), 54 deletions(-) diff --git a/src/nix-tests/nixmd.mts b/src/nix-tests/nixmd.mts index 62aa913..faa51cb 100644 --- a/src/nix-tests/nixmd.mts +++ b/src/nix-tests/nixmd.mts @@ -78,6 +78,7 @@ const VARIABLE_PATTERNS: [RegExp, string][] = [ [/Bearer +[^"'\s]+/g, 'Bearer ${token}'], [/(X-Asset-Name:[ \t]*)[^"'\s]+/g, '$1${assetName}'], [//g, '${assetName}'], + [//g, '${secret}'], [/ghcr\.io\/org\/image:tag/g, TEST_IMAGE], [/registry\.example\.com\/org\/image:tag/g, TEST_IMAGE], ] @@ -232,6 +233,24 @@ function assertRequiredEnv(): void { } } +function dependencyProxySecret(): string { + const urls = JSON.parse( + execFileSync( + 'devguard-scanner', + [ + 'curl', + '--token', + process.env.token!, + '-s', + `${process.env.apiUrl}/api/v1/organizations/${process.env.assetName}/dependency-proxy-urls/`, + ], + { encoding: 'utf8' }, + ), + ) + + return urls.oci.split('/').pop() +} + function main(): void { assertRequiredEnv() @@ -240,6 +259,7 @@ function main(): void { DEVGUARD_APIURL: process.env.apiUrl, // host[:port] of the API, e.g. for pip's trusted-host (no URLs allowed there) apiHost: new URL(process.env.apiUrl!).host, + secret: dependencyProxySecret(), } rmSync(TMP_DIR, { recursive: true, force: true }) diff --git a/src/pages/how-to-guides/dependency-proxy/ci-runners.mdx b/src/pages/how-to-guides/dependency-proxy/ci-runners.mdx index 75d0556..0f6e66d 100644 --- a/src/pages/how-to-guides/dependency-proxy/ci-runners.mdx +++ b/src/pages/how-to-guides/dependency-proxy/ci-runners.mdx @@ -29,7 +29,7 @@ The proxy URLs in the runner configuration contain a dependency proxy secret: | Go modules | `GOPROXY` | `https:///api/v1/dependency-proxy//go/` | | PyPI | `PIP_INDEX_URL` | `https:///api/v1/dependency-proxy//pypi/simple/` | -The secret links the requests to your organization, project or repository in DevGuard, so the proxy applies the rules and the [minimum package age](/how-to-guides/dependency-proxy#minimum-package-age) configured there. Requests without a secret are only checked against the malicious package database. +The secret links the requests to your organization, project or repository in DevGuard, so the proxy applies the rules and the [minimum package age](/how-to-guides/dependency-proxy#minimum-package-age) configured there. Requests without a valid secret are rejected. Treat the secret like a credential. Store the runner configuration in a Kubernetes secret or a sealed/encrypted secret instead of committing it in plain text. diff --git a/src/pages/how-to-guides/dependency-proxy/index.mdx b/src/pages/how-to-guides/dependency-proxy/index.mdx index 102cb15..43117ac 100644 --- a/src/pages/how-to-guides/dependency-proxy/index.mdx +++ b/src/pages/how-to-guides/dependency-proxy/index.mdx @@ -29,13 +29,15 @@ DevGuard includes a built-in dependency proxy that acts as a protective layer be | Ecosystem | Registry URL | |-----------|-------------| -| npm | `/api/v1/dependency-proxy/npm` | -| Go modules | `/api/v1/dependency-proxy/go` | -| PyPI | `/api/v1/dependency-proxy/pypi/simple` | -| Maven | `/api/v1/dependency-proxy/maven` | -| Composer (Packagist) | `/api/v1/dependency-proxy/composer` | -| Debian (apt) | `/api/v1/dependency-proxy/deb/debian` | -| OCI (container images) | `/v2/` — pull `//:` | +| npm | `/api/v1/dependency-proxy//npm` | +| Go modules | `/api/v1/dependency-proxy//go` | +| PyPI | `/api/v1/dependency-proxy//pypi/simple` | +| Maven | `/api/v1/dependency-proxy//maven` | +| Composer (Packagist) | `/api/v1/dependency-proxy//composer` | +| Debian (apt) | `/api/v1/dependency-proxy//deb/debian` | +| OCI (container images) | `/v2//` — pull `///:` | + +`` is the dependency proxy secret of your organization, project or repository. It links the requests to the rules configured there, and the proxy rejects requests without a valid secret. The complete proxy URLs are shown in its **Dependency Proxy Settings** in DevGuard. For setup instructions, see the ecosystem-specific guides: diff --git a/src/pages/how-to-guides/dependency-proxy/setup-composer-proxy.mdx b/src/pages/how-to-guides/dependency-proxy/setup-composer-proxy.mdx index 868f2d9..e7857c7 100644 --- a/src/pages/how-to-guides/dependency-proxy/setup-composer-proxy.mdx +++ b/src/pages/how-to-guides/dependency-proxy/setup-composer-proxy.mdx @@ -23,7 +23,9 @@ Composer and Packagist have responded with [several hardening measures](https:// The DevGuard dependency proxy sits between Composer and Packagist and enforces protection centrally, for every Composer version and without any client configuration. Every package request is checked against the [OSV dataset](https://osv.dev) before it reaches your `vendor` directory, blocking known malicious packages automatically. Combined with the [minimum package age](/how-to-guides/dependency-proxy#minimum-package-age), the proxy complements Composer's own policies rather than replacing them. -- **Registry URL**: `/api/v1/dependency-proxy/composer` +- **Registry URL**: `/api/v1/dependency-proxy//composer` + +Replace `` with the dependency proxy secret of your organization, project or repository. The complete proxy URLs are shown in its **Dependency Proxy Settings** in DevGuard. ## Configuration @@ -31,7 +33,7 @@ The DevGuard dependency proxy sits between Composer and Packagist and enforces p ```bash {ignore} composer config --global repo.packagist.org false -composer config --global repo.devguard composer https:///api/v1/dependency-proxy/composer +composer config --global repo.devguard composer https:///api/v1/dependency-proxy//composer ``` To scope it to a single project, run the same commands without `--global` in the project directory. Composer then adds the repositories to your `composer.json`: @@ -42,7 +44,7 @@ To scope it to a single project, run the same commands without `--global` in the { "name": "devguard", "type": "composer", - "url": "https:///api/v1/dependency-proxy/composer" + "url": "https:///api/v1/dependency-proxy//composer" }, { "packagist.org": false @@ -81,7 +83,7 @@ composer init --name=devguard/composer-proxy-test # only needed for a DevGuard instance without TLS, such as a local test instance composer config secure-http false composer config repo.packagist.org false -composer config repo.devguard composer https:///api/v1/dependency-proxy/composer +composer config repo.devguard composer https:///api/v1/dependency-proxy//composer # psr/log installs through the proxy composer require psr/log:3.0.2 @@ -96,7 +98,7 @@ fi for path in p2/fake-org/malicious-package.json \ dist/fake-org/malicious-package/1.0.0.zip; do status=$(curl -s -o /dev/null -w "%{http_code}" \ - "https:///api/v1/dependency-proxy/composer/$path") + "https:///api/v1/dependency-proxy//composer/$path") if [ "$status" != "403" ]; then echo "expected 403 Forbidden for $path, got $status" >&2 exit 1 diff --git a/src/pages/how-to-guides/dependency-proxy/setup-debian-proxy.mdx b/src/pages/how-to-guides/dependency-proxy/setup-debian-proxy.mdx index 52eeaca..51f2146 100644 --- a/src/pages/how-to-guides/dependency-proxy/setup-debian-proxy.mdx +++ b/src/pages/how-to-guides/dependency-proxy/setup-debian-proxy.mdx @@ -21,28 +21,30 @@ System packages are part of your supply chain too. In 2024, the backdoored `xz-u The DevGuard dependency proxy sits between apt and the Debian archive. Every package download is checked against the [OSV dataset](https://osv.dev) before it reaches your system, blocking known malicious packages automatically. -- **Registry URL**: `/api/v1/dependency-proxy/deb/debian` -- **Security updates**: `/api/v1/dependency-proxy/deb/debian-security` +- **Registry URL**: `/api/v1/dependency-proxy//deb/debian` +- **Security updates**: `/api/v1/dependency-proxy//deb/debian-security` + +Replace `` with the dependency proxy secret of your organization, project or repository. The complete proxy URLs are shown in its **Dependency Proxy Settings** in DevGuard. ## Configuration Since Debian 12, apt reads its package sources from `/etc/apt/sources.list.d/debian.sources`. Point both the main archive and the security archive at DevGuard by replacing the default mirror: ```bash {ignore} -sed -i 's|http://deb.debian.org|https:///api/v1/dependency-proxy/deb|' /etc/apt/sources.list.d/debian.sources +sed -i 's|http://deb.debian.org|https:///api/v1/dependency-proxy//deb|' /etc/apt/sources.list.d/debian.sources ``` Afterwards, the file looks like this: ```text Types: deb -URIs: https:///api/v1/dependency-proxy/deb/debian +URIs: https:///api/v1/dependency-proxy//deb/debian Suites: trixie trixie-updates Components: main Signed-By: /usr/share/keyrings/debian-archive-keyring.pgp Types: deb -URIs: https:///api/v1/dependency-proxy/deb/debian-security +URIs: https:///api/v1/dependency-proxy//deb/debian-security Suites: trixie-security Components: main Signed-By: /usr/share/keyrings/debian-archive-keyring.pgp @@ -77,7 +79,7 @@ apt therefore always sees the newest version, tries to download it, receives `40 DevGuard ships a test package, `fake-malicious-package`, that is permanently flagged as malicious for all versions. The script below runs apt in a throwaway `debian:trixie-slim` container whose only package source is the DevGuard proxy, so your own system stays untouched: ```bash -DEVGUARD_DEB="https:///api/v1/dependency-proxy/deb" +DEVGUARD_DEB="https:///api/v1/dependency-proxy//deb" # hello installs through the proxy, apt verifies the signed package lists as usual docker run --rm -e DEVGUARD_DEB="$DEVGUARD_DEB" debian:trixie-slim bash -euc ' diff --git a/src/pages/how-to-guides/dependency-proxy/setup-go-proxy.mdx b/src/pages/how-to-guides/dependency-proxy/setup-go-proxy.mdx index 8354362..18200be 100644 --- a/src/pages/how-to-guides/dependency-proxy/setup-go-proxy.mdx +++ b/src/pages/how-to-guides/dependency-proxy/setup-go-proxy.mdx @@ -19,14 +19,16 @@ import { Callout } from '@document-writing-tools/kernux-theme' The XZ Utils backdoor discovered in 2024 (CVE-2024-3094) was a stark reminder that supply chain attacks are not limited to dynamic language ecosystems — a malicious contributor spent years gaining trust before inserting a backdoor into a widely deployed compression library. While the Go module proxy protocol provides strong integrity guarantees through checksums, it does not protect against modules that are malicious by design. The DevGuard dependency proxy adds that missing layer, checking every module against the [OSV dataset](https://osv.dev) before it is written to your module cache. -- **Registry URL**: `/api/v1/dependency-proxy/go` +- **Registry URL**: `/api/v1/dependency-proxy//go` + +Replace `` with the dependency proxy secret of your organization, project or repository. The complete proxy URLs are shown in its **Dependency Proxy Settings** in DevGuard. ## Configuration Set the `GOPROXY` environment variable to point at DevGuard. Go will use it for all subsequent module downloads in that shell session: ```bash -export GOPROXY="https:///api/v1/dependency-proxy/go" +export GOPROXY="https:///api/v1/dependency-proxy//go" ``` To make this permanent, add it to your CI environment or shell profile. For project-scoped configuration, set it in your CI/CD platform's environment variable configuration alongside your other build variables. diff --git a/src/pages/how-to-guides/dependency-proxy/setup-maven-proxy.mdx b/src/pages/how-to-guides/dependency-proxy/setup-maven-proxy.mdx index 3942a3b..2d72a85 100644 --- a/src/pages/how-to-guides/dependency-proxy/setup-maven-proxy.mdx +++ b/src/pages/how-to-guides/dependency-proxy/setup-maven-proxy.mdx @@ -21,7 +21,9 @@ Maven Central is a target for typosquatting just like npm or PyPI. Attackers pub The DevGuard dependency proxy sits between your build tool and Maven Central. Every artifact request is checked against the [OSV dataset](https://osv.dev) before it reaches your local repository, blocking known malicious packages automatically. -- **Registry URL**: `/api/v1/dependency-proxy/maven` +- **Registry URL**: `/api/v1/dependency-proxy//maven` + +Replace `` with the dependency proxy secret of your organization, project or repository. The complete proxy URLs are shown in its **Dependency Proxy Settings** in DevGuard. ## Configuration @@ -35,7 +37,7 @@ Add a mirror to your Maven settings (`~/.m2/settings.xml`). `* devguard DevGuard Dependency Proxy - https:///api/v1/dependency-proxy/maven + https:///api/v1/dependency-proxy//maven * @@ -55,7 +57,7 @@ Replace `mavenCentral()` with the proxy in your `settings.gradle.kts`: ```kotlin dependencyResolutionManagement { repositories { - maven { url = uri("https:///api/v1/dependency-proxy/maven") } + maven { url = uri("https:///api/v1/dependency-proxy//maven") } } } ``` @@ -77,7 +79,7 @@ cat > settings.xml < devguard - https:///api/v1/dependency-proxy/maven + https:///api/v1/dependency-proxy//maven * @@ -99,7 +101,7 @@ fi for path in com/fake/malicious-package/1.0.0/malicious-package-1.0.0.pom \ com/fake/malicious-package/maven-metadata.xml; do status=$(curl -s -o /dev/null -w "%{http_code}" \ - "https:///api/v1/dependency-proxy/maven/$path") + "https:///api/v1/dependency-proxy//maven/$path") if [ "$status" != "403" ]; then echo "expected 403 Forbidden for $path, got $status" >&2 exit 1 diff --git a/src/pages/how-to-guides/dependency-proxy/setup-npm-proxy.mdx b/src/pages/how-to-guides/dependency-proxy/setup-npm-proxy.mdx index 5a017a3..cb7fb9f 100644 --- a/src/pages/how-to-guides/dependency-proxy/setup-npm-proxy.mdx +++ b/src/pages/how-to-guides/dependency-proxy/setup-npm-proxy.mdx @@ -21,14 +21,16 @@ Supply chain attacks through npm are a growing threat. In 2025, attackers publis The DevGuard dependency proxy sits between your developers and the public npm registry. Every package request is checked against the [OSV dataset](https://osv.dev) before it reaches your machine, blocking known malicious packages automatically. -- **Registry URL**: `/api/v1/dependency-proxy/npm` +- **Registry URL**: `/api/v1/dependency-proxy//npm` + +Replace `` with the dependency proxy secret of your organization, project or repository. The complete proxy URLs are shown in its **Dependency Proxy Settings** in DevGuard. ## Configuration Point npm at the DevGuard proxy by adding a registry entry to your `.npmrc`. This file can live at the project level (`./.npmrc`) to scope only that project, or at the user level (`~/.npmrc`) to apply globally. ```ini -registry=https:///api/v1/dependency-proxy/npm +registry=https:///api/v1/dependency-proxy//npm ``` Once set, all `npm install` and `npm ci` invocations route through DevGuard transparently. No changes to your `package.json` or CI scripts are required. @@ -67,7 +69,7 @@ The same check as a script, installing each package separately so the failure of ```bash mkdir -p npm-proxy-test && cd npm-proxy-test npm init -y > /dev/null -echo "registry=https:///api/v1/dependency-proxy/npm" > .npmrc +echo "registry=https:///api/v1/dependency-proxy//npm" > .npmrc # lodash installs through the proxy npm install lodash@^4.17.21 @@ -93,7 +95,7 @@ Semver equality is not textual: `1.0.0` and `v1.0.0` denote the same version, an DevGuard ships a second test package, `fake-malicious-npm-package-versioned`, flagged at the specific version `v1.0.0` rather than for all versions, so it can prove version comparison itself is correct. The proxy checks a tarball download against the malicious package database before ever fetching it from upstream, so the file below does not need to exist for this check: ```bash -REGISTRY_URL="https:///api/v1/dependency-proxy/npm" +REGISTRY_URL="https:///api/v1/dependency-proxy//npm" # 1.0.0 is an equivalent spelling of the flagged v1.0.0 and must be rejected status=$(curl -s -o /dev/null -w "%{http_code}" \ diff --git a/src/pages/how-to-guides/dependency-proxy/setup-oci-proxy.mdx b/src/pages/how-to-guides/dependency-proxy/setup-oci-proxy.mdx index 1fe1df3..e19ce6b 100644 --- a/src/pages/how-to-guides/dependency-proxy/setup-oci-proxy.mdx +++ b/src/pages/how-to-guides/dependency-proxy/setup-oci-proxy.mdx @@ -21,30 +21,24 @@ Container images are dependencies too: a single `FROM` line or an image referenc ## Configuration -The proxy implements the OCI Distribution API, so every standard client (Docker, Podman, containerd, crane, skopeo) can pull through it. Prefix the fully qualified image reference with your DevGuard host: +The proxy implements the OCI Distribution API, so every standard client (Docker, Podman, containerd, crane, skopeo) can pull through it. Prefix the fully qualified image reference with your DevGuard host and the dependency proxy secret of your organization, project or repository. The complete proxy URLs are shown in its **Dependency Proxy Settings** in DevGuard: ```bash {ignore} -docker pull /docker.io/library/nginx:latest +docker pull //docker.io/library/nginx:latest ``` The registry must be part of the reference — `docker.io/library/nginx`, not just `nginx`. The following upstream registries are supported: | Registry | Example reference | |----------|-------------------| -| Docker Hub | `/docker.io/library/alpine:3.20` | -| GitHub Container Registry | `/ghcr.io/org/image:tag` | -| Quay | `/quay.io/org/image:tag` | -| Google Container Registry | `/gcr.io/project/image:tag` | -| Kubernetes | `/registry.k8s.io/pause:3.10` | -| Amazon ECR Public | `/public.ecr.aws/org/image:tag` | -| Microsoft Container Registry | `/mcr.microsoft.com/dotnet/runtime:8.0` | -| GitLab Container Registry | `/registry.gitlab.com/group/project:tag` | - -To apply the firewall rules of a repository, use its proxy secret as the first path segment: - -```bash {ignore} -docker pull //docker.io/library/nginx:latest -``` +| Docker Hub | `//docker.io/library/alpine:3.20` | +| GitHub Container Registry | `//ghcr.io/org/image:tag` | +| Quay | `//quay.io/org/image:tag` | +| Google Container Registry | `//gcr.io/project/image:tag` | +| Kubernetes | `//registry.k8s.io/pause:3.10` | +| Amazon ECR Public | `//public.ecr.aws/org/image:tag` | +| Microsoft Container Registry | `//mcr.microsoft.com/dotnet/runtime:8.0` | +| GitLab Container Registry | `//registry.gitlab.com/group/project:tag` | The [minimum package age](/how-to-guides/dependency-proxy#minimum-package-age) does not apply to container images. Unlike npm, PyPI and Go, the OCI registry protocol has no publish date for an image: the `created` timestamp inside an image is set by the build and is often fixed to 1970 or missing for reproducible builds, and registry-side push times are only available through registry-specific APIs. Pin images by digest (`image@sha256:…`) to control exactly which image version you pull. @@ -60,17 +54,17 @@ DevGuard ships a test image, `docker.io/fake-org/malicious-image`, flagged as ma ```bash # alpine is pulled through the proxy: tags, manifest and all layers -crane ls --insecure /docker.io/library/alpine > /dev/null -crane pull --insecure /docker.io/library/alpine:3.20 alpine.tar +crane ls --insecure //docker.io/library/alpine > /dev/null +crane pull --insecure //docker.io/library/alpine:3.20 alpine.tar # docker.io/fake-org/malicious-image must be rejected -if crane manifest --insecure /docker.io/fake-org/malicious-image:latest; then +if crane manifest --insecure //docker.io/fake-org/malicious-image:latest; then echo "docker.io/fake-org/malicious-image was NOT blocked - check your proxy configuration" >&2 exit 1 fi # check that the proxy answered with 403 Forbidden -status=$(curl -s -o /dev/null -w "%{http_code}" "https:///v2/docker.io/fake-org/malicious-image/manifests/latest") +status=$(curl -s -o /dev/null -w "%{http_code}" "https:///v2//docker.io/fake-org/malicious-image/manifests/latest") if [ "$status" != "403" ]; then echo "expected 403 Forbidden for docker.io/fake-org/malicious-image, got $status" >&2 exit 1 @@ -85,7 +79,7 @@ Tags are compared as versions the same way Go module versions are: `1.0.0` and ` # 1.0.0 and v1.0.0 both denote the flagged version and must be rejected for tag in 1.0.0 v1.0.0; do status=$(curl -s -o /dev/null -w "%{http_code}" \ - "https:///v2/docker.io/fake-org/malicious-image/manifests/$tag") + "https:///v2//docker.io/fake-org/malicious-image/manifests/$tag") if [ "$status" != "403" ]; then echo "expected 403 Forbidden for docker.io/fake-org/malicious-image:$tag, got $status" >&2 exit 1 diff --git a/src/pages/how-to-guides/dependency-proxy/setup-pypi-proxy.mdx b/src/pages/how-to-guides/dependency-proxy/setup-pypi-proxy.mdx index 8cd8c78..a65b625 100644 --- a/src/pages/how-to-guides/dependency-proxy/setup-pypi-proxy.mdx +++ b/src/pages/how-to-guides/dependency-proxy/setup-pypi-proxy.mdx @@ -21,7 +21,9 @@ PyPI is one of the most actively abused package registries for supply chain atta The DevGuard dependency proxy sits between your Python tooling and the upstream PyPI registry, checking every package against the [OSV dataset](https://osv.dev) before it reaches your environment. -- **Registry URL**: `/api/v1/dependency-proxy/pypi/simple` +- **Registry URL**: `/api/v1/dependency-proxy//pypi/simple` + +Replace `` with the dependency proxy secret of your organization, project or repository. The complete proxy URLs are shown in its **Dependency Proxy Settings** in DevGuard. ## Configuration @@ -31,7 +33,7 @@ Create or edit `pip.conf` (Linux/macOS: `~/.config/pip/pip.conf`, Windows: `%APP ```ini [global] -index-url = https:///api/v1/dependency-proxy/pypi/simple +index-url = https:///api/v1/dependency-proxy//pypi/simple trusted-host = ``` @@ -47,7 +49,7 @@ python3 -m venv .venv Then point pip at the proxy: ```bash -export PIP_INDEX_URL="https:///api/v1/dependency-proxy/pypi/simple" +export PIP_INDEX_URL="https:///api/v1/dependency-proxy//pypi/simple" export PIP_TRUSTED_HOST="" pip install requests ```