fix: enforce SPIFFE 2048-byte ID cap at URI construction (§2.4)#106
fix: enforce SPIFFE 2048-byte ID cap at URI construction (§2.4)#106safayavatsal wants to merge 3 commits intohighflame-ai:mainfrom
Conversation
BuildWIMSEURI was a Sprintf with no length check, so the SPIFFE §2.4 cap was unenforced. Today's varchar(255) schema bounds the URI to about 1080 bytes, so this isn't reachable through the API — but the invariant belongs at the construction site so a future schema relaxation can't silently mint non-conformant SPIFFE IDs. Changes BuildWIMSEURI to return (string, error), introduces MaxSPIFFEIDBytes = 2048, and propagates the error from RegisterIdentity. Domain tests cover rejection, happy path, and the inclusive boundary at exactly 2048 bytes. Note: signature change to a public domain function. No external consumers in the repo (pkg/authjwt does not call it). Fixes highflame-ai#48
There was a problem hiding this comment.
Code Review
This pull request implements a 2048-byte size limit for SPIFFE IDs as per the SPIFFE specification, updating the BuildWIMSEURI function to return an error upon exceeding this limit. It also includes comprehensive unit tests for boundary conditions and updates the identity service to handle the new error return. Feedback was provided to improve the robustness of error message formatting by avoiding unsafe string slicing and to wrap errors in the service layer for better traceability.
| func BuildWIMSEURI(wimseDomain, accountID, projectID string, identityType IdentityType, externalID string) (string, error) { | ||
| uri := fmt.Sprintf("spiffe://%s/%s/%s/%s/%s", wimseDomain, accountID, projectID, identityType, externalID) | ||
| if n := len(uri); n > MaxSPIFFEIDBytes { | ||
| return "", fmt.Errorf("SPIFFE ID exceeds %d bytes (got %d): %s…", MaxSPIFFEIDBytes, n, uri[:64]) |
There was a problem hiding this comment.
Slicing a string by bytes (uri[:64]) can be unsafe if the truncation happens in the middle of a multi-byte UTF-8 character. While SPIFFE IDs are expected to be ASCII-based URIs, using the %.64q format specifier is more robust as it handles rune boundaries correctly, escapes special characters, and provides a clear, quoted representation in the error message.
| return "", fmt.Errorf("SPIFFE ID exceeds %d bytes (got %d): %s…", MaxSPIFFEIDBytes, n, uri[:64]) | |
| return "", fmt.Errorf("SPIFFE ID exceeds %d bytes (got %d): %.64q...", MaxSPIFFEIDBytes, n, uri) |
| wimseURI, err := domain.BuildWIMSEURI(s.wimseDomain, req.AccountID, req.ProjectID, req.IdentityType, req.ExternalID) | ||
| if err != nil { | ||
| return nil, err | ||
| } |
There was a problem hiding this comment.
To maintain consistency with other error handling in RegisterIdentity (e.g., line 196) and across the service, consider wrapping the error from domain.BuildWIMSEURI. This ensures that the error trace includes the context of the operation being performed.
| wimseURI, err := domain.BuildWIMSEURI(s.wimseDomain, req.AccountID, req.ProjectID, req.IdentityType, req.ExternalID) | |
| if err != nil { | |
| return nil, err | |
| } | |
| wimseURI, err := domain.BuildWIMSEURI(s.wimseDomain, req.AccountID, req.ProjectID, req.IdentityType, req.ExternalID) | |
| if err != nil { | |
| return nil, fmt.Errorf("failed to build WIMSE URI: %w", err) | |
| } |
saucam
left a comment
There was a problem hiding this comment.
Please also resolve gemini comments
| t.Fatalf("URI of exactly %d bytes should be allowed: %v", MaxSPIFFEIDBytes, err) | ||
| } | ||
|
|
||
| overCap := atCap + "a" |
There was a problem hiding this comment.
should we assert the error is actually the length exceeded ?
|
Thanks for the careful work on this — three asks before merge: 1. Apply Gemini's
|
Summary
Caveat — public API signature change
`BuildWIMSEURI` is a top-level `domain` function, which `CONTRIBUTING.md` calls out as part of the public API. The single in-repo caller is updated; there are no other consumers (`pkg/authjwt` does not call it). Surfacing this so the maintainers can decide whether to ship the signature change or wrap with a `Must`-style helper.
Fixes #48
Test plan