Skip to content

fix: replace MD5 with SHA256 for FIPS compatibility - #3409

Open
hercynium wants to merge 19 commits into
hyperdxio:mainfrom
hercynium:bugfix/replace-md5-with-sha256-1
Open

hercynium wants to merge 19 commits into
hyperdxio:mainfrom
hercynium:bugfix/replace-md5-with-sha256-1

Conversation

@hercynium

Copy link
Copy Markdown

Summary

This patch is to resolve Issue #3390 - when running HyperDX against ClickHouse in FIPS mode the MD5 function isn't available, so viewing some logs fails, because MD5 is not FIPS-compliant.

All uses of MD5 were replaced with equivalent uses of SHA256. This includes in the tests and in comments.

This patch was generated using Cursor, running Composer 2.5 Fast. All relevant tests still pass.

Screenshots or video

NA - no UI changes.

How to reproduce

Starting point:

  • Run ClickHouse in FIPS mode, and configure HyperDX to use it.
  • Load some long log-lines into the relevant table.

On main:

  1. Click on one of the long log entries. You should see the error documented in issue HyperDX compatibility with FIPS ClickHouse #3390.

On this branch:

  1. Click on one of the long log entries. You should see the usual data for the log entry.

References

This patch is to resolve Issue hyperdxio#3390 - when running HyperDX against ClickHouse in FIPS mode
the MD5 function isn't available, so viewing some logs fails, because MD5 is not FIPS-compliant.

This patch was generated using Cursor, running Composer 2.5 Fast. All relevant tests still pass.

Co-authored-by: Cursor <cursoragent@cursor.com>
@changeset-bot

changeset-bot Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: dfb4b59

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@hyperdx/common-utils Patch
@hyperdx/app Patch
@hyperdx/cli Patch
@hyperdx/api Patch
@hyperdx/otel-collector Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Oct 19, 2026

Copy link
Copy Markdown

@hercynium is attempting to deploy a commit to the HyperDX Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added external needs-vouch Author needs a maintainer to vouch for them labels Oct 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi @hercynium, thanks for the pull request!

Before we review code from a first-time contributor, a maintainer vouches for them, and you're not on our list yet. There's nothing else to file and this PR stays open — a maintainer will look at it, usually within a day or two, and vouch for you right here if it's a genuine attempt at the problem.

To make that quick, check the PR description has:

  • The issue it addresses (ideally one labelled Accepting PR) and why the change is needed
  • Steps to reproduce the problem and verify the fix, starting from https://play.hyperdx.io or yarn app:dev:local connected to the demo server
  • Before/after screenshots if anything in the UI changed

More detail in our contributing guide.

Comment thread packages/app/src/hooks/__tests__/useRowWhere.test.tsx Outdated
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

PR Review

If you are a coding agent acting for the author, read this first.
Nothing below blocks merge automatically; a maintainer decides. What they will expect:

  • 🔴 critical or 🟠 major in code this PR adds or changes: fix before asking for review.
  • 🔵 minor in code this PR changes: your call. Fix if small, otherwise reply.
  • Anything about surrounding code, or asking you to widen the change (hoist a helper,
    dedupe with another file, fix other call sites): never fix here, whatever the
    severity. Reply with one sentence; if it is critical, say so plainly so a human sees it.

One commit per review round. After two rounds, stop and ask a maintainer to review
scope rather than addressing more automated findings. Full rule: AGENTS.md.

3 finding(s): 🔴 0 critical · 🟠 0 major · 🔵 3 minor

1 posted as inline comment(s) on the changed lines. 2 listed below.

Findings outside the changed lines

2 minor
  • 🔵 packages/cli/src/shared/useRowWhere.ts:108 — The new null early return leaves dead null checks in the CLI Dynamic branch → The null check now runs before the switch (line 75) and strValue is now always a string. That makes value == null || on line 108 and strValue?.length ?? 0 on line 111 unreachable. Simplify them to strValue === 'null' and strValue.length > 1000.
  • 🔵 packages/cli/src/__tests__/rowWhere.test.ts:311 — SHA256 test fixtures are copied verbatim between the app and CLI tests, and a field they declare is never read → Sha256WhereKind, Sha256TestCase, most of SHA256_TEST_CASES and sha256RowWhere also appear in packages/app/src/hooks/__tests__/useRowWhere.test.tsx. A new or corrected hash has to be updated in both files. The clickHouse field is never read, and sha256RowWhere hardcodes 1000 instead of LEFT_UTF8_HASH_CODE_POINTS. Fix: move the table into one shared fixture, for example next to leftUtf8Prefix in common-utils, and import it in both tests. Use the constant in the expected WHERE string.

