Watch
1
0
Fork
You've already forked pkg-proxy
1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-11-10 08:09:17 -05:00
Commit graph pkg-proxy/internal/server
Author SHA1 Message Date
simon chrzanowski
5e66688873
Push, list and delete npm and Composer archives through /api/hosted
With hosted.enabled, PUT /api/hosted/{ecosystem}/{name}/{version} stores the request body as the archive for that version, marked local. It replaces any cached upstream copy and is served from the cache without contacting the upstream. GET /api/hosted/{ecosystem}/{name} lists what was pushed and DELETE removes a version again, after which clients get the upstream archive if there is one. Pushing and deleting need the bearer token; listing is open like the rest of the API.

Download handlers look archives up by the filename clients request, so a push is stored under the name its ecosystem derives from name and version: {name}-{version}.tgz for npm, without the scope, and {package}-{version}.zip for Composer. Other ecosystems are refused for now.

The Composer download handler resolves the upstream metadata before it looks at the cache and answers 404 for versions the upstream doesn't list. It now serves a pushed archive first. Only artifacts marked local take that path, so cached upstream archives still go through the metadata and commit checks.

The version's integrity is set to the pushed bytes, so the cache's integrity check verifies them rather than an upstream digest. Names and versions with path separators, dot segments or control characters are refused before anything is stored.

Refs #133
2026-10-14 13:23:19 +02:00
simon chrzanowski
ea696a7abc
Add a hosted config block and a bearer token check for writes
hosted.enabled turns on pushing packages to the proxy and is off by default, like mirror_api. A pushed version is served in place of the upstream version with the same number, so anyone who can write could swap a package for every client. Enabling it therefore requires hosted.token, which supports ${VAR_NAME} expansion and can be set with PROXY_HOSTED_TOKEN. hosted.max_upload_size caps one archive and defaults to 100MB.

requireBearerToken compares SHA-256 digests of the presented and configured tokens in constant time and answers 401 with a WWW-Authenticate challenge otherwise.

Refs #133
2026-10-14 13:23:19 +02:00
Andrew Nesbitt
02ae49d401
Merge remote-tracking branch 'origin/main' into issue-411-mirror-images
# Conflicts:
#	cmd/proxy/main.go
#	internal/server/server.go
2026-10-14 12:21:14 +01:00
Andrew Nesbitt
784402ed30
Merge pull request #472 from simonchrz/fix/artifact-size-limits
Harden against resource exhaustion: partial blobs, artifact size, concurrent diffs
2026-10-14 12:19:53 +01:00
Andrew Nesbitt
61eeaf59ad
Merge remote-tracking branch 'origin/main' into issue-411-mirror-images
# Conflicts:
#	internal/server/server.go
2026-10-14 12:19:25 +01:00
Andrew Nesbitt
167e6b8ce7
Merge pull request #462 from pinguinfuss/issue-409-go-sumdb
Serve the Go checksum database through the proxy
2026-10-14 12:10:03 +01:00
Andrew Nesbitt
53c03c9694
Merge pull request #465 from pinguinfuss/issue-306-retention-deb
Enable storage retention for deb
2026-10-14 12:05:48 +01:00
Andrew Nesbitt
65ffb7c6bd
Merge pull request #471 from simonchrz/fix/diff2html-pin
Serve diff2html from the proxy instead of jsDelivr
2026-10-14 11:59:30 +01:00
Andrew Nesbitt
ea4c1ba125
Encode file paths when loading files in the source browser
loadFile put the archive path into the URL as-is, so a file name
containing # or ? was cut short and failed to load.
2026-10-14 11:58:16 +01:00
Andrew Nesbitt
320149399d
Merge pull request #470 from simonchrz/fix/browse-xss
Stop interpolating archive paths into inline handlers
2026-10-14 11:51:27 +01:00
simon chrzanowski
a9cff7fa60
Limit how many version diffs run at once
The compare endpoint buffers both archives whole, up to 512 MB each, and
anyone who can reach the UI can call it. A handful of parallel requests for
large cached versions was enough to exhaust the proxy's memory.

- Run at most two diffs at once; refuse more with 503, code BUSY and
  Retry-After instead of queueing them
