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
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type EndpointConfig struct {
SourceType string `yaml:"source_type"`
Health string `yaml:"health"`
RedactRequestHeaders []string `yaml:"redact_request_headers"`
RedactResponseHeaders []string `yaml:"redact_response_headers"`
ResponseHeaderReplacements []HeaderReplacement `yaml:"response_header_replacements"`
}

Expand Down
15 changes: 9 additions & 6 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ func TestReadConfigWithFs(t *testing.T) {
target_type: https
redact_request_headers:
- X-Goog-Api-Key
redact_response_headers:
- X-Google-*
- target_host: api.example.com
target_port: 8080
source_port: 8081
Expand All @@ -51,12 +53,13 @@ func TestReadConfigWithFs(t *testing.T) {
wantConfig: &TestServerConfig{
Endpoints: []EndpointConfig{
{
TargetHost: "www.google.com",
TargetPort: 443,
SourcePort: 1443,
SourceType: "http",
TargetType: "https",
RedactRequestHeaders: []string{"X-Goog-Api-Key"},
TargetHost: "www.google.com",
TargetPort: 443,
SourcePort: 1443,
SourceType: "http",
TargetType: "https",
RedactRequestHeaders: []string{"X-Goog-Api-Key"},
Comment on lines +56 to +61

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

FYI this is just gofmt changing for alignment

RedactResponseHeaders: []string{"X-Google-*"},
},
{
TargetHost: "api.example.com",
Expand Down
3 changes: 3 additions & 0 deletions internal/record/recording_https_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ func (r *RecordingHTTPSProxy) recordResponse(recReq *store.RecordedRequest, resp
return err
}

// Redact response headers matching config patterns
recordedResponse.RedactHeaders(r.config.RedactResponseHeaders)

recordFile, ok := r.seenFiles[fileName]
if !ok {
r.seenFiles[fileName] = store.RecordFile{RecordID: fileName, Interactions: []*store.RecordInteraction{}}
Expand Down
11 changes: 11 additions & 0 deletions internal/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,17 @@ func (r *RecordedRequest) RedactHeaders(headers []string) {
}
}

// RedactHeaders removes the specified headers matching exact names or wildcard prefixes from the RecordedResponse.
func (r *RecordedResponse) RedactHeaders(patterns []string) {
for _, pattern := range patterns {
for key := range r.Headers {
if strings.EqualFold(key, pattern) || (strings.HasSuffix(pattern, "*") && strings.HasPrefix(strings.ToLower(key), strings.ToLower(strings.TrimSuffix(pattern, "*")))) {
delete(r.Headers, key)
}
}
}
}

func NewRecordedResponse(resp *http.Response, redactor *redact.Redact, body []byte) (*RecordedResponse, error) {
if resp.Header.Get("Content-Encoding") == "gzip" {
gzipReader, err := gzip.NewReader(bytes.NewReader(body))
Expand Down
60 changes: 60 additions & 0 deletions internal/store/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,3 +366,63 @@ type errorReader struct{}
func (e *errorReader) Read(p []byte) (n int, err error) {
return 0, fmt.Errorf("simulated error")
}

func TestRecordedResponse_RedactHeaders(t *testing.T) {
testCases := []struct {
name string
response RecordedResponse
patterns []string
expectedHeaders map[string]string
}{
{
name: "Redact exact matches",
response: RecordedResponse{
Headers: map[string]string{
"Content-Type": "application/json",
"X-Google-Service": "firebase",
"Server": "ESF",
},
},
patterns: []string{"X-Google-Service", "Server"},
expectedHeaders: map[string]string{
"Content-Type": "application/json",
},
},
{
name: "Redact wildcard patterns",
response: RecordedResponse{
Headers: map[string]string{
"Content-Type": "application/json",
"X-Google-Service": "firebase",
"X-Google-Gfe-Version": "1.0",
"Server": "ESF",
},
},
patterns: []string{"X-Google-*"},
expectedHeaders: map[string]string{
"Content-Type": "application/json",
"Server": "ESF",
},
},
{
name: "Redact case insensitivity",
response: RecordedResponse{
Headers: map[string]string{
"Content-Type": "application/json",
"x-google-service": "firebase",
},
},
patterns: []string{"X-Google-Service"},
expectedHeaders: map[string]string{
"Content-Type": "application/json",
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tc.response.RedactHeaders(tc.patterns)
require.Equal(t, tc.expectedHeaders, tc.response.Headers, "RedactHeaders() result mismatch")
})
}
}
Loading