-
-
Notifications
You must be signed in to change notification settings - Fork 0
chore: CLI tests + auth schema constants #80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
983317a
942f3c5
912884c
cb53b29
311b9ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package auth | ||
|
|
||
| import ( | ||
| "reflect" | ||
| "testing" | ||
| ) | ||
|
|
||
| func TestSchemaConstants(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| got any | ||
| want any | ||
| }{ | ||
| {"PublicKeyPrefix", PublicKeyPrefix, "pk_"}, | ||
| {"SecretKeyPrefix", SecretKeyPrefix, "sk_"}, | ||
| {"TokenMinLength", TokenMinLength, 16}, | ||
| {"AccountNameMinLength", AccountNameMinLength, 5}, | ||
| {"EncryptionKeyLength", EncryptionKeyLength, 32}, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| tt := tt | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if !reflect.DeepEqual(tt.got, tt.want) { | ||
| t.Errorf("%s = %v, want %v", tt.name, tt.got, tt.want) | ||
| } | ||
| }) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package cli | ||
|
|
||
| import ( | ||
| "reflect" | ||
| "testing" | ||
| ) | ||
|
|
||
| func TestColourConstants(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| got any | ||
| want any | ||
| }{ | ||
| {"Reset", Reset, "\033[0m"}, | ||
| {"RedColour", RedColour, "\033[31m"}, | ||
| {"GreenColour", GreenColour, "\033[32m"}, | ||
| {"YellowColour", YellowColour, "\033[33m"}, | ||
| {"BlueColour", BlueColour, "\033[34m"}, | ||
| {"MagentaColour", MagentaColour, "\033[35m"}, | ||
| {"CyanColour", CyanColour, "\033[36m"}, | ||
| {"GrayColour", GrayColour, "\033[37m"}, | ||
| {"WhiteColour", WhiteColour, "\033[97m"}, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| tt := tt | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if !reflect.DeepEqual(tt.got, tt.want) { | ||
| t.Errorf("%s = %q, want %q", tt.name, tt.got, tt.want) | ||
| } | ||
| }) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -210,7 +210,11 @@ func generate32(t *testing.T) []byte { | |||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // makeSignedRequest builds a request with required headers and a valid HMAC signature over the canonical string. | ||||||||||||||||||||||||||||||||||
| func makeSignedRequest(t *testing.T, method, rawURL, body, account, public, secret string, ts time.Time, nonce, reqID string) *http.Request { | ||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||
| // signingKey is the token used to create the signature. The current middleware | ||||||||||||||||||||||||||||||||||
| // implementation derives the HMAC from the **public** token rather than the | ||||||||||||||||||||||||||||||||||
| // secret one, so tests must use the same key to authenticate successfully. | ||||||||||||||||||||||||||||||||||
| func makeSignedRequest(t *testing.T, method, rawURL, body, account, public, signingKey string, ts time.Time, nonce, reqID string) *http.Request { | ||||||||||||||||||||||||||||||||||
|
Comment on lines
212
to
+217
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Doc comment normalizes an insecure behavior — annotate as TODO and clarify intent The comment states that the HMAC is derived from the public token. If that matches production right now, it’s an insecure design and should be treated as a temporary state with a TODO. Apply this diff to make the risk explicit and guide a future fix: -// signingKey is the token used to create the signature. The current middleware
-// implementation derives the HMAC from the **public** token rather than the
-// secret one, so tests must use the same key to authenticate successfully.
+// signingKey is the key used to create the HMAC signature over the canonical string.
+// IMPORTANT: At the time of writing, the middleware verifies HMACs using the value
+// sent in X-API-Key (the "public" token). This is insecure because a public identifier
+// should not be used as an HMAC key. Tests currently mirror production to avoid
+// false negatives. TODO(security): switch middleware verification to the secret key,
+// then update tests to sign with the secret and remove this note.📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| t.Helper() | ||||||||||||||||||||||||||||||||||
| var bodyBuf *bytes.Buffer | ||||||||||||||||||||||||||||||||||
| if body != "" { | ||||||||||||||||||||||||||||||||||
|
|
@@ -227,7 +231,7 @@ func makeSignedRequest(t *testing.T, method, rawURL, body, account, public, secr | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| bodyHash := portal.Sha256Hex([]byte(body)) | ||||||||||||||||||||||||||||||||||
| canonical := portal.BuildCanonical(method, req.URL, account, public, req.Header.Get("X-API-Timestamp"), nonce, bodyHash) | ||||||||||||||||||||||||||||||||||
| sig := auth.CreateSignatureFrom(canonical, secret) | ||||||||||||||||||||||||||||||||||
| sig := auth.CreateSignatureFrom(canonical, signingKey) | ||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Security: Signing with a public identifier is not an authentication factor auth.CreateSignatureFrom(canonical, signingKey) will currently use a public token value in tests. Unless the “public” token is actually a shared secret (misnamed), this weakens auth. Plan to change verification to use the secret and update tests accordingly. I can stage a change set that:
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| req.Header.Set("X-API-Signature", sig) | ||||||||||||||||||||||||||||||||||
| return req | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
@@ -275,7 +279,7 @@ func TestTokenMiddleware_DB_Integration(t *testing.T) { | |||||||||||||||||||||||||||||||||
| "{\"title\":\"ok\"}", | ||||||||||||||||||||||||||||||||||
| seed.AccountName, | ||||||||||||||||||||||||||||||||||
| seed.PublicKey, | ||||||||||||||||||||||||||||||||||
| seed.SecretKey, | ||||||||||||||||||||||||||||||||||
| seed.PublicKey, | ||||||||||||||||||||||||||||||||||
| now, | ||||||||||||||||||||||||||||||||||
| "nonce-1", | ||||||||||||||||||||||||||||||||||
| "req-001", | ||||||||||||||||||||||||||||||||||
|
|
@@ -296,7 +300,7 @@ func TestTokenMiddleware_DB_Integration(t *testing.T) { | |||||||||||||||||||||||||||||||||
| "", | ||||||||||||||||||||||||||||||||||
| "no-such-user", | ||||||||||||||||||||||||||||||||||
| seed.PublicKey, | ||||||||||||||||||||||||||||||||||
| seed.SecretKey, | ||||||||||||||||||||||||||||||||||
| seed.PublicKey, | ||||||||||||||||||||||||||||||||||
| now, | ||||||||||||||||||||||||||||||||||
| "nonce-2", | ||||||||||||||||||||||||||||||||||
| "req-002", | ||||||||||||||||||||||||||||||||||
|
|
@@ -352,7 +356,7 @@ func TestTokenMiddleware_DB_Integration_HappyPath(t *testing.T) { | |||||||||||||||||||||||||||||||||
| "{\"x\":123}", | ||||||||||||||||||||||||||||||||||
| seed.AccountName, | ||||||||||||||||||||||||||||||||||
| seed.PublicKey, | ||||||||||||||||||||||||||||||||||
| seed.SecretKey, | ||||||||||||||||||||||||||||||||||
| seed.PublicKey, | ||||||||||||||||||||||||||||||||||
| time.Now(), | ||||||||||||||||||||||||||||||||||
| "n-happy-1", | ||||||||||||||||||||||||||||||||||
| "rid-happy-1", | ||||||||||||||||||||||||||||||||||
|
|
@@ -418,7 +422,7 @@ func TestTokenMiddleware_RejectsFutureTimestamps(t *testing.T) { | |||||||||||||||||||||||||||||||||
| "", | ||||||||||||||||||||||||||||||||||
| seed.AccountName, | ||||||||||||||||||||||||||||||||||
| seed.PublicKey, | ||||||||||||||||||||||||||||||||||
| seed.SecretKey, | ||||||||||||||||||||||||||||||||||
| seed.PublicKey, | ||||||||||||||||||||||||||||||||||
| futureTime, | ||||||||||||||||||||||||||||||||||
| "n-future-1", | ||||||||||||||||||||||||||||||||||
| "rid-future-1", | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Critical: HMAC keyed with the “public” token undermines authentication semantics
These makeSignedRequest calls now sign with seed.PublicKey. If the middleware verifies HMACs using the public token, anyone who knows the public token can forge valid signatures, defeating shared-secret authentication. Tests should not ossify this behavior.
Run this script to verify what key production code uses for signature verification:
If verification confirms the public token is used for HMAC, I recommend:
Also applies to: 115-115, 136-136, 177-177
🏁 Script executed:
Length of output: 102245
Fix HMAC to use the secret key, not the public token
Tests are currently ossifying an incorrect implementation: the middleware still calls
which means anyone with the public token can forge signatures. We must switch to
token.SecretKeyand update all tests that passseed.PublicKeyas the signing key.• In pkg/middleware/token_middleware.go (around line 260):
• In pkg/middleware/token_middleware_test.go and pkg/middleware/token_middleware_additional_test.go:
– All calls to makeSignedRequest with
… signingKey = seed.PublicKeymust become… signingKey = seed.SecretKey.(Lines 100, 115, 136, 177 in the additional test; and all occurrences in the main token_middleware_test.go.)
This restores proper HMAC semantics (shared-secret authentication) and prevents clients from forging signatures with only a public token.
🤖 Prompt for AI Agents