fix(meshcore): hashtag channel secret not derived in non-secure HTTP contexts (#3606)#3608
Conversation
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
…back when crypto.subtle is unavailable (fixes #3606) crypto.subtle is only available in secure contexts (HTTPS or localhost). In the common Docker-on-a-separate-host case where users access MeshMonitor over plain HTTP via an IP address, crypto.subtle is undefined. The deriveHashtagSecretHex call in the useEffect threw a silent TypeError which was swallowed by the catch block, leaving the random seed in place. Every "Add channel" click produced a new random key and typing a #hashtag name never updated the secret to the deterministic value. Add a pure-JS SHA-256 implementation (FIPS 180-4) as a fallback used automatically when crypto.subtle is unavailable. deriveHashtagSecretHex now works in all deployment contexts without any API changes. Three regression tests added: known-vector checks for the pure-JS path (#test and #general) and a test that simulates the non-secure context by overriding crypto.subtle to undefined. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFCHwsMfG4XUctxKt2oT2k
dc38112 to
171bdd2
Compare
|
Claude finished @Yeraze's task —— View job PR Review: fix(meshcore): hashtag channel secret not derived in non-secure HTTP contexts
SummaryThis is a well-targeted fix for a real deployment issue. The root cause analysis is correct — Code Quality
The FIPS 180-4 SHA-256 implementation looks correct. A few observations:
The fallback detection is clean: if (typeof crypto !== 'undefined' && crypto.subtle) {This correctly handles both Minor issue — Potential Issue: Writable
|
| Test | Purpose |
|---|---|
sha256PureJS matches #test vector |
Verifies the pure-JS impl against a known-good value |
sha256PureJS matches #general vector |
Second authoritative vector, cross-checked against base64 from component tests |
deriveHashtagSecretHex fallback path |
End-to-end regression for the original bug, simulates the non-secure context |
The existing tests (case sensitivity, format, normalization) continue to exercise the crypto.subtle path. Good balance.
One gap: There's no test verifying that sha256PureJS produces the full 32-byte output correctly (the existing tests only check result.slice(0, 16)). This isn't a blocking concern for this use case since SHA-256 truncation to the first 16 bytes is well-defined, but a full-digest comparison would make the unit test more robust.
Verdict
Approve. The fix is correct, targeted, and well-tested. The pure-JS SHA-256 fallback is a standard approach for this class of problem. No blocking issues found.
|
Reviewed and verified independently before merge: the two test vectors are authoritative (SHA256("#test")[:16]= |
Summary
Fixes #3606 — MeshCore
#channelsecrets were always random instead of being derived from the channel name.Root cause:
crypto.subtleis only available in secure contexts (HTTPS orlocalhost). When MeshMonitor is accessed over plain HTTP via an IP address — the typical Docker-on-a-separate-host deployment —crypto.subtleisundefined. The call tocrypto.subtle.digest(...)insidederiveHashtagSecretHexthrew aTypeErrorthat was caught and silently discarded, leaving the randomly-seeded secret in place.crypto.getRandomValues(used to generate the seed) is available in non-secure contexts, so the seed was always set, but was never replaced by the deterministic derived value.Fix: Add a pure-JS SHA-256 implementation (FIPS 180-4) to
meshcoreHelpers.tsas a fallback that activates automatically whencrypto.subtleis unavailable.deriveHashtagSecretHexis unchanged from the caller's perspective and now works correctly in all deployment contexts.Changes
src/utils/meshcoreHelpers.ts: Addedsha256PureJS(data: Uint8Array): Uint8Array(FIPS 180-4, ~60 LOC). UpdatedderiveHashtagSecretHexto detectcrypto.subtleavailability and use the pure-JS fallback when it's absent.src/utils/meshcoreHelpers.test.ts: Three new regression tests:sha256PureJSmatches the authoritative#testvector (9cd8fcf22a47333b591d96a2b848b73f)sha256PureJSmatches the authoritative#generalvector (4c49f3f24629f5ee4ad5b3965db47985)deriveHashtagSecretHexproduces the correct result whencrypto.subtleis overridden toundefined(simulates HTTP-over-IP context)Test plan
meshcoreHelperstests pass (7 tests)MeshCoreChannelsConfigSectiontests pass (10 tests)🤖 Generated with Claude Code
https://claude.ai/code/session_01BFCHwsMfG4XUctxKt2oT2k
Generated by Claude Code