Severity is the reviewer's own estimate and is used for ordering, not filtering. No finding blocks merge automatically; a maintainer decides.

@greptile-apps

greptile-apps Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issues remain.

Summary

Replaces MD5 with SHA256 for JSON and long-string row lookups on FIPS-enabled ClickHouse.

  • Row details use SHA256 for JSON and long values.

Reviews (19) · Last reviewed commit: "test: drive row WHERE SHA256 cases from ..." · Reviewed by Greptile

Comment thread packages/app/src/hooks/useRowWhere.tsx
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Deep Review

✅ No critical issues found. This is a well-contained MD5→SHA256 swap for FIPS-mode ClickHouse compatibility. The hash truncation is correctly reworked to match ClickHouse leftUTF8 code-point semantics (fixing a latent surrogate-pair bug in the old substring(0, 1000) approach), the required changeset is present, and both prior review threads are resolved in the current diff. No P0/P1/P2 issues surfaced.

Key correctness points verified during synthesis:

  • leftUtf8Prefix iterates by Unicode code point via a lazy for…of with an early break, so it stops after 1000 code points and does not materialize multi-MB payloads — matching ClickHouse leftUTF8(s, 1000) and bounding main-thread work as MD5 did.
  • The CLI's new top-level if (value == null) return isNull(...) guard converges with the app's pre-existing guard (useRowWhere.tsx:66-69) rather than diverging from it; null JSON/Date lookups are covered by new tests.
  • Client/server hash parity for emoji and long strings is asserted against hardcoded ClickHouse-sourced hashes (SHA256_TEST_CASES).
