Skip to content

Fix hashIdentifier normalization bug - #43

Merged
Just-Bamford merged 3 commits into
AnonVote:mainfrom
pgreat123:fix/hash-identifier-normalization
Jul 28, 2026
Merged

Fix hashIdentifier normalization bug#43
Just-Bamford merged 3 commits into
AnonVote:mainfrom
pgreat123:fix/hash-identifier-normalization

Conversation

@pgreat123

Copy link
Copy Markdown

Summary

Fixes #22hashIdentifier did not normalize input before hashing, so
identifiers differing only in whitespace, case, Unicode representation,
or incidental punctuation produced different hashes, causing token
validation to fail for otherwise-identical voter identifiers.

Changes

  • Added a normalizeIdentifier helper in src/crypto.ts that:
    1. Trims leading/trailing whitespace
    2. Lowercases
    3. Unicode-normalizes to NFC (so precomposed and combining-mark forms
      of the same character collapse to one)
    4. Strips any character that isn't alphanumeric, -, or _
  • hashIdentifier now runs input through normalizeIdentifier before
    hashing. Algorithm (SHA-256) and output format (hex string) are
    unchanged.
  • Added test coverage in tests/crypto.test.ts for: Unicode NFC/NFD
    equivalence, stripping of stray punctuation/symbols, and preservation
    of hyphens/underscores — on top of the existing whitespace/case tests.

⚠️ Breaking change — please review before merge

This changes hash output for any identifier that wasn't already clean
lowercase alphanumeric
, not just whitespace/case variants called out
in the issue. In particular, @ and . are stripped, so an
email-shaped identifier like alice@example.com now hashes identically
to aliceexamplecom — the @ and . no longer contribute to the hash
at all.

If eligibility data was seeded/hashed using the old (unnormalized or
trim+lowercase-only) version, those hashes will no longer match after
this change. Per the issue's acceptance criteria, this needs to be
verified against the backend — and if identifiers are email addresses,
we may want to confirm this stripping behavior is actually desired
before merging, or existing eligibility data will need to be
regenerated.

Testing

npx jest crypto.test.ts

kenny added 3 commits July 28, 2026 11:40
…-normalization

modified:   package.json
:wq#
…ternally inconsistent(missing terser, and yocto-queue/resolve-cwd were listed but notactually being installed), which broke npm ci and eslint/jest on aclean install. Regenerating from package.json fixes it.Also removed spurious os restrictions (aix/linux-only) that had beenadded to pretty-format, resolve.exports, and semver in the previouslockfile - those are pure-JS packages with no such restriction on thereal npm registry, and the restriction was blocking installs onWindows/macOS.
@pgreat123

Copy link
Copy Markdown
Author

While working on #22 (fix/hash-identifier-normalization), I found that
package-lock.json on main has fabricated os restrictions on three
pure-JavaScript packages that have no such restriction on the real npm
registry:

  • pretty-format@29.7.0 → locked to "os": ["aix"]
  • resolve.exports@2.0.3 → locked to "os": ["linux"]
  • semver@6.3.1 → locked to "os": ["linux"]

I verified the actual package content (integrity hashes) matches the
real npm registry exactly for all three — so this isn't a swapped/
malicious package, just incorrect metadata in the lockfile. But since
npm enforces os fields at install time, this currently breaks:

  1. npm install/npm ci on Windows and macOS for any contributor
    pulling main
  2. The "Crypto Benchmarks" CI workflow's "Compare against main" step
    on every PR — it checks out main fresh into a worktree and runs
    npm ci there, which fails on the Linux runner too (wants aix,
    runner is linux). Screenshot/log:
    [attach the error you saw]

Separately, the workflow itself
(.github/workflows/benchmarks.yml) only has a failure fallback on
the npm run bench line, not on the preceding npm ci:

npm ci
npm run bench > /tmp/main-benchmarks.txt || echo "no baseline available on main" > /tmp/main-benchmarks.txt

Since the comment above this step says the comparison is meant to be
a non-blocking "manual side-by-side, not an automated pass/fail
gate," it'd probably be worth wrapping the npm ci call the same way,
so a broken main install doesn't fail every PR's benchmark job.

Suggest: regenerate package-lock.json on main from scratch
(rm package-lock.json && npm install), and add a fallback around
npm ci in the benchmark workflow.

@pgreat123

Copy link
Copy Markdown
Author

@Just-Bamford
Kindly check out the comment I gave above on the current issue that we have
Thank you

@Just-Bamford

Copy link
Copy Markdown
Contributor

@pgreat123 Thanks for identifying this. I’ve created an issue to track the lockfile and benchmark workflow fixes. Feel free to apply for it and take it up.

@Just-Bamford

Copy link
Copy Markdown
Contributor

@pgreat123 Just leave a comment on here

#46

@Just-Bamford
Just-Bamford merged commit 8a02767 into AnonVote:main Jul 28, 2026
2 of 3 checks passed
@grantfox-oss grantfox-oss Bot mentioned this pull request Jul 28, 2026
3 tasks
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.

Fix hashIdentifier normalization bug

2 participants