From 77ab90e4496fb20ccde5b43fe8c21a7a3ffb7b89 Mon Sep 17 00:00:00 2001 From: Wael Nasreddine Date: Wed, 11 Feb 2026 17:52:02 -0800 Subject: [PATCH] fix: harden the narinfo route pattern (#841) The narinfo hash was updated in #840 to reflect the upstream definition in NixOS/nix#15004 and so the server should only allow narinfo requests that match this pattern. (cherry picked from commit e91ec45e62b3a0cb529408b16ede2be87d4fcdba) --- pkg/server/security_test.go | 11 ++--------- pkg/server/server.go | 8 ++------ 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/pkg/server/security_test.go b/pkg/server/security_test.go index a495b223..68b85573 100644 --- a/pkg/server/security_test.go +++ b/pkg/server/security_test.go @@ -97,13 +97,6 @@ L: expectedStatus: http.StatusNotFound, // Not found upstream, but reached it shouldReachUpstream: true, }, - { - name: "Valid 52-char narinfo hash", - method: http.MethodGet, - path: "/1lid9xrpirkzcpqsxfq02qwiq0yd70chfl860wzsqd1739ih0nri.narinfo", - expectedStatus: http.StatusNotFound, - shouldReachUpstream: true, - }, { name: "Invalid hash length (31 chars)", method: http.MethodGet, @@ -121,8 +114,8 @@ L: { name: "Path traversal attempt (alphanumeric but malicious)", method: http.MethodGet, - path: "/abcdefghijklmnopqrstuvwxyz0123456789.narinfo", // 44 chars - expectedStatus: http.StatusBadRequest, // Rejected by helper.IsValidHash + path: "/aeou456789abcdfghijklmnpqrsvwxy.narinfo", // contains all 4 chars not allowed aeou + expectedStatus: http.StatusNotFound, shouldReachUpstream: false, }, { diff --git a/pkg/server/server.go b/pkg/server/server.go index e4b65eb8..bfe3e26e 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -26,6 +26,7 @@ import ( "github.com/kalbasit/ncps/pkg/cache/upstream" "github.com/kalbasit/ncps/pkg/helper" "github.com/kalbasit/ncps/pkg/nar" + "github.com/kalbasit/ncps/pkg/narinfo" "github.com/kalbasit/ncps/pkg/storage" ) @@ -33,7 +34,7 @@ const ( routeIndex = "/" routeNar = "/nar/{hash:[a-z0-9]{32,52}}.nar" routeNarCompression = "/nar/{hash:[a-z0-9]{32,52}}.nar.{compression:*}" - routeNarInfo = "/{hash:[a-z0-9]{32,52}}.narinfo" + routeNarInfo = "/{hash:" + narinfo.HashPattern + "}.narinfo" routeCacheInfo = "/nix-cache-info" routeCachePublicKey = "/pubkey" @@ -291,11 +292,6 @@ func (s *Server) getNixCachePublicKey(w http.ResponseWriter, r *http.Request) { func (s *Server) getNarInfo(withBody bool) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { hash := chi.URLParam(r, "hash") - if !helper.IsValidHash(hash) { - http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) - - return - } ctx, span := tracer.Start( r.Context(),