- Keep the per-archive cap, which legitimately large packages need
- Move the compare test's setup into a helper shared by the new tests
2026-10-14 08:41:57 +02:00
simon chrzanowski
b7b7a64239
Add a size limit for upstream artifacts
Artifacts had no size limit. A fetch was bounded only by the server's write
timeout, and storage.max_size evicts only after the bytes are written, so
any client could fill storage or keep fetches busy by requesting huge
artifacts.

- Add artifact_max_size (PROXY_ARTIFACT_MAX_SIZE), unlimited by default
- Refuse an artifact whose declared size is over the limit before reading
  it, and fail one of unknown size once the download passes the limit
- Apply it in the fetcher so stored, streamed and mirrored artifacts all
  honour it; nothing is cached and clients get 502
2026-10-14 08:41:57 +02:00
simon chrzanowski
aa0b543ae8
Stop forwarding custom auth headers across redirects
Handlers set the configured upstream auth header on the request itself
before handing it to the client. When upstream redirected to another host,
Go's client copied that header to the next hop: it strips only
Authorization and Cookie, so a "header" type credential such as
X-JFrog-Art-Api reached the redirect target. The auth transport already
applies credentials per hop, scoped to each hop's URL.

- Remove Proxy.applyUpstreamAuth and Proxy.AuthForURL; auth comes only
  from the transport
- Add a server test that redirects a header-authenticated upstream to
  another host and asserts the header stays with the upstream
- Wire handler tests' auth through the transport, as the server does
2026-10-14 08:38:51 +02:00
simon chrzanowski
832c800ea5
Serve diff2html from the proxy instead of jsDelivr
The compare page loaded diff2html from cdn.jsdelivr.net without a version
or integrity hash, so whatever jsDelivr served as the latest release ran
with the proxy origin's privileges, and the page broke where the proxy
cannot reach the internet.

- Vendor diff2html 3.4.56 (MIT) under static/vendor like tailwind and lucide
- Load its script and stylesheet from /ui/static/vendor
- Fail a test when any template loads a script or stylesheet from another origin
2026-10-14 08:37:43 +02:00
simon chrzanowski
f5e7599749
Stop interpolating archive paths into inline handlers
The source browser built its file tree with onclick handlers such as
loadFile('${escapeHTML(file.path)}'). The browser decodes HTML entities in
an attribute before running the handler, so the ' that escapeHTML
produces turns back into a quote and ends the string. A cached package
with a crafted file name ran script on the proxy origin for anyone who
browsed it.

- Carry the path in data-path and dispatch clicks from one delegated
  listener on the file tree
- Render the binary download button as a link instead of an onclick
  that assigns window.location
- Fail the template tests when an inline handler interpolates data
2026-10-14 08:37:40 +02:00
Christian Heim
329780050f Enable storage retention for deb
Packages are stored as pkg:deb/{name}, without a vendor and whichever configured archive they came from. Package rules may name a vendor the way PURLs usually do (pkg:deb/debian/curl, pkg:deb/ubuntu/curl); it's dropped, so a rule covers the package from every archive. The docs row warns that Debian and Ubuntu archives only keep the current version per suite.

The mirror can't fetch deb packages, so there's no mirror test.

Refs #306
2026-10-13 20:08:24 +02:00
Christian Heim
9532c6a94e Add upstream.go_sumdbs_offline for networks where only the proxy has egress
On a build network where clients cannot reach sum.golang.org at all, the
proxy should keep answering for the database during an outage, since a 404
on supported would only send the go command somewhere it cannot go. With
the switch set, a cached tree of any age is enough; without it, the tree
has to be fresh, as before.

That does not make every cached module verify. The go command checks a
lookup against tiles of the tree that came with it, and sum.golang.org
attaches a different tree to almost every lookup, so it can ask for a tile
nobody fetched through the proxy before. configuration.md says so.

While the database is known to be unreachable, offline mode also serves
cached lookups and tiles straight away. Otherwise each of them would sit
through the upstream timeout before the fetch falls back to the cache,
and a build would take minutes for as long as the outage lasts. A lookup
or tile that falls back to the cache renews what the proxy knows about
the database, unless a probe has reached it since the request started.
The go command asks supported only once per process, so a long build would
otherwise be back to waiting a minute into the outage. recordTimeout is
now recordFailure, since lookups use it too.

