Skip to content
Merged
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
1 change: 1 addition & 0 deletions .bk.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
selected_org: cash
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this?

3 changes: 3 additions & 0 deletions cmd/cachewd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ func newServer(ctx context.Context, muxHandler http.Handler, bind string, metric
)(handler)

handler = httputil.LoggingMiddleware(handler)
if os.Getenv("IS_PLAYPEN") != "true" {
handler = httputil.PlaypenGuardMiddleware(handler)
}

logger := logging.FromContext(ctx)
return &http.Server{
Expand Down
31 changes: 31 additions & 0 deletions internal/httputil/playpen_guard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package httputil

import (
"net/http"
"strings"
)

// PlaypenGuardMiddleware rejects requests that target a playpen instance
// (via the Baggage header) when this instance is not a playpen.
// This prevents requests from silently falling through to staging.
func PlaypenGuardMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if baggage := r.Header.Get("Baggage"); hasCachewPlaypenKey(baggage) {
http.Error(w, "no matching cachew playpen found — start one with: sq playpen sync", http.StatusServiceUnavailable)
return
}
next.ServeHTTP(w, r)
})
}

func hasCachewPlaypenKey(baggage string) bool {
if baggage == "" {
return false
}
for entry := range strings.SplitSeq(baggage, ",") {
if strings.HasPrefix(strings.TrimSpace(entry), "cachew-playpen=") {
return true
}
}
return false
}
41 changes: 41 additions & 0 deletions internal/httputil/playpen_guard_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package httputil_test

import (
"net/http"
"net/http/httptest"
"testing"

"github.com/alecthomas/assert/v2"

"github.com/block/cachew/internal/httputil"
)

func TestPlaypenGuardMiddleware(t *testing.T) {
ok := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
})
guard := httputil.PlaypenGuardMiddleware(ok)

t.Run("allows normal requests", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
rr := httptest.NewRecorder()
guard.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
})

t.Run("blocks requests with cachew playpen baggage", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.Header.Set("Baggage", "cachew-playpen=jrobotham")
rr := httptest.NewRecorder()
guard.ServeHTTP(rr, req)
assert.Equal(t, http.StatusServiceUnavailable, rr.Code)
})

t.Run("allows requests with unrelated baggage", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.Header.Set("Baggage", "blox-playpen=jrobotham")
rr := httptest.NewRecorder()
guard.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
})
}