1package middleware
2
3import (
4 "encoding/json"
5 "net"
6 "net/http"
7 "net/http/httptest"
8 "testing"
9
10 "github.com/bnema/gordon/internal/adapters/dto"
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13)
14
15func TestRegistryCIDRAllowlist(t *testing.T) {
16 log := testLogger()
17 ok := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
18 w.WriteHeader(http.StatusOK)
19 })
20
21 tests := []struct {
22 name string
23 allowedNets []*net.IPNet
24 trustedNets []*net.IPNet
25 remoteAddr string
26 xff string
27 wantStatus int
28 }{
29 {
30 name: "no CIDRs configured passes through",
31 allowedNets: nil,
32 remoteAddr: "203.0.113.50:1234",
33 wantStatus: http.StatusOK,
34 },
35 {
36 name: "empty CIDRs passes through",
37 allowedNets: []*net.IPNet{},
38 remoteAddr: "203.0.113.50:1234",
39 wantStatus: http.StatusOK,
40 },
41 {
42 name: "allowed IP returns 200",
43 allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
44 remoteAddr: "100.100.1.1:1234",
45 wantStatus: http.StatusOK,
46 },
47 {
48 name: "denied IP returns 403",
49 allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
50 remoteAddr: "203.0.113.50:1234",
51 wantStatus: http.StatusForbidden,
52 },
53 {
54 name: "localhost always allowed even when not in allowlist",
55 allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
56 remoteAddr: "127.0.0.1:1234",
57 wantStatus: http.StatusOK,
58 },
59 {
60 name: "127.x.x.x always allowed",
61 allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
62 remoteAddr: "127.0.0.2:1234",
63 wantStatus: http.StatusOK,
64 },
65 {
66 name: "IPv6 localhost always allowed",
67 allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
68 remoteAddr: "[::1]:1234",
69 wantStatus: http.StatusOK,
70 },
71 {
72 name: "trusted proxy forwards allowed client IP",
73 allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
74 trustedNets: ParseTrustedProxies([]string{"10.0.0.1"}),
75 remoteAddr: "10.0.0.1:1234",
76 xff: "100.100.1.1",
77 wantStatus: http.StatusOK,
78 },
79 {
80 name: "trusted proxy forwards denied client IP",
81 allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
82 trustedNets: ParseTrustedProxies([]string{"10.0.0.1"}),
83 remoteAddr: "10.0.0.1:1234",
84 xff: "203.0.113.50",
85 wantStatus: http.StatusForbidden,
86 },
87 }
88
89 for _, tt := range tests {
90 t.Run(tt.name, func(t *testing.T) {
91 handler := RegistryCIDRAllowlist(tt.allowedNets, tt.trustedNets, log)(ok)
92
93 req := httptest.NewRequest(http.MethodGet, "/v2/", nil)
94 req.RemoteAddr = tt.remoteAddr
95 if tt.xff != "" {
96 req.Header.Set("X-Forwarded-For", tt.xff)
97 }
98 rr := httptest.NewRecorder()
99
100 handler.ServeHTTP(rr, req)
101
102 assert.Equal(t, tt.wantStatus, rr.Code)
103
104 if tt.wantStatus == http.StatusForbidden {
105 var errResp dto.ErrorResponse
106 require.NoError(t, json.NewDecoder(rr.Body).Decode(&errResp))
107 assert.Equal(t, "Forbidden", errResp.Error)
108 }
109 })
110 }
111}