d237098d83
GitHub PATs and Stripe secret keys have no scriptable rotation API (GitHub's create-token API was removed in 2020; Stripe has no create-key endpoint), so they belong in the guided change-password layer, not as Rotators. But they arrive from the env/file scanner as generic Source="env" tokens with no host, so the worklist showed them as "manual — no web page". Recognise them by their well-known PUBLIC value prefixes (ghp_/gho_/ghs_/ github_pat_/…, sk_live_/sk_test_/rk_live_/…) at scan time and record a non-secret Meta["service"] hint — a fixed service NAME, never the secret bytes. discover.ServiceForSecret does the detection; env.go attaches it for generic tokens, file.go before Store wipes the buffer. links.HostFor consults the hint and maps github→github.com / stripe→stripe.com, so the curated change-password URLs (already in the table) now light up for these credentials. Leak-safe (service name is derived from a public prefix, not the secret) and verified by the existing assertNoLeak checks. go build/vet clean; full suite 179 tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
102 lines
3.3 KiB
Go
102 lines
3.3 KiB
Go
package links
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"incredigo/internal/discover"
|
|
)
|
|
|
|
func TestFor(t *testing.T) {
|
|
cases := []struct {
|
|
host string
|
|
wantURL string
|
|
wantSrc Source
|
|
}{
|
|
{"github.com", "https://github.com/settings/tokens", SourceKnown},
|
|
{"GitHub.com:443", "https://github.com/settings/tokens", SourceKnown},
|
|
{"api.github.com", "https://github.com/settings/tokens", SourceKnown}, // parent-domain fallback
|
|
{"s3.amazonaws.com", "https://console.aws.amazon.com/iam/home#/security_credentials", SourceKnown},
|
|
{"example.com", "https://example.com/.well-known/change-password", SourceWellKnown},
|
|
{"registry.internal.corp", "https://registry.internal.corp/.well-known/change-password", SourceWellKnown},
|
|
{"", "", SourceNone},
|
|
}
|
|
for _, c := range cases {
|
|
got := For(c.host)
|
|
if got.URL != c.wantURL || got.Source != c.wantSrc {
|
|
t.Errorf("For(%q) = {%q,%s}, want {%q,%s}", c.host, got.URL, got.Source, c.wantURL, c.wantSrc)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHostFor(t *testing.T) {
|
|
cases := []struct {
|
|
cred discover.Credential
|
|
want string
|
|
}{
|
|
{discover.Credential{Source: "git", Identity: "alice @ github.com"}, "github.com"},
|
|
{discover.Credential{Source: "netrc", Identity: "bob @ api.example.com"}, "api.example.com"},
|
|
{discover.Credential{Source: "netrc", Identity: "bob @ default"}, ""}, // netrc "default" is not a host
|
|
{discover.Credential{Source: "docker", Identity: "ghcr.io"}, "ghcr.io"},
|
|
{discover.Credential{Source: "aws", Identity: "default / AKIA***"}, "console.aws.amazon.com"},
|
|
{discover.Credential{Source: "ssh", Identity: "id_ed25519"}, ""},
|
|
{discover.Credential{Source: "file", Identity: "keys/deploy"}, ""},
|
|
}
|
|
for _, c := range cases {
|
|
if got := HostFor(c.cred); got != c.want {
|
|
t.Errorf("HostFor(%s/%q) = %q, want %q", c.cred.Source, c.cred.Identity, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLinkForGit(t *testing.T) {
|
|
l := LinkFor(discover.Credential{Source: "git", Identity: "alice @ github.com"})
|
|
if l.Source != SourceKnown || l.URL != "https://github.com/settings/tokens" {
|
|
t.Errorf("LinkFor git github = {%q,%s}", l.URL, l.Source)
|
|
}
|
|
}
|
|
|
|
// A GitHub PAT / Stripe key arrives from the env or file scanner with no host of
|
|
// its own, only a non-secret service hint. HostFor must turn that hint into the
|
|
// curated change-password host so the guided worklist gets a real link.
|
|
func TestHostForServiceHint(t *testing.T) {
|
|
cases := []struct {
|
|
service string
|
|
want string
|
|
}{
|
|
{"github", "github.com"},
|
|
{"stripe", "stripe.com"},
|
|
{"unknown", ""},
|
|
}
|
|
for _, c := range cases {
|
|
cred := discover.Credential{
|
|
Source: "env",
|
|
Identity: ".env / API_KEY",
|
|
Meta: map[string]string{discover.MetaService: c.service},
|
|
}
|
|
if got := HostFor(cred); got != c.want {
|
|
t.Errorf("HostFor(service=%q) = %q, want %q", c.service, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLinkForServiceHint(t *testing.T) {
|
|
cases := []struct {
|
|
service string
|
|
wantURL string
|
|
}{
|
|
{"github", "https://github.com/settings/tokens"},
|
|
{"stripe", "https://dashboard.stripe.com/apikeys"},
|
|
}
|
|
for _, c := range cases {
|
|
cred := discover.Credential{
|
|
Source: "env",
|
|
Identity: ".env / TOKEN",
|
|
Meta: map[string]string{discover.MetaService: c.service},
|
|
}
|
|
l := LinkFor(cred)
|
|
if l.Source != SourceKnown || l.URL != c.wantURL {
|
|
t.Errorf("LinkFor(service=%q) = {%q,%s}, want {%q,known}", c.service, l.URL, l.Source, c.wantURL)
|
|
}
|
|
}
|
|
}
|