Skip to content

[FIX] support OPRF replacements in request URLs too#75

Open
Scratch-net wants to merge 3 commits into
mainfrom
oprf-in-requests
Open

[FIX] support OPRF replacements in request URLs too#75
Scratch-net wants to merge 3 commits into
mainfrom
oprf-in-requests

Conversation

@Scratch-net
Copy link
Copy Markdown
Contributor

@Scratch-net Scratch-net commented Apr 16, 2026

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Improved handling of parameter updates in encrypted requests to ensure consistency across all request components.
    • Fixed URL reconciliation when using zero-knowledge protocols to properly synchronize updated parameters with request packets.
    • Enhanced packet content validation to match updated parameter values before processing secure communications.

@Scratch-net Scratch-net requested a review from adiwajshing April 16, 2026 20:01
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

Warning

Rate limit exceeded

@Scratch-net has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 50 minutes and 40 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 50 minutes and 40 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6e72ecd7-98cf-4ce9-bddc-334ec3c5106d

📥 Commits

Reviewing files that changed from the base of the PR and between 73019cf and d22ac0e.

📒 Files selected for processing (2)
  • src/server/utils/assert-valid-claim-request.ts
  • src/utils/generics.ts
📝 Walkthrough

Walkthrough

The pull request adds byte-level replacement logic for TOPRF parameters across client and server contexts. When TOPRF parameters are rewritten, the code now mutates the underlying byte sequences in client request packets to ensure consistency. This includes a new utility for in-place byte sequence replacement, client-side packet mutation, server-side TOPRF nullifier extraction, and HTTP provider URL reconciliation with TOPRF data.

Changes

Cohort / File(s) Summary
Type Extensions
src/types/general.ts, src/types/providers.ts
Added optional toprfNullifiers?: string[] field to IDecryptedTranscript and ProviderCtx types to carry TOPRF nullifier strings throughout the request/response processing pipeline.
Utility Functions
src/utils/generics.ts
Introduced replaceByteSequence utility that performs in-place byte-pattern substitution by scanning and overwriting matched regions in a Uint8Array buffer.
Client-Side TOPRF Packet Mutation
src/client/create-claim.ts
When TOPRF parameters are rewritten, mutated client sender packets by replacing corresponding byte sequences in packet messages to match updated parameter values using byte-converted original and hashed strings.
Server-Side TOPRF Processing
src/server/utils/assert-valid-claim-request.ts
Extended transcript decryption to extract TOPRF nullifiers from ZK reveals and propagate them via providerCtx; added packet-level byte replacement in assertValidProviderTranscript to align decrypted request packet content with updated TOPRF parameters.
HTTP Provider URL Reconciliation
src/providers/http/index.ts
Added reconcileToprfInUrl function that detects mismatches in URL validation, derives original substrings from nullifiers, mutates client packets via byte replacement, and re-validates URLs after mutation.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Server
    participant HTTPProvider
    
    Client->>Client: User initiates request with TOPRF parameters
    Client->>Client: OPRF parameter rewriting triggered
    Client->>Client: Replace params in request string
    Client->>Client: Mutate client packet bytes<br/>(replaceByteSequence)
    
    Client->>Server: Send encrypted request packet<br/>with mutated bytes
    
    Server->>Server: Decrypt transcript
    Server->>Server: Extract TOPRF nullifiers<br/>from ZK reveals
    Server->>Server: Store nullifiers in<br/>decrypted transcript
    
    Server->>HTTPProvider: Validate request via<br/>assertValidProviderReceipt
    
    HTTPProvider->>HTTPProvider: matchRedactedStrings fails
    HTTPProvider->>HTTPProvider: reconcileToprfInUrl:<br/>compare expectedPath<br/>vs actualUrl
    HTTPProvider->>HTTPProvider: Extract original substrings<br/>from nullifiers
    HTTPProvider->>HTTPProvider: Mutate receipt packets<br/>(replaceByteSequence)
    HTTPProvider->>HTTPProvider: Re-validate with<br/>updated packets
    HTTPProvider-->>Server: URL validation result
    
    Server-->>Client: Request accepted/rejected
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • reclaimprotocol/attestor-core#68: Shares identical byte-replacement utility additions and TOPRF/OPRF packet mutation patterns across client create-claim, server assert-valid-claim-request, and HTTP provider reconciliation.
  • reclaimprotocol/attestor-core#63: Extends OPRF/TOPRF handling with type and transcript-level changes in src/types/general.ts and related transcript processing logic.

Suggested reviewers

  • adiwajshing
  • Sajjad21990

Poem

🐰 Bytes dance and swap in packets bright,

TOPRF nullifiers align just right,

From client through server to provider's gate,

Reconciliation makes hashes mate! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '[FIX] support OPRF replacements in request URLs too' directly and clearly describes the main change: extending OPRF parameter replacements to also apply within request URLs/packets, not just parameters.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch oprf-in-requests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/server/utils/assert-valid-claim-request.ts (2)

26-37: ⚠️ Potential issue | 🟡 Minor

Fix ESLint import sort to unblock CI.

Pipeline GitHub Actions: Test fails with simple-import-sort/imports. SIGNATURES should come before strToUint8Array under case-insensitive natural ordering.

🧹 Proposed fix
 import {
 	AttestorError,
 	binaryHashToStr,
 	canonicalStringify,
 	decryptDirect,
 	extractApplicationDataFromTranscript,
 	hashProviderParams,
 	replaceByteSequence,
-	strToUint8Array,
 	SIGNATURES,
+	strToUint8Array,
 	verifyZkPacket
 } from '#src/utils/index.ts'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/utils/assert-valid-claim-request.ts` around lines 26 - 37, Reorder
the named imports so they follow simple-import-sort's case-insensitive natural
ordering: move SIGNATURES to appear before strToUint8Array in the import list
(i.e., ensure SIGNATURES comes earlier than strToUint8Array among the imported
symbols such as SIGNATURES, strToUint8Array, replaceByteSequence, etc. in the
import from '#src/utils/index.ts') so ESLint rule simple-import-sort/imports
passes.

144-167: ⚠️ Potential issue | 🟡 Minor

Same over-broad-replacement caveat as reconcileToprfInUrl.

replaceByteSequence(pkt.message, ogBytes, hashBytes) is applied to the entire client applData packet. If originalText also appears outside the URL (headers, body, cookies that happen to echo the value), those bytes will also be rewritten and silently alter what downstream provider.assertValidProviderReceipt sees. For OPRF-raw (server-revealed plaintext) this is often a real value visible on the wire, so collisions are plausible. Consider scoping to the request line, or requiring the substring to be unique within the client packet before replacing. See the related comment on src/providers/http/index.ts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/utils/assert-valid-claim-request.ts` around lines 144 - 167, The
current loop uses replaceByteSequence(pkt.message, ogBytes, hashBytes) across
all client applData packets which may accidentally rewrite occurrences of
originalText outside the request URL (headers/body/cookies); modify the logic in
this block to restrict replacements to the HTTP request-line or only when
originalText is uniquely found in pkt.message (e.g., verify the substring
appears exactly once and within the request-line region) before calling
replaceByteSequence, and ensure info.parameters and params are still updated as
done now; reference the variables/functions oprfRawReplacements,
originalText/nullifierText, applData, pkt.message, replaceByteSequence and
reconcileToprfInUrl when implementing the change.
src/client/create-claim.ts (1)

533-550: ⚠️ Potential issue | 🟡 Minor

Verify byte-length invariant before calling replaceByteSequence.

ogBytes is produced by round-tripping toprf.plaintext through uint8ArrayToStrstrToUint8Array. Since toprf.plaintext is raw TLS message data (binary), it may contain invalid UTF-8 sequences. When TextDecoder encounters these, it replaces them with U+FFFD (encoded as 3 bytes in UTF-8), so ogBytes.length can differ from both toprf.plaintext.length and hashBytes.length (which equals toprf.dataLocation.length). Because replaceByteSequence does not enforce equal lengths, a mismatch silently corrupts pkt.message.

Safer approaches: slice the plaintext directly using toprf.dataLocation bounds to avoid string encoding loss, or explicitly assert that ogBytes.length === hashBytes.length before calling replaceByteSequence.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/create-claim.ts` around lines 533 - 550, The code converts binary
TLS data (toprf.plaintext) through uint8ArrayToStr and strToUint8Array which can
change byte lengths due to UTF-8 decoding, then calls replaceByteSequence with
ogBytes and hashBytes; instead, avoid the string round-trip and operate on the
original bytes or validate lengths before replacement: use
toprf.plaintext.slice(toprf.dataLocation.start, toprf.dataLocation.end) (or
equivalent) to produce ogBytes that match toprf.dataLocation.length, compute
hashBytes from binaryHashToStr/profile length, then for each pkt in
revealedPackets where pkt.sender === 'client' call replaceByteSequence with the
validated same-length byte arrays (or assert ogBytes.length === hashBytes.length
and handle mismatch explicitly) to prevent silent corruption of pkt.message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/providers/http/index.ts`:
- Around line 285-293: reconcileToprfInUrl mutates receipt even when it
ultimately fails, so capture the original bytes before calling it and restore or
log them on failure; specifically, before calling
reconcileToprfInUrl(expectedPath, req.url, ctx, receipt) clone the current
receipt (e.g., Buffer.from(receipt) or appropriate copy) and if
reconcileToprfInUrl returns false, either restore receipt from the clone so
downstream code sees the original bytes or include the cloned original in the
error/log (e.g., when logging params.url or throwing the Error) so the
transcript reflects what was actually received; update the code around
matchRedactedStrings, reconcileToprfInUrl, and the error path that now throws to
ensure assertValidProviderReceipt and logTranscript consumers are not given the
mutated receipt.
- Around line 462-504: In reconcileToprfInUrl: the code calls
replaceByteSequence on every client pkt.message using original derived with JS
substring (UTF-16) and strToUint8Array (UTF-8), which can corrupt non-ASCII URLs
and unintentionally replace occurrences outside the request-line; fix by (1)
deriving the original as a byte-slice (use the transcript/packet bytes or a
byte-aware helper instead of actualUrl.substring) so ogBytes length matches
hashBytes, (2) assert ogBytes.length === hashBytes.length (or restrict to ASCII)
before calling replaceByteSequence, and (3) restrict replacements to the
request-line only (e.g., find the first client packet and limit replacement to
bytes up to the first "\r\n") or only apply if the original is unique within
that packet; use identifiers reconcileToprfInUrl, replaceByteSequence,
strToUint8Array, getHttpRequestDataFromTranscript, matchRedactedStrings, and
pkt.message to locate and implement these checks.

In `@src/utils/generics.ts`:
- Around line 82-101: The replaceByteSequence function must enforce the
documented invariant that search and replace are the same length; add a
fail-fast guard at the start of replaceByteSequence that checks if search.length
!== replace.length and throws a descriptive Error (including the mismatched
lengths and the names search/replace) before running the loop so callers cannot
silently corrupt buf when lengths differ.

---

Outside diff comments:
In `@src/client/create-claim.ts`:
- Around line 533-550: The code converts binary TLS data (toprf.plaintext)
through uint8ArrayToStr and strToUint8Array which can change byte lengths due to
UTF-8 decoding, then calls replaceByteSequence with ogBytes and hashBytes;
instead, avoid the string round-trip and operate on the original bytes or
validate lengths before replacement: use
toprf.plaintext.slice(toprf.dataLocation.start, toprf.dataLocation.end) (or
equivalent) to produce ogBytes that match toprf.dataLocation.length, compute
hashBytes from binaryHashToStr/profile length, then for each pkt in
revealedPackets where pkt.sender === 'client' call replaceByteSequence with the
validated same-length byte arrays (or assert ogBytes.length === hashBytes.length
and handle mismatch explicitly) to prevent silent corruption of pkt.message.

In `@src/server/utils/assert-valid-claim-request.ts`:
- Around line 26-37: Reorder the named imports so they follow
simple-import-sort's case-insensitive natural ordering: move SIGNATURES to
appear before strToUint8Array in the import list (i.e., ensure SIGNATURES comes
earlier than strToUint8Array among the imported symbols such as SIGNATURES,
strToUint8Array, replaceByteSequence, etc. in the import from
'#src/utils/index.ts') so ESLint rule simple-import-sort/imports passes.
- Around line 144-167: The current loop uses replaceByteSequence(pkt.message,
ogBytes, hashBytes) across all client applData packets which may accidentally
rewrite occurrences of originalText outside the request URL
(headers/body/cookies); modify the logic in this block to restrict replacements
to the HTTP request-line or only when originalText is uniquely found in
pkt.message (e.g., verify the substring appears exactly once and within the
request-line region) before calling replaceByteSequence, and ensure
info.parameters and params are still updated as done now; reference the
variables/functions oprfRawReplacements, originalText/nullifierText, applData,
pkt.message, replaceByteSequence and reconcileToprfInUrl when implementing the
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ec9454d5-4003-4d4b-9b2d-8c85cee4a58b

📥 Commits

Reviewing files that changed from the base of the PR and between 23a0579 and 73019cf.

📒 Files selected for processing (6)
  • src/client/create-claim.ts
  • src/providers/http/index.ts
  • src/server/utils/assert-valid-claim-request.ts
  • src/types/general.ts
  • src/types/providers.ts
  • src/utils/generics.ts

Comment thread src/providers/http/index.ts
Comment thread src/providers/http/index.ts
Comment thread src/utils/generics.ts
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