Skip to content

fix(auth): sweep the in-process challenge store, and take redis 8 and cryptography 50 - #4

Merged
CtrlAltDevelop merged 3 commits into
mainfrom
develop
Oct 15, 2026
Merged

CtrlAltDevelop merged 3 commits into
mainfrom
develop

Conversation

@CtrlAltDevelop

Copy link
Copy Markdown
Owner

What

  • A memory leak in LocMemChallengeStore. Nothing expires a key in a dict, and the store
    dropped a lapsed record only when something looked that same key up again -- which the two
    kinds that accumulate never are: a code requested and never used, and a rate-limit counter,
    one per address, per destination and per account tried. Both were held for the life of the
    process, so an anonymous caller could grow a worker's memory by asking for login codes. The
    two write paths sweep what has expired first. The Redis store was always fine.
  • The dependency update. cryptography<51 and redis<9, here and in the generated
    project. redis 8's own type hints make two cast(int, ...) around hincrby redundant and
    refuse a dict[str, str] as hset(mapping=), Mapping being invariant in its key.
  • Both fixes are mirrored into the generator template, so a generated project gets them too.

Checks

  • make check clean: ruff, ruff format, mypy (1213 files), manage.py check,
    makemigrations --check, protos --check, every optional app enabled.
  • pytest --cov: 3162 passed, 2 skipped, 95.10% coverage. One regression test added for the
    sweep.

Dependencies were upgraded to current releases locally (Django 6.1.2, cryptography 50.0.2,
redis 8.1.0, strawberry-graphql-django 0.93, grpcio 1.84, mypy 2.4, ruff 0.17, pytest 9.1) and
only those two caps needed widening.

🤖 Generated with Claude Code

… cryptography 50

Nothing expires a key in a dict. LocMemChallengeStore dropped a lapsed record
only when something looked that same key up again, and the two kinds that
accumulate are never looked up twice: a code requested and never used, and a
rate-limit counter -- one per address, per destination, per account tried. Both
were held for the life of the process, so an anonymous caller could grow a
worker's memory by asking for codes. The write paths sweep what has expired
first; the Redis store was always fine, because Redis expires its own keys.

`cryptography<51` and `redis<9`, here and in the generated project. redis 8's
own type hints make two `cast(int, ...)` around `hincrby` redundant and refuse
a `dict[str, str]` as `hset(mapping=)`, Mapping being invariant in its key.
@CtrlAltDevelop
CtrlAltDevelop merged commit 6313aa3 into main Oct 15, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant