Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/nix-tests/nixmd.mts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const VARIABLE_PATTERNS: [RegExp, string][] = [
[/Bearer +[^"'\s]+/g, 'Bearer ${token}'],
[/(X-Asset-Name:[ \t]*)[^"'\s]+/g, '$1${assetName}'],
[/<assetName>/g, '${assetName}'],
[/<secret>/g, '${secret}'],
[/ghcr\.io\/org\/image:tag/g, TEST_IMAGE],
[/registry\.example\.com\/org\/image:tag/g, TEST_IMAGE],
]
Expand Down Expand Up @@ -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()

Expand All @@ -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 })
Expand Down
2 changes: 1 addition & 1 deletion src/pages/how-to-guides/dependency-proxy/ci-runners.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ The proxy URLs in the runner configuration contain a dependency proxy secret:
| Go modules | `GOPROXY` | `https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/go/` |
| PyPI | `PIP_INDEX_URL` | `https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/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.

<Callout type="warning">
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.
Expand Down
16 changes: 9 additions & 7 deletions src/pages/how-to-guides/dependency-proxy/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<your-devguard-host>/<registry>/<image>:<tag>` |
| npm | `/api/v1/dependency-proxy/<secret>/npm` |
| Go modules | `/api/v1/dependency-proxy/<secret>/go` |
| PyPI | `/api/v1/dependency-proxy/<secret>/pypi/simple` |
| Maven | `/api/v1/dependency-proxy/<secret>/maven` |
| Composer (Packagist) | `/api/v1/dependency-proxy/<secret>/composer` |
| Debian (apt) | `/api/v1/dependency-proxy/<secret>/deb/debian` |
| OCI (container images) | `/v2/<secret>/` — pull `<your-devguard-host>/<secret>/<registry>/<image>:<tag>` |

`<secret>` 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:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,17 @@ 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**: `<your-devguard-url>/api/v1/dependency-proxy/composer`
- **Registry URL**: `<your-devguard-url>/api/v1/dependency-proxy/<secret>/composer`

Replace `<secret>` 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

[Disable the default Packagist repository](https://getcomposer.org/doc/05-repositories.md#disabling-packagist-org) and add the DevGuard proxy instead. Disabling `packagist.org` is required: otherwise Composer keeps it as a fallback and downloads packages directly from Packagist whenever the proxy does not serve them. To apply it to every project on your machine, set it in your global Composer configuration:

```bash {ignore}
composer config --global repo.packagist.org false
composer config --global repo.devguard composer https://<your-devguard-url>/api/v1/dependency-proxy/composer
composer config --global repo.devguard composer https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/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`:
Expand All @@ -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://<your-devguard-url>/api/v1/dependency-proxy/composer"
"url": "https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/composer"
},
{
"packagist.org": false
Expand Down Expand Up @@ -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://<your-devguard-url>/api/v1/dependency-proxy/composer
composer config repo.devguard composer https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/composer

# psr/log installs through the proxy
composer require psr/log:3.0.2
Expand All @@ -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://<your-devguard-url>/api/v1/dependency-proxy/composer/$path")
"https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/composer/$path")
if [ "$status" != "403" ]; then
echo "expected 403 Forbidden for $path, got $status" >&2
exit 1
Expand Down
14 changes: 8 additions & 6 deletions src/pages/how-to-guides/dependency-proxy/setup-debian-proxy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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**: `<your-devguard-url>/api/v1/dependency-proxy/deb/debian`
- **Security updates**: `<your-devguard-url>/api/v1/dependency-proxy/deb/debian-security`
- **Registry URL**: `<your-devguard-url>/api/v1/dependency-proxy/<secret>/deb/debian`
- **Security updates**: `<your-devguard-url>/api/v1/dependency-proxy/<secret>/deb/debian-security`

Replace `<secret>` 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://<your-devguard-url>/api/v1/dependency-proxy/deb|' /etc/apt/sources.list.d/debian.sources
sed -i 's|http://deb.debian.org|https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/deb|' /etc/apt/sources.list.d/debian.sources
```

Afterwards, the file looks like this:

```text
Types: deb
URIs: https://<your-devguard-url>/api/v1/dependency-proxy/deb/debian
URIs: https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/deb/debian
Suites: trixie trixie-updates
Components: main
Signed-By: /usr/share/keyrings/debian-archive-keyring.pgp

Types: deb
URIs: https://<your-devguard-url>/api/v1/dependency-proxy/deb/debian-security
URIs: https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/deb/debian-security
Suites: trixie-security
Components: main
Signed-By: /usr/share/keyrings/debian-archive-keyring.pgp
Expand Down Expand Up @@ -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://<your-devguard-url>/api/v1/dependency-proxy/deb"
DEVGUARD_DEB="https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/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 '
Expand Down
6 changes: 4 additions & 2 deletions src/pages/how-to-guides/dependency-proxy/setup-go-proxy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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**: `<your-devguard-url>/api/v1/dependency-proxy/go`
- **Registry URL**: `<your-devguard-url>/api/v1/dependency-proxy/<secret>/go`

Replace `<secret>` 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://<your-devguard-url>/api/v1/dependency-proxy/go"
export GOPROXY="https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/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.
Expand Down
12 changes: 7 additions & 5 deletions src/pages/how-to-guides/dependency-proxy/setup-maven-proxy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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**: `<your-devguard-url>/api/v1/dependency-proxy/maven`
- **Registry URL**: `<your-devguard-url>/api/v1/dependency-proxy/<secret>/maven`

Replace `<secret>` 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

Expand All @@ -35,7 +37,7 @@ Add a mirror to your Maven settings (`~/.m2/settings.xml`). `<mirrorOf>*</mirror
<mirror>
<id>devguard</id>
<name>DevGuard Dependency Proxy</name>
<url>https://<your-devguard-url>/api/v1/dependency-proxy/maven</url>
<url>https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/maven</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
Expand All @@ -55,7 +57,7 @@ Replace `mavenCentral()` with the proxy in your `settings.gradle.kts`:
```kotlin
dependencyResolutionManagement {
repositories {
maven { url = uri("https://<your-devguard-url>/api/v1/dependency-proxy/maven") }
maven { url = uri("https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/maven") }
}
}
```
Expand All @@ -77,7 +79,7 @@ cat > settings.xml <<EOF
<mirrors>
<mirror>
<id>devguard</id>
<url>https://<your-devguard-url>/api/v1/dependency-proxy/maven</url>
<url>https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/maven</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
Expand All @@ -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://<your-devguard-url>/api/v1/dependency-proxy/maven/$path")
"https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/maven/$path")
if [ "$status" != "403" ]; then
echo "expected 403 Forbidden for $path, got $status" >&2
exit 1
Expand Down
10 changes: 6 additions & 4 deletions src/pages/how-to-guides/dependency-proxy/setup-npm-proxy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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**: `<your-devguard-url>/api/v1/dependency-proxy/npm`
- **Registry URL**: `<your-devguard-url>/api/v1/dependency-proxy/<secret>/npm`

Replace `<secret>` 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://<your-devguard-url>/api/v1/dependency-proxy/npm
registry=https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/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.
Expand Down Expand Up @@ -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://<your-devguard-url>/api/v1/dependency-proxy/npm" > .npmrc
echo "registry=https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/npm" > .npmrc

# lodash installs through the proxy
npm install lodash@^4.17.21
Expand All @@ -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://<your-devguard-url>/api/v1/dependency-proxy/npm"
REGISTRY_URL="https://<your-devguard-url>/api/v1/dependency-proxy/<secret>/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}" \
Expand Down
Loading
Loading