Sanitize affiliate tracking code prefixes#463
Conversation
Greptile SummaryThis PR sanitizes the username prefix in
Confidence Score: 4/5Safe to merge — the sanitization is isolated to new code generation and does not affect existing stored tracking codes or any lookup paths. The change is small and well-targeted. Existing tracking codes in the database are unaffected because they are looked up by exact stored value. The one thing worth a second look is the ordering of the dash-trim and the slice: trimming before slicing means a username whose 48th character lands inside a hyphen-run will produce a prefix ending in a dash, giving a double-dash in the final code. This is cosmetic rather than functional, but adding a second trim after the slice would close it completely. Only tracking.ts warrants a close look — specifically the order of .replace(/^-+|-+$/g, "") relative to .slice(0, 48). Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["generateTrackingCode(username, offerSlug)"] --> B["base = username + '-' + offerSlug\n(unchanged, used for hash seed)"]
A --> C["username.toLowerCase()"]
C --> D["replace([^a-z0-9]+ → '-')"]
D --> E["trim leading/trailing dashes"]
E --> F["slice(0, 48)"]
F --> G{result empty?}
G -- yes --> H["prefix = 'affiliate'"]
G -- no --> I["prefix = sanitized string"]
H --> J["hash = SHA256(base + Date.now()).slice(0,6)"]
I --> J
B --> J
J --> K["return prefix + '-' + hash"]
Reviews (1): Last reviewed commit: "Sanitize affiliate tracking code prefixe..." | Re-trigger Greptile |
| const prefix = username | ||
| .toLowerCase() | ||
| .replace(/[^a-z0-9]+/g, "-") | ||
| .replace(/^-+|-+$/g, "") | ||
| .slice(0, 48) || "affiliate"; |
There was a problem hiding this comment.
Trailing dash possible after slice
The dash-trim (.replace(/^-+|-+$/g, "")) runs before .slice(0, 48), so if the slice boundary falls on a hyphen the prefix can end with -, producing a double-dash in the final tracking code (e.g. aaaa...a--abc123). A username like "a" * 47 + " extra" sanitizes to "a"*47 + "-extra" and slices to "a"*47 + "-", yielding the prefix aaaa...a-. Apply the trim a second time after slicing to prevent this.
| @@ -19,12 +19,17 @@ async function getAttributionWindowStart(admin: SupabaseClient, offerId: string) | |||
| */ | |||
| export function generateTrackingCode(username: string, offerSlug: string): string { | |||
| const base = `${username}-${offerSlug}`; | |||
There was a problem hiding this comment.
Hash seed uses original unsanitized username
The base variable (used as the hash seed) still embeds the raw username, but the prefix returned in the tracking code is now the sanitized form. This means the human-readable portion of the code (prefix) no longer matches the entropy source (base). For usernames that sanitize to "affiliate" (the fallback), all such usernames share the same prefix but generate hashes from their distinct raw values — so codes are still unique. The disconnect is harmless today, but if base were ever used to reverse-lookup or validate the prefix this divergence would be surprising. Consider using prefix (or a normalized form) as the base instead of the raw username.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
CI is green for PR #463. Verification:
uGig invoice evidence has been sent for this PR. |
Closes #462.
Summary
generateTrackingCode()to URL-safe lowercase slug characters.affiliatefallback when a username has no safe characters.Verification
corepack pnpm vitest run src/lib/affiliates/tracking.test.tscorepack pnpm tsc --noEmit