The option needs cache_metadata, since without it there is no cached tree
to fall back on, and the config refuses the combination.
2026-10-13 16:42:03 +02:00
Christian Heim
1c6cfae813 Serve the Go checksum database from its own URL
Everything under /go/sumdb/ was passed on to the module upstream. But
proxy.golang.org doesn't serve the checksum database, so those requests
always came back 404 and every client had to talk to sum.golang.org itself.

The Go handler now serves /sumdb/{name}/ for the databases in
upstream.go_sumdbs, which defaults to sum.golang.org. Lookups, latest and
tiles come from the database through the metadata cache. Complete tiles
never change, so once they're cached we don't ask again. Unknown names and
paths that aren't part of the protocol get a 404 without going upstream.

If the database answers 404 we pass on up to 4 KiB of its text, because the
go command prints it and it tells you why a version is missing. For that the
not-found error from the metadata fetch now carries the start of the body.
Other handlers still just see ErrUpstreamNotFound.
2026-10-13 16:41:53 +02:00
Andrew Nesbitt
0ce007fdea
Merge pull request #436 from pinguinfuss/issue-263-arch-pacman
Add Arch Linux pacman repository support
2026-10-13 13:30:55 +01:00
Andrew Nesbitt
00e812421b
Merge pull request #439 from simonchrz/feature/failure-details
List recent failures under each failure count on /ui/analytics
2026-10-13 13:28:41 +01:00
Andrew Nesbitt
9a6b467412
Merge pull request #446 from pinguinfuss/issue-306-retention-nuget
Enable storage retention for nuget
2026-10-13 13:22:28 +01:00
Andrew Nesbitt
ff2c9cf37f
Merge pull request #450 from pinguinfuss/issue-306-retention-gem
Enable storage retention for gem
2026-10-13 13:21:32 +01:00
Andrew Nesbitt
c96b4a955b
Merge pull request #452 from pinguinfuss/issue-306-retention-hex
Enable storage retention for hex
2026-10-13 13:19:53 +01:00
Christian Heim
bffba28262 Enable storage retention for conda
The handler stores packages as {channel}/{name}, which purl writes as pkg:conda/conda-forge%2Fnumpy. Package rules name the channel as the namespace (pkg:conda/conda-forge/numpy) and get mapped to that form, with the package name lowercased the way conda treats it. A key without a channel or with an empty part could never match and is refused.

The mirror can't fetch conda packages today, so there's no mirror test.

Refs #306
2026-10-12 19:56:45 +02:00
Christian Heim
743fbe5026 Enable storage retention for hex
Tarballs are stored under the name from the tarball filename, and purl keeps hex names as they are, so package rules use the default mapping with the name as published.

The filename parser splits versions like 4.1.5-1 at the wrong hyphen and files them under a package like amqp_client-4.1.5, so package rules don't reach them; the docs row says so until the parser is fixed.

Refs #306
2026-10-12 19:56:45 +02:00
Christian Heim
5d7855ffc8 Store mirrored NuGet ids lowercased
The mirror passed ids through as written, so pkg:nuget/Newtonsoft.Json@13.0.3 was stored under a copy no client is served while the handler kept serving its lowercased one. GetOrFetchArtifact now lowercases nuget names, and the docs no longer ask for lowercase ids in mirror lists.

Refs #306
2026-10-12 19:56:38 +02:00
Andrew Nesbitt
093ae8295e
Merge pull request #445 from pinguinfuss/issue-306-retention-swift
Enable storage retention for swift
2026-10-12 17:44:37 +01:00
Simon Chrzanowski
b131db394d Isolate the recent failure history in tests
The history is process-wide, and tests cleared it only after running, and
not all of them. Records from earlier tests or an earlier -count repetition
reached later assertions: TestRecordUpstreamOperations left a record for
TestRecentFailuresNewestFirst, and the server tests never cleared it.

- Export ResetRecentFailures so tests outside the metrics package can use it
- Clear the history before and after every test that records or reads
  failures directly, in metrics, server and handler
- Assert exactly one failure in the handler tests instead of looking only
  at the newest
