fix(02): scan leftover passwords and redact secrets in diffs (WR-04)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jakub Zych
2026-09-17 14:43:01 +02:00
parent d3d202e0e2
commit 0ac9dc45d0
4 changed files with 114 additions and 10 deletions

View File

@@ -17,14 +17,24 @@ import (
)
var (
placeholderRe = regexp.MustCompile(`\{\{([^{}]+)\}\}`)
jwtShapeRe = regexp.MustCompile(`eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+`)
invShapeRe = regexp.MustCompile(`inv_[A-Za-z0-9]{8,}`)
cookieRe = regexp.MustCompile(`(?i)auth_token=([^;]+)`)
secretFormRe = regexp.MustCompile(`(?i)client_secret=([^&\s]+)`)
pkceFormRe = regexp.MustCompile(`(?i)code_verifier=([^&\s]+)`)
placeholderRe = regexp.MustCompile(`\{\{([^{}]+)\}\}`)
jwtShapeRe = regexp.MustCompile(`eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+`)
invShapeRe = regexp.MustCompile(`inv_[A-Za-z0-9]{8,}`)
cookieRe = regexp.MustCompile(`(?i)auth_token=([^;]+)`)
secretFormRe = regexp.MustCompile(`(?i)client_secret=([^&\s]+)`)
pkceFormRe = regexp.MustCompile(`(?i)code_verifier=([^&\s]+)`)
passwordJSONRe = regexp.MustCompile(`(?i)"password"\s*:\s*"([^"]*)"`)
passwordFormRe = regexp.MustCompile(`(?i)(?:^|&)password=([^&\s]*)`)
accessTokenJSONRe = regexp.MustCompile(`(?i)"access_token"\s*:\s*"([^"]*)"`)
secretJSONRe = regexp.MustCompile(`(?i)"client_secret"\s*:\s*"[^"]*"`)
)
// allowedTestPasswords are documented onboarding secrets that remain in fixtures
// as plaintext. Any other leftover password-shaped value is a capture leak.
var allowedTestPasswords = map[string]struct{}{
"parity-alice-pass": {},
}
// Store holds named capture values. When Path is set it is a mode-0600 private file.
type Store struct {
mu sync.Mutex
@@ -623,9 +633,55 @@ func remainingCredential(s string) string {
if pkceFormRe.MatchString(s) {
return "pkce"
}
if leftoverPassword(s) {
return "password"
}
if leftoverAccessToken(s) {
return "access_token"
}
return ""
}
func leftoverPassword(s string) bool {
for _, m := range passwordJSONRe.FindAllStringSubmatch(s, -1) {
if m[1] != "" {
if _, ok := allowedTestPasswords[m[1]]; !ok {
return true
}
}
}
for _, m := range passwordFormRe.FindAllStringSubmatch(s, -1) {
v, err := url.QueryUnescape(m[1])
if err != nil {
v = m[1]
}
if v != "" {
if _, ok := allowedTestPasswords[v]; !ok {
return true
}
}
}
return false
}
func leftoverAccessToken(s string) bool {
for _, m := range accessTokenJSONRe.FindAllStringSubmatch(s, -1) {
if strings.TrimSpace(m[1]) != "" {
return true
}
}
return false
}
func redactSecrets(s string) string {
s = jwtShapeRe.ReplaceAllString(s, "<redacted-jwt>")
s = invShapeRe.ReplaceAllString(s, "<redacted-inv>")
s = secretFormRe.ReplaceAllString(s, "client_secret=<redacted>")
s = secretJSONRe.ReplaceAllString(s, `"client_secret":"<redacted>"`)
s = cookieRe.ReplaceAllString(s, "auth_token=<redacted>")
return s
}
func varsOutsideFixtures(varsPath, fixtures string) error {
if varsPath == "" || fixtures == "" {
return nil