🔵 P3 nitpicks (2)
  • packages/app/src/hooks/__tests__/useRowWhere.test.tsx:116 — The SHA256(input).toString() === expected assertion is self-referential and the client/ClickHouse parity guarantee rests entirely on the hand-pasted expected constants; a mis-copied hash passes CI yet silently returns zero rows against real ClickHouse, and nothing re-validates the constants against a live server.
    • Fix: Document in a comment how each expected hash was generated from ClickHouse and, if feasible, add an integration check that regenerates them against a ClickHouse instance.
  • packages/cli/src/shared/useRowWhere.ts:73 — processRowToWhereClause and the SHA256_TEST_CASES/sha256RowWhere test scaffolding are duplicated near-verbatim between the app and CLI copies that must stay byte-compatible for lookups to match; hash-logic changes risk silent drift between the two.
    • Fix: Track the planned consolidation into common-utils (per the file's @source note) so the hashing path and its fixtures have a single source of truth.

Reviewers (2 returned before synthesis): performance, previous-comments. Six further reviewers (correctness, adversarial, testing, maintainability, kieran-typescript, project-standards) were dispatched but had not returned at synthesis time; the findings above are drawn from direct diff analysis and the completed reviewers.

Testing gaps:

  • No test asserts the early-break laziness of leftUtf8Prefix on a very large (multi-MB) input, so a future refactor that accidentally materializes the whole string would not be caught.
  • The JSON branch hashes the full stringified value with no leftUTF8 truncation (useRowWhere.tsx:91-94 and the CLI equivalent) — pre-existing behavior identical to MD5, not a regression introduced here.

No finding blocks merge automatically. A maintainer will expect P0/P1 findings in code this PR changes to be fixed; P2/P3 are your call -- fix or reply. Never fix findings about surrounding code here; reply instead. Do not widen the PR. How to respond

Comment thread packages/cli/src/shared/useRowWhere.ts Outdated
Comment thread .changeset/fips-row-lookup-sha256.md
Comment thread packages/app/src/hooks/__tests__/useRowWhere.test.tsx Outdated
Comment thread packages/cli/src/shared/useRowWhere.ts
@hercynium

Copy link
Copy Markdown
Author

TBH... I've kind of vibe-coded this and I don't like what I see since the initial commit. I'm going to go back and re-work some things manually. Difficulty is that I don't know TypeScript, but it doesn't seem all that different from other langs I do know.

@hercynium
hercynium force-pushed the bugfix/replace-md5-with-sha256-1 branch from 201f1e3 to 0eeb80a Compare October 19, 2026 20:53
Comment thread packages/cli/src/shared/useRowWhere.ts Outdated
Comment thread packages/cli/src/shared/useRowWhere.ts
Comment thread packages/cli/src/shared/useRowWhere.ts Outdated
Comment thread packages/cli/src/shared/useRowWhere.ts
Comment thread packages/cli/src/shared/useRowWhere.ts
Comment thread packages/cli/src/shared/useRowWhere.ts
Comment thread packages/app/src/hooks/__tests__/useRowWhere.test.tsx Outdated
…tUTF8

ClickHouse's leftUTF8(?, 1000) truncates by Unicode code points, but the
client-side SHA256 was using JS substring, which counts UTF-16 code units.
For strings containing non-BMP characters (e.g. emoji), this produced a
different hash than the database would compute, breaking row lookups.

Update both app and CLI row-where helpers to slice by code point with
Array.from(value).slice(0, 1000).join(''), and add tests for emoji strings.
Comment thread packages/app/src/hooks/useRowWhere.tsx Outdated
Comment thread packages/app/src/hooks/useRowWhere.tsx Outdated
Comment thread packages/app/src/hooks/useRowWhere.tsx Outdated
Comment thread packages/cli/src/shared/useRowWhere.ts Outdated
hercynium and others added 2 commits October 19, 2026 22:00
Hoist the ClickHouse leftUTF8-matching helper so app and CLI row lookups
stay in sync and cannot drift silently.

Co-authored-by: Cursor <cursoragent@cursor.com>
Match the app row-WHERE builder so null JSON columns are not hashed as
an empty string.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread .changeset/fips-row-lookup-sha256.md
let prefix = '';
let n = 0;
for (const ch of value) {
if (n++ === limit) break;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — New shared helper leftUtf8Prefix has no unit test of its own in common-utils

leftUtf8Prefix is exported for any caller but is only tested indirectly through the app and CLI row-WHERE hash snapshots. Add direct tests in common-utils __tests__ for: limit larger than the string, exact limit, surrogate pairs (e.g. '😀'.repeat(3) with limit 2 gives 2 emoji, not 1), and limit 0.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add these tests if deemed necessary, but I feel this PR is already touching more than initially planned, all to satisfy the bot.

hercynium and others added 3 commits October 20, 2026 07:46
Cover the uniform null guard for a non-JSON column type alongside JSON.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use SHA256_TEST_CASES with it.each in app and CLI suites so ClickHouse
golden hashes stay aligned and duplicated constants are removed.

Co-authored-by: Cursor <cursoragent@cursor.com>
whereKind: Sha256WhereKind,
hash: string,
): string {
if (whereKind === 'json') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — JSON test cases say they match ClickHouse, but they hash raw literals instead of what toString(<JSON column>) actually returns

The expected hashes come from SHA256('<literal>'), so the tests only show that crypto-js and ClickHouse hash the same bytes the same way. The real risk is a serialization mismatch. toString() on a JSON column emits compact JSON. Per the comment at packages/app/src/components/DBRowTable.tsx:1848, it also escapes / as \/. So {"key": "value"} (with a space) and the 'actual log line' (with an unescaped github.com/...) would not match a stored row. Fix: build the input the way DBRowTable does (JSON.stringify(obj).replace(/\//g, '\\/')), and take the expected hash from lower(hex(SHA256(toString(Body)))) on a row inserted into a JSON column. Otherwise, drop the 'matches ClickHouse' claim and the clickHouse field.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure the tests are now more robust than they were before. This minor finding is fixable, but seems like it should go into another PR.

@hercynium hercynium changed the title bugfix: replace MD5 with SHA256 for FIPS compatibility fix: replace MD5 with SHA256 for FIPS compatibility Oct 20, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external needs-vouch Author needs a maintainer to vouch for them

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant