fix(02): scan leftover passwords and redact secrets in diffs (WR-04)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user