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
5 changes: 2 additions & 3 deletions controllers/dependencyfirewall/composer.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func expandComposerVersions(entries []json.RawMessage, minified string) ([]json.
return expanded, nil
}

var composerProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/(?:[^/]+/)?composer(?:/|$)`)
var composerProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/[^/]+/composer(?:/|$)`)

// ComposerDependencyProxyController handles php dependency proxy requests.
// It embeds DependencyProxyController to reuse shared helpers and state.
Expand Down Expand Up @@ -152,9 +152,8 @@ func (composerEcosystem) writeResponse(c shared.Context, data []byte, path strin
// @Tags Dependency Firewall
// @Security PATAuth
// @Security BearerAuth
// @Param secret path string false "dependency proxy secret"
// @param secret path string true "dependency proxy secret"
// @Success 200 {file} binary
// @Router /dependency-proxy/composer/{path} [get]
// @Router /dependency-proxy/{secret}/composer/{path} [get]
func (d *ComposerDependencyProxyController) ProxyComposer(c shared.Context) error {
path := composer.trimPrefix(c.Request().URL.Path)
Expand Down
11 changes: 3 additions & 8 deletions controllers/dependencyfirewall/composer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,25 @@ import (
)

func TestComposerEcosystem(t *testing.T) {
t.Run("trimPrefix with and without secret", func(t *testing.T) {
t.Run("trimPrefix", func(t *testing.T) {
cases := []struct {
name string
path string
expected string
}{
{
name: "without secret",
path: "/api/v1/dependency-proxy/composer/p2/monolog/monolog.json",
expected: "p2/monolog/monolog.json",
},
{
name: "with secret",
path: "/api/v1/dependency-proxy/550e8400-e29b-41d4-a716-446655440000/composer/p2/monolog/monolog.json",
expected: "p2/monolog/monolog.json",
},
{
name: "with trailing slash",
path: "/api/v1/dependency-proxy/composer/p2/monolog/monolog.json/",
path: "/api/v1/dependency-proxy/550e8400-e29b-41d4-a716-446655440000/composer/p2/monolog/monolog.json/",
expected: "p2/monolog/monolog.json",
},
{
name: "without trailing path",
path: "/api/v1/dependency-proxy/composer",
path: "/api/v1/dependency-proxy/550e8400-e29b-41d4-a716-446655440000/composer",
expected: "",
},
}
Expand Down
4 changes: 2 additions & 2 deletions controllers/dependencyfirewall/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package dependencyfirewall
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
Expand Down Expand Up @@ -246,11 +247,10 @@ func (d *DependencyProxyController) GetDependencyProxyURLs(ctx shared.Context) e
}

// LoadConfigsBySecret resolves DependencyProxyConfigs for a secret string.
// An empty secret returns empty (permissive) configs without error.
func (d *DependencyProxyController) LoadConfigsBySecret(c shared.Context, secret string) (DependencyProxyConfigs, error) {
var configs DependencyProxyConfigs
if secret == "" {
return configs, nil
return configs, errors.New("invalid dependency proxy secret: empty")
}
uuidSecret, err := uuid.Parse(secret)
if err != nil {
Expand Down
5 changes: 2 additions & 3 deletions controllers/dependencyfirewall/debian.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const (
debCacheTTL = 168 * time.Hour
)

var debProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/(?:[^/]+/)?deb(?:/|$)`)
var debProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/[^/]+/deb(?:/|$)`)

// DEBDependencyProxyController handles debian dependency proxy requests.
// It embeds DependencyProxyController to reuse shared helpers and state.
Expand Down Expand Up @@ -136,9 +136,8 @@ func (debEcosystem) writeResponse(c shared.Context, data []byte, path string, ca
// @Tags Dependency Firewall
// @Security PATAuth
// @Security BearerAuth
// @Param secret path string false "dependency proxy secret"
// @param secret path string true "dependency proxy secret"
// @Success 200 {file} binary
// @Router /dependency-proxy/deb/{path} [get]
// @Router /dependency-proxy/{secret}/deb/{path} [get]
func (d *DebDependencyProxyController) ProxyDeb(c shared.Context) error {
if strings.HasSuffix(strings.TrimSuffix(c.Request().URL.Path, "/"), ".deb") {
Expand Down
9 changes: 2 additions & 7 deletions controllers/dependencyfirewall/debian_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,20 @@ package dependencyfirewall
import "testing"

func TestDebEcosystem(t *testing.T) {
t.Run("trimPrefix with and without secret", func(t *testing.T) {
t.Run("trimPrefix", func(t *testing.T) {
cases := []struct {
name string
path string
expected string
}{
{
name: "without secret",
path: "/api/v1/dependency-proxy/deb/debian/dists/trixie/InRelease",
expected: "debian/dists/trixie/InRelease",
},
{
name: "with secret",
path: "/api/v1/dependency-proxy/550e8400-e29b-41d4-a716-446655440000/deb/debian/dists/trixie/InRelease",
expected: "debian/dists/trixie/InRelease",
},
{
name: "url encoded version is decoded",
path: "/api/v1/dependency-proxy/deb/debian/pool/main/c/curl/curl_8.14.1-2%2bdeb13u5_arm64.deb",
path: "/api/v1/dependency-proxy/550e8400-e29b-41d4-a716-446655440000/deb/debian/pool/main/c/curl/curl_8.14.1-2%2bdeb13u5_arm64.deb",
expected: "debian/pool/main/c/curl/curl_8.14.1-2+deb13u5_arm64.deb",
},
}
Expand Down
6 changes: 2 additions & 4 deletions controllers/dependencyfirewall/golang.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import (
const goProxyURL = "https://proxy.golang.org"

var (
goProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/(?:[^/]+/)?go(?:/|$)`)
goProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/[^/]+/go(?:/|$)`)
goPathRe = regexp.MustCompile(`^([^@]+)(?:@v/([^/]+))?`)
)

Expand Down Expand Up @@ -123,10 +123,8 @@ func (goEcosystem) writeResponse(c shared.Context, data []byte, path string, cac
// @Tags Dependency Firewall
// @Security PATAuth
// @Security BearerAuth
// @Param secret path string false "dependency proxy secret"
// @param secret path string true "dependency proxy secret"
// @Success 200 {file} binary
// @Router /dependency-proxy/go [get]
// @Router /dependency-proxy/go/{path} [get]
// @Router /dependency-proxy/{secret}/go [get]
// @Router /dependency-proxy/{secret}/go/{path} [get]
func (d *GoDependencyProxyController) ProxyGo(c shared.Context) error {
Expand Down
9 changes: 2 additions & 7 deletions controllers/dependencyfirewall/golang_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,20 @@ package dependencyfirewall
import "testing"

func TestGoEcosystem(t *testing.T) {
t.Run("trimPrefix with and without secret", func(t *testing.T) {
t.Run("trimPrefix", func(t *testing.T) {
cases := []struct {
name string
path string
expected string
}{
{
name: "without secret",
path: "/api/v1/dependency-proxy/go/github.com/foo/bar",
expected: "github.com/foo/bar",
},
{
name: "with secret",
path: "/api/v1/dependency-proxy/550e8400-e29b-41d4-a716-446655440000/go/github.com/foo/bar",
expected: "github.com/foo/bar",
},
{
name: "trailing slash is trimmed",
path: "/api/v1/dependency-proxy/go/github.com/foo/bar/",
path: "/api/v1/dependency-proxy/550e8400-e29b-41d4-a716-446655440000/go/github.com/foo/bar/",
expected: "github.com/foo/bar",
},
}
Expand Down
5 changes: 2 additions & 3 deletions controllers/dependencyfirewall/maven.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import (
const mavenRegistry = "https://repo1.maven.org/maven2"

var (
mavenProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/(?:[^/]+/)?maven(?:/|$)`)
mavenProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/[^/]+/maven(?:/|$)`)
mavenVersionDirRe = regexp.MustCompile(`^\d[A-Za-z0-9._+-]*$|-SNAPSHOT$`)

mavenVersionsBlockRe = regexp.MustCompile(`(?s)<versions>(.*?)</versions>`)
Expand Down Expand Up @@ -153,9 +153,8 @@ func mavenContentType(path string) string {
// @Tags Dependency Firewall
// @Security PATAuth
// @Security BearerAuth
// @Param secret path string false "dependency proxy secret"
// @param secret path string true "dependency proxy secret"
// @Success 200 {file} binary
// @Router /dependency-proxy/maven/{path} [get]
// @Router /dependency-proxy/{secret}/maven/{path} [get]
func (d *MavenDependencyProxyController) ProxyMaven(c shared.Context) error {
if strings.Contains(c.Request().URL.Path, "maven-metadata.xml") {
Expand Down
5 changes: 0 additions & 5 deletions controllers/dependencyfirewall/maven_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,6 @@ func TestMavenEcosystemTrimPrefix(t *testing.T) {
path string
expected string
}{
{
name: "without secret",
path: "/api/v1/dependency-proxy/maven/com/fake/artifact/maven-metadata.xml",
expected: "com/fake/artifact/maven-metadata.xml",
},
{
name: "with secret",
path: "/api/v1/dependency-proxy/550e8400-e29b-41d4-a716-446655440000/maven/com/fake/artifact/1.2.3/artifact-1.2.3.jar",
Expand Down
14 changes: 4 additions & 10 deletions controllers/dependencyfirewall/npm.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (

const npmRegistry = "https://registry.npmjs.org"

var npmProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/(?:[^/]+/)?npm(?:/|$)`)
var npmProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/[^/]+/npm(?:/|$)`)

// NPMDependencyProxyController handles npm dependency proxy requests.
// It embeds DependencyProxyController to reuse shared helpers and state.
Expand Down Expand Up @@ -110,13 +110,11 @@ func (npmEcosystem) writeResponse(c shared.Context, data []byte, path string, ca
// @Tags Dependency Firewall
// @Security PATAuth
// @Security BearerAuth
// @Param secret path string false "dependency proxy secret"
// @param secret path string true "dependency proxy secret"
// @Param package path string false "npm package name"
// @Param scope path string false "npm scope"
// @Param name path string false "npm package name (scoped)"
// @Success 200 {file} binary
// @Router /dependency-proxy/npm/{package}/-/{path} [get]
// @Router /dependency-proxy/npm/{scope}/{name}/-/{path} [get]
// @Router /dependency-proxy/{secret}/npm/{package}/-/{path} [get]
// @Router /dependency-proxy/{secret}/npm/{scope}/{name}/-/{path} [get]
func (d *NPMDependencyProxyController) ProxyNPMTarball(c shared.Context) error {
Expand Down Expand Up @@ -284,13 +282,11 @@ func (d *NPMDependencyProxyController) fetchNPMReleaseTime(ctx context.Context,
// @Tags Dependency Firewall
// @Security PATAuth
// @Security BearerAuth
// @Param secret path string false "dependency proxy secret"
// @param secret path string true "dependency proxy secret"
// @Param package path string false "npm package name"
// @Param scope path string false "npm scope"
// @Param name path string false "npm package name (scoped)"
// @Success 200 {object} map[string]interface{}
// @Router /dependency-proxy/npm/{package} [get]
// @Router /dependency-proxy/npm/{scope}/{name} [get]
// @Router /dependency-proxy/{secret}/npm/{package} [get]
// @Router /dependency-proxy/{secret}/npm/{scope}/{name} [get]
func (d *NPMDependencyProxyController) ProxyNPMMetadata(c shared.Context) error {
Expand Down Expand Up @@ -386,10 +382,8 @@ func (d *NPMDependencyProxyController) ProxyNPMMetadata(c shared.Context) error
// @Tags Dependency Firewall
// @Security PATAuth
// @Security BearerAuth
// @Param secret path string false "dependency proxy secret"
// @param secret path string true "dependency proxy secret"
// @Success 200 {object} map[string]interface{}
// @Router /dependency-proxy/npm/-/{path} [get]
// @Router /dependency-proxy/npm/-/{path} [post]
// @Router /dependency-proxy/{secret}/npm/-/{path} [get]
// @Router /dependency-proxy/{secret}/npm/-/{path} [post]
func (d *NPMDependencyProxyController) ProxyNPMRegistryAPI(c shared.Context) error {
Expand Down
7 changes: 1 addition & 6 deletions controllers/dependencyfirewall/npm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,12 @@ import (
)

func TestNPMEcosystem(t *testing.T) {
t.Run("trimPrefix with and without secret", func(t *testing.T) {
t.Run("trimPrefix", func(t *testing.T) {
cases := []struct {
name string
path string
expected string
}{
{
name: "without secret",
path: "/api/v1/dependency-proxy/npm/lodash",
expected: "lodash",
},
{
name: "with secret",
path: "/api/v1/dependency-proxy/550e8400-e29b-41d4-a716-446655440000/npm/@babel/core",
Expand Down
22 changes: 5 additions & 17 deletions controllers/dependencyfirewall/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import (
"go.opentelemetry.io/otel/trace"
)

var ociProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/(?:[^/]+/)?oci(?:/|$)`)
var ociProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/[^/]+/oci(?:/|$)`)

// OCI Distribution Spec path-param formats. Anything outside these is
// rejected at the edge so that path traversal, NUL injection, or other
Expand Down Expand Up @@ -397,16 +397,12 @@ func (d *OCIDependencyProxyController) ProxyOCIVersionCheck(c shared.Context) er
// @Tags Dependency Firewall
// @Security PATAuth
// @Security BearerAuth
// @Param secret path string false "dependency proxy secret"
// @param secret path string true "dependency proxy secret"
// @Param registry path string true "registry hostname"
// @Param namespace path string false "image namespace"
// @Param image path string true "image name"
// @Param reference path string true "tag or digest"
// @Success 200 {object} map[string]interface{}
// @Router /v2/{registry}/{image}/manifests/{reference} [get]
// @Router /v2/{registry}/{image}/manifests/{reference} [head]
// @Router /v2/{registry}/{namespace}/{image}/manifests/{reference} [get]
// @Router /v2/{registry}/{namespace}/{image}/manifests/{reference} [head]
// @Router /v2/{secret}/{registry}/{namespace}/{image}/manifests/{reference} [get]
// @Router /v2/{secret}/{registry}/{namespace}/{image}/manifests/{reference} [head]
// @Router /v2/{secret}/{registry}/{ns1}/{ns2}/{image}/manifests/{reference} [get]
Expand Down Expand Up @@ -529,16 +525,12 @@ func (d *OCIDependencyProxyController) ProxyOCIManifest(c shared.Context) error
// @Tags Dependency Firewall
// @Security PATAuth
// @Security BearerAuth
// @Param secret path string false "dependency proxy secret"
// @param secret path string true "dependency proxy secret"
// @Param registry path string true "registry hostname"
// @Param namespace path string false "image namespace"
// @Param image path string true "image name"
// @Param digest path string true "blob digest"
// @Success 200 {file} binary
// @Router /v2/{registry}/{image}/blobs/{digest} [get]
// @Router /v2/{registry}/{image}/blobs/{digest} [head]
// @Router /v2/{registry}/{namespace}/{image}/blobs/{digest} [get]
// @Router /v2/{registry}/{namespace}/{image}/blobs/{digest} [head]
// @Router /v2/{secret}/{registry}/{namespace}/{image}/blobs/{digest} [get]
// @Router /v2/{secret}/{registry}/{namespace}/{image}/blobs/{digest} [head]
// @Router /v2/{secret}/{registry}/{ns1}/{ns2}/{image}/blobs/{digest} [get]
Expand Down Expand Up @@ -665,14 +657,12 @@ func (d *OCIDependencyProxyController) ProxyOCIBlob(c shared.Context) error {
// @Tags Dependency Firewall
// @Security PATAuth
// @Security BearerAuth
// @Param secret path string false "dependency proxy secret"
// @param secret path string true "dependency proxy secret"
// @Param registry path string true "registry hostname"
// @Param namespace path string false "image namespace"
// @Param image path string true "image name"
// @Param digest path string true "subject digest"
// @Success 200 {object} map[string]interface{}
// @Router /v2/{registry}/{image}/referrers/{digest} [get]
// @Router /v2/{registry}/{namespace}/{image}/referrers/{digest} [get]
// @Router /v2/{secret}/{registry}/{namespace}/{image}/referrers/{digest} [get]
// @Router /v2/{secret}/{registry}/{ns1}/{ns2}/{image}/referrers/{digest} [get]
func (d *OCIDependencyProxyController) ProxyOCIReferrers(c shared.Context) error {
Expand Down Expand Up @@ -737,13 +727,11 @@ func (d *OCIDependencyProxyController) ProxyOCIReferrers(c shared.Context) error
// @Tags Dependency Firewall
// @Security PATAuth
// @Security BearerAuth
// @Param secret path string false "dependency proxy secret"
// @param secret path string true "dependency proxy secret"
// @Param registry path string true "registry hostname"
// @Param namespace path string false "image namespace"
// @Param image path string true "image name"
// @Success 200 {object} map[string]interface{}
// @Router /v2/{registry}/{image}/tags/list [get]
// @Router /v2/{registry}/{namespace}/{image}/tags/list [get]
// @Router /v2/{secret}/{registry}/{namespace}/{image}/tags/list [get]
// @Router /v2/{secret}/{registry}/{ns1}/{ns2}/{image}/tags/list [get]
func (d *OCIDependencyProxyController) ProxyOCITagsList(c shared.Context) error {
Expand Down
9 changes: 3 additions & 6 deletions controllers/dependencyfirewall/python.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const (
)

var (
pypiProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/(?:[^/]+/)?pypi(?:/|$)`)
pypiProxyPrefixRe = regexp.MustCompile(`^/api/v1/dependency-proxy/[^/]+/pypi(?:/|$)`)
pypiFilenameRe = regexp.MustCompile(`^(.+?)-(\d[^-]*?)(?:-.+\.whl|\.zip|\.tar(?:\.gz|\.bz2|\.xz|\.lz|\.lzma)?|\.t[bgx]z|\.tlz)$`)
pypiNameSeparatorRe = regexp.MustCompile(`[-_.]+`)
pypiAbsoluteURLRe = regexp.MustCompile(`(?:https?:)?//[^/"'\s]+/`)
Expand Down Expand Up @@ -164,9 +164,8 @@ func (pypiEcosystem) writeResponse(c shared.Context, data []byte, path string, c
// @Tags Dependency Firewall
// @Security PATAuth
// @Security BearerAuth
// @Param secret path string false "dependency proxy secret"
// @param secret path string true "dependency proxy secret"
// @Success 200 {file} binary
// @Router /dependency-proxy/pypi/packages/{path} [get]
// @Router /dependency-proxy/{secret}/pypi/packages/{path} [get]
func (d *PythonDependencyProxyController) ProxyPyPIPackage(c shared.Context) error {
configs, err := d.GetDependencyProxyConfigs(c)
Expand Down Expand Up @@ -312,10 +311,9 @@ type pySimple struct {
// @Tags Dependency Firewall
// @Security PATAuth
// @Security BearerAuth
// @Param secret path string false "dependency proxy secret"
// @param secret path string true "dependency proxy secret"
// @Param package path string true "PyPI package name"
// @Success 200 {string} string "HTML simple index"
// @Router /dependency-proxy/pypi/simple/{package} [get]
// @Router /dependency-proxy/{secret}/pypi/simple/{package} [get]
func (d *PythonDependencyProxyController) ProxyPyPISimple(c shared.Context) error {
config, err := d.GetDependencyProxyConfigs(c)
Expand All @@ -327,7 +325,6 @@ func (d *PythonDependencyProxyController) ProxyPyPISimple(c shared.Context) erro
return echo.NewHTTPError(http.StatusInternalServerError, "failed to load dependency proxy configuration")
}


pkgName := normalizePyPIName(c.Param("package"))
requestPath := pypi.trimPrefix(c.Request().URL.Path)

Expand Down
Loading