From eb57b40d3380bad2cef481af5bc8cfddbfc4e81f Mon Sep 17 00:00:00 2001 From: David Balderree Date: Wed, 14 Oct 2026 15:27:39 -0500 Subject: [PATCH] Derive downloads.cdn.telerik.com for jobs holding a nuget.telerik.com credential The licensed Telerik NuGet feed (nuget.telerik.com, declared in dependabot.yml as a nuget-feed registry with an api-key credential) answers every package download with a 302 to a pre-signed URL on downloads.cdn.telerik.com. The proxy refuses that second hop ("egress not allowlisted downloads.cdn.telerik.com"), so every nuget job on the Telerik feed ends in private_source_authentication_failure. Add it under registry_redirect_derivations rather than the static nuget list: only a job holding the feed credential can obtain the redirect, so the CDN is opened only for those jobs. Fixes #306 Co-Authored-By: Claude Fable 5.1 --- .../handlers/egress_allowlist_defaults.yaml | 8 +++ internal/handlers/egress_allowlist_test.go | 2 + .../handlers/egress_dynamic_hosts_test.go | 55 +++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/internal/handlers/egress_allowlist_defaults.yaml b/internal/handlers/egress_allowlist_defaults.yaml index 408646b..3b23c72 100644 --- a/internal/handlers/egress_allowlist_defaults.yaml +++ b/internal/handlers/egress_allowlist_defaults.yaml @@ -182,6 +182,14 @@ registry_redirect_derivations: derived: - gemfury.s3-accelerate.dualstack.amazonaws.com - gemfury.s3-accelerate.amazonaws.com + # The licensed Telerik NuGet feed (nuget.telerik.com, declared as a nuget-feed + # registry with an api-key credential) 302-redirects every package download to + # a pre-signed URL on Progress/Telerik's own CDN. Exact host, no user-creatable + # names, serves only their package artefacts; derived rather than static + # because only a job holding the feed credential can obtain the redirect. + - credential_host: nuget.telerik.com + derived: + - downloads.cdn.telerik.com # ecosystem_default_domains lists the public registry and CDN hosts each # Dependabot ecosystem needs. The map is kept keyed by ecosystem for provenance diff --git a/internal/handlers/egress_allowlist_test.go b/internal/handlers/egress_allowlist_test.go index 9dd86ce..83454c3 100644 --- a/internal/handlers/egress_allowlist_test.go +++ b/internal/handlers/egress_allowlist_test.go @@ -1067,6 +1067,8 @@ func TestEgressDefaults_RegistryRedirectDerivationsLoadedFromYAML(t *testing.T) "gemfury.s3-accelerate.amazonaws.com", }, registryRedirectHosts([]string{"pypi.fury.io"})) + assert.Equal(t, []string{"downloads.cdn.telerik.com"}, + registryRedirectHosts([]string{"nuget.telerik.com"})) } func TestValidateRegistryRedirectDerivations_RejectsUnmatchableEntries(t *testing.T) { diff --git a/internal/handlers/egress_dynamic_hosts_test.go b/internal/handlers/egress_dynamic_hosts_test.go index 5c12a2b..1af4171 100644 --- a/internal/handlers/egress_dynamic_hosts_test.go +++ b/internal/handlers/egress_dynamic_hosts_test.go @@ -239,6 +239,61 @@ func TestRegistryRedirectHosts_GemfuryStorageDerived(t *testing.T) { } } +func TestRegistryRedirectHosts_TelerikDownloadCDNDerived(t *testing.T) { + // The licensed Telerik NuGet feed 302-redirects package downloads to a + // pre-signed URL on Telerik's own CDN, which appears in no credential field. + creds := config.Credentials{ + {"type": "nuget_feed", "url": "https://nuget.telerik.com/v3/index.json", "username": "api-key", "password": "x"}, + } + h := newEgressHandlerWithCreds(creds) + + assert.Nil(t, egressResult(t, h, "https://nuget.telerik.com/v3/package/telerik.zip/2025.2.520/telerik.zip.2025.2.520.nupkg"), + "the configured Telerik feed must be allowed") + assert.Nil(t, egressResult(t, h, "https://downloads.cdn.telerik.com/542ec0c3a7622c32cb2992fe37990abc_Telerik.Zip.2025.2.520.nupkg_4.0.0?Expires=1&Signature=x"), + "the Telerik download CDN must be allowed for a job with a Telerik feed credential") + + for _, blocked := range []string{ + // Dynamic hosts are matched exactly, so no child or lookalike widens it. + "https://evil.downloads.cdn.telerik.com/loot", + "https://downloads.cdn.telerik.com.attacker.com/loot", + // The vendor's parent namespace and other hosts stay closed. + "https://cdn.telerik.com/loot", + "https://telerik.com/loot", + "https://www.telerik.com/loot", + } { + assert.NotNil(t, egressResult(t, h, blocked), "must remain blocked: "+blocked) + } +} + +func TestRegistryRedirectHosts_TelerikMatchedExactly(t *testing.T) { + // Only nuget.telerik.com itself opens the CDN, not a lookalike. + assert.Equal(t, + []string{"downloads.cdn.telerik.com"}, + registryRedirectHosts([]string{"nuget.telerik.com"})) + assert.Equal(t, + []string{"downloads.cdn.telerik.com"}, + registryRedirectHosts([]string{"nuget.telerik.com."}), + "an absolute DNS name derives like its relative form") + + for _, host := range []string{ + "nuget.telerik.com.attacker.com", + "evil.nuget.telerik.com", + "telerik.com", + "nuget.telerik.net", + "notnuget.telerik.com", + } { + assert.Empty(t, registryRedirectHosts([]string{host}), "must derive nothing from %q", host) + } +} + +func TestRegistryRedirectHosts_TelerikCDNNotAllowedWithoutCredential(t *testing.T) { + h := newEgressHandlerWithCreds(config.Credentials{ + {"type": "nuget_feed", "url": "https://nexus.internal.example.com/repository/nuget/index.json"}, + }) + assert.NotNil(t, egressResult(t, h, "https://downloads.cdn.telerik.com/loot"), + "download CDN must not be allowed for a job with no Telerik feed credential") +} + func TestRegistryRedirectHosts_OnlyGemfuryHosts(t *testing.T) { gemfuryStorage := []string{ "gemfury.s3-accelerate.dualstack.amazonaws.com",