update
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user