Watch
1
0
Fork
You've already forked pkg-proxy
1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-11-09 15:49:16 -05:00
Commit graph

1,094 commits

Author SHA1 Message Date
Simon Chrzanowski
50e65a31d8
Serve npm attestations through the proxy
Packuments list each version's provenance at dist.attestations.url on
registry.npmjs.org. npm takes only the path of that URL and resolves it
against the configured registry, so behind the proxy `npm audit
signatures` asked for /-/npm/v1/attestations/... at the proxy's root,
outside /npm, and could not verify attestations.

The rewrite now points dist.attestations.url at
/npm/-/npm/v1/attestations/{name}@{version}, and the handler serves that
path. It reads the version's attestations URL from the packument and
fetches it from the package's registry, the route's for routed packages;
a URL on another host is not followed and the registry's standard path
is asked instead. Attestations never change once published, so they are
cached as immutable metadata.

Versions blocked by the denylist get 403 and versions held back by
cooldown 404, as their tarballs do. Pushed versions have no upstream
attestations and get 404.
2026-10-30 08:26:27 +00:00
Andrew Nesbitt
e9b74be959
Merge pull request #633 from simonchrz/perf/rewrite-cache-gzip
Keep rewritten metadata gzip-only and longer with cooldown
2026-10-29 20:37:24 +00:00
Simon Chrzanowski
58e87c2571 Cache cooldown rewrites for a 24th of the shortest cooldown
An hour is long next to a cooldown of a few hours. The TTL is now a 24th
of the shortest cooldown configured, between a minute and an hour, and
the RubyGems and Hex section no longer says a minute.
2026-10-29 19:19:12 +01:00
simon chrzanowski
efe682327c
Merge pull request #635 from git-pkgs/dependabot/go_modules/github.com/klauspost/compress-1.20.3
Bump github.com/klauspost/compress from 1.20.1 to 1.20.3
2026-10-29 19:05:07 +01:00
simon chrzanowski
5288f6ae13
Merge pull request #637 from git-pkgs/dependabot/go_modules/modernc.org/sqlite-1.61.0
Bump modernc.org/sqlite from 1.60.1 to 1.61.0
2026-10-29 19:04:42 +01:00
simon chrzanowski
c9a3a6496d
Merge pull request #636 from git-pkgs/dependabot/go_modules/github.com/git-pkgs/archives-0.10.0
Bump github.com/git-pkgs/archives from 0.9.0 to 0.10.0
2026-10-29 19:04:36 +01:00
simon chrzanowski
568f4024b5
Merge pull request #634 from git-pkgs/dependabot/go_modules/github.com/git-pkgs/magic-0.6.0
Bump github.com/git-pkgs/magic from 0.5.0 to 0.6.0
2026-10-29 19:04:30 +01:00
dependabot[bot]
b31ee702a5
Bump modernc.org/sqlite from 1.60.1 to 1.61.0
Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.60.1 to 1.61.0.
- [Changelog](https://gitlab.com/cznic/sqlite/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.60.1...v1.61.0)

---
updated-dependencies:
- dependency-name: modernc.org/sqlite
  dependency-version: 1.61.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-29 15:14:31 +00:00
dependabot[bot]
359ba32866
Bump github.com/git-pkgs/archives from 0.9.0 to 0.10.0
Bumps [github.com/git-pkgs/archives](https://github.com/git-pkgs/archives) from 0.9.0 to 0.10.0.
- [Commits](https://github.com/git-pkgs/archives/compare/v0.9.0...v0.10.0)

---
updated-dependencies:
- dependency-name: github.com/git-pkgs/archives
  dependency-version: 0.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-29 15:14:22 +00:00
dependabot[bot]
ce60611391
Bump github.com/klauspost/compress from 1.20.1 to 1.20.3
Bumps [github.com/klauspost/compress](https://github.com/klauspost/compress) from 1.20.1 to 1.20.3.
- [Release notes](https://github.com/klauspost/compress/releases)
- [Commits](https://github.com/klauspost/compress/compare/v1.20.1...v1.20.3)

---
updated-dependencies:
- dependency-name: github.com/klauspost/compress
  dependency-version: 1.20.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-29 15:14:17 +00:00
dependabot[bot]
96bd0f4a59
Bump github.com/git-pkgs/magic from 0.5.0 to 0.6.0
Bumps [github.com/git-pkgs/magic](https://github.com/git-pkgs/magic) from 0.5.0 to 0.6.0.
- [Release notes](https://github.com/git-pkgs/magic/releases)
- [Commits](https://github.com/git-pkgs/magic/compare/v0.5.0...v0.6.0)

---
updated-dependencies:
- dependency-name: github.com/git-pkgs/magic
  dependency-version: 0.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-29 15:13:56 +00:00
Simon Chrzanowski
10bc2f3309 Keep rewritten metadata gzip-only and longer with cooldown
pnpm 12 checks every lockfile entry against the registry on each install
unless trustLockfile is set, so each install reads the packument of every
package in the lockfile. The rewrite cache kept each document twice, plain
and gzipped, and with cooldown on for a minute only, so these packuments
were rewritten and compressed again on every install.

Keep only the gzip copy of a cached rewrite, about an eighth of the
document, and decompress it for clients and handlers that need the plain
bytes. Keep rewrites made with cooldown on for an hour. Answer HEAD for
packuments, which pnpm 12 sends before each GET.
2026-10-29 10:52:39 +01:00
Andrew Nesbitt
28ce5f49fc
Make the slow-client storage failure test deterministic 2026-10-28 15:57:06 +00:00
Simon Chrzanowski
2ea13b00d6 Show cooldown only for ecosystems that apply it
The package page showed the default cooldown for every ecosystem, also
for Debian, Alpine, OCI and generic files, which have no publish time to
check. It now shows none there, and the dashboard marks an ecosystem
override for one as not supported. The docs list Helm, which applies
cooldown from the index's created times.
2026-10-28 16:25:08 +01:00
Simon Chrzanowski
f4423ab956
Move the PURL's mirror name and qualifiers out of Enumerate
Enumerate went over the cognitive complexity limit after main added the
Julia uuid qualifier.
2026-10-28 15:18:29 +00:00
Simon Chrzanowski
db8a60e175
Drop the Homebrew sibling formulae filter
git-pkgs/registries#120 stops listing versioned formulae as versions.
2026-10-28 15:18:28 +00:00
Simon Chrzanowski
2a8ceb9389
Mirror Homebrew bottles
pkg:brew PURLs failed with "unsupported ecosystem: brew", although listing their versions worked. Homebrew publishes a formula's bottles as an image on its artifact upstream, which proxy mirror can already fetch, so a brew PURL is now resolved to that image and mirrored like one: the formula's JSON API gives the tag, which carries the revision and the bottles' rebuild, and --platform selects bottles like image platforms.

Listing a formula's versions returned the versioned siblings the Homebrew client adds (openssl@3.6 for openssl@3) as versions of it; they are now left out.
2026-10-28 15:18:14 +00:00
Simon Chrzanowski
842fa21c0a
Fetch archived CRAN versions unchecked and drop the version dedup
PACKAGES.rds comes from the same host as the tarball, so checking against it
doesn't justify a decoder for R serialization. The duplicate archived
versions are tracked in git-pkgs/registries#119.
2026-10-28 15:17:49 +00:00
Simon Chrzanowski
3fd79ccb36
Fuzz the PACKAGES.rds decoder
It reads files from the network, so malformed input must fail with an error. Seeded with the real PACKAGES.rds; 3.7M executions found nothing.
2026-10-28 15:17:49 +00:00
Simon Chrzanowski
f5251d3834
Mirror CRAN source packages
proxy mirror pkg:cran/<name>@<version> now fetches the source tarball
from upstream.cran and caches it under the key the CRAN handler reads,
("cran", name, version, name_version.tar.gz), which serves both
/src/contrib/{file} and /src/contrib/Archive/{name}/{file}.

A current version comes from src/contrib and is checked against the
MD5sum in src/contrib/PACKAGES.gz, read once per five minutes. Other
versions come from src/contrib/Archive/{name}/, checked against the
MD5sum in that directory's PACKAGES.rds, an R-serialized character
matrix read by a small decoder. Without a PACKAGES.rds the archived
tarball is fetched unchecked, as the handler fetches it.

Listing versions dropped duplicates: the CRAN client lists each archived
version twice.
2026-10-28 15:17:48 +00:00
Andrew Nesbitt
5741791c7c
Merge pull request #628 from git-pkgs/bump-registries-v0.12.0
Bump git-pkgs/registries to v0.12.0
2026-10-28 15:10:14 +00:00
Andrew Nesbitt
1c4b9d9994
Merge pull request #621 from simonchrz/feat/go-vulndb
Serve the Go vulnerability database at /go/vulndb
2026-10-28 13:45:46 +00:00
Andrew Nesbitt
9374437960
Bump git-pkgs/registries to v0.12.0 2026-10-28 13:45:11 +00:00
Simon Chrzanowski
9240f9edef Keep the upstream Composer index in memory
Serving packages.json fetched the upstream's on every request with
cache_metadata off, and after each TTL with it on, so every Composer
command waited on the upstream and on http_timeout when it was down.
The parsed index is now kept for the metadata TTL, or an hour when it
is 0, and a failed fetch is not retried within that time.
2026-10-28 14:10:48 +01:00
Simon Chrzanowski
32406d69cd Merge remote-tracking branch 'origin/main' into feat/composer-advisories 2026-10-28 14:07:39 +01:00
Simon Chrzanowski
7a3b79b10a Skip the monthly release while the last one is a draft
Building on a draft's tag would release its breaking changes as a patch
release, without the upgrade notes the draft waits for.
2026-10-28 14:07:33 +01:00
Andrew Nesbitt
58925cadea
Merge pull request #609 from simonchrz/feat/stream-cache-miss
Send artifacts to the client while a cache miss is stored
2026-10-28 12:59:52 +00:00
Andrew Nesbitt
7eed8bc135
Refer to govulncheck's -db flag in comments 2026-10-28 12:58:27 +00:00
Andrew Nesbitt
8d15cdb966
Merge pull request #618 from simonchrz/feat/cooldown-ui
Show the cooldown configuration in the web UI
2026-10-28 12:58:24 +00:00
Andrew Nesbitt
16bd0e5a59
Drop FillVersionPublishedAt until it has a caller 2026-10-28 12:50:57 +00:00
Andrew Nesbitt
626429d337
Merge pull request #624 from simonchrz/feat/npm-dist-tags
Answer npm dist-tag ls from the served packument
2026-10-28 12:45:16 +00:00
Andrew Nesbitt
6916e82207
Merge pull request #611 from simonchrz/feat/mirror-swift
Mirror Swift registry releases
2026-10-28 12:44:00 +00:00
Andrew Nesbitt
9037d937bf
Document using proxy in Docker builds 2026-10-28 11:39:28 +00:00
Simon Chrzanowski
1c706fffd0 Review fixes for the project inventory
- GetProjectsUsingPackage no longer counts each project's components
  per row; the package page only needs names and versions. The ?purl=
  listing leaves component_count out accordingly.
- The delete button sends to DELETE /ui/api/projects/{project}, the same
  handler as the API and still behind the token, so it sits under /ui
  like the cache deletes.
- The project page answers 500 when its components can't be read,
  instead of showing an empty project.
- DELETE of a path ending in /sbom that names no project answers 400
  with a hint, since it's most likely the upload path.
- The upload endpoint lists JSON, XML and plain text (SPDX tag-value)
  as accepted.
- The docs say that versions are compared as the SBOM spells them.
2026-10-27 20:23:14 +01:00
Simon Chrzanowski
8855c86fc5 Match more SBOM names to cached packages
FromPURL now names packages the way their handlers store them:

- NuGet IDs are lowercased, as the NuGet handler stores them.
- PyPI names are normalized as in PEP 503. The PyPI handler takes the
  name from the file name, which spells "." as "_", so
  pkg:pypi/zope.interface has to match the cached zope-interface.
- Conda package URLs without a channel qualifier are skipped: the conda
  handler records packages as channel/name, so they can never match.
- The Go main-module check ignores case. syft keeps the module path's
  case in its mainModule property, while golang package URLs are
  lowercased.

A table test checks every mapped PURL type against the name its handler
passes when caching.
2026-10-27 20:18:50 +01:00
Simon Chrzanowski
b5eec9c696 Drop vulnerability markers from the project inventory
The vulnerabilities table is only written by tests: nothing in the proxy
calls UpsertVulnerability, so the dashboard card, the per-project count
and the per-component markers were always empty. Remove them along with
GetComponentVulnerabilities and inventory.Affected, and keep the
outdated marker, which uses the latest version the handlers record.
2026-10-27 20:15:50 +01:00
Simon Chrzanowski
bd05533bff Delete projects from the project page
The page asks for the upload token and sends it to DELETE
/api/projects/{project}, so /ui gains no unauthenticated way to delete.
The token is kept in session storage for the next delete.
2026-10-27 19:46:51 +01:00
Simon Chrzanowski
895f22a601 Skip the project's own packages in uploaded SBOMs
A syft SBOM of a repository lists the repository itself: the module in
go.mod and the root of package-lock.json, both without a version or with
the version from package.json, and the module its Go binaries were built
from. The project page showed them as dependencies.

Entries without a version are now skipped, as are lock-file roots (lock
entries that depend on others while nothing depends on them) and modules
syft marks as a binary's main module.
2026-10-27 19:43:43 +01:00
Simon Chrzanowski
0bc0e903f8 Add a project inventory built from uploaded SBOMs
Repositories upload an SBOM of their default branch, for example from
syft in CI, to PUT /api/projects/{project}/sbom. The proxy records the
package versions it lists, keyed by the same unversioned PURL the
handlers record packages under, so the inventory joins with what the
proxy has cached and the vulnerabilities it holds.

That answers which repository uses which version of a package: the new
/ui/projects pages list each project's package versions with outdated
and vulnerability markers, package pages show the projects per version,
the dashboard lists vulnerable versions in use, and
GET /api/projects?purl= does the same for scripts.

The feature is off by default. Uploads and deletes need projects.token;
an upload replaces the project's components in one transaction.
projects.max_age removes projects that stopped uploading.
2026-10-27 19:30:34 +01:00
Simon Chrzanowski
92b1fb20a6 Follow npm and Packagist change feeds to revalidate changed metadata
Within metadata_ttl cached npm and Composer metadata is served without
asking upstream, so a new release can take that long to show up. Both
registries publish a feed of the packages that changed.

upstream.npm_change_feed and upstream.composer_change_feed (off by
default, require cache_metadata) start a reader per feed that runs every
minute and clears fetched_at on the cached rows of listed packages that
were fetched before the read. The rows keep their document and ETag, so
the next request revalidates them upstream. Routed packages are cached
under other keys and never match. A Packagist resync expires all cached
Composer metadata. Failed reads back off up to 15 minutes. The feed
position is kept in memory, so after a restart the reader starts from the
feed's current position.
2026-10-27 16:31:47 +01:00
Simon Chrzanowski
e97d4aa306 Answer npm dist-tag ls from the served packument
`npm dist-tag ls` reads /-/package/{name}/dist-tags. The proxy had no
route for it, so the request ended up in the metadata path and failed.

The tags are taken from the packument the proxy serves rather than
relayed from upstream, so latest never names a version cooldown holds
back, routed packages use their route, and pushed versions and
hosted-only scopes count as they do for the packument. Changing tags
stays unsupported and still gets 405.
2026-10-27 16:19:14 +01:00
Simon Chrzanowski
1abb8c029a Serve dated snapshots of Alpine APK indexes
Alpine mirrors keep only the current build of each package, so once a
package is upgraded `apk add curl=8.5.0-r0` fails: the current index no
longer lists the old build, even when the proxy still has it cached.

With upstream.apk_snapshots set (e.g. "90d"), each version of an index or
detached signature the metadata cache stores is copied to
_snapshots/alpine/ and recorded in a new metadata_snapshots table with the
time it was first served. /apk/{name}@{YYYY-MM-DD}/ serves the version that
was current at the end of that day; packages below it are the plain path's
requests. The kept bytes are the signed upstream bytes, so apk verifies
them unchanged.

An hourly job deletes versions replaced longer ago than the configured
duration and queues their blobs on the pending-delete queue; the newest
version of each index stays. Packages listed by kept snapshots are not
protected from eviction or retention; the docs say so.
2026-10-27 16:18:26 +01:00
Simon Chrzanowski
29ecb20733 Serve the Go vulnerability database at /go/vulndb
govulncheck reads vuln.go.dev directly, so on networks that only reach
the proxy it cannot run. upstream.go_vulndb (default https://vuln.go.dev)
is now served at /go/vulndb for `govulncheck -db`.

Only the files the database API defines are passed on (index/db,
index/modules, index/vulns and ID/{id}, as .json and .json.gz), with IDs
limited to letters, digits, '-' and '_'. They are stored and served
byte-for-byte; .json.gz files are gzip data without Content-Encoding.
All files, entries included, follow the metadata TTL since entries change
when their modified date does, and fall back to the cached copy when the
database is unreachable. Non-JSON responses are refused so a block page
does not replace the copy kept for an outage. HEAD is allowed under
/vulndb/, because govulncheck checks a non-default database with HEAD on
index/modules.json.gz.
2026-10-27 16:16:04 +01:00
Simon Chrzanowski
b2553ddd49 Pass Composer security advisories and filter lists through
The proxy's packages.json listed only metadata, search and notify URLs,
so a client using the proxy as its only Composer repository lost
`composer audit`, `audit.block-insecure` and Packagist's malware filter
list without any warning.

The service index now carries the security-advisories and filter entries
of the upstream repository's own packages.json, cached for the metadata
TTL, with URLs pointing at the proxy. An upstream that does not offer
them gets nothing advertised, so a custom upstream.composer_repository
without the API keeps working as before. Advisories and filter entries
embedded in p2 metadata were already passed on unchanged.

Advisories queries and the filter summary are relayed uncached. Names of
routed packages and hosted-only vendors are removed from advisories
queries first, so private package names are not sent to the public API.
2026-10-27 16:15:31 +01:00
Simon Chrzanowski
b79cd4d14a Show the cooldown configuration in the web UI
The dashboard shows the default and the ecosystem overrides, and counts
package overrides and patterns without naming them, since the UI has no
auth. A package page shows the cooldown that applies to the package and
the kind of rule it comes from.

Also add FillVersionPublishedAt, which stores a publish time only where
none is stored, for recording the versions cooldown holds back.
2026-10-27 13:03:42 +01:00
Simon Chrzanowski
fb346ae355 Hold an unrecorded object for a client reading its stored copy
A client that fell behind is switched to the stored copy, which for an
unrecorded refetch is deleted once the waiters close. It now takes its
own hold, released when it closes.
2026-10-27 12:30:59 +01:00
Simon Chrzanowski
4094b0fca9 Create breaking monthly releases as drafts
A breaking release gets upgrade notes before it goes out. release.yml
creates it as a draft and publish.yml isn't started; publishing the
draft by hand starts publish.yml for the images and the chart.
2026-10-27 12:16:50 +01:00
Simon Chrzanowski
c8be267586 Merge remote-tracking branch 'origin/main' into mirror-swift
# Conflicts:
#	docs/configuration.md
#	internal/mirror/mirror.go
#	internal/mirror/versions.go
2026-10-27 11:00:10 +01:00
Andrew Nesbitt
4682c1e563
Merge pull request #616 from kokes/helm-extra-volumes
Add extraVolumes, extraVolumeMounts and extraInitContainers to Helm chart
2026-10-27 09:04:55 +00:00
Andrew Nesbitt
e7f285273e
Merge pull request #607 from simonchrz/feat/mirror-julia
Mirror Julia packages
2026-10-27 08:47:52 +00:00