Repository navigation
fix(api): enforce canonical hex format on validate_key_format (#2427) - #2491
Merged
endxxxx merged 1 commit intoOct 29, 2026
Merged
Conversation
Collaborator
🤖 Open Code ReviewTarget: PR #2491 ✅ OpenCodeReview: Review complete: 0 finding(s) across 2 selected item(s). Generated by cloud-assistant via Open Code Review. |
Collaborator
✅ Automated Test Results: PASSEDAll tests passed (21/21 executed). memos_python_core/changed-repo-python: 21/21. Duration: 9s Branch: |
…sor#2427) validate_key_format exists as a separate copy in two modules — src/memos/api/utils/api_keys.py (canonical home, used by the key-creation utilities) and src/memos/api/middleware/auth.py (the middleware that gates every authenticated request). Both used `int(hex_part, 16)` to verify the 64-char hex tail, which silently passes for: - a leading `+` or `-` sign (`int("+a..a", 16)` == ...), - a `0x` / `0X` prefix (`int("0xa..", 16)` == ...), - `_` separators (PEP 515: `int("a_a", 16)`), - surrounding whitespace. So the documented format `krlk_<64-lowercase-hex>` was not actually enforced — keys like `krlk_+a..a`, `krlk_0x..`, or `krlk_a_a` were accepted by the middleware, hashed verbatim, and looked up against the database, bypassing the canonical form that `generate_api_key()` produces. This commit: - Replaces the `int(hex_part, 16)` check in the canonical api_keys.py implementation with an anchored regex (`[0-9a-f]{64}`) so only the exact lowercase 64-hex form is valid. - Imports the fixed implementation into auth.py and keeps the public `validate_key_format` name as a thin re-export, so the middleware shares the same logic as the canonical utils copy. This prevents the two copies from drifting again — the original bug class. New tests/api/test_validate_key_format.py covers 21 cases: - canonical format and `generate_api_key()` output always pass - 15 non-canonical variants are rejected (signs, 0x prefix, underscore, uppercase, whitespace, wrong length, missing prefix, empty) - both copies (utils and middleware) enforce the same contract Fixes MemTensor#2427.
RerankerGuo
force-pushed
the
fix/issue-2427-canonical-hex
branch
from
October 27, 2026 18:52
ba76840 to
981130c
Compare
endxxxx
approved these changes
Oct 28, 2026
Contributor
Author
Collaborator
✅ Automated Test Results: PASSEDAll tests passed (21/21 executed). memos_python_core/changed-repo-python: 21/21. Duration: 16s Branch: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
validate_key_formatexists as a separate copy in two modules. Both usedint(hex_part, 16)to verify the 64-char hex tail, which silently passes for leading signs,0xprefixes,_separators, and surrounding whitespace — so the documented formatkrlk_<64-lowercase-hex>was not actually enforced at the middleware boundary.Root cause
Two copies of the same buggy validator:
src/memos/api/utils/api_keys.py:48— used by the key-creation utilitiessrc/memos/api/middleware/auth.py:65— gates every authenticated requestBoth check the tail via
int(hex_part, 16). CPython accepts:int(..., 16)result+aaaa…(64 chars)0xaaaa…(62 chars)aaaa_bbbb…(with_)AAAA…/AAAA…So
krlk_+a..a,krlk_0x..,krlk_a_a,krlk_A..A, etc. were all accepted by the middleware, hashed verbatim, and looked up against the database — bypassing the canonical form thatgenerate_api_key()produces (lowercase, no prefixes, no separators, no signs, no whitespace).Fix
Two changes that together eliminate the bug class:
src/memos/api/utils/api_keys.py: replace theint(hex_part, 16)check with an anchored regexre.fullmatch(r"[0-9a-f]{64}", hex_part). Only the exact 64-lowercase-hex form is valid.src/memos/api/middleware/auth.py: import the canonical implementation under the namevalidate_key_format_canonical, keep the public symbolvalidate_key_formatas a one-line re-export. This prevents the two copies from drifting again (the original bug class).Tests
New
tests/api/test_validate_key_format.py, 21 cases:generate_api_key()output (20 random samples) always pass0xprefix, underscore separators, uppercase hex, leading/trailing whitespace, wrong length, missingkrlk_prefix,Bearerprefix, empty stringutilsandmiddlewarecopies enforce the same contractVerification
Ruff:
ruff format --checkandruff checkboth clean on touched files (0 new findings vsmain; the 3 pre-existingDTZ/datetime.utcnowwarnings in api_keys.py are out of scope).Out of scope
Closes #2427