2026-10-12 18:44:12 +02:00
Andrew Nesbitt
7cb05b4cf6
Merge pull request #455 from pinguinfuss/issue-306-retention-conan
Enable storage retention for conan
2026-10-12 16:37:08 +01:00
Christian Heim
f8ca190274
Enable storage retention for swift
Source archives are stored under the registry identity, pkg:generic/swift-registry/{scope}.{name}, lowercased. Package rules write the scope as the namespace (pkg:swift/apple/swift-nio) or use the stored form, in any case. A source repository key like pkg:swift/github.com/apple/swift-nio doesn't name a registry identity and is refused.

The mirror can't fetch swift packages, so there's no mirror test.

Refs #306
2026-10-12 16:35:51 +01:00
Andrew Nesbitt
9f75e05e8c
Merge pull request #447 from pinguinfuss/issue-306-retention-julia
Enable storage retention for julia
2026-10-12 16:32:53 +01:00
Andrew Nesbitt
4958f22e46
Merge pull request #448 from pinguinfuss/issue-306-retention-rpm
Enable storage retention for rpm
2026-10-12 16:31:35 +01:00
Christian Heim
ff4d5ca10d
Enable storage retention for conan
Since #431 the handler stores Conan archives under pkg:conan/{name} with the version as the version and the revisions and any user/channel in the filename. purl keeps conan names as they are, so package rules use the default mapping, and pkg:conan/zlib covers every version, revision and user/channel of the recipe.

The mirror can't fetch conan packages, so there's no mirror test.

Refs #306
2026-10-12 16:29:59 +01:00
Andrew Nesbitt
4256aac92c
Merge pull request #449 from pinguinfuss/issue-306-retention-helm
Enable storage retention for helm
2026-10-12 16:29:24 +01:00
Andrew Nesbitt
f8c234fe1a
Merge pull request #451 from pinguinfuss/issue-306-retention-pub
Enable storage retention for pub
2026-10-12 16:29:18 +01:00
Andrew Nesbitt
3c1c118fd9
Merge pull request #453 from pinguinfuss/issue-306-retention-cran
Enable storage retention for cran
2026-10-12 16:29:11 +01:00
Christian Heim
9c75e59ef4
Enable storage retention for composer
Archives are stored under pkg:composer/{vendor}/{package}, which purl lowercases, so case in a package rule doesn't matter. A key without a vendor could never match and is refused. Packages that git-pkgs wrote as "packagist" fall under the composer rules too.

The mirror can't fetch composer packages today (its resolver has no composer support, and #415 would file them under packagist), so there's no mirror test.

Refs #306
2026-10-12 16:23:36 +01:00
Christian Heim
70ea9215c6
Enable storage retention for cargo
Crates are stored under the name they were published with, and purl keeps cargo names as they are. Package rules have to match that name exactly, so pkg:cargo/inflector doesn't cover Inflector.

Refs #306
2026-10-12 16:16:22 +01:00
Andrew Nesbitt
adf844cb26
Merge pull request #458 from pinguinfuss/issue-306-retention-alpine
Enable storage retention for alpine
2026-10-12 16:14:14 +01:00
Christian Heim
dcfb9a820d Enable storage retention for nuget
The handler stores NuGet ids lowercased, but purl keeps nuget names as written, so package rules get lowercased before they're matched. Case in a rule doesn't matter.

The mirror stores ids as given, so a mixed-case PURL ends up as a copy clients never get. Until that's fixed, the docs ask for lowercase ids in mirror lists.

Refs #306
2026-10-12 15:24:21 +02:00
Christian Heim
42f459e961 Enable storage retention for rpm
Packages are stored as pkg:rpm/{name}, without a vendor and with the name's case kept. Package rules may name a vendor the way PURLs usually do (pkg:rpm/fedora/curl); it's dropped. The docs row warns that Fedora and EPEL update repositories only keep the latest build.

The mirror can't fetch rpm packages, so there's no mirror test.

Refs #306
2026-10-12 15:24:21 +02:00
Christian Heim
48edbb8ba0 Enable storage retention for pub
Packages are stored under the name from the download path, and purl keeps pub names as they are, so package rules use the default mapping with the name as published.

