mirror of
https://github.com/lukaszraczylo/claude-mnemonic.git
synced 2026-06-05 23:03:55 +00:00
d04b60517a
* Make things 'betterer' across the board * fix: reorganize struct fields and config parameters for consistency - [x] Reorder Config struct fields alphabetically and by related functionality - [x] Reorganize Observation model fields with archival fields grouped together - [x] Reorder ObservationStore fields to group related members - [x] Reorder Store struct fields with health check caching grouped - [x] Reorganize HealthInfo and PoolMetrics struct field order - [x] Reorder maintenance Service struct fields logically - [x] Reorganize MCP server handler parameter structs alphabetically - [x] Reorder pattern detector candidate tracking fields - [x] Reorganize search Manager struct fields by functionality - [x] Reorder vector Client struct fields with mutex protections grouped - [x] Reorganize handler request/response struct fields - [x] Update handlers_test.go to expect wrapped response format - [x] Reorder middleware TokenAuth and rate limiter fields - [x] Reorganize Service struct fields with grouped functionality - [x] Fix RateLimiter field ordering for clarity - [x] Reorder CircuitBreaker metrics fields * fix(security): improve JSON output safety and path traversal protection - [x] Replace unsafe JSON string formatting with proper json.Marshal in export handler - [x] Remove escapeJSONString helper function in favor of standard JSON marshaling - [x] Add safeResolvePath function to validate paths and prevent directory traversal - [x] Apply path traversal validation in captureFileMtimes operations - [x] Cap result slice capacity in getRecentSearchQueries to prevent DoS via excessive allocation * fix(sdk): improve path traversal protection and allocation safety - [x] Enhance safeResolvePath with stricter validation using filepath.Rel - [x] Reject paths containing ".." after cleaning to prevent traversal - [x] Validate absolute paths are within cwd when cwd is specified - [x] Apply safeResolvePath validation to GetFileContent for consistency - [x] Add comprehensive test coverage for path traversal protection - [x] Fix allocation safety in getRecentSearchQueries by using constant capacity
214 lines
4.7 KiB
Go
214 lines
4.7 KiB
Go
package privacy
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func TestContainsSecrets(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "empty string",
|
|
input: "",
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "normal text",
|
|
input: "This is just some regular text about a bug fix",
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "API key pattern",
|
|
input: "api_key=abc123def456ghi789jkl012mno345pqr678",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "api-key with dash",
|
|
input: `api-key: "abc123def456ghi789jkl012mno"`,
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "password in config",
|
|
input: `password="super_secret_password_123"`,
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "OpenAI key format",
|
|
input: "sk-abc123def456ghi789jkl012mno345pqr678",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Anthropic key format",
|
|
input: "sk-ant-api03-abc123def456ghi789jkl012mno345",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "GitHub PAT",
|
|
input: "ghp_1234567890abcdefghijklmnopqrstuvwxyz",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "GitHub PAT new format",
|
|
input: "github_pat_12ABCDEFGHIJ3456789abc_defghijklmno",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "AWS access key",
|
|
input: "AKIAIOSFODNN7EXAMPLE",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Private key header",
|
|
input: "-----BEGIN RSA PRIVATE KEY-----",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "JWT token",
|
|
input: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "bearer token",
|
|
input: "Bearer abc123def456ghi789jkl012mno345",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "secret_key in code",
|
|
input: `secret_key = "my_super_secret_token_here"`,
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "short password is not detected",
|
|
input: `password="short"`,
|
|
expected: false, // Too short to trigger
|
|
},
|
|
{
|
|
name: "word password in sentence",
|
|
input: "The password field should be validated",
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "word api in code",
|
|
input: "The API returns JSON data",
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := ContainsSecrets(tt.input)
|
|
if result != tt.expected {
|
|
t.Errorf("ContainsSecrets(%q) = %v, want %v", tt.input, result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRedactSecrets(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "empty string",
|
|
input: "",
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "no secrets",
|
|
input: "This is safe text",
|
|
expected: "This is safe text",
|
|
},
|
|
{
|
|
name: "API key gets redacted",
|
|
input: "api_key=abc123def456ghi789jkl012mno345pqr678",
|
|
expected: "api_key=[REDACTED]",
|
|
},
|
|
{
|
|
name: "OpenAI key gets redacted",
|
|
input: "The key is sk-abc123def456ghi789jkl012mno345pqr678",
|
|
expected: "The key is sk-a...[REDACTED]",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := RedactSecrets(tt.input)
|
|
if result != tt.expected {
|
|
t.Errorf("RedactSecrets(%q) = %q, want %q", tt.input, result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSanitizeObservation(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
narrative string
|
|
facts []string
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "clean observation",
|
|
narrative: "Fixed a bug in the login flow",
|
|
facts: []string{"Users can now log in", "Session management improved"},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "secret in narrative",
|
|
narrative: "Set API key api_key=abc123def456ghi789jkl012mno345",
|
|
facts: []string{"Configuration updated"},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "secret in facts",
|
|
narrative: "Updated configuration",
|
|
facts: []string{"Added api_key=abc123def456ghi789jkl012mno345"},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "empty facts",
|
|
narrative: "Clean narrative",
|
|
facts: []string{},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "nil facts",
|
|
narrative: "Clean narrative",
|
|
facts: nil,
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := SanitizeObservation(tt.narrative, tt.facts)
|
|
if result != tt.expected {
|
|
t.Errorf("SanitizeObservation() = %v, want %v", result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkContainsSecrets(b *testing.B) {
|
|
text := "This is a normal piece of text that does not contain any secrets or sensitive information"
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
ContainsSecrets(text)
|
|
}
|
|
}
|
|
|
|
func BenchmarkContainsSecretsWithSecret(b *testing.B) {
|
|
text := "api_key=abc123def456ghi789jkl012mno345pqr678"
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
ContainsSecrets(text)
|
|
}
|
|
}
|