Files
2026-07-28 21:17:29 -05:00

145 lines
4.4 KiB
Go

package app
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
func TestCodeExchangeFailureDetailsRetainsOnlySafeDiagnostics(t *testing.T) {
t.Parallel()
code := "TAPM-12345-ABCDE"
details := codeExchangeFailureDetails(code, errCodeNotFound)
for _, expected := range []string{
"reason=code_not_found",
"code_hint=ABCDE",
"normalized_length=16",
"format=expected",
} {
if !strings.Contains(details, expected) {
t.Errorf("details %q do not contain %q", details, expected)
}
}
if strings.Contains(details, code) || strings.Contains(details, "12345") {
t.Fatalf("details contain more of the submitted code than its hint: %q", details)
}
}
func TestCodeExchangeFailureDetailsDoesNotHintUnrelatedInput(t *testing.T) {
t.Parallel()
details := codeExchangeFailureDetails("not-a-deployment-secret", errCodeNotFound)
if !strings.Contains(details, "code_hint=unavailable") ||
!strings.Contains(details, "format=unexpected") {
t.Fatalf("unexpected details for malformed input: %q", details)
}
if strings.Contains(details, "ecret") {
t.Fatalf("details retained part of unrelated input: %q", details)
}
}
func TestCodeExchangeFailureReasons(t *testing.T) {
t.Parallel()
tests := []struct {
err error
reason string
}{
{errCodeExpired, "code_expired"},
{errCodeRevoked, "code_revoked"},
{errActionNotAuthorized, "action_not_authorized"},
{errPackageNotAuthorized, "package_not_authorized"},
{errHostLimitReached, "host_limit_reached"},
}
for _, test := range tests {
if details := codeExchangeFailureDetails("TAPM-12345-ABCDE", test.err); !strings.Contains(details, "reason="+test.reason) {
t.Errorf("details %q do not contain reason %q", details, test.reason)
}
}
}
func TestMalformedCodeAttemptIsAudited(t *testing.T) {
server, _ := newAuthorizationTestServer(t)
body, err := json.Marshal(exchangeRequest{
Code: "not-a-code",
HostFingerprint: "0123456789abcdef",
Hostname: "pve01",
})
if err != nil {
t.Fatal(err)
}
request := httptest.NewRequest(http.MethodPost, "/api/v1/exchange", bytes.NewReader(body))
response := httptest.NewRecorder()
server.handleExchange(response, request)
if response.Code != http.StatusForbidden {
t.Fatalf("status = %d, want %d", response.Code, http.StatusForbidden)
}
var hostname, details string
if err := server.db.QueryRow(
`SELECT hostname, details
FROM audit_events
WHERE event_type = 'code_exchange_failed'`,
).Scan(&hostname, &details); err != nil {
t.Fatal(err)
}
if hostname != "pve01" ||
!strings.Contains(details, "reason=code_not_found") ||
!strings.Contains(details, "normalized_length=10") ||
!strings.Contains(details, "format=unexpected") ||
!strings.Contains(details, "code_hint=unavailable") ||
strings.Contains(details, "not-a-code") {
t.Fatalf("unsafe or incomplete malformed-code audit: hostname=%q details=%q", hostname, details)
}
}
func TestFailedExpiredCodeAttemptIsLinkedToDeployment(t *testing.T) {
server, _ := newAuthorizationTestServer(t)
code := "TAPM-12345-ABCDE"
codeHash := hashValue(code)
if _, err := server.db.Exec(
`INSERT INTO authorizations
(id, code_hash, code_hint, created_by, customer_label, host_limit, expires_at)
VALUES (42, ?, 'ABCDE', 'taiadmin', 'Acme migration', 3, ?)`,
codeHash[:], time.Now().UTC().Add(-time.Hour),
); err != nil {
t.Fatal(err)
}
body, err := json.Marshal(exchangeRequest{
Code: code,
HostFingerprint: "0123456789abcdef",
Hostname: "pve01",
})
if err != nil {
t.Fatal(err)
}
request := httptest.NewRequest(http.MethodPost, "/api/v1/exchange", bytes.NewReader(body))
response := httptest.NewRecorder()
server.handleExchange(response, request)
if response.Code != http.StatusForbidden {
t.Fatalf("status = %d, want %d", response.Code, http.StatusForbidden)
}
var authorizationID uint64
var hostname, details string
if err := server.db.QueryRow(
`SELECT authorization_id, hostname, details
FROM audit_events
WHERE event_type = 'code_exchange_failed'`,
).Scan(&authorizationID, &hostname, &details); err != nil {
t.Fatal(err)
}
if authorizationID != 42 || hostname != "pve01" {
t.Fatalf("audit event authorization/hostname = %d/%q", authorizationID, hostname)
}
if !strings.Contains(details, "reason=code_expired") ||
!strings.Contains(details, "code_hint=ABCDE") ||
strings.Contains(details, code) {
t.Fatalf("unsafe or incomplete audit details: %q", details)
}
}