Refs #306
2026-10-12 15:24:21 +02:00
Christian Heim
20855cae21 Enable storage retention for gem
Gems are stored under the name from the gem filename, and purl keeps gem names as they are, so package rules use the default mapping and match the name as published, case included. Packages that git-pkgs wrote as "rubygems" fall under the gem rules too.

The filename parser splits platforms that end in a number (universal-darwin-22) at the wrong hyphen and files those gems under a package like sorbet-static-0.5.11002-universal-darwin, so package rules don't reach them; the docs row says so until the parser is fixed.

The mirror can't fetch gems today (it asks the resolver for "rubygems", see #415), so there's no mirror test.

Refs #306
2026-10-12 15:24:20 +02:00
Christian Heim
8f30c6d478 Enable storage retention for julia
Julia takes no package rules, since its PURLs need a uuid qualifier the cache doesn't store; configuring one already fails validation. The julia rule and the default cover package tarballs, artifacts and registry snapshots alike. Everything there is content-addressed, so an evicted tarball comes back unchanged.

Refs #306
2026-10-12 15:24:20 +02:00
Christian Heim
f3505ad557 Enable storage retention for helm
Helm takes no package rules: charts are cached under the configured repository's name and the chart digest, not under the chart name, so a key like pkg:helm/nginx would name a repository. The helm rule and the default cover charts from every HTTP repository; charts pulled over OCI go through the container handler and fall under oci.

Refs #306
2026-10-12 15:24:20 +02:00
Christian Heim
4b8678c2c5
Enable storage retention for npm (#440)
* Enable storage retention for npm

npm stores packages under the PURL purl builds from the full name, so package rules use the default mapping. Both pkg:npm/@babel/core and pkg:npm/%40babel/core work.

Refs #306

* Tighten npm retention tests and docs

The docs now say npm package rules match the name exactly, so pkg:npm/jsonstream doesn't cover JSONStream. The refetch, package rule and mirror tests were weaker than their names and now check what they claim.

Refs #306

---------

Co-authored-by: Andrew Nesbitt <andrewnez@gmail.com>
2026-10-12 13:21:38 +01:00
Christian Heim
18f410d021
Enable storage retention for pypi (#441)
* Enable storage retention for pypi

PyPI names are stored the way purl normalizes them, lowercased and with _ turned into -, so Typing_Extensions and typing-extensions hit the same package. The mirror can't fetch PyPI files today, so there's no mirror test.

Refs #306

* Correct the pypi package rule note

Dots in PyPI names aren't normalized, so zope.interface and zope_interface files end up under two packages. The docs now say to add a rule for each.

Refs #306

* Mention repeated separators in the pypi rule note

Runs like "__" aren't collapsed either, so a__b is stored as a--b.

Refs #306
2026-10-12 13:06:37 +01:00
Christian Heim
c61cb51a0e
Enable storage retention for golang (#442)
Module paths are stored lowercased, so package rules match whatever case they're written in. A major version like /v2 is its own module and needs its own rule. Keys in the !-escaped form from proxy URLs and the module cache are refused, since they could never match.

Refs #306
2026-10-12 13:04:16 +01:00
Christian Heim
b40a3f067a
Enable storage retention for oci (#443)
Blobs are stored under the repository name of the pull, which purl writes as one name (pkg:oci/library%2Fnginx). Package rules write it as a path, pkg:oci/library/nginx, and get mapped to that form; keys with an empty segment, a tag or digest, or a bare upstream/{name} prefix are refused. Named upstreams keep their prefix, also when containerd reaches them through ns. Homebrew bottles and Helm charts pulled over OCI fall under oci too, bottles under Homebrew's image name (openssl@3 is homebrew/core/openssl/3). Manifests live in the metadata cache and aren't touched.

Refs #306
2026-10-12 13:00:57 +01:00
Christian Heim
90989eb030
Enable storage retention for maven (#444)
Package rules use pkg:maven/{group}/{artifact} with the case as published. Keys without a group or with the group written as a path can never match and are refused. Files from the Gradle Plugin Portal fallback fall under the maven rule; the Gradle build cache isn't affected.

The mirror can't fetch Maven packages today (it passes group/artifact where the resolver wants group:artifact), so there's no mirror test.

Refs #306
2026-10-12 12:58:09 +01:00