package config import ( "fmt" "os" "strings" ) // Secret key references. Values are never logged and never stored by the // harness; only their resolved bytes reach the outgoing HTTP headers. // // Formats: // // env:NAME environment variable (must be set and non-empty) // file:PATH file whose trimmed contents are the key // literal:VALUE inline key (last resort; still never logged) // bw:REF bitwarden item (reserved; lands with the bw wrapper, phase 3) func CheckKeyRef(ref string) error { switch { case ref == "": return fmt.Errorf("empty key reference") case strings.HasPrefix(ref, "env:"): if len(ref) <= 4 { return fmt.Errorf("key ref %q: env: needs a variable name", redact(ref)) } case strings.HasPrefix(ref, "file:"): if len(ref) <= 5 { return fmt.Errorf("key ref %q: file: needs a path", redact(ref)) } case strings.HasPrefix(ref, "literal:"): if len(ref) <= 8 { return fmt.Errorf("key ref: literal: value is empty") } case strings.HasPrefix(ref, "bw:"): return fmt.Errorf("key ref bw: not implemented yet (bitwarden wrapper lands in build phase 3)") default: return fmt.Errorf("key ref must use env:, file:, literal:, or bw: prefixes (unrecognized ref redacted)") } return nil } // ResolveKeyRef resolves a key reference to its value. Never log the result. func ResolveKeyRef(ref string) (string, error) { if err := CheckKeyRef(ref); err != nil { return "", err } switch { case strings.HasPrefix(ref, "env:"): v := os.Getenv(ref[4:]) if v == "" { return "", fmt.Errorf("environment variable %s is not set", ref[4:]) } return v, nil case strings.HasPrefix(ref, "file:"): data, err := os.ReadFile(ref[5:]) if err != nil { return "", fmt.Errorf("read key file: %w", err) } v := strings.TrimSpace(string(data)) if v == "" { return "", fmt.Errorf("key file %s is empty", ref[5:]) } return v, nil case strings.HasPrefix(ref, "literal:"): return ref[8:], nil } return "", fmt.Errorf("unreachable key ref branch") } // redact masks everything after a recognized prefix so malformed refs can // be reported without echoing a possibly-pasted secret. Refs without a // recognizable prefix are never echoed at all. func redact(ref string) string { if i := strings.IndexByte(ref, ':'); i >= 0 && len(ref) > i+1 { return ref[:i+1] + "****" } return "" }