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
8 changes: 8 additions & 0 deletions internal/handlers/npm_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ func (h *NPMRegistryHandler) HandleRequest(req *http.Request, ctx *goproxy.Proxy
continue
}

// Path-segment-aware matching prevents credentials configured for one
// path-scoped registry from being applied to sibling paths on the same
// host (e.g., /team-a-npm should not match /team-b-npm).
regPath := strings.TrimSuffix(regURL.Path, "/")
if regPath != "" && !strings.HasPrefix(req.URL.Path, regPath+"/") && req.URL.Path != regPath {
continue
}

if cred.token == "" && cred.password != "" {
cred.token = cred.username + ":" + cred.password
}
Expand Down
36 changes: 35 additions & 1 deletion internal/handlers/npm_registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ func TestNPMRegistryHandler(t *testing.T) {
req = handleRequestAndClose(handler, req, nil)
assertHasTokenAuth(t, req, "Bearer", privateRegToken, "valid registry request with port and path")

// Sibling path on the same host should NOT receive credentials from /reg-path
req = httptest.NewRequest("GET", "https://example.com/other-path/private-package", nil)
req = handleRequestAndClose(handler, req, nil)
assertHasTokenAuth(t, req, "Bearer", privateRegToken, "different path")
assertUnauthenticated(t, req, "sibling path should not match")

req = httptest.NewRequest("GET", "https://nexus.some-company.com/private-package", nil)
req = handleRequestAndClose(handler, req, nil)
Expand All @@ -87,3 +88,36 @@ func TestNPMRegistryHandler(t *testing.T) {
req = handleRequestAndClose(handler, req, nil)
assertHasBasicAuth(t, req, nexusUser, nexusPassword, "azure devops case insensitive registry request")
}

func TestNPMRegistryHandler_SameHostDifferentPaths(t *testing.T) {
teamAToken := "team-a-token"
teamBToken := "team-b-token"
credentials := config.Credentials{
config.Credential{
"type": "npm_registry",
"registry": "https://artifactory.example.com/api/npm/team-a-npm",
"token": teamAToken,
},
config.Credential{
"type": "npm_registry",
"registry": "https://artifactory.example.com/api/npm/team-b-npm",
"token": teamBToken,
},
}
handler := NewNPMRegistryHandler(credentials)

// Request to team-a path should use team-a token
req := httptest.NewRequest("GET", "https://artifactory.example.com/api/npm/team-a-npm/@scope/pkg", nil)
req = handleRequestAndClose(handler, req, nil)
assertHasTokenAuth(t, req, "Bearer", teamAToken, "team-a path should use team-a token")

// Request to team-b path should use team-b token, not team-a
req = httptest.NewRequest("GET", "https://artifactory.example.com/api/npm/team-b-npm/@scope/pkg", nil)
req = handleRequestAndClose(handler, req, nil)
assertHasTokenAuth(t, req, "Bearer", teamBToken, "team-b path should use team-b token")

// Request to unrelated path should not be authenticated
req = httptest.NewRequest("GET", "https://artifactory.example.com/api/npm/team-c-npm/@scope/pkg", nil)
req = handleRequestAndClose(handler, req, nil)
assertUnauthenticated(t, req, "unrelated path should not match any credential")
}
Loading