Skip to content

chore: update browser tests to use staging conductor URL#43

Open
wryonik wants to merge 71 commits into
mainfrom
chore/remove-dev-environment
Open

chore: update browser tests to use staging conductor URL#43
wryonik wants to merge 71 commits into
mainfrom
chore/remove-dev-environment

Conversation

@wryonik
Copy link
Copy Markdown
Contributor

@wryonik wryonik commented Apr 13, 2026

Summary

  • Updates prover-server.ts, circomProver.ts, and noirProver.ts to use staging-conductor.zk.email instead of dev-conductor.zk.email
  • Dev environment is being decommissioned

Summary by CodeRabbit

Release Notes

  • New Features

    • Added browser-based testing application with dual proving implementations
    • Introduced support for Noir circuit inputs alongside existing Circom support
  • Improvements

    • Enhanced error handling and validation throughout the codebase
    • Refactored circuit input generation into modular architecture
    • Improved regex extraction utilities with better configuration options
  • Dependencies

    • Updated sp1-verifier to version 5.0.0
    • Added thiserror, fancy-regex, and num-traits dependencies

rutefig and others added 28 commits October 31, 2025 16:22
…dregex-in-sdk-and-relayerutils

Rutefig/reg 473 fix testdecomposedregex in sdk and relayerutils
…ation-for-sp1

chore: update sp1-verifier version to 5.0.0
…neration

chore: updated zk-regex version
[REG-590][REG-588] fix: generate partial body hash when the body verification is enabled
Support both legacy and new circuit input formats by:
- Adding optional fields (max_match_length, max_haystack_length, regex_graph_json) with aliases for old field names
- Implementing legacy path that generates {name}RegexIdx format for old circuits
- Using NFAGraph-based generation only when regex_graph_json is present
- Adding helper methods to detect circuit version and calculate defaults
- Fixing typo: removeSoftLinesBreaks -> removeSoftLineBreaks in tests

This ensures circuits compiled before the new compiler integration continue to work while supporting the new format.
Add rsa_key_bits parameter to allow specifying RSA key size (1024 or 2048).
When not specified, key size is auto-detected from the public key length.
Change RSA key size auto-detection to explicitly match supported sizes
(1024-bit and 2048-bit) and return an error for unsupported sizes.

Previously, any key larger than 128 bytes would silently default to
2048-bit parameters, which would cause incorrect behavior for 4096-bit
keys. Now unsupported key sizes return a clear error message.
…024-for-noir-in-our-registry

feat: add support for 1024-bit RSA keys in Noir circuits
Switch from dev-conductor.zk.email to staging-conductor.zk.email
as dev environment is decommissioned
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

📝 Walkthrough

Walkthrough

This PR refactors the circuit input generation system into modular components for Circom and Noir, introduces a new regex module replacing direct zk_regex_apis usage, adds a browser-based ZK Email prover application, updates dependencies, and improves error handling throughout the codebase.

Changes

Cohort / File(s) Summary
Configuration and Version Updates
.gitignore, Cargo.toml, package.json, browser_test/.gitignore
Updated version from 0.4.65 to 0.4.66-18, bumped sp1-verifier from 4.0.0 to 5.0.0, added dependencies (thiserror, fancy-regex, num-traits), pinned zk-regex-compiler Git revision, and added gitignore files.
Circuit Architecture Refactoring
src/circuit.rs (deleted), src/circuit/mod.rs, src/circuit/circom.rs, src/circuit/noir/mod.rs, src/circuit/noir/structs.rs, src/circuit/noir/utils.rs, src/circuit/utils.rs
Removed monolithic circuit.rs and introduced modular structure with separate circom and noir submodules. Added circuit parameter types, decomposed regex handling, email/claim input generation, and Noir-specific big-integer/header parsing utilities.
Regex Module Extraction
src/regex/mod.rs, src/regex/extract.rs, src/regex/padding.rs, src/regex/patterns.rs, src/regex/types.rs, src/regex/wasm.rs
Created new regex module to encapsulate regex extraction logic, replacing direct zk_regex_apis imports. Includes substring extraction with NFA graph support, pre-defined patterns for email fields, padding utilities, and WASM bindings.
Error Handling Improvements
src/converters.rs, src/cryptos.rs, src/parse_email.rs, src/lib.rs
Converted functions from panicking to returning Result types. Updated bytes_to_fields, hex_to_field, PaddedEmailAddr::from_email_addr, calculate_account_salt, and generate_partial_sha to propagate errors. Introduced generate_partial_sha_old for backward compatibility.
WASM Updates
src/wasm.rs
Added async generateNoirCircuitInputsWithRegexesAndExternalInputs, updated calls to use local regex::wasm module, improved error handling for PaddedEmailAddr validation and field conversion with panic catch-unwind safety net.
Browser Test Application
browser_test/package.json, browser_test/tsconfig.json, browser_test/vite.config.js, browser_test/index.html, browser_test/src/main.ts, browser_test/src/style.css
Added Vite-based browser test project with TypeScript configuration, development/build scripts, HTML entry point, and styling. Includes Node polyfill configuration for browser compatibility.
Browser Prover Implementations
browser_test/src/noirProver.ts, browser_test/src/circomProver.ts, browser_test/src/localProverWorker.js, browser_test/src/prover-server.ts
Implemented browser-based proving flows for both Noir and Circom circuits using ZK Email SDK, plus a web worker for local proof generation and a Node.js prover server script for testing.
Test Data and Fixtures
browser_test/public/amazon.eml, browser_test/public/github.eml, browser_test/public/residency.eml, browser_test/public/x.eml
Added sample email files in RFC 822 format for testing email validation and parsing workflows.
Test Updates
ts_tests/circuit_input.test.ts
Fixed test configuration option naming: removeSoftLinesBreaksremoveSoftLineBreaks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Possibly related PRs

Suggested reviewers

  • BornPsych

Poem

🐰 A bunny hops through circuits grand,
Refactoring with regex in hand!
Noir and Circom, now apart,
Browser provers steal the heart,
Errors caught, no panics loud,
This ZK code makes us proud!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is incomplete. It lacks required template sections (Type of change, How Has This Been Tested, Checklist) and only provides a brief summary without following the repository's description template structure. Complete the PR description by filling out all required template sections including Type of change, testing details, and the checklist items.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: updating browser tests to use a staging conductor URL instead of a dev one.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/remove-dev-environment
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch chore/remove-dev-environment

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: 19

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (7)
browser_test/src/main.ts-9-15 (1)

9-15: ⚠️ Potential issue | 🟡 Minor

Use class instead of className in HTML template strings.

In raw HTML assigned to innerHTML on line 5, browsers expect the standard class attribute. className is a React/JSX convention and will not be recognized as a valid HTML attribute, preventing CSS classes from being applied to those elements.

🔧 Proposed fix
-      <div className="flex mt-5">
+      <div class="flex mt-5">
         <button class="prove">Prove with Noir</button>
       </div>
@@
-      <div className="flex mt-5">
+      <div class="flex mt-5">
         <button class="prove">Prove with Circom</button>
       </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_test/src/main.ts` around lines 9 - 15, The HTML template uses
React/JSX attribute "className" which isn't recognized when setting innerHTML;
update the template for the elements under the "noir-prover" and "circom-prover"
blocks to use the standard HTML attribute "class" (e.g., change className="flex
mt-5" and className="prove" to class="flex mt-5" and class="prove") so the CSS
classes on the <div> and <button> elements (including the "prove" button) are
applied correctly.
browser_test/src/noirProver.ts-254-257 (1)

254-257: ⚠️ Potential issue | 🟡 Minor

Type mismatch in length comparison.

partStr.length is the string length (number of characters), while parseInt(publicOutputs[publicOutputIterator], 16) parses a hex string to get byte count. For ASCII content these match, but for multi-byte UTF-8 characters, partStr.length (code units) may differ from byte length.

Consider whether this comparison is intentional or if you need to compare byte lengths instead:

// If comparing byte lengths:
const partByteLength = new TextEncoder().encode(partStr).length;
if (partByteLength !== partLength) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_test/src/noirProver.ts` around lines 254 - 257, The comparison
currently uses partStr.length (code units) vs partLength parsed from
publicOutputs[publicOutputIterator]; replace that check to compare the UTF‑8
byte length instead by computing the number of bytes of partStr (e.g. using a
TextEncoder) and compare that byte count to partLength so multi‑byte characters
are measured correctly; update the conditional around partStr, partLength,
publicOutputs and publicOutputIterator accordingly.
browser_test/src/prover-server.ts-59-59 (1)

59-59: ⚠️ Potential issue | 🟡 Minor

Boolean default using || will ignore explicit false values.

blueprint.props.removeSoftLinebreaks || true will always be true even if removeSoftLinebreaks is explicitly set to false. Use nullish coalescing (??) instead.

🐛 Proposed fix
-      removeSoftLineBreaks: blueprint.props.removeSoftLinebreaks || true,
+      removeSoftLineBreaks: blueprint.props.removeSoftLinebreaks ?? true,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_test/src/prover-server.ts` at line 59, The assignment for
removeSoftLineBreaks is using the boolean-or pattern which forces true even when
blueprint.props.removeSoftLinebreaks is explicitly false; update the initializer
for removeSoftLineBreaks (the object key removeSoftLineBreaks) to use the
nullish coalescing operator (blueprint.props.removeSoftLinebreaks ?? true) so
that false is preserved and only undefined/null fall back to true.
browser_test/src/localProverWorker.js-112-116 (1)

112-116: ⚠️ Potential issue | 🟡 Minor

Verify that inputs is a string before parsing.

JSON.parse(inputs) will throw if inputs is already an object (e.g., if the caller posts structured data instead of a serialized string). Consider defensive handling.

🛡️ Proposed fix with type check
+    const parsedInputs = typeof inputs === 'string' ? JSON.parse(inputs) : inputs;
     const { proof, publicSignals } = await snarkjs.groth16.fullProve(
-      JSON.parse(inputs),
+      parsedInputs,
       wasmFile,
       circuitfile
     );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_test/src/localProverWorker.js` around lines 112 - 116, The call to
snarkjs.groth16.fullProve uses JSON.parse(inputs) which will throw if inputs is
already an object; update the code around the snarkjs.groth16.fullProve
invocation to first check the type of inputs (e.g., if typeof inputs ===
'string' then JSON.parse(inputs) else use inputs directly), optionally validate
the resulting object shape before passing it to fullProve, and ensure any
parsing/validation is wrapped so errors are handled/logged rather than letting
JSON.parse throw; refer to the variables inputs and the
snarkjs.groth16.fullProve call to locate the change.
src/circuit/utils.rs-72-86 (1)

72-86: ⚠️ Potential issue | 🟡 Minor

Potential panic: slicing original_body with mapped indices without bounds validation.

After obtaining original_start_index and original_end_index from the position map, the code directly slices original_body[original_start_index..original_end_index] at Line 84 without verifying these indices are within original_body bounds. If the position map contains values that exceed original_body.len(), this will panic.

🛡️ Proposed fix to add bounds validation
     let (_, original_start_index, original_end_index) =
         find_selector_in_clean_content(clean_content, selector, position_map)?;
 
+    if original_end_index > original_body.len() || original_start_index > original_end_index {
+        return Err(anyhow!("Mapped indices out of bounds for original body"));
+    }
+
     // Retrieve the substring from the original body that corresponds to the found selector
     let adjusted_selector = &original_body[original_start_index..original_end_index];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/circuit/utils.rs` around lines 72 - 86, The code slices original_body
using original_start_index and original_end_index returned by
find_selector_in_clean_content without validating bounds; update the logic in
the selector-mapping block to verify that original_start_index and
original_end_index are within 0..=original_body.len() and that
original_start_index <= original_end_index before slicing, and if not return an
Err (propagate a descriptive error) or clamp the indices safely; reference the
variables original_body, original_start_index, original_end_index and the helper
find_selector_in_clean_content so you add the checks immediately before creating
adjusted_selector and only perform String::from_utf8 on a safe slice.
src/converters.rs-436-451 (1)

436-451: ⚠️ Potential issue | 🟡 Minor

Add comprehensive test coverage for edge cases in uint_to_decimal_string.

The logic is sound—checked_sub bounds safety is appropriate, and trailing-zero skipping via the found_non_zero_decimal flag correctly handles cases like converting 0 and values with all-zero decimal places (e.g., 0.1 represented as 100 with decimal=3). However, this critical function has no test coverage. Add tests for:

  • Converting 0
  • Values with all trailing zeros in decimal portion
  • Edge cases where decimal places exceed the value magnitude
  • Maximum u128 values with various decimal offsets
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/converters.rs` around lines 436 - 451, Add comprehensive unit tests for
the uint_to_decimal_string function to cover edge cases: include tests for
converting 0, values where the decimal portion is all trailing zeros (e.g.,
input representing 0.1 with decimal=3), cases where decimal places exceed the
integer magnitude, and maximum u128 values with several decimal offsets; ensure
tests assert exact string outputs and exercise paths that use checked_sub and
the found_non_zero_decimal behavior to prevent regressions.
src/circuit/circom.rs-290-306 (1)

290-306: ⚠️ Potential issue | 🟡 Minor

Fallback to index 0 may mask errors.

When get_invitation_code_idxes, get_timestamp_idxes, or get_command_idxes fail, the code silently defaults to index 0. Since 0 could be a valid index position, this makes it impossible to distinguish between "field found at position 0" and "field not found."

Consider using Option<usize> for these fields, or using a sentinel value that cannot be a valid index (e.g., usize::MAX).

💡 Alternative approach
-    let mut code_idx = match parsed_email.get_invitation_code_idxes(...) {
-        Ok(indexes) => indexes.0,
-        Err(_) => 0,
-    };
+    let mut code_idx: Option<usize> = parsed_email.get_invitation_code_idxes(...).ok().map(|i| i.0);

This would require changing EmailCircuitInput.code_idx to Option<usize> and updating downstream consumers.

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

In `@src/circuit/circom.rs` around lines 290 - 306, The current code masks errors
by defaulting to index 0 when parsed_email.get_invitation_code_idxes,
parsed_email.get_timestamp_idxes, or parsed_email.get_command_idxes return Err,
which conflates "found at 0" with "not found"; change the code to return
Option<usize> (e.g., let code_idx: Option<usize> = match ... { Ok((i,_)) =>
Some(i), Err(_) => None }) or use a sentinel like usize::MAX, and update
EmailCircuitInput.code_idx (and similar timestamp_idx/command_idx fields) and
all downstream consumers to handle Option<usize> (or the sentinel) instead of
assuming 0 is valid so failures are distinguishable.
🧹 Nitpick comments (13)
src/regex/padding.rs (1)

42-58: Add regression coverage for truncation semantics.

The docs make silent truncation part of the contract for both helpers, but the tests only cover shorter inputs. A couple of truncation cases here would lock down the behavior callers depend on.

♻️ Suggested test additions
 #[cfg(test)]
 mod tests {
     use super::*;
@@
     fn test_pad_bytes() {
         let input = vec![1, 2, 3];
         let padded = pad_bytes(&input, 6);
         assert_eq!(padded.len(), 6);
         assert_eq!(&padded[0..3], &[1, 2, 3]);
         assert_eq!(&padded[3..6], &[0, 0, 0]);
     }
+
+    #[test]
+    fn test_pad_string_truncates() {
+        let padded = pad_string("hello world", 5);
+        assert_eq!(padded, b"hello");
+    }
+
+    #[test]
+    fn test_pad_bytes_truncates() {
+        let padded = pad_bytes(&[1, 2, 3, 4], 2);
+        assert_eq!(padded, vec![1, 2]);
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/regex/padding.rs` around lines 42 - 58, Add tests covering truncation
semantics for pad_string and pad_bytes: when the input length exceeds the target
size, assert the returned buffer has length equal to the target and contains
only the first target bytes of the input (i.e., verify pad_string("hello world",
5) yields "hello" and pad_bytes(vec![...longer...], N) yields the first N
bytes). Add both a string truncation case for pad_string and a byte-vector
truncation case for pad_bytes to lock down the silent-truncation behavior.
browser_test/src/noirProver.ts (2)

42-42: Remove unused parsedEmail variable.

parsedEmail is assigned but never used.

♻️ Remove unused variable
-        const parsedEmail = await parseEmail(eml!);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_test/src/noirProver.ts` at line 42, The local variable parsedEmail is
assigned from await parseEmail(eml!) but never used; remove the unused variable
by either eliminating the assignment entirely (call await parseEmail(eml!)
without binding) or delete the line, ensuring you keep the call only if
side-effects are needed; target the statement that assigns parsedEmail (the
parseEmail/eml usage) and update it accordingly.

24-26: Remove or use the created prover instance.

The prover is created but never used. The code manually generates inputs and proofs instead of using the prover abstraction.

♻️ Remove unused prover
-        const prover = blueprint.createProver({ isLocal: true });
-        console.log("prover");
-        console.log("typeof prover", typeof prover);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_test/src/noirProver.ts` around lines 24 - 26, The code creates a
prover via blueprint.createProver({ isLocal: true }) but never uses it; either
remove the unused creation and its console.logs, or switch the manual
input/proof generation to the prover abstraction by calling the prover's methods
(e.g., prover.generateInputs(...) to produce circuit inputs and
prover.createProof(...) or prover.prove(...) to build the proof) in place of the
current manual steps so the created prover instance is actually used.
browser_test/src/prover-server.ts (1)

17-17: Remove unused import.

pako is imported but never used in this file.

♻️ Remove unused import
-import pako from "pako";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_test/src/prover-server.ts` at line 17, Remove the unused import
"pako" from the top of prover-server.ts: locate the import statement `import
pako from "pako";` and delete it (or consolidate imports if later added) so
there are no unused dependencies in the file; ensure no other code in
prover-server.ts references `pako` after removal.
browser_test/src/localProverWorker.js (2)

18-27: Consider adding backoff delay between retry attempts.

The downloadWithRetries function retries immediately without any delay. If the server is temporarily overloaded or rate-limiting, immediate retries may exacerbate the issue.

♻️ Proposed fix with exponential backoff
 async function downloadWithRetries(link, downloadAttempts) {
   for (let i = 1; i <= downloadAttempts; i++) {
     logger.debug(`download attempt ${i} for ${link}`);
-    const response = await fetch(link, { method: "GET" });
-    if (response.status === 200) {
-      return response;
+    try {
+      const response = await fetch(link, { method: "GET" });
+      if (response.status === 200) {
+        return response;
+      }
+    } catch (e) {
+      logger.debug(`Fetch error on attempt ${i}: ${e.message}`);
     }
+    if (i < downloadAttempts) {
+      await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i - 1)));
+    }
   }
   throw new Error(`Error downloading ${link} after ${downloadAttempts} retries`);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_test/src/localProverWorker.js` around lines 18 - 27, The
downloadWithRetries function retries immediately; add an exponential backoff
(with optional jitter) between attempts to avoid hammering a
rate-limited/overloaded server: compute a delay like baseDelayMs * 2^(attempt-1)
(add small random jitter) and await a sleep Promise before the next fetch
attempt in downloadWithRetries, but do not delay after a successful fetch or
after the final failed attempt; ensure the sleep is implemented using a Promise
around setTimeout so the function remains async.

118-118: localforage.clear() removes all stored data, not just circuit files.

If other parts of the application use localforage for persistence, this will wipe their data. Consider removing only the specific keys used by this worker.

♻️ Proposed fix to clear only circuit-related keys
-    await localforage.clear();
+    // Clear only the circuit files we stored
+    await localforage.removeItem(`${circuitName}.zkey`);
+    await localforage.removeItem(`${circuitName}.wasm`);
+    for (const { suffix } of chunkedZkeyUrls) {
+      await localforage.removeItem(`${circuitName}.zkey${suffix}`);
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_test/src/localProverWorker.js` at line 118, The code calls await
localforage.clear() which wipes all app storage; instead change the logic in
localProverWorker.js to remove only circuit-related entries: obtain the list of
keys via await localforage.keys(), filter for the worker's circuit key
pattern/prefix (e.g. keys that startWith or include "circuit", "prover", or
whatever key names this worker uses), then call await
localforage.removeItem(key) for each matching key (or remove specific known keys
like "circuitFiles" if those are the exact names) rather than clearing the
entire store.
src/circuit/circom.rs (3)

144-163: Manual Clone implementation could use #[derive(Clone)].

All fields of DecomposedRegex implement Clone, so the manual implementation can be replaced with #[derive(Clone)]. The current implementation is equivalent to what derive would generate.

♻️ Proposed simplification
+#[derive(Clone)]
 #[derive(Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct DecomposedRegex {
     // ... fields ...
 }

-impl Clone for DecomposedRegex {
-    fn clone(&self) -> Self {
-        DecomposedRegex {
-            parts: self
-                .parts
-                .iter()
-                .map(|p| match p {
-                    RegexPart::Pattern(s) => RegexPart::Pattern(s.clone()),
-                    RegexPart::PublicPattern((s, n)) => RegexPart::PublicPattern((s.clone(), *n)),
-                })
-                .collect(),
-            name: self.name.clone(),
-            max_match_length: self.max_match_length,
-            max_haystack_length: self.max_haystack_length,
-            haystack_location: self.haystack_location.clone(),
-            regex_graph_json: self.regex_graph_json.clone(),
-            proving_framework: self.proving_framework,
-        }
-    }
-}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/circuit/circom.rs` around lines 144 - 163, The manual impl Clone for
DecomposedRegex duplicates behavior that derive would provide; remove the impl
Clone block for DecomposedRegex and add #[derive(Clone)] to the DecomposedRegex
struct definition so all fields (parts, name, max_match_length,
max_haystack_length, haystack_location, regex_graph_json, proving_framework) are
cloned automatically, ensuring you don't change any field semantics.

708-725: LGTM, but address the TODO for test coverage.

The function is correct and well-documented. Consider adding unit tests as indicated by the TODO comment on line 725.

Do you want me to generate unit tests for compute_signal_length?

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

In `@src/circuit/circom.rs` around lines 708 - 725, Add unit tests for
compute_signal_length to satisfy the TODO: create a #[cfg(test)] mod (either in
this file or in tests/) with multiple #[test] cases that call
compute_signal_length and assert expected outputs — include edge cases like
max_length = 0, exact multiples (31, 62), values with remainder (1, 30, 32), and
a large value to ensure no overflow; use assert_eq!(compute_signal_length(x),
expected) for each and remove or update the TODO comment once tests are added.

683-689: Inefficient padding creates an intermediate vector.

The code creates vec![0; ...] then maps each element to a string. This can be simplified using std::iter::repeat.

♻️ More efficient padding
         // Pad the value to the signal length
         if value.len() < signal_length {
-            value.extend(
-                vec![0; signal_length - value.len()]
-                    .into_iter()
-                    .map(|num| num.to_string()),
-            );
+            value.extend(
+                std::iter::repeat("0".to_string())
+                    .take(signal_length - value.len()),
+            );
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/circuit/circom.rs` around lines 683 - 689, The padding creates an
intermediate Vec by building vec![0; signal_length - value.len()] then mapping
to strings; replace that with an iterator using std::iter::repeat to avoid the
temporary allocation: when checking if value.len() < signal_length, call
value.extend(std::iter::repeat("0".to_string()).take(signal_length -
value.len())) (or equivalent) so you extend the existing Vec<String> directly;
update the block around value and signal_length to use repeat().take(...)
instead of vec![...].into_iter().map(...).
src/circuit/noir/mod.rs (4)

267-273: Clarify the prover_address array format.

The prover_address is wrapped in an array containing a single string element. If the Noir circuit expects a different format (e.g., a plain string, or an array of bytes), this could cause input validation issues. Consider adding a comment documenting the expected format or verifying against the circuit specification.

// prover_address is serialized as ["0x..."] - single-element array of hex string
circuit_inputs["prover_address"] =
    serde_json::Value::Array(vec![serde_json::Value::String(prover_eth_address.clone())]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/circuit/noir/mod.rs` around lines 267 - 273, The code sets
circuit_inputs["prover_address"] to a single-element JSON array of a hex string
(using params.prover_eth_address) which may not match the Noir circuit's
expected input shape; update or document this: either change the value to the
exact format the circuit expects (e.g., a plain JSON string, an array of bytes,
or a zero-padded hex string) in the block that sets
circuit_inputs["prover_address"] (where params.prover_eth_address is used), or
add a concise comment above that assignment explicitly stating the expected Noir
format ("prover_address is serialized as [\"0x...\"]") and add a small
validation/assertion against that expectation to fail fast if
params.prover_eth_address does not match the required pattern.

441-467: Test lacks explicit assertions.

The test only verifies that the functions don't panic (via unwrap()), but doesn't validate the correctness of the output. Consider adding assertions to verify expected fields are present and have valid values.

💡 Example assertions to add
// Test the struct-based approach
let noir_circuit_input = generate_noir_circuit_input(email, params.clone())
    .await
    .unwrap();

// Add assertions
assert!(!noir_circuit_input.header.storage.is_empty(), "header should not be empty");
assert!(noir_circuit_input.header.len > 0, "header length should be positive");
assert!(!noir_circuit_input.pubkey.modulus.is_empty(), "pubkey modulus should not be empty");
assert!(!noir_circuit_input.signature.is_empty(), "signature should not be empty");
assert!(noir_circuit_input.from_header_sequence.is_some(), "from_header_sequence should be present when extract_from is true");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/circuit/noir/mod.rs` around lines 441 - 467, The test
test_generate_noir_circuit_input only checks for panics; add explicit assertions
to validate the returned structure from generate_noir_circuit_input and the JSON
from generate_noir_circuit_input_json: after calling
generate_noir_circuit_input(email, params.clone()).await.unwrap(), assert that
noir_circuit_input.header.storage is not empty, noir_circuit_input.header.len >
0, noir_circuit_input.pubkey.modulus is not empty, noir_circuit_input.signature
is not empty, and (since params.extract_from is Some(true))
noir_circuit_input.from_header_sequence.is_some(); similarly parse or inspect
the string returned by generate_noir_circuit_input_json(email,
params).await.unwrap() to assert presence of key fields (header, pubkey,
signature, from_header_sequence) to ensure backward-compatible JSON output.

152-186: Duplicate condition check can be consolidated.

Lines 152 and 179 both check params.ignore_body_hash_check.is_some_and(|x| !x). The second block (lines 178-186) could be merged into the first block (lines 152-176) to reduce redundancy and improve readability.

♻️ Proposed consolidation
         if params.ignore_body_hash_check.is_some_and(|x| !x) {
             if email_circuit_inputs.body_hash_idx.is_none() {
                 return Err(anyhow::anyhow!(
                     "Body hash check is enabled but body hash index is missing"
                 ));
             }

             // When sha_precompute_selector is used, body_padded is the REMAINING body after cutoff
             // and body_padded_len is the actual length of the remaining body
             let body_len = if params.sha_precompute_selector.is_some() {
                 // Use the remaining body length from email_circuit_inputs
                 email_circuit_inputs
                     .body_padded_len
                     .unwrap_or(parsed_email.canonicalized_body.len())
             } else {
                 // No selector - use the full canonicalized body length
                 parsed_email.canonicalized_body.len()
             };

             noir_circuit_input.body = Some(BoundedVec {
                 storage: body_storage_zero_padded.clone(),
                 len: body_len,
             });
             noir_circuit_input.body_hash_index = email_circuit_inputs.body_hash_idx;
-        }
 
-        // Always generate partial_body_hash when body verification is enabled
-        if params.ignore_body_hash_check.is_some_and(|x| !x) {
+            // Generate partial_body_hash when body verification is enabled
             noir_circuit_input.partial_body_real_length =
                 Some(parsed_email.canonicalized_body.len());
             let precomputed_sha = email_circuit_inputs.precomputed_sha
                 .ok_or_else(|| anyhow::anyhow!("Precomputed SHA is missing but body hash check is enabled"))?;
             let partial_hash = u8_to_u32(precomputed_sha.as_slice())?;
             noir_circuit_input.partial_body_hash = Some(partial_hash);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/circuit/noir/mod.rs` around lines 152 - 186, The two identical guards
checking params.ignore_body_hash_check.is_some_and(|x| !x) should be
consolidated into a single block: inside the existing if that sets
noir_circuit_input.body and body_hash_index, also set
noir_circuit_input.partial_body_real_length =
Some(parsed_email.canonicalized_body.len()), retrieve precomputed_sha from
email_circuit_inputs (returning the same anyhow error if missing), convert it
via u8_to_u32 and assign noir_circuit_input.partial_body_hash; then remove the
redundant second if entirely so all body-hash related assignments (body,
body_hash_index, partial_body_real_length, partial_body_hash) live together.

24-25: Remove unused DEFAULT_MODULUS_BITS constant or document its intended use.

The constant is defined but never used in the codebase. Key bit detection (lines 38-54) derives the value from the email's public key or the explicit rsa_key_bits parameter instead.

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

In `@src/circuit/noir/mod.rs` around lines 24 - 25, The constant
DEFAULT_MODULUS_BITS is declared but never used; either remove
DEFAULT_MODULUS_BITS from src/circuit/noir/mod.rs to avoid dead code, or
explicitly document its intended fallback role and wire it into the RSA
bit-detection path (the logic that currently derives bits from the email public
key or the rsa_key_bits parameter) so that when rsa_key_bits is None and no
public key bits can be determined the code uses DEFAULT_MODULUS_BITS as the
fallback; update the constant's doc comment if keeping it and add a reference to
DEFAULT_MODULUS_BITS in the key-detection code path so its purpose is clear.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@browser_test/index.html`:
- Line 11: The HTML entry script src is pointing to the compiled JS file that
doesn't exist ("/src/main.js"); update the <script type="module" src="..."> tag
to reference the actual TypeScript entry ("src/main.ts") so the browser loads
the correct module, and keep type="module" intact; verify the file name used in
the tag matches the actual file in the repo (main.ts) and commit the change.

In `@browser_test/public/amazon.eml`:
- Around line 1-1000: This fixture contains real-looking PII (Delivered-To,
From: "Amazon.com" <order-update@amazon.com>, Message-ID, order number
111-3061760-5513056 and other headers/body links); replace them with anonymized
synthetic values across the file (e.g. redact Delivered-To, replace
order-update@amazon.com and any real email addresses with test@example.com,
remove or replace Message-ID and ARC/SPF metadata, and convert the order Id and
shipmentId to a clearly fake token like ORDER-TEST-0000) so the public test
asset contains no real personal identifiers while preserving the same structure
for tests.

In `@browser_test/public/github.eml`:
- Around line 1-75: The committed EML fixture (github.eml) contains real PII and
an actual GitHub OAuth event; replace it with a redacted or synthetic fixture:
remove or replace real email addresses (From/To), strip or genericize security
URLs and message-id, and replace the body with a clearly synthetic message
(e.g., placeholder sender, recipient, and fake OAuth app name/scopes) so tests
exercise parsing without exposing real data; ensure the new fixture preserves
the same MIME structure/headers your tests expect and update any tests that
assert exact strings to match the redacted placeholders.

In `@browser_test/public/residency.eml`:
- Around line 1-127: The public .eml fixture contains real PII (e.g., "From:
Leland Lee <leland@succinct.xyz>", "Cc: Uma Roy <uma@succinct.xyz>, Eli Yang
<eli@succinct.xyz>", "Bcc: adityabisht64@gmail.com", Message-ID and the real
subject "Welcome to the Succinct ZK Residency!"); replace the file contents with
a fully sanitized, synthetic email preserving the MIME structure and boundaries
but using fake names/addresses (e.g., "From: Test Sender <sender@example.com>"),
anonymized Subject and Message-ID, non-sensitive placeholder links
(example.com), and remove any real telegram/invite or personal content so the
fixture can remain in browser_test/public without exposing real data.

In `@browser_test/src/circomProver.ts`:
- Around line 59-65: The params construction in GenerateProofInputsParams (in
circomProver.ts) incorrectly uses the || true pattern for removeSoftLineBreaks
(and the duplicate block at lines ~336-342), which overrides an explicit false;
change those assignments to only default to true when the blueprint prop is
undefined (e.g., use nullish coalescing or an explicit undefined check) so that
blueprint.props.removeSoftLinebreaks === false remains respected; update both
occurrences where removeSoftLineBreaks / removeSoftLinebreaks are set and ensure
the property name matches the expected casing used by GenerateProofInputsParams.
- Around line 96-100: The mapping in dcr.parts is incorrectly converting the
snake_case regex into a boolean (`!!p.regex_def`), so change the `regex_def`
assignment in the dcr.parts mapping to preserve the actual regex string (e.g.,
pick p.regexDef if present otherwise p.regex_def) instead of coercing to
boolean; update the same pattern at the other site around the second mapping
(the block referenced at lines ~379-383) so both `regex_def` fields use the
string value from p.regexDef or p.regex_def (use nullish/coalescing or a simple
truthy fallback) while leaving `is_public` handling as-is.
- Around line 83-89: The map branch that computes maxHaystackLength (using
dcr.location and blueprint.props.emailHeaderMaxLength/emailBodyMaxLength) can
return undefined and introduce holes into decomposedRegexesCleaned; change the
logic so it never yields undefined by either substituting the corresponding
default from params (e.g., params.emailHeaderMaxLength or
params.emailBodyMaxLength) when blueprint.props value is missing, or by throwing
a clear error before building the array, and ensure the map never returns
undefined (alternatively filter out falsy results after mapping) so
decomposedRegexesCleaned contains only valid entries for the WASM serializer.
- Around line 13-19: The current init() catch only logs and leaves
relayerUtilsInit unresolved; update the promise setup so you capture both
resolve and reject (e.g., relayerUtilsResolve and relayerUtilsReject) instead of
only relayerUtilsResolver, and in the catch handler call relayerUtilsReject(err)
(or otherwise reject relayerUtilsInit with the error) so awaiting
relayerUtilsInit fails fast on WASM init errors; locate the init() call and the
relayerUtilsResolver (promise creation for relayerUtilsInit) and add the
corresponding relayerUtilsReject usage in the catch branch.

In `@browser_test/src/noirProver.ts`:
- Around line 98-101: The call to addMaxLengthToExternalInputs([],
blueprint.props.externalInputs) will throw whenever
blueprint.props.externalInputs has definitions but you pass an empty values
array; update the call site in noirProver.ts to either (a) pass the actual
external input values array (not []), e.g. the variable that holds provided
external inputs for the run, or (b) guard the call with a check on
blueprint.props.externalInputs.length and only call addMaxLengthToExternalInputs
when there are both definitions and corresponding values, otherwise
return/handle the no-external-inputs case early; reference
addMaxLengthToExternalInputs and blueprint.props.externalInputs to locate the
change.

In `@browser_test/src/prover-server.ts`:
- Around line 94-97: In the dcr.parts mapping (the arrow mapping that builds
parts objects), fix the incorrect fallback for regex_def by replacing the bogus
fallback "!!p.is_public" with the correct "p.regex_def" so that regex_def is set
from p.regexDef || p.regex_def; update the mapping where p.regexDef and
p.isPublic are used to ensure regex_def uses p.regex_def as the fallback.

In `@src/circuit/circom.rs`:
- Around line 243-250: The call to ParsedEmail::new_from_raw_email uses
params.as_ref().and_then(|p| p.ignore_body_hash_check).unwrap_or(false) which
defaults ignore_body_hash_check to false, causing inconsistency with Noir (which
defaults to true); change the unwrap_or(false) to unwrap_or(true) so
ParsedEmail::new_from_raw_email receives the same default, and ensure any unit
tests or callers that rely on ParsedEmail::new_from_raw_email or the params
struct reflect this default behavior.
- Around line 503-509: The code currently indexes decomposed_regexes[0] which
will panic if decomposed_regexes is empty; add a guard at the start of the logic
(check decomposed_regexes.is_empty()) and return a descriptive Err instead of
indexing, then only branch to call generate_circuit_inputs(...) or
generate_circuit_inputs_old(...) after confirming the vector has at least one
element; reference the decomposed_regexes variable and the
generate_circuit_inputs / generate_circuit_inputs_old call sites so you modify
the same conditional block to return an error result (with a clear message like
"no decomposed regexes provided") when empty.

In `@src/circuit/mod.rs`:
- Around line 137-138: Before calling sha256_pad, validate that the unpadded
input will not exceed the requested max once SHA-256 padding is applied: compute
the padded length (or reuse a helper like sha256_padded_length) for
params.header and compare it to params.max_header_length, and if it would
exceed, return an Err from the builder instead of calling sha256_pad (which
currently asserts/panics). Apply the same explicit check for params.body vs
params.max_body_length (the related code around lines 195–218) so oversized
inputs are rejected via Result rather than causing a panic.

In `@src/circuit/noir/utils.rs`:
- Around line 216-234: The regex currently uses .*/.*? which stops at the first
physical newline and therefore misses RFC‑822 folded header continuations;
update the pattern building in the functions (used by get_header_sequence and
get_address_header_sequence) to allow folded lines by matching the header field
name then the rest of the header value including any subsequent lines that start
with space or tab (e.g. append a repeated group like (?:\r?\n[ \t]+[^\r\n]*)* or
enable DOTALL and explicitly consume folded continuations) before ending the
match; apply the same fix to the other affected block (around the code at
260-296) and ensure the Regex::new error handling remains intact.

In `@src/circuit/utils.rs`:
- Around line 29-31: The current bounds check and mapping for selector_end_index
are wrong: allow selector_end_index == position_map.len() (use <=) and compute
the exclusive original end correctly by mapping selector_end_index to the
original byte after the match; specifically, keep original_start_index =
position_map[selector_start_index], but set original_end_index = if
selector_end_index == position_map.len() { original_body.len() } else {
position_map[selector_end_index - 1] + 1 }; update the if-condition from
selector_end_index < position_map.len() to selector_end_index <=
position_map.len() and use these computed values for slicing to avoid usize::MAX
and off-by-one errors when matches end at the cleaned content end.

In `@src/cryptos.rs`:
- Around line 523-533: The loop that strips SHA padding in the body_str
construction (working on variable trimmed_body) can underflow when
trimmed_body.len() < 2; change the logic to loop only while trimmed_body.len()
>= 2 and the last two bytes are not b"\r\n", popping bytes as you go, and after
the loop return an Err (parse error) if trimmed_body.len() < 2 or the final two
bytes are not CRLF; apply the same fix to the analogous loop around lines
589-599. Ensure you use trimmed_body.last() and trimmed_body.get(len-2) safely
by checking len >= 2 before accessing len-2 and preserve the existing Result
error type when returning the parse failure.

In `@src/regex/patterns.rs`:
- Around line 24-38: Update FROM_ADDR_CONFIG and TO_ADDR_CONFIG so they match
both display-name-with-brackets and bare-address forms; replace the surrounding
fixed Pattern parts with versions that allow optional angle-bracketed display
names (e.g. change RegexPart::Pattern(r"(?i)from:.*<") to something like
r"(?i)from:\s*(?:.*<)?", and change the trailing RegexPart::Pattern(r">") to an
optional r"(?:>)?"), and do the analogous change for the TO config; keep the
existing RegexPart::PublicPattern (the email capture) unchanged so
DecomposedRegexConfig still extracts the same group.
- Around line 42-46: The SUBJECT_CONFIG's public pattern
(RegexPart::PublicPattern currently using r".+?(?=\r?\n\S|\r?\n\r?\n)") stops at
physical newlines and thus drops RFC5322 folded continuation lines; update that
pattern to allow CRLF followed by SP/HTAB as part of the subject (for example
replace the dot-plus with a construct that matches either non-newline chars or
CRLF followed by a space/tab, e.g. a repeated group like (?:[^\r\n]|\r?\n[ \t])+
with the same lookahead and keep the existing 512 length cap) so
extract_subject_all_idxes captures folded Subject: lines fully in SUBJECT_CONFIG
/ DecomposedRegexConfig / RegexPart::PublicPattern.

---

Minor comments:
In `@browser_test/src/localProverWorker.js`:
- Around line 112-116: The call to snarkjs.groth16.fullProve uses
JSON.parse(inputs) which will throw if inputs is already an object; update the
code around the snarkjs.groth16.fullProve invocation to first check the type of
inputs (e.g., if typeof inputs === 'string' then JSON.parse(inputs) else use
inputs directly), optionally validate the resulting object shape before passing
it to fullProve, and ensure any parsing/validation is wrapped so errors are
handled/logged rather than letting JSON.parse throw; refer to the variables
inputs and the snarkjs.groth16.fullProve call to locate the change.

In `@browser_test/src/main.ts`:
- Around line 9-15: The HTML template uses React/JSX attribute "className" which
isn't recognized when setting innerHTML; update the template for the elements
under the "noir-prover" and "circom-prover" blocks to use the standard HTML
attribute "class" (e.g., change className="flex mt-5" and className="prove" to
class="flex mt-5" and class="prove") so the CSS classes on the <div> and
<button> elements (including the "prove" button) are applied correctly.

In `@browser_test/src/noirProver.ts`:
- Around line 254-257: The comparison currently uses partStr.length (code units)
vs partLength parsed from publicOutputs[publicOutputIterator]; replace that
check to compare the UTF‑8 byte length instead by computing the number of bytes
of partStr (e.g. using a TextEncoder) and compare that byte count to partLength
so multi‑byte characters are measured correctly; update the conditional around
partStr, partLength, publicOutputs and publicOutputIterator accordingly.

In `@browser_test/src/prover-server.ts`:
- Line 59: The assignment for removeSoftLineBreaks is using the boolean-or
pattern which forces true even when blueprint.props.removeSoftLinebreaks is
explicitly false; update the initializer for removeSoftLineBreaks (the object
key removeSoftLineBreaks) to use the nullish coalescing operator
(blueprint.props.removeSoftLinebreaks ?? true) so that false is preserved and
only undefined/null fall back to true.

In `@src/circuit/circom.rs`:
- Around line 290-306: The current code masks errors by defaulting to index 0
when parsed_email.get_invitation_code_idxes, parsed_email.get_timestamp_idxes,
or parsed_email.get_command_idxes return Err, which conflates "found at 0" with
"not found"; change the code to return Option<usize> (e.g., let code_idx:
Option<usize> = match ... { Ok((i,_)) => Some(i), Err(_) => None }) or use a
sentinel like usize::MAX, and update EmailCircuitInput.code_idx (and similar
timestamp_idx/command_idx fields) and all downstream consumers to handle
Option<usize> (or the sentinel) instead of assuming 0 is valid so failures are
distinguishable.

In `@src/circuit/utils.rs`:
- Around line 72-86: The code slices original_body using original_start_index
and original_end_index returned by find_selector_in_clean_content without
validating bounds; update the logic in the selector-mapping block to verify that
original_start_index and original_end_index are within 0..=original_body.len()
and that original_start_index <= original_end_index before slicing, and if not
return an Err (propagate a descriptive error) or clamp the indices safely;
reference the variables original_body, original_start_index, original_end_index
and the helper find_selector_in_clean_content so you add the checks immediately
before creating adjusted_selector and only perform String::from_utf8 on a safe
slice.

In `@src/converters.rs`:
- Around line 436-451: Add comprehensive unit tests for the
uint_to_decimal_string function to cover edge cases: include tests for
converting 0, values where the decimal portion is all trailing zeros (e.g.,
input representing 0.1 with decimal=3), cases where decimal places exceed the
integer magnitude, and maximum u128 values with several decimal offsets; ensure
tests assert exact string outputs and exercise paths that use checked_sub and
the found_non_zero_decimal behavior to prevent regressions.

---

Nitpick comments:
In `@browser_test/src/localProverWorker.js`:
- Around line 18-27: The downloadWithRetries function retries immediately; add
an exponential backoff (with optional jitter) between attempts to avoid
hammering a rate-limited/overloaded server: compute a delay like baseDelayMs *
2^(attempt-1) (add small random jitter) and await a sleep Promise before the
next fetch attempt in downloadWithRetries, but do not delay after a successful
fetch or after the final failed attempt; ensure the sleep is implemented using a
Promise around setTimeout so the function remains async.
- Line 118: The code calls await localforage.clear() which wipes all app
storage; instead change the logic in localProverWorker.js to remove only
circuit-related entries: obtain the list of keys via await localforage.keys(),
filter for the worker's circuit key pattern/prefix (e.g. keys that startWith or
include "circuit", "prover", or whatever key names this worker uses), then call
await localforage.removeItem(key) for each matching key (or remove specific
known keys like "circuitFiles" if those are the exact names) rather than
clearing the entire store.

In `@browser_test/src/noirProver.ts`:
- Line 42: The local variable parsedEmail is assigned from await
parseEmail(eml!) but never used; remove the unused variable by either
eliminating the assignment entirely (call await parseEmail(eml!) without
binding) or delete the line, ensuring you keep the call only if side-effects are
needed; target the statement that assigns parsedEmail (the parseEmail/eml usage)
and update it accordingly.
- Around line 24-26: The code creates a prover via blueprint.createProver({
isLocal: true }) but never uses it; either remove the unused creation and its
console.logs, or switch the manual input/proof generation to the prover
abstraction by calling the prover's methods (e.g., prover.generateInputs(...) to
produce circuit inputs and prover.createProof(...) or prover.prove(...) to build
the proof) in place of the current manual steps so the created prover instance
is actually used.

In `@browser_test/src/prover-server.ts`:
- Line 17: Remove the unused import "pako" from the top of prover-server.ts:
locate the import statement `import pako from "pako";` and delete it (or
consolidate imports if later added) so there are no unused dependencies in the
file; ensure no other code in prover-server.ts references `pako` after removal.

In `@src/circuit/circom.rs`:
- Around line 144-163: The manual impl Clone for DecomposedRegex duplicates
behavior that derive would provide; remove the impl Clone block for
DecomposedRegex and add #[derive(Clone)] to the DecomposedRegex struct
definition so all fields (parts, name, max_match_length, max_haystack_length,
haystack_location, regex_graph_json, proving_framework) are cloned
automatically, ensuring you don't change any field semantics.
- Around line 708-725: Add unit tests for compute_signal_length to satisfy the
TODO: create a #[cfg(test)] mod (either in this file or in tests/) with multiple
#[test] cases that call compute_signal_length and assert expected outputs —
include edge cases like max_length = 0, exact multiples (31, 62), values with
remainder (1, 30, 32), and a large value to ensure no overflow; use
assert_eq!(compute_signal_length(x), expected) for each and remove or update the
TODO comment once tests are added.
- Around line 683-689: The padding creates an intermediate Vec by building
vec![0; signal_length - value.len()] then mapping to strings; replace that with
an iterator using std::iter::repeat to avoid the temporary allocation: when
checking if value.len() < signal_length, call
value.extend(std::iter::repeat("0".to_string()).take(signal_length -
value.len())) (or equivalent) so you extend the existing Vec<String> directly;
update the block around value and signal_length to use repeat().take(...)
instead of vec![...].into_iter().map(...).

In `@src/circuit/noir/mod.rs`:
- Around line 267-273: The code sets circuit_inputs["prover_address"] to a
single-element JSON array of a hex string (using params.prover_eth_address)
which may not match the Noir circuit's expected input shape; update or document
this: either change the value to the exact format the circuit expects (e.g., a
plain JSON string, an array of bytes, or a zero-padded hex string) in the block
that sets circuit_inputs["prover_address"] (where params.prover_eth_address is
used), or add a concise comment above that assignment explicitly stating the
expected Noir format ("prover_address is serialized as [\"0x...\"]") and add a
small validation/assertion against that expectation to fail fast if
params.prover_eth_address does not match the required pattern.
- Around line 441-467: The test test_generate_noir_circuit_input only checks for
panics; add explicit assertions to validate the returned structure from
generate_noir_circuit_input and the JSON from generate_noir_circuit_input_json:
after calling generate_noir_circuit_input(email, params.clone()).await.unwrap(),
assert that noir_circuit_input.header.storage is not empty,
noir_circuit_input.header.len > 0, noir_circuit_input.pubkey.modulus is not
empty, noir_circuit_input.signature is not empty, and (since params.extract_from
is Some(true)) noir_circuit_input.from_header_sequence.is_some(); similarly
parse or inspect the string returned by generate_noir_circuit_input_json(email,
params).await.unwrap() to assert presence of key fields (header, pubkey,
signature, from_header_sequence) to ensure backward-compatible JSON output.
- Around line 152-186: The two identical guards checking
params.ignore_body_hash_check.is_some_and(|x| !x) should be consolidated into a
single block: inside the existing if that sets noir_circuit_input.body and
body_hash_index, also set noir_circuit_input.partial_body_real_length =
Some(parsed_email.canonicalized_body.len()), retrieve precomputed_sha from
email_circuit_inputs (returning the same anyhow error if missing), convert it
via u8_to_u32 and assign noir_circuit_input.partial_body_hash; then remove the
redundant second if entirely so all body-hash related assignments (body,
body_hash_index, partial_body_real_length, partial_body_hash) live together.
- Around line 24-25: The constant DEFAULT_MODULUS_BITS is declared but never
used; either remove DEFAULT_MODULUS_BITS from src/circuit/noir/mod.rs to avoid
dead code, or explicitly document its intended fallback role and wire it into
the RSA bit-detection path (the logic that currently derives bits from the email
public key or the rsa_key_bits parameter) so that when rsa_key_bits is None and
no public key bits can be determined the code uses DEFAULT_MODULUS_BITS as the
fallback; update the constant's doc comment if keeping it and add a reference to
DEFAULT_MODULUS_BITS in the key-detection code path so its purpose is clear.

In `@src/regex/padding.rs`:
- Around line 42-58: Add tests covering truncation semantics for pad_string and
pad_bytes: when the input length exceeds the target size, assert the returned
buffer has length equal to the target and contains only the first target bytes
of the input (i.e., verify pad_string("hello world", 5) yields "hello" and
pad_bytes(vec![...longer...], N) yields the first N bytes). Add both a string
truncation case for pad_string and a byte-vector truncation case for pad_bytes
to lock down the silent-truncation behavior.
🪄 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: 08f641aa-14aa-4fd8-8d0e-6166c5d34231

📥 Commits

Reviewing files that changed from the base of the PR and between 98f323b and 15d4530.

⛔ Files ignored due to path filters (4)
  • Cargo.lock is excluded by !**/*.lock
  • browser_test/bun.lock is excluded by !**/*.lock
  • browser_test/public/vite.svg is excluded by !**/*.svg
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (38)
  • .gitignore
  • Cargo.toml
  • browser_test/.gitignore
  • browser_test/index.html
  • browser_test/package.json
  • browser_test/public/amazon.eml
  • browser_test/public/github.eml
  • browser_test/public/residency.eml
  • browser_test/public/x.eml
  • browser_test/src/circomProver.ts
  • browser_test/src/localProverWorker.js
  • browser_test/src/localProverWorkerString.ts
  • browser_test/src/main.ts
  • browser_test/src/noirProver.ts
  • browser_test/src/prover-server.ts
  • browser_test/src/style.css
  • browser_test/tsconfig.json
  • browser_test/vite.config.js
  • package.json
  • src/circuit.rs
  • src/circuit/circom.rs
  • src/circuit/mod.rs
  • src/circuit/noir/mod.rs
  • src/circuit/noir/structs.rs
  • src/circuit/noir/utils.rs
  • src/circuit/utils.rs
  • src/converters.rs
  • src/cryptos.rs
  • src/lib.rs
  • src/parse_email.rs
  • src/regex/extract.rs
  • src/regex/mod.rs
  • src/regex/padding.rs
  • src/regex/patterns.rs
  • src/regex/types.rs
  • src/regex/wasm.rs
  • src/wasm.rs
  • ts_tests/circuit_input.test.ts
💤 Files with no reviewable changes (1)
  • src/circuit.rs

Comment thread browser_test/index.html
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Entry script reference:"
rg -n '<script[^>]+src="/src/main\.(js|ts)"' browser_test/index.html

echo "Available main entry files:"
fd -a 'main\.(js|ts)$' browser_test/src

Repository: zkemail/relayer-utils

Length of output: 218


Fix entry script path to the actual TypeScript entry file.

Line 11 references /src/main.js, but only /src/main.ts exists in this PR. This causes module loading to fail and breaks app bootstrap.

🔧 Proposed fix
-    <script type="module" src="/src/main.js"></script>
+    <script type="module" src="/src/main.ts"></script>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<script type="module" src="/src/main.js"></script>
<script type="module" src="/src/main.ts"></script>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_test/index.html` at line 11, The HTML entry script src is pointing to
the compiled JS file that doesn't exist ("/src/main.js"); update the <script
type="module" src="..."> tag to reference the actual TypeScript entry
("src/main.ts") so the browser loads the correct module, and keep type="module"
intact; verify the file name used in the tag matches the actual file in the repo
(main.ts) and commit the change.

Comment on lines +1 to +1000
Delivered-To: aayushgupta05@gmail.com
Received: by 2002:a05:6520:4d11:b0:298:de86:b196 with SMTP id cy17csp1851324lkb;
Thu, 15 Aug 2024 18:01:48 -0700 (PDT)
X-Forwarded-Encrypted: i=3; AJvYcCWCx1Cb3kEOBBorrIS5l2e6GCzKJxzBxybTlA6eTJnE+qGUWcW3x0U2DbMEuuE94csCWxFvt47ftW92zpoSTnvWP+8Mc1JAPw==
X-Google-Smtp-Source: AGHT+IFy7itmDPuDlSHhbsl0mXcp4WaIqPBhoxgtMIUJ4rixUL/hmu7Y7lNb/3SE/waaKnYbynS7
X-Received: by 2002:a05:6000:a8b:b0:36b:8f94:e206 with SMTP id ffacd0b85a97d-37194315781mr855253f8f.5.1723770107871;
Thu, 15 Aug 2024 18:01:47 -0700 (PDT)
ARC-Seal: i=2; a=rsa-sha256; t=1723770107; cv=pass;
d=google.com; s=arc-20240605;
b=L4ifZud+phJJXmpjp/GJ3cSjA1D4Uhl604pR5GCQDZy4O/9FpFTTyQrlajv20a3CLA
LDohZyTwGvxXuUGOp4AnE3lb/GAeb9AQ3dfKQ5NRnBbbC5j01ow9/3hi/CQUG3K3uRxG
pCJKwe65M0PZryicz8+ZB1It0JoWHN9b9rWhTUesMV60vRLKHa76jBdrS3n5ykx6u+Hg
nDR/S+g3/qmdmopzKC4T5rKleVpLdBmBBfnpURBhyrbzR4YRAYrxHh6/Xmfxm2CXnP1G
BngzD5RfJcQJVBe2Hl7hTsPXNgDIW8Ng+G1w1sU12uUFdLUNMFL3Hmp0YXx52NGCsAxM
HjSg==
ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605;
h=feedback-id:bounces-to:mime-version:subject:message-id:to:from:date
:dkim-signature:dkim-signature;
bh=a+iNAUuMllM38rUYf1E0PCsZMSzHqdP5Rs6lXnKmcp4=;
fh=aslmXTwzSzJ1CMviWDZT1UuXOGBFyKTE9qzq5T44VcQ=;
b=Obl6fTQjJwbp1Z313OnnWM2NPRbOCUSY9p+6WdIDaWpqRQ0scS/tkVJPHyocDMPOFy
JxFpFA85L6u+XEVp249LYKd2p7z9z+uQaSW/sp0CrL5NPWk/jQcY37eLzlz9UDRVY0OR
Z8CxOzIiBJtsYGgMN594/rlliZQMEv7+EE7/bp4p0rEMweLeWmS73aT5JjmYvS6D0DrH
/shB9jMo5zS6qUNR8v8REiTJzcnN+yJ6iZ35c8Bg1rOu94hgJmnWNFLiiC47FEn5bzo9
VvuVRFPJh/F8c3pEHTWLfNfjjzzZZA4PvQJS8YJ0isrIoCzeuqJMV0dOs2U8mc+H39Y5
IGFg==;
dara=google.com
ARC-Authentication-Results: i=2; mx.google.com;
dkim=pass header.i=@amazon.com header.s=yg4mwqurec7fkhzutopddd3ytuaqrvuz header.b="sk0yWvc/";
dkim=pass header.i=@amazonses.com header.s=ug7nbtf4gccmlpwj322ax3p6ow6yfsug header.b=lUY1ph+6;
arc=pass (i=1 dkim=pass dkdomain=amazon.com dkim=pass dkdomain=amazonses.com);
spf=pass (google.com: domain of aayushg@mit.edu designates 18.9.21.22 as permitted sender) smtp.mailfrom=aayushg@mit.edu;
dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=amazon.com
Return-Path: <aayushg@mit.edu>
Received: from exchange-forwarding-2.mit.edu (exchange-forwarding-2.mit.edu. [18.9.21.22])
by mx.google.com with ESMTPS id 5b1f17b1804b1-429ded70f98si27296005e9.143.2024.08.15.18.01.47
for <aayushgupta05@gmail.com>
(version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128);
Thu, 15 Aug 2024 18:01:47 -0700 (PDT)
Received-SPF: pass (google.com: domain of aayushg@mit.edu designates 18.9.21.22 as permitted sender) client-ip=18.9.21.22;
Authentication-Results: mx.google.com;
dkim=pass header.i=@amazon.com header.s=yg4mwqurec7fkhzutopddd3ytuaqrvuz header.b="sk0yWvc/";
dkim=pass header.i=@amazonses.com header.s=ug7nbtf4gccmlpwj322ax3p6ow6yfsug header.b=lUY1ph+6;
arc=pass (i=1 dkim=pass dkdomain=amazon.com dkim=pass dkdomain=amazonses.com);
spf=pass (google.com: domain of aayushg@mit.edu designates 18.9.21.22 as permitted sender) smtp.mailfrom=aayushg@mit.edu;
dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=amazon.com
Received: from mailhub-dmz-4.mit.edu (MAILHUB-DMZ-4.MIT.EDU [18.7.62.38])
by exchange-forwarding-2.mit.edu (8.14.7/8.12.4) with ESMTP id 47G11hAQ014612
for <aayushg@EXCHANGE-FORWARDING.MIT.EDU>; Thu, 15 Aug 2024 21:01:45 -0400
Authentication-Results: mit.edu; dmarc=pass (p=quarantine dis=none) header.from=amazon.com
Authentication-Results: mit.edu; arc=none smtp.remote-ip=18.7.62.38
ARC-Seal: i=1; a=rsa-sha256; d=mit.edu; s=arc; t=1723770105; cv=none; b=I3uoniJRBjPCLIEB3xP3hVBkvc+i2uRrt/Qa98Z9Pew6S0B9YMYkhfyoEzqG9SAM9Me5KO0YAvZMbfMtIqtaDh99KBS+iv77kBhEsx5OqUpzb3Wl2zIM+VbewBxhPFUae85UUUYlc3FcomH+e3BYp3s0M+3tU+UeXxMu+BbkcdomHRwIHAPNE4122eo78KPjc0cWQSrX/fL+/v0Z8PY7f99hN5Rqmv15JNhgWDpyrQ7S8UZ2YwzEzN6T8pRH7VhxZDNnwP6JRhylWcaeVEOZt9/x8YaVXBxAxmzz/+mt0J19JRLghzrzbLeUJyiR2M2uqzhH2koHQFf4r5N4/zmXBA==
ARC-Message-Signature: i=1; a=rsa-sha256; d=mit.edu; s=arc; t=1723770105;
c=relaxed/relaxed; bh=a+iNAUuMllM38rUYf1E0PCsZMSzHqdP5Rs6lXnKmcp4=;
h=Date:From:Message-ID:Subject:MIME-Version:Content-Type; b=qB8UXISZyqJo1Cu2Oca57DPK/fwTk63YJ3gOT3iewE5H9ZuhwLDffJOtj+5oTIpeuPsDtEJPfxMP6K3466KjPIhHycLkjJqM1qKZM5XGjcXE2YaWVdeZvFLtPdXAbuxBc98x0GuqXpWkQUuXuDHsOMpGxeIoD0x395i4XQF6JOKCJhqf6airXGRVQ0TvLgOc1sKAsUCiy8LVFxTZIFc959rHxSCd6KN+xomr+QVCELUtwwujGEz/0d2Py7Sf6GmvpDi1Csg8B9VeLWX+vyJ08XheWzGKu7lLRZvJ9KXOTnGskQCwTXs5Melz/84G8ewhiOrHEz4sUW5fx1ksFKJmlw==
ARC-Authentication-Results: i=1; mit.edu; dkim=pass (1024-bit key; unprotected) header.d=amazon.com header.i=@amazon.com header.a=rsa-sha256 header.s=yg4mwqurec7fkhzutopddd3ytuaqrvuz header.b=sk0yWvc/; dkim=pass (1024-bit key; unprotected) header.d=amazonses.com header.i=@amazonses.com header.a=rsa-sha256 header.s=ug7nbtf4gccmlpwj322ax3p6ow6yfsug header.b=lUY1ph+6
Authentication-Results: mit.edu;
dkim=pass (1024-bit key; unprotected) header.d=amazon.com header.i=@amazon.com header.a=rsa-sha256 header.s=yg4mwqurec7fkhzutopddd3ytuaqrvuz header.b=sk0yWvc/;
dkim=pass (1024-bit key; unprotected) header.d=amazonses.com header.i=@amazonses.com header.a=rsa-sha256 header.s=ug7nbtf4gccmlpwj322ax3p6ow6yfsug header.b=lUY1ph+6
Received: from mailhub-dmz-4.mit.edu (mailhub-dmz-4.mit.edu [127.0.0.1])
by mailhub-dmz-4.mit.edu (8.14.7/8.9.2) with ESMTP id 47G11hGU006354
for <aayushg@exchange-forwarding.mit.edu>; Thu, 15 Aug 2024 21:01:43 -0400
Received: (from mdefang@localhost)
by mailhub-dmz-4.mit.edu (8.14.7/8.13.8/Submit) id 47G11di4006343
for aayushg@exchange-forwarding.mit.edu; Thu, 15 Aug 2024 21:01:39 -0400
Received: from BYAPR05CU005.outbound.protection.outlook.com (mail-westusazlp17010006.outbound.protection.outlook.com [40.93.1.6])
by MAILHUB-DMZ-4.MIT.EDU (envelope-sender <20240816010133af409d4398414b769006b814f360p0na-C1MH0WGB67NIL9@bounces.amazon.com>) (MIMEDefang) with ESMTP id 47G11bAg006339
for <aayushg@mit.edu>; Thu, 15 Aug 2024 21:01:39 -0400
Received: from SA9PR11CA0015.namprd11.prod.outlook.com (2603:10b6:806:6e::20)
by SA0PR01MB6188.prod.exchangelabs.com (2603:10b6:806:da::21) with Microsoft
SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id
15.20.7875.18; Fri, 16 Aug 2024 01:01:34 +0000
Received: from SA2PEPF00003F61.namprd04.prod.outlook.com
(2603:10b6:806:6e:cafe::b0) by SA9PR11CA0015.outlook.office365.com
(2603:10b6:806:6e::20) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7828.33 via Frontend
Transport; Fri, 16 Aug 2024 01:01:34 +0000
Authentication-Results: spf=pass (sender IP is 54.240.13.78)
smtp.mailfrom=bounces.amazon.com; dkim=pass (signature was verified)
header.d=amazon.com;dmarc=pass action=none
header.from=amazon.com;compauth=pass reason=100
Received-SPF: Pass (protection.outlook.com: domain of bounces.amazon.com
designates 54.240.13.78 as permitted sender) receiver=protection.outlook.com;
client-ip=54.240.13.78; helo=a13-78.smtp-out.amazonses.com; pr=C
Received: from a13-78.smtp-out.amazonses.com (54.240.13.78) by
SA2PEPF00003F61.mail.protection.outlook.com (10.167.248.36) with Microsoft
SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.7849.8
via Frontend Transport; Fri, 16 Aug 2024 01:01:34 +0000
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple;
s=yg4mwqurec7fkhzutopddd3ytuaqrvuz; d=amazon.com; t=1723770094;
h=Date:From:To:Message-ID:Subject:MIME-Version:Content-Type;
bh=fHIH8oc4fnqEfBc2yA88CwZjgGRwbXrwhe87j4ItCcQ=;
b=sk0yWvc/xR48M8krmQkjZbwkeIFlDIHr96+9q2KY0UxYchD4Usu4LurSXTq0YF/8
oXyRB0j9ocgDkpl6ASsyHrQJUn/Vy1nqvmJOAFRTV9v4NJyCquwME/hanRWmyDV8AJC
SpsrDeISMzFsQMvX3/BZ09/wAO+XPKNPx3iYrwfg=
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple;
s=ug7nbtf4gccmlpwj322ax3p6ow6yfsug; d=amazonses.com; t=1723770094;
h=Date:From:To:Message-ID:Subject:MIME-Version:Content-Type:Feedback-ID;
bh=fHIH8oc4fnqEfBc2yA88CwZjgGRwbXrwhe87j4ItCcQ=;
b=lUY1ph+6Hec4F2znvRMGzsMh87R1d8Zm/OiEqyOJdcoaC7SxQgTyiCqlG09+4OH6
fH1GNOI1pZmXZ3eBxbRLf0r9wpTn+VpX7PkwvW6qZK4bDuEXGWcD7BOCL49qiDYgQ5E
WYWaB24Gp9ZuGr+aB8mX1q7X0tkjSQPh+SPwdWGA=
Date: Fri, 16 Aug 2024 01:01:34 +0000
From: "Amazon.com" <order-update@amazon.com>
To: aayushg@mit.edu
Message-ID: <0100019158b441de-d0733a8e-e37c-4906-9fc0-59a546ba46cc-000000@email.amazonses.com>
Subject: Delivered: Your Amazon.com order #111-3061760-5513056
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_Part_533333_1464710557.1723770094034"
X-AMAZON-MAIL-RELAY-TYPE: notification
Bounces-to: 20240816010133af409d4398414b769006b814f360p0na-C1MH0WGB67NIL9@bounces.amazon.com
X-AMAZON-METADATA: CA=C1MH0WGB67NIL9-CU=A2WSYR24L0Q7DI
X-Original-MessageID: <urn.rtn.msg.20240816010133af409d4398414b769006b814f360p0na@1723770094035.>
Feedback-ID: 917195550::1.us-east-1.ZHcGJK6s+x+i9lRHKog4RW3tECwWIf1xzTYCZyUaiec=:AmazonSES
X-SES-Outgoing: 2024.08.16-54.240.13.78
X-EOPAttributedMessage: 0
X-EOPTenantAttributedMessage: 64afd9ba-0ecf-4acf-bc36-935f6235ba8b:0
X-MS-PublicTrafficType: Email
X-MS-TrafficTypeDiagnostic: SA2PEPF00003F61:EE_|SA0PR01MB6188:EE_
X-MS-Office365-Filtering-Correlation-Id: 8b39e185-a9d4-4bc2-4904-08dcbd8ef988
X-MS-Exchange-AtpMessageProperties: SA|SL
X-MIT-ForwardedCount: 1
X-Microsoft-Antispam:
BCL:0;ARA:13230040|1032899013|12012899012|3092899012|6062899009|5062899012|5082899009|3072899012|32142699015|2092899012|69100299015|4092899012|13102899012|13012899012;
X-Microsoft-Antispam-Message-Info:
=?utf-8?B?dDA1dE50U1hkbFJkQVd3dzF0dzZoSk53clFKYU1JTmFkY2pWQ3FvZ2poQWpM?=
=?utf-8?B?NHZNR2pkWFpDQTc4dFYzUWRQTWdwSlZNRFBLdmMwZ1NoYXd6K21sQ25VTjha?=
=?utf-8?B?Y3hvdStXM0V6WDFZc1JxaGxKYXEyZnRvOFlhREN2LzZuVzVHS09CNjBQUDA3?=
=?utf-8?B?eTgrekxSUllJWm9lMVBiV1FSME1HMHhZUkdKZkNIWG92ZzdUZ09pNDYzRGVi?=
=?utf-8?B?L001VmVhbkhJWWR4L3p5NjRLNnM3Qlc1bitEWkh5dkx2VlVGNG5NRnJGa3Yz?=
=?utf-8?B?YklZVnhVaVFkM1VjR3BBZnIvU3Q3dXQ0SndNZmMyMnNNSThtN2FnUzNyNFVh?=
=?utf-8?B?SS9VZFJ2RVdrS2RjV2pNeG1iTUYvaXFJaXdaUWJSanJ4dEJqMUcyNGo5c0wz?=
=?utf-8?B?YnVLUUM4NHNsOVpkekFqSTlHbWpqQjdWWVBBeDMxL0VLdGRiekNiL3UxZlds?=
=?utf-8?B?cGZnakJmUzJQTHdHaXp0QVkrVnpVNGFXN2ZTRW4wMEtiblR6ODUvcUxoWG5H?=
=?utf-8?B?WTVoei9kbS9yVTBJOWk1RnVzL2cvL3JadEVtNUNFVktORnlJL1FMM2dWRFVv?=
=?utf-8?B?WGdydlFDRjRaMHRBZHVpTnVrMjBpZnplcHE0MGpISHErczJRYVRrMS9Lb1BQ?=
=?utf-8?B?eWFZU1ZiVzZhbDhScFNpb0czOUU3dkNVWXdTOXQ1M3BFNmw2eDNLNjU1UnRW?=
=?utf-8?B?ZDZLZDY0b1FVWkxoeVlqTSswbmwrc09tMzdjSk1FSEZ4d3R6dWRtT1NNRnc0?=
=?utf-8?B?VUp2Y01KN3pEbHJKUXYxdTRDak5zaE1QMDh1akg4NGxOUmNodTVydVErL2J4?=
=?utf-8?B?RksvdTlROFgyUjlDbzlkZXBMYjFhUC8wcDJHR0FIYVFuZmhadmEvUjhJcC9I?=
=?utf-8?B?UERhR3VxTEFBVzBaV01weDhLWjlUZVZseVN0cytlYmtTcnNQMTFDdGsyd2sr?=
=?utf-8?B?Zk9GaXFiWmE3d2lWREc0Q3NqOXRNTlE3K29DdHJuK1NsdFB2NldVbTQ5M0Ri?=
=?utf-8?B?L1ZHTWtSTlNYZmhVbVc5WUtITVlITHJ2aWo4dmY4ZG5FTGZSNUh6a0dEdTR4?=
=?utf-8?B?TDVuS2RmT1p3bElKSFc1Z3JPSVlYM0J5YXVyOUJGKzNCbERLK3dydlk0TnV1?=
=?utf-8?B?dHJlTU1HQktidEF5ajkvd29yUEtSSmt2SDMvejY5dU9XYzZzTUhKczdGTU9j?=
=?utf-8?B?QnZQSlBVT3RtcDF5NkxEeThNaGFrRG1pdlFhWUVSSXZmYnRkTEpsenJuQy91?=
=?utf-8?B?ZnRBQ21abC9NV2pscUFZQ1JGTStOUFZSOWZNdEIzcWYvTStiQ01OUE1hazVh?=
=?utf-8?B?enN6b25qbDN5OTZmVS9sUDBXQTVXWDhoNjFxS1o1S0FjYmVxQUFneFh6QkJJ?=
=?utf-8?B?K1dVUW1nTW5PbGF2MlNiNi9rRjhvbUMxWDNXZnBvQXhDZ0FWNFRjSi93RG9I?=
=?utf-8?B?SVdIc01aSVhqMEhnRmxQdlA4MmVBYWtQdXFFcGxZTnZ4Tk5hZVdsTnRoNDlk?=
=?utf-8?B?MWthWVUxZTArYUtOSUN5MVpxWjdQbmRpSHJ6VFJUYUt1VEV0WmdsWFRtNWIv?=
=?utf-8?B?T3ZXaTVtWGM2bzJlMkN5Z0pmRGoxQ2I0MjBYUGt6WkUzSkhwZHplMjVYY1Fn?=
=?utf-8?B?RG04bTBEQXJ1NmlMVi9rN3R2NDdvSTZ0RGVrNE42dGtSNVdWZXpjaUNFaGgx?=
=?utf-8?B?V2Jja3h1WXZVaktNdm1sSjBxTVVqTGdoMTlMbTVSVjVlbk9IR3NtVlFPRzhP?=
=?utf-8?B?TWhZMkluZGllNzJqRFhKNG1sMnBpLy9nTnc0WVdzbE1DNHNzbVZFTTlvRTc2?=
=?utf-8?B?Vi8vcVdSbmRUaDJNclY0a2QxM2NTNmljR01JNHF0SjBQUklkeEllYU1wNERl?=
=?utf-8?B?ekdJVlNpa09iWkZlbEJqMXR6b1dWWHE1aWpZK3JpaXlROFJtSDRFZHZjRHp3?=
=?utf-8?B?R29COXZpN2I3ajljaGlRRjkyZDIyenA4R0dIb2xBZkFLY1FJMU1kR3J3SG1U?=
=?utf-8?B?QmNMQnYzRnNqVHdQYmtlbzdES0xtRkExZE84d3FxelRYV3gyUjBySmZjdUM3?=
=?utf-8?B?QzNWVzZUKzdaL3hVb0ZvLzZ2MFFmcEM0VHNjaVZWUjE0MDlZR3dIQnc1bkIx?=
=?utf-8?B?N2VOUlZoSnF2RGYyZzgyWWZEeXJHSHlDakNIUFJhZmU1d1VXMlBUYzZTVFcw?=
=?utf-8?B?Uk5aMit5Q3ZCNzg2eFEwNTJBT3VIZlFuYlVhRnozZGoyajNhc2dVemo0SXNN?=
=?utf-8?B?RVp2RHQ1SHcvOG5ZWC93QnpQbFMycWYzUzErM0VMeGxmUnIzcnB0OThVS1VZ?=
=?utf-8?B?bk9kNk1PNS9kSFpmU2ZxUXRhWVJkTzN5ZWFqOVN1c2lDZVhaN0VjZW56cGE4?=
=?utf-8?B?eUVlNUtWTXNMVWZxVHFydzVEVDNIUjRmc3ltWVMxU3BXUFBlSnZTeEZ5SHZJ?=
=?utf-8?B?UUVHTTA4RVR3NjFYOWlGbVp1eU1FN0NDL2F6RzROanEzMEMyQzhhd3I4enh0?=
=?utf-8?Q?y43yNOONjO+?=
X-Forefront-Antispam-Report:
CIP:54.240.13.78;CTRY:US;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:a13-78.smtp-out.amazonses.com;PTR:a13-78.smtp-out.amazonses.com;CAT:NONE;SFS:(13230040)(1032899013)(12012899012)(3092899012)(6062899009)(5062899012)(5082899009)(3072899012)(32142699015)(2092899012)(69100299015)(4092899012)(13102899012)(13012899012);DIR:INB;
X-MS-Exchange-AntiSpam-ExternalHop-MessageData-ChunkCount: 1
X-MS-Exchange-AntiSpam-ExternalHop-MessageData-0:
z1rzeYVa+P7w3Uut5PZY4XFxKFVPPMoRlChKSBEkqD70if+MiDWrj2kOzicowSH5/Ryv/yUxMBE5phZvD5onyE/TRH0DlXbWQHsVZq/ckPMOoPW60O70g7+Xp+ugQamDxA9jkDTgheWYPKDqTWmDgIAXrXe+gvQMpon+9pX4OvOUeIvap679lvHzOG7B3yR46+gn1J40mD9blXsZKSE+iDvWuc36A1bTZ8LPrpO4aWQP8UvqI8U+EebELungZppda6de5Si6ilLMSkckWRbqY/9LP9t+EUw6v9vh0GA2ExA/9TLvC/O9ARvbd4Yi2ZETUNj2awhkwVCrChWM05WWbbLXT1MqMzR1ErJxnDGIcs0jQ32ITjTNdC0zR4icAc72+gVhXdW8hdP5smLE1aD2h2zTGBo9nBHRterNuL1H1Cidl0rIjL3AA+5bgi01KTZNIatilr5DG9prwfrHNNkM2w/FT95g8nslWM2nEwu02YnP+XJEHHrxFXPPhJJHuGnqOI/dRGZ3YqEeO8VJMeC/dEqdsQMkVp8WR4TSd/rUgt46esSmEVXuMxpxaAMvcltPbFp9IFrfSz7JUCmxmgNAw6uGmPyiX1mFIA3PpRPODOpe2y/Bo/kJZul19eejoX2i
X-OriginatorOrg: mitprod.onmicrosoft.com
X-MS-Exchange-CrossTenant-OriginalArrivalTime: 16 Aug 2024 01:01:34.7825
(UTC)
X-MS-Exchange-CrossTenant-Network-Message-Id: 8b39e185-a9d4-4bc2-4904-08dcbd8ef988
X-MS-Exchange-CrossTenant-Id: 64afd9ba-0ecf-4acf-bc36-935f6235ba8b
X-MS-Exchange-CrossTenant-AuthSource:
SA2PEPF00003F61.namprd04.prod.outlook.com
X-MS-Exchange-CrossTenant-AuthAs: Anonymous
X-MS-Exchange-CrossTenant-FromEntityHeader: Internet
X-MS-Exchange-Transport-CrossTenantHeadersStamped: SA0PR01MB6188
X-MS-Exchange-CrossPremises-OriginalClientIPAddress: 54.240.13.78
X-MS-Exchange-CrossPremises-TransportTrafficType: Email
X-MS-Exchange-CrossPremises-AuthSource:
SA2PEPF00003F61.namprd04.prod.outlook.com
X-MS-Exchange-CrossPremises-AuthAs: Anonymous
X-MS-Exchange-CrossPremises-Antispam-ScanContext:
DIR:Incoming;SFV:NSPM;SKIP:0;
X-MS-Exchange-CrossPremises-SCL: 1
X-MS-Exchange-CrossPremises-Processed-By-Journaling: Journal Agent
X-OrganizationHeadersPreserved: SA0PR01MB6188.prod.exchangelabs.com

------=_Part_533333_1464710557.1723770094034
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable


=20


Hi Aayush,
Your package has been delivered to reception.





How was your delivery?
It was great https://www.amazon.com/progress-tracker/package?_encoding=3DUT=
F8&itemId=3D&orderId=3D111-3061760-5513056&packageIndex=3D0&shipmentId=3D30=
0612189807301&ingress=3Demail&intent=3Dfeedback_happy&ref_=3Dpdc_f_h
Not so great https://www.amazon.com/progress-tracker/package?_encoding=3DUT=
F8&itemId=3D&orderId=3D111-3061760-5513056&packageIndex=3D0&shipmentId=3D30=
0612189807301&ingress=3Demail&intent=3Dfeedback_unhappy&ref_=3Dpdc_f_s

Track package
https://www.amazon.com/gp/your-account/ship-track?ie=3DUTF8&itemId=3D&order=
Id=3D111-3061760-5513056&packageIndex=3D0&shipmentId=3DGhsHRNhnG&ref_=3Dpdc=
_pt_track

Order # 111-3061760-5513056





Return or replace items in [Your Orders](https://www.amazon.com/your-orders=
/orders?ref_=3Dpdc_yo).



=C2=A92024 Amazon.com, Inc. or its affiliates. Amazon and all related marks=
are trademarks of Amazon.com, Inc. or its affiliates, Amazon.com, Inc. 410=
Terry Avenue N., Seattle, WA 98109.

Amazon.com
------=_Part_533333_1464710557.1723770094034
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

<!doctype html><html lang=3D"en" dir=3D"auto" xmlns=3D"http://www.w3.org/19=
99/xhtml" xmlns:v=3D"urn:schemas-microsoft-com:vml" xmlns:o=3D"urn:schemas-=
microsoft-com:office:office"><head><title></title><!--[if !mso]><!--><meta =
http-equiv=3D"X-UA-Compatible" content=3D"IE=3Dedge"><!--<![endif]--><meta =
http-equiv=3D"Content-Type" content=3D"text/html; charset=3DUTF-8"><meta na=
me=3D"viewport" content=3D"width=3Ddevice-width,initial-scale=3D1"><style t=
ype=3D"text/css">#outlook a{padding:0}body{margin:0;padding:0;-webkit-text-=
size-adjust:100%;-ms-text-size-adjust:100%}table,td{border-collapse:collaps=
e;mso-table-lspace:0pt;mso-table-rspace:0pt}img{border:0;height:auto;line-h=
eight:100%;outline:0;text-decoration:none;-ms-interpolation-mode:bicubic}p{=
display:block;margin:13px 0}</style><!--[if mso]>
<noscript>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
</noscript>
<![endif]--><!--[if lte mso 11]>
<style type=3D"text/css">.mj-outlook-group-fix{width:100%!important}</s=
tyle>
<![endif]--><style type=3D"text/css">@media only screen and (min-width:=
600px){.mj-column-per-100{width:100%!important;max-width:100%}.mj-column-pe=
r-50{width:50%!important;max-width:50%}.mj-column-per-33{width:33%!importan=
t;max-width:33%}.mj-column-per-67{width:67%!important;max-width:67%}}</styl=
e><style media=3D"screen and (min-width:600px)">.moz-text-html .mj-column-p=
er-100 { width:100% !important; max-width: 100%; }
.moz-text-html .mj-column-per-50 { width:50% !important; max-width: 50%; }
.moz-text-html .mj-column-per-33 { width:33% !important; max-width: 33%; }
.moz-text-html .mj-column-per-67 { width:67% !important; max-width: 67%; }<=
/style><style type=3D"text/css">.rio-card-70 p+p{margin-top:1.2em}@media (p=
refers-color-scheme:dark){.rio-header-64,.rio-header-64 *{color:#fff!import=
ant}}@media (prefers-color-scheme:dark){.rio-header-65,.rio-header-65 *{col=
or:#fff!important}}@media (prefers-color-scheme:dark){.rio-text-121{color:#=
fff}}@media (prefers-color-scheme:dark){.rio-text-122{color:#fff}}@media (p=
refers-color-scheme:dark){.rio-text-123{color:#fff}}@media (prefers-color-s=
cheme:dark){.rio-text-124{color:#fff}}@media (prefers-color-scheme:dark){.r=
io-text-125{color:#fff}}@media (prefers-color-scheme:dark){.rio-text-126{co=
lor:#fff}}@media only screen and (min-width:600px){.rio-asin-image-1NSmq2Ns=
zkzYrLmYYAndnK .productImageTd{height:165px!important}.rio-asin-image-1NSmq=
2NszkzYrLmYYAndnK .productImage{max-height:165px!important;max-width:165px!=
important}}@media (prefers-color-scheme:dark){.rio-text-127{color:#fff}}@me=
dia (prefers-color-scheme:dark){.rio-text-128{color:#fff}}.rio-card-72 p+p{=
margin-top:1.2em}@media (prefers-color-scheme:dark){.rio-header-66,.rio-hea=
der-66 *{color:#fff!important}}@media (prefers-color-scheme:dark){.rio-head=
er-67,.rio-header-67 *{color:#fff!important}}@media (prefers-color-scheme:d=
ark){.rio-text-129{color:#fff}}@media (prefers-color-scheme:dark){.rio-card=
,.rio-card>table{background-color:#181a1a!important}}[data-ogsc] .rio-card,=
[data-ogsc] .rio-card>table{background-color:#181a1a!important}.rio-header =
strong{color:#067d62}@media (prefers-color-scheme:dark){.rio-header a{color=
:#6ed6e6!important}.rio-header strong{color:#13bd96!important}}[data-ogsc] =
.rio-header,[data-ogsc] .rio-header *{color:#fff!important}[data-ogsc] .rio=
-header a{color:#6ed6e6!important}[data-ogsc] .rio-header strong{color:#13b=
d96!important}@media only screen and (max-width:599px){table.mj-full-width-=
mobile{width:100%!important}td.mj-full-width-mobile{width:auto!important}}.=
rio-text strong{color:#067d62}.rio-text img{width:100%;height:auto}@media (=
prefers-color-scheme:dark){.rio-text *{color:#fff!important}.rio-text a,.ri=
o-text a>*{color:#6ed6e6!important}.rio-text strong{color:#13bd96!important=
}}[data-ogsc] .rio-text *{color:#fff!important}[data-ogsc] .rio-text a,[dat=
a-ogsc] .rio-text a>*{color:#6ed6e6!important}[data-ogsc] .rio-text strong{=
color:#13bd96!important}@media (prefers-color-scheme:dark){.rio-button-seco=
ndary *{color:#0f1111!important;background:#fefefe!important;background-col=
or:linear-gradient(#fefefe,#fefefe)!important}}[data-ogsc] .rio-button-seco=
ndary *{color:#0f1111!important;background:#fefefe!important;background-col=
or:linear-gradient(#fefefe,#fefefe)!important}.rio-asin-image .productImage=
{mix-blend-mode:multiply}u+.body .rio-asin-image .productImage{mix-blend-mo=
de:initial!important}@media (prefers-color-scheme:dark){.rio-asin-image .pr=
oductImageTd{background-image:linear-gradient(#fff,#fff)!important}.rio-asi=
n-image .productImage{mix-blend-mode:initial!important}}[data-ogsc] .rio-as=
in-image .productImageTd{background-image:linear-gradient(#fff,#fff)!import=
ant}[data-ogsc] .rio-asin-image .productImage{mix-blend-mode:initial!import=
ant}@media only screen and (min-width:600px){u+.body .rio-asin-image .produ=
ctImage{mix-blend-mode:multiply!important}}.footerCard li,.footerCard p{col=
or:inherit}.footerCard p+p{margin-top:1.2em}@media (prefers-color-scheme:da=
rk){.footerCard div{background-color:#303333!important}.footerLink div{colo=
r:#6ed6e6!important}.footerText div{color:#c8cccc!important}.lightFooterImg=
{display:none!important}.darkFooterImg{display:block!important}}[data-ogsb]=
.footerCard div{background-color:#303333!important}[data-ogsc] .footerLink=
div{color:#6ed6e6!important}[data-ogsc] .footerText div{color:#c8cccc!impo=
rtant}[data-ogsc] .lightFooterImg{display:none!important}[data-ogsc] .darkF=
ooterImg{display:block!important}</style><style type=3D"text/css">@font-fac=
e{font-family:Ember;font-weight:700;src:local("Ember"),url(https://m.media-=
amazon.com/images/G/01/outbound/AmazonEmber_Bd._CB1515450239_.WOFF) format(=
"woff");mso-generic-font-family:swiss;mso-font-alt:"Arial"}@font-face{font-=
family:Ember;font-weight:600;src:local("Ember"),url(https://m.media-amazon.=
com/images/G/01/outbound/AmazonEmber_Bd._CB1515450239_.WOFF) format("woff")=
;mso-generic-font-family:swiss;mso-font-alt:"Arial"}@font-face{font-family:=
Ember;font-weight:500;src:local("Ember"),url(https://m.media-amazon.com/ima=
ges/G/01/outbound/AmazonEmber_Md._CB1515450239_.WOFF) format("woff");mso-ge=
neric-font-family:swiss;mso-font-alt:"Arial"}@font-face{font-family:Ember;f=
ont-weight:400;font-style:normal;src:local("Ember"),url(https://m.media-ama=
zon.com/images/G/01/outbound/AmazonEmber_Rg._CB1515450239_.WOFF) format("wo=
ff");mso-generic-font-family:swiss;mso-font-alt:"Arial"}@font-face{font-fam=
ily:Ember;font-weight:200;src:local("Ember"),url(https://m.media-amazon.com=
/images/G/01/outbound/AmazonEmber_Lt._CB1515450239_.WOFF) format("woff");ms=
o-generic-font-family:swiss;mso-font-alt:"Arial"}@font-face{font-family:"Am=
azon Ember";font-weight:700;src:local("Amazon Ember"),url(https://m.media-a=
mazon.com/images/G/01/outbound/AmazonEmber_Bd._CB1515450239_.WOFF) format("=
woff");mso-generic-font-family:swiss;mso-font-alt:"Arial"}@font-face{font-f=
amily:"Amazon Ember";font-weight:600;src:local("Amazon Ember"),url(https://=
m.media-amazon.com/images/G/01/outbound/AmazonEmber_Bd._CB1515450239_.WOFF)=
format("woff");mso-generic-font-family:swiss;mso-font-alt:"Arial"}@font-fa=
ce{font-family:"Amazon Ember";font-weight:500;src:local("Amazon Ember"),url=
(https://m.media-amazon.com/images/G/01/outbound/AmazonEmber_Md._CB15154502=
39_.WOFF) format("woff");mso-generic-font-family:swiss;mso-font-alt:"Arial"=
}@font-face{font-family:"Amazon Ember";font-style:normal;font-weight:400;sr=
c:local("Amazon Ember"),url(https://m.media-amazon.com/images/G/01/outbound=
/AmazonEmber_Rg._CB1515450239_.WOFF) format("woff");mso-generic-font-family=
:swiss;mso-font-alt:"Arial"}@font-face{font-family:"Amazon Ember";font-weig=
ht:200;src:local("Amazon Ember"),url(https://m.media-amazon.com/images/G/01=
/outbound/AmazonEmber_Lt._CB1515450239_.WOFF) format("woff");mso-generic-fo=
nt-family:swiss;mso-font-alt:"Arial"}*{font-family:Ember,'Amazon Ember',Ari=
al,sans-serif;border-spacing:0;margin:0;padding:0}[data-ogsc] :root{--body-=
bg:#181A1A;--body-color:#ffffff}.rootContent{background:#d5d9d9!important}.=
fonts,.sans,h1,h2,h3,h4,h5,li,p,table,td,th{color:#0f1111}a{color:#007185;t=
ext-decoration:none}@media screen and (max-width:599px){.mobile-only{displa=
y:initial!important}.desktop-only{display:none!important;mso-hide:all!impor=
tant}}@media screen and (min-width:600px){.mobile-only{display:none!importa=
nt;mso-hide:all!important}}@media (prefers-color-scheme:light){:root{--body=
-bg:#ffffff;--body-color:#000000}}@media (prefers-color-scheme:dark){:root{=
--body-bg:#181A1A;--body-color:#ffffff}body{background-color:#181a1a!import=
ant}.fonts,.sans,h1,h2,h3,h4,h5,li,p,table,td,th{color:#fff}a{color:#6ed6e6=
}.rootContent,.rootContent>table{background:#181a1a!important}}[data-ogsc] =
.fonts,[data-ogsc] .sans,[data-ogsc] h1,[data-ogsc] h2,[data-ogsc] h3,[data=
-ogsc] h4,[data-ogsc] h5,[data-ogsc] li,[data-ogsc] p{color:#fff}[data-ogsc=
] a{color:#6ed6e6}[data-ogsc] .rootContent,[data-ogsc] .rootContent>table{b=
ackground:#181a1a!important}body{background-color:var(--body-bg)!important;=
color:var(--body-color)!important;margin:0!important;padding:0}body>img{pos=
ition:absolute}table{border-spacing:0}h3,h4,h5,p,table th{font-weight:400;m=
argin:0;padding:0}td{padding:0}img{border:0}a,span,td{word-break:break-word=
!important}ol,ul{margin-left:32px!important}.button{background-color:#ffd81=
4;color:#0f1111!important;border-radius:24px;padding:1px 16px;display:inlin=
e-block;box-shadow:1px 2px 4px rgba(153,153,153,.2);font-size:13px;line-hei=
ght:29px;white-space:nowrap;text-decoration:none;margin-top:4px}.box-shadow=
a{box-shadow:1px 2px 4px rgba(153,153,153,.2)}a,body,table,td{-ms-text-siz=
e-adjust:100%;-webkit-text-size-adjust:100%}.appleBody a,.appleFooter a{col=
or:#007185!important;text-decoration:none}a[x-apple-data-detectors]{color:#=
007185!important;font-family:inherit!important;font-size:inherit!important;=
font-weight:inherit!important;line-height:inherit!important}u+#body a{color=
:#007185!important;font-family:inherit!important;font-size:inherit!importan=
t;font-weight:inherit!important;line-height:inherit!important}#MessageViewB=
ody a{color:#007185!important;font-family:inherit!important;font-size:inher=
it!important;font-weight:inherit!important;line-height:inherit!important}</=
style><meta content=3D"text/html; charset=3DUTF-8" http-equiv=3D"Content-Ty=
pe"><meta content=3D"telephone=3Dno" name=3D"format-detection"><meta conten=
t=3D"width=3Ddevice-width,initial-scale=3D1;user-scalable=3Dno;" name=3D"vi=
ewport"><meta content=3D"IE=3D9; IE=3D8; IE=3D7; IE=3DEDGE" http-equiv=3D"X=
-UA-Compatible"><meta name=3D"x-apple-disable-message-reformatting"><meta c=
ontent=3D"light dark" name=3D"color-scheme"><meta content=3D"light dark" na=
me=3D"supported-color-schemes"><!--
=20
=20
=20
--><style type=3D"text/css">.productListPrice{color:#565959}.=
productDiscount{color:#cc0c39}.productPrice{color:#0f1111}@media (prefers-c=
olor-scheme:dark){.productListPrice{color:#fff!important}.productDiscount{c=
olor:#ff8c8c!important}.productPrice{color:#fff!important}}[data-ogsc] .pro=
ductListPrice{color:#fff!important}[data-ogsc] .productDiscount{color:#ff8c=
8c!important}[data-ogsc] .productPrice{color:#fff!important}</style><style =
type=3D"text/css">.dealBadge{background-color:#cc0c39;color:#fff}.dealText{=
color:#cc0c39}@media (prefers-color-scheme:dark){.dealBadge{background-colo=
r:#ff8c8c!important;color:#000!important}.dealText{color:#ff8c8c!important}=
}[data-ogsc] .dealBadge{background-color:#ff8c8c!important;color:#000!impor=
tant}[data-ogsc] .dealText{color:#ff8c8c!important}</style><style type=3D"t=
ext/css">#amazonLogo6ifDMeVChiamBC39CTSZBx.full{max-width:100%!important;pa=
dding-left:0!important;padding-right:0!important;width:100%!important}#amaz=
onLogo6ifDMeVChiamBC39CTSZBx.zeroBorder{border:0;border-collapse:collapse;b=
order-spacing:0}#amazonLogo6ifDMeVChiamBC39CTSZBx .full{max-width:100%!impo=
rtant;padding-left:0!important;padding-right:0!important;width:100%!importa=
nt}#amazonLogo6ifDMeVChiamBC39CTSZBx .zeroBorder{border:0;border-collapse:c=
ollapse;border-spacing:0}#amazonLogo6ifDMeVChiamBC39CTSZBx .light-img{backg=
round-color:#fff;background-image:linear-gradient(#fff,#fff)}@media (prefer=
s-color-scheme:light){#amazonLogo6ifDMeVChiamBC39CTSZBx .light-img{display:=
block!important}#amazonLogo6ifDMeVChiamBC39CTSZBx .dark-img{display:none!im=
portant}}@media (prefers-color-scheme:dark){#amazonLogo6ifDMeVChiamBC39CTSZ=
Bx .content{background-color:#181a1a!important}#amazonLogo6ifDMeVChiamBC39C=
TSZBx .light-img{display:none!important}#amazonLogo6ifDMeVChiamBC39CTSZBx .=
dark-img{display:block!important}}[data-ogsc] #amazonLogo6ifDMeVChiamBC39CT=
SZBx .content{background-color:#181a1a!important}[data-ogsc] #amazonLogo6if=
DMeVChiamBC39CTSZBx .light-img{display:none!important}[data-ogsc] #amazonLo=
go6ifDMeVChiamBC39CTSZBx .dark-img{display:block!important}</style><!--[if =
gte mso 9]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG />
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<style>
body, h1, h2, h3, h4, table, th, td, p, li, a, .sans, .fonts {
font-family: Helvetica, Arial, sans-serif !important;
}
[data-ogsc] .rootContent, [data-ogsc] .rootContent > table{
background: #181A1A !important;
}
</style>
<![endif]--></head><body class=3D"body" style=3D"word-spacing:normal;">=
<img width=3D"1" height=3D"1" src=3D"https://www.amazon.com/gp/r.html?C=3D1=
Z6HAGN0Q74SS&K=3D1PCIPA854472X&M=3Durn:rtn:msg:20240816010133af409d4398414b=
769006b814f360p0na&R=3DE5DJPKCG5QDU&T=3DO&U=3Dhttps%3A%2F%2Fimages-na.ssl-i=
mages-amazon.com%2Fimages%2FG%2F01%2Fnav%2Ftransp.gif&H=3DF2PRANDPDWFKMJP01=
PAQRSV6UISA&ref_=3Dpe_108877690_917195550_opens" /><div style=3D"display:no=
ne;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px=
;opacity:0;overflow:hidden;">Delivered: Your Amazon.com order #111-3061760-=
5513056&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;=
&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; =
&zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; =
&nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; =
&#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199;=
&shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&=
#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &=
zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &=
nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &=
#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; =
&shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#=
847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &z=
wnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &n=
bsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#=
8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &=
shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#8=
47; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zw=
nj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nb=
sp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8=
199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &s=
hy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#84=
7; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwn=
j; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbs=
p; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#81=
99; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &sh=
y;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847=
; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj=
; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp=
; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#819=
9; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy=
;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847;=
&zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj;=
&nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp;=
&#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199=
; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;=
&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; =
&zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; =
&nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; =
&#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199;=
&shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&=
#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &=
zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &=
nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &=
#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; =
&shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#=
847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &z=
wnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &n=
bsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#=
8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &=
shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#8=
47; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zw=
nj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nb=
sp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8=
199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &s=
hy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#84=
7; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwn=
j; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbs=
p; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#81=
99; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &sh=
y;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847=
; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj=
; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp=
; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#819=
9; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy=
;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847;=
&zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj;=
&nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp;=
&#8199; &shy;&#847; &zwnj; &nbsp; &#8199; &shy;&#847; &zwnj; &nbsp; &#8199=
; &shy;</div><div class=3D"body" lang=3D"en" dir=3D"auto"><!--[if mso | IE]=
><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" c=
lass=3D"rootContent-outlook" role=3D"presentation" style=3D"width:600px;" w=
idth=3D"600" bgcolor=3D"#D5D9D9" ><tr><td style=3D"line-height:0px;font-siz=
e:0px;mso-line-height-rule:exactly;"><![endif]--><div class=3D"rootContent"=
style=3D"background:#D5D9D9;background-color:#D5D9D9;margin:0px auto;max-w=
idth:600px;"><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspa=
cing=3D"0" role=3D"presentation" style=3D"background:#D5D9D9;background-col=
or:#D5D9D9;width:100%;"><tbody><tr><td style=3D"direction:ltr;font-size:0px=
;padding:0px 0px 4px 0px;text-align:left;"><!--[if mso | IE]><table role=3D=
"presentation" border=3D"0" cellpadding=3D"0" cellspacing=3D"0"><![endif]--=
> <!-- PRIME LOGO --><!-- ALEXA LOGO --><!-- AMAZON BUSINESS LOGO --><!-- A=
ll European Union marketplaces need to use dma compliant logo --> <tab=
le id=3D"amazonLogo6ifDMeVChiamBC39CTSZBx" class=3D"full zeroBorder" style=
=3D"width: 100%;" role=3D"presentation"><tbody><tr><td class=3D"content" st=
yle=3D"padding:0; text-align:left; background-color: #FFFFFF"><!--[if gte m=
so 9]><table width=3D"600"><tr><td><![endif]--><a href=3D"https://www.amazo=
n.com/gp/r.html?C=3D1Z6HAGN0Q74SS&K=3D1PCIPA854472X&M=3Durn:rtn:msg:2024081=
6010133af409d4398414b769006b814f360p0na&R=3D9WJYLFG12W1E&T=3DC&U=3Dhttps%3A=
%2F%2Fwww.amazon.com%3Fref_%3Dpe_108877690_917195550_LG0001IMG&H=3DJGWECUYR=
AY0HTEB3KEJCZQPE9H4A&ref_=3Dpe_108877690_917195550_LG0001IMG" target=3D"_bl=
ank"><img class=3D"light-img" height=3D"58" role=3D"presentation" src=3D"ht=
tps://m.media-amazon.com/images/G/01/outbound/OutboundTemplates/Amazon_logo=
_US._BG255,255,255_.png" style=3D" display:block;background-color: #ffffff;=
width:auto; height:58px; max-height:58px; max-width:100%; border:0; float:l=
eft;" alt=3D""> <!--[if !mso]><! --><img class=3D"dark-img" height=3D"58" r=
ole=3D"presentation" src=3D"https://m.media-amazon.com/images/G/01/outbound=
/OutboundTemplates/Amazon_logo_whiterev_US.png" style=3D"display:none; widt=
h:auto; height:58px; max-height:58px; max-width:100%; border:0; float:left;=
" alt=3D""><!--<![endif]--> </a><!--[if gte mso 9]></td></tr></table><![end=
if]--></td></tr></tbody></table><!--[if mso | IE]></table><![endif]--></td>=
</tr></tbody></table></div><!--[if mso | IE]></td></tr></table><![endif]-->=
<!--[if mso | IE]><table align=3D"center" border=3D"0" cellpadding=3D"0" ce=
llspacing=3D"0" class=3D"rootContent-outlook" role=3D"presentation" style=
=3D"width:600px;" width=3D"600" bgcolor=3D"#D5D9D9" ><tr><td style=3D"line-=
height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div cl=
ass=3D"rootContent" style=3D"background:#D5D9D9;background-color:#D5D9D9;ma=
rgin:0px auto;max-width:600px;"><table align=3D"center" border=3D"0" cellpa=
dding=3D"0" cellspacing=3D"0" role=3D"presentation" style=3D"background:#D5=
D9D9;background-color:#D5D9D9;width:100%;"><tbody><tr><td style=3D"directio=
n:ltr;font-size:0px;padding:4px 8px 4px 8px;text-align:left;"><!--[if mso |=
IE]><table role=3D"presentation" border=3D"0" cellpadding=3D"0" cellspacin=
g=3D"0"><![endif]--> <!--[if mso | IE]><tr><td align=3D"left" class=
=3D"sonar-informational-outlook sonar-informational-v1-outlook" width=3D"60=
0px" ><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D=
"0" class=3D"rio-card-outlook rio-card-70-outlook" role=3D"presentation" st=
yle=3D"width:584px;" width=3D"584" bgcolor=3D"#ffffff" ><tr><td style=3D"li=
ne-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div=
class=3D"rio-card rio-card-70" style=3D"background:#ffffff;background-colo=
r:#ffffff;margin:0px auto;border-radius:4px;max-width:584px;"><table align=
=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presen=
tation" style=3D"background:#ffffff;background-color:#ffffff;width:100%;bor=
der-radius:4px;"><tbody><tr><td style=3D"direction:ltr;font-size:0px;paddin=
g:12px 8px 12px 8px;text-align:left;"><!--[if mso | IE]><table role=3D"pres=
entation" border=3D"0" cellpadding=3D"0" cellspacing=3D"0"><tr><td align=3D=
"left" class=3D"" width=3D"584px" ><table align=3D"center" border=3D"0" cel=
lpadding=3D"0" cellspacing=3D"0" class=3D"" role=3D"presentation" style=3D"=
width:568px;" width=3D"568" ><tr><td style=3D"line-height:0px;font-size:0px=
;mso-line-height-rule:exactly;"><![endif]--><div style=3D"margin:0px auto;m=
ax-width:568px;"><table align=3D"center" border=3D"0" cellpadding=3D"0" cel=
lspacing=3D"0" role=3D"presentation" style=3D"width:100%;"><tbody><tr><td s=
tyle=3D"direction:ltr;font-size:0px;padding:0;text-align:left;"><!--[if mso=
| IE]><table role=3D"presentation" border=3D"0" cellpadding=3D"0" cellspac=
ing=3D"0"><tr><td align=3D"left" class=3D"" style=3D"vertical-align:top;wid=
th:568px;" ><![endif]--><div class=3D"mj-column-per-100 mj-outlook-group-fi=
x" style=3D"font-size:0px;text-align:left;direction:ltr;display:inline-bloc=
k;vertical-align:top;width:100%;"><table border=3D"0" cellpadding=3D"0" cel=
lspacing=3D"0" role=3D"presentation" width=3D"100%"><tbody><tr><td style=3D=
"vertical-align:top;padding:0;"><table border=3D"0" cellpadding=3D"0" cells=
pacing=3D"0" role=3D"presentation" width=3D"100%"><tbody> <tr><td align=3D"=
left" style=3D"font-size:0px;padding:0;word-break:break-word;"><div style=
=3D"font-family:Ember,'Amazon Ember',Arial,sans-serif;font-size:15px;line-h=
eight:1;text-align:left;color:#000000;"><h2 style=3D"margin:0;padding:0;fon=
t-family:Ember,'Amazon Ember',Arial,sans-serif;font-weight:700;font-size:22=
px;line-height:28px;color:#0F1111;" class=3D"rio-header rio-header-64">Hi A=
ayush,</h2></div></td></tr></tbody></table></td></tr></tbody></table></div>=
<!--[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody></table><=
/div><!--[if mso | IE]></td></tr></table></td></tr><tr><td align=3D"left" c=
lass=3D"" width=3D"584px" ><![endif]--> <!--[if mso | IE]><table align=
=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" class=3D"" rol=
e=3D"presentation" style=3D"width:568px;" width=3D"568" ><tr><td style=3D"l=
ine-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><di=
v style=3D"margin:0px auto;max-width:568px;"><table align=3D"center" border=
=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentation" style=3D"w=
idth:100%;"><tbody><tr><td style=3D"direction:ltr;font-size:0px;padding:0;t=
ext-align:left;"><!--[if mso | IE]><table role=3D"presentation" border=3D"0=
" cellpadding=3D"0" cellspacing=3D"0"><tr><td align=3D"left" class=3D"" sty=
le=3D"vertical-align:top;width:568px;" ><![endif]--><div class=3D"mj-column=
-per-100 mj-outlook-group-fix" style=3D"font-size:0px;text-align:left;direc=
tion:ltr;display:inline-block;vertical-align:top;width:100%;"><table border=
=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentation" width=3D"1=
00%"><tbody><tr><td style=3D"vertical-align:top;padding:0;"><table border=
=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentation" width=3D"1=
00%"><tbody><tr><td align=3D"left" style=3D"font-size:0px;padding:0;word-br=
eak:break-word;"><div style=3D"font-family:Ember,'Amazon Ember',Arial,sans-=
serif;font-size:15px;font-weight:400;line-height:20px;text-align:left;color=
:#0F1111;"><span class=3D"rio-text rio-text-121">Your package has been deli=
vered to reception.</span></div></td></tr></tbody></table></td></tr></tbody=
></table></div><!--[if mso | IE]></td></tr></table><![endif]--></td></tr></=
tbody></table></div><!--[if mso | IE]></td></tr></table><![endif]--> =
<!--[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody></table>=
</div><!--[if mso | IE]></td></tr></table></td></tr><![endif]--> <!--[if m=
so | IE]></table><![endif]--></td></tr></tbody></table></div><!--[if mso | =
IE]></td></tr></table><![endif]--><!--[if mso | IE]><table align=3D"center"=
border=3D"0" cellpadding=3D"0" cellspacing=3D"0" class=3D"rootContent-outl=
ook" role=3D"presentation" style=3D"width:600px;" width=3D"600" bgcolor=3D"=
#D5D9D9" ><tr><td style=3D"line-height:0px;font-size:0px;mso-line-height-ru=
le:exactly;"><![endif]--><div class=3D"rootContent" style=3D"background:#D5=
D9D9;background-color:#D5D9D9;margin:0px auto;max-width:600px;"><table alig=
n=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"prese=
ntation" style=3D"background:#D5D9D9;background-color:#D5D9D9;width:100%;">=
<tbody><tr><td style=3D"direction:ltr;font-size:0px;padding:4px 8px 4px 8px=
;text-align:left;"><!--[if mso | IE]><table role=3D"presentation" border=3D=
"0" cellpadding=3D"0" cellspacing=3D"0"><![endif]--> <!--[if mso | IE]><tr>=
<td align=3D"left" class=3D"rex-order-card-outlook rex-order-card-v1-outloo=
k" width=3D"600px" ><table align=3D"center" border=3D"0" cellpadding=3D"0" =
cellspacing=3D"0" class=3D"rio-card-outlook rio-card-71-outlook" role=3D"pr=
esentation" style=3D"width:584px;" width=3D"584" bgcolor=3D"#ffffff" ><tr><=
td style=3D"line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><!=
[endif]--><div class=3D"rio-card rio-card-71" style=3D"background:#ffffff;b=
ackground-color:#ffffff;margin:0px auto;border-radius:4px;max-width:584px;"=
><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" r=
ole=3D"presentation" style=3D"background:#ffffff;background-color:#ffffff;w=
idth:100%;border-radius:4px;"><tbody><tr><td style=3D"direction:ltr;font-si=
ze:0px;padding:12px 8px 16px 8px;text-align:left;"><!--[if mso | IE]><table=
role=3D"presentation" border=3D"0" cellpadding=3D"0" cellspacing=3D"0"><![=
endif]--> <!--[if mso | IE]><tr><td align=3D"left" class=3D"" width=
=3D"584px" ><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspac=
ing=3D"0" class=3D"" role=3D"presentation" style=3D"width:568px;" width=3D"=
568" ><tr><td style=3D"line-height:0px;font-size:0px;mso-line-height-rule:e=
xactly;"><![endif]--><div style=3D"margin:0px auto;max-width:568px;"><table=
align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"=
presentation" style=3D"width:100%;"><tbody><tr><td style=3D"direction:ltr;f=
ont-size:0px;padding:0;text-align:left;"><!--[if mso | IE]><table role=3D"p=
resentation" border=3D"0" cellpadding=3D"0" cellspacing=3D"0"><tr><td align=
=3D"left" class=3D"" style=3D"vertical-align:middle;width:568px;" ><![endif=
]--><div class=3D"mj-column-per-100 mj-outlook-group-fix" style=3D"font-siz=
e:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:mid=
dle;width:100%;"><table border=3D"0" cellpadding=3D"0" cellspacing=3D"0" ro=
le=3D"presentation" width=3D"100%"><tbody><tr><td style=3D"vertical-align:m=
iddle;padding:0;"><table border=3D"0" cellpadding=3D"0" cellspacing=3D"0" r=
ole=3D"presentation" width=3D"100%"><tbody><tr><td align=3D"left" style=3D"=
font-size:0px;padding:0;word-break:break-word;"><div style=3D"font-family:E=
mber,'Amazon Ember',Arial,sans-serif;font-size:18px;font-weight:700;line-he=
ight:22px;text-align:left;color:#0F1111;"><span class=3D"rio-text rio-text-=
122">How was your delivery?</span></div></td></tr><tr><td align=3D"left" cl=
ass=3D"rio-spacer" style=3D"font-size:0px;padding:0;word-break:break-word;"=
><div style=3D"height:12px;line-height:12px;">&#8202;</div></td></tr></tbod=
y></table></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></tab=
le><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr>=
</table><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=
=3D"0" class=3D"" role=3D"presentation" style=3D"width:568px;" width=3D"568=
" ><tr><td style=3D"line-height:0px;font-size:0px;mso-line-height-rule:exac=
tly;"><![endif]--><div style=3D"margin:0px auto;max-width:568px;"><table al=
ign=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"pre=
sentation" style=3D"width:100%;"><tbody><tr><td style=3D"direction:ltr;font=
-size:0px;padding:0;text-align:left;"><!--[if mso | IE]><table role=3D"pres=
entation" border=3D"0" cellpadding=3D"0" cellspacing=3D"0"><tr><td align=3D=
"left" class=3D"" style=3D"width:568px;" ><![endif]--><div class=3D"mj-colu=
mn-per-100 mj-outlook-group-fix" style=3D"font-size:0;line-height:0;text-al=
ign:left;display:inline-block;width:100%;direction:ltr;"><!--[if mso | IE]>=
<table border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentatio=
n" ><tr><td style=3D"align:left;vertical-align:middle;width:284px;" ><![end=
if]--><div class=3D"mj-column-per-50 mj-outlook-group-fix" style=3D"font-si=
ze:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:mi=
ddle;width:50%;"><table border=3D"0" cellpadding=3D"0" cellspacing=3D"0" ro=
le=3D"presentation" width=3D"100%"><tbody><tr><td style=3D"vertical-align:m=
iddle;padding:0;padding-right:4px;padding-left:0;"><table border=3D"0" cell=
padding=3D"0" cellspacing=3D"0" role=3D"presentation" width=3D"100%"><tbody=
><tr><td align=3D"left" style=3D"font-size:0px;padding:0;word-break:break-w=
ord;"><!--[if mso | IE]><table align=3D"center" border=3D"0" cellpadding=3D=
"0" cellspacing=3D"0" class=3D"" role=3D"presentation" style=3D"width:280px=
;" width=3D"280" ><tr><td style=3D"line-height:0px;font-size:0px;mso-line-h=
eight-rule:exactly;"><![endif]--><div style=3D"margin:0px auto;max-width:28=
0px;"><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D=
"0" role=3D"presentation" style=3D"width:100%;"><tbody><tr><td style=3D"dir=
ection:ltr;font-size:0px;padding:0;text-align:left;"><!--[if mso | IE]><tab=
le role=3D"presentation" border=3D"0" cellpadding=3D"0" cellspacing=3D"0"><=
tr><td align=3D"left" class=3D"" style=3D"vertical-align:top;width:280px;" =
><![endif]--><div class=3D"mj-column-per-100 mj-outlook-group-fix" style=3D=
"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-=
align:top;width:100%;"><table border=3D"0" cellpadding=3D"0" cellspacing=3D=
"0" role=3D"presentation" width=3D"100%"><tbody><tr><td style=3D"background=
-color:#F0F2F2;border-radius:4px;vertical-align:top;padding:0;"><table bord=
er=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentation" width=3D=
"100%"><tbody><tr><td align=3D"left" style=3D"font-size:0px;padding:0;word-=
break:break-word;"><div style=3D"font-family:Ember,'Amazon Ember',Arial,san=
s-serif;font-size:13px;font-weight:400;line-height:18px;text-align:center;c=
olor:#0F1111;"><span class=3D"rio-text rio-text-123"><a href=3D"https://www=
.amazon.com/gp/r.html?C=3D1Z6HAGN0Q74SS&K=3D1PCIPA854472X&M=3Durn:rtn:msg:2=
0240816010133af409d4398414b769006b814f360p0na&R=3DSIRM1R078GGM&T=3DC&U=3Dht=
tps%3A%2F%2Fwww.amazon.com%2Fprogress-tracker%2Fpackage%3F_encoding%3DUTF8%=
26itemId%3D%26orderId%3D111-3061760-5513056%26packageIndex%3D0%26shipmentId=
%3D300612189807301%26ingress%3Demail%26intent%3Dfeedback_happy%26ref_%3Dpe_=
108877690_917195550_pdc_f_h&H=3DC5X8OQYHSNU4HZWASVBUOHCGVGGA&ref_=3Dpe_1088=
77690_917195550_pdc_f_h" target=3D"_blank" style=3D"mso-margin-top-alt: 16p=
x;"><img href=3D"https://www.amazon.com/progress-tracker/package?_encoding=
=3DUTF8&amp;itemId=3D&amp;orderId=3D111-3061760-5513056&amp;packageIndex=3D=
0&amp;shipmentId=3D300612189807301&amp;ingress=3Demail&amp;intent=3Dfeedbac=
k_happy&amp;ref_=3Dpdc_f_h" src=3D"https://m.media-amazon.com/images/G/01/T=
xNotifications/Rio/thumbs_up.png" alt=3D"It was great" width=3D"24" height=
=3D"24" style=3D"margin: auto; padding: 8px; mso-padding-alt: 0; display: b=
lock; width: 24px"><p style=3D"padding-bottom: 8px; margin: 0; mso-margin-b=
ottom-alt: 8px">It was great</p></a></span></div></td></tr></tbody></table>=
</td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table><![endi=
f]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table><!=
[endif]--></td></tr></tbody></table></td></tr></tbody></table></div><!--[if=
mso | IE]></td><td style=3D"align:left;vertical-align:middle;width:284px;"=
><![endif]--><div class=3D"mj-column-per-50 mj-outlook-group-fix" style=3D=
"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-=
align:middle;width:50%;"><table border=3D"0" cellpadding=3D"0" cellspacing=
=3D"0" role=3D"presentation" width=3D"100%"><tbody><tr><td style=3D"vertica=
l-align:middle;padding:0;padding-right:0;padding-left:4px;"><table border=
=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentation" width=3D"1=
00%"><tbody><tr><td align=3D"left" style=3D"font-size:0px;padding:0;word-br=
eak:break-word;"><!--[if mso | IE]><table align=3D"center" border=3D"0" cel=
lpadding=3D"0" cellspacing=3D"0" class=3D"" role=3D"presentation" style=3D"=
width:280px;" width=3D"280" ><tr><td style=3D"line-height:0px;font-size:0px=
;mso-line-height-rule:exactly;"><![endif]--><div style=3D"margin:0px auto;m=
ax-width:280px;"><table align=3D"center" border=3D"0" cellpadding=3D"0" cel=
lspacing=3D"0" role=3D"presentation" style=3D"width:100%;"><tbody><tr><td s=
tyle=3D"direction:ltr;font-size:0px;padding:0;text-align:left;"><!--[if mso=
| IE]><table role=3D"presentation" border=3D"0" cellpadding=3D"0" cellspac=
ing=3D"0"><tr><td align=3D"left" class=3D"" style=3D"vertical-align:top;wid=
th:280px;" ><![endif]--><div class=3D"mj-column-per-100 mj-outlook-group-fi=
x" style=3D"font-size:0px;text-align:left;direction:ltr;display:inline-bloc=
k;vertical-align:top;width:100%;"><table border=3D"0" cellpadding=3D"0" cel=
lspacing=3D"0" role=3D"presentation" width=3D"100%"><tbody><tr><td style=3D=
"background-color:#F0F2F2;border-radius:4px;vertical-align:top;padding:0;">=
<table border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentatio=
n" width=3D"100%"><tbody><tr><td align=3D"left" style=3D"font-size:0px;padd=
ing:0;word-break:break-word;"><div style=3D"font-family:Ember,'Amazon Ember=
',Arial,sans-serif;font-size:13px;font-weight:400;line-height:18px;text-ali=
gn:center;color:#0F1111;"><span class=3D"rio-text rio-text-124"><a href=3D"=
https://www.amazon.com/gp/r.html?C=3D1Z6HAGN0Q74SS&K=3D1PCIPA854472X&M=3Dur=
n:rtn:msg:20240816010133af409d4398414b769006b814f360p0na&R=3DMJGP66VISZXC&T=
=3DC&U=3Dhttps%3A%2F%2Fwww.amazon.com%2Fprogress-tracker%2Fpackage%3F_encod=
ing%3DUTF8%26itemId%3D%26orderId%3D111-3061760-5513056%26packageIndex%3D0%2=
6shipmentId%3D300612189807301%26ingress%3Demail%26intent%3Dfeedback_unhappy=
%26ref_%3Dpe_108877690_917195550_pdc_f_s&H=3DGWG8R3TO7P0CBBABRRGLVRPQV9YA&r=
ef_=3Dpe_108877690_917195550_pdc_f_s" target=3D"_blank" style=3D"mso-margin=
-top-alt: 16px;"><img href=3D"https://www.amazon.com/progress-tracker/packa=
ge?_encoding=3DUTF8&amp;itemId=3D&amp;orderId=3D111-3061760-5513056&amp;pac=
kageIndex=3D0&amp;shipmentId=3D300612189807301&amp;ingress=3Demail&amp;inte=
nt=3Dfeedback_unhappy&amp;ref_=3Dpdc_f_s" src=3D"https://m.media-amazon.com=
/images/G/01/TxNotifications/Rio/thumbs_down.png" alt=3D"Not so great" widt=
h=3D"24" height=3D"24" style=3D"margin: auto; padding: 8px; mso-padding-alt=
: 0; display: block; width: 24px"><p style=3D"padding-bottom: 8px; margin: =
0; mso-margin-bottom-alt: 8px">Not so great</p></a></span></div></td></tr><=
/tbody></table></td></tr></tbody></table></div><!--[if mso | IE]></td></tr>=
</table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td>=
</tr></table><![endif]--></td></tr></tbody></table></td></tr></tbody></tabl=
e></div><!--[if mso | IE]></td></tr></table><![endif]--></div><!--[if mso |=
IE]></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if =
mso | IE]></td></tr></table></td></tr><![endif]--> <!--[if mso | IE]><tr><t=
d align=3D"left" class=3D"rio-spacer-outlook" width=3D"584px" ><![endif]-->=
<div style=3D"height:20px;line-height:20px;">&#8202;</div><!--[if mso | IE]=
></td></tr><![endif]--> <!--[if mso | IE]><tr><td align=3D"left" class=3D""=
width=3D"584px" ><table align=3D"center" border=3D"0" cellpadding=3D"0" ce=
llspacing=3D"0" class=3D"" role=3D"presentation" style=3D"width:568px;" wid=
th=3D"568" ><tr><td style=3D"line-height:0px;font-size:0px;mso-line-height-=
rule:exactly;"><![endif]--><div style=3D"margin:0px auto;max-width:568px;">=
<table align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" ro=
le=3D"presentation" style=3D"width:100%;"><tbody><tr><td style=3D"direction=
:ltr;font-size:0px;padding:0;text-align:left;"><!--[if mso | IE]><table rol=
e=3D"presentation" border=3D"0" cellpadding=3D"0" cellspacing=3D"0"><tr><td=
align=3D"left" class=3D"" style=3D"vertical-align:top;width:568px;" ><![en=
dif]--><div class=3D"mj-column-per-100 mj-outlook-group-fix" style=3D"font-=
size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:=
top;width:100%;"><table border=3D"0" cellpadding=3D"0" cellspacing=3D"0" ro=
le=3D"presentation" width=3D"100%"><tbody><tr><td style=3D"vertical-align:t=
op;padding:0;"><table border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=
=3D"presentation" width=3D"100%"><tbody><tr><td align=3D"left" style=3D"fon=
t-size:0px;padding:0;word-break:break-word;"><div style=3D"font-family:Embe=
r,'Amazon Ember',Arial,sans-serif;font-size:15px;font-weight:400;line-heigh=
t:21px;text-align:left;color:#565959;"><span class=3D"rio-text rio-text-125=
">Order #</span></div></td></tr><tr><td align=3D"left" style=3D"font-size:0=
px;padding:0;word-break:break-word;"><div style=3D"font-family:Ember,'Amazo=
n Ember',Arial,sans-serif;font-size:15px;font-weight:700;line-height:21px;t=
ext-align:left;color:#0F1111;"><span class=3D"rio-text rio-text-126">111-30=
61760-5513056</span></div></td></tr></tbody></table></td></tr></tbody></tab=
le></div><!--[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody>=
</table></div><!--[if mso | IE]></td></tr></table></td></tr><tr><td align=
=3D"left" class=3D"" width=3D"584px" ><table align=3D"center" border=3D"0" =
cellpadding=3D"0" cellspacing=3D"0" class=3D"" role=3D"presentation" style=
=3D"width:568px;" width=3D"568" ><tr><td style=3D"line-height:0px;font-size=
:0px;mso-line-height-rule:exactly;"><![endif]--><div style=3D"margin:0px au=
to;max-width:568px;"><table align=3D"center" border=3D"0" cellpadding=3D"0"=
cellspacing=3D"0" role=3D"presentation" style=3D"width:100%;"><tbody><tr><=
td style=3D"direction:ltr;font-size:0px;padding:0;text-align:left;"><!--[if=
mso | IE]><table role=3D"presentation" border=3D"0" cellpadding=3D"0" cell=
spacing=3D"0"><tr><td align=3D"left" class=3D"" style=3D"width:568px;" ><![=
endif]--><div class=3D"mj-column-per-100 mj-outlook-group-fix" style=3D"fon=
t-size:0;line-height:0;text-align:left;display:inline-block;width:100%;dire=
ction:ltr;"><!--[if mso | IE]><table border=3D"0" cellpadding=3D"0" cellspa=
cing=3D"0" role=3D"presentation" ><tr><td style=3D"align:left;vertical-alig=
n:top;width:284px;" ><![endif]--><div class=3D"mj-column-per-50 mj-outlook-=
group-fix" style=3D"font-size:0px;text-align:left;direction:ltr;display:inl=
ine-block;vertical-align:top;width:50%;"><table border=3D"0" cellpadding=3D=
"0" cellspacing=3D"0" role=3D"presentation" width=3D"100%"><tbody><tr><td s=
tyle=3D"vertical-align:top;padding:0;"><table border=3D"0" cellpadding=3D"0=
" cellspacing=3D"0" role=3D"presentation" width=3D"100%"><tbody> <tr><td =
align=3D"left" class=3D"rio-spacer" style=3D"font-size:0px;padding:0;word-b=
reak:break-word;"><div style=3D"height:20px;line-height:20px;">&#8202;</div=
></td></tr><tr><td align=3D"left" style=3D"font-size:0px;padding:0;word-bre=
ak:break-word;"><!--[if gte mso 9]><style type=3D"text/css">.rio-button-cta=
-primary{background-color:#ffd814!important;border:1px solid #fcd200!import=
ant;padding:6px 16px!important;text-align:center;border-collapse:collapse!i=
mportant}.rio-button-primary{border:none!important}</style><![endif]--><tab=
le cellpadding=3D"0" cellspacing=3D"0" border=3D"0" role=3D"presentation"><=
tr><td class=3D"rio-button-cta-primary" style=3D"text-align: center"><a tar=
get=3D"_blank" aria-describedby=3D"Track package" class=3D"rio-button-prima=
ry" href=3D"https://www.amazon.com/gp/r.html?C=3D1Z6HAGN0Q74SS&K=3D1PCIPA85=
4472X&M=3Durn:rtn:msg:20240816010133af409d4398414b769006b814f360p0na&R=3DBU=
2DG84QM77R&T=3DC&U=3Dhttps%3A%2F%2Fwww.amazon.com%2Fgp%2Fyour-account%2Fshi=
p-track%3Fie%3DUTF8%26itemId%3D%26orderId%3D111-3061760-5513056%26packageIn=
dex%3D0%26shipmentId%3DGhsHRNhnG%26ref_%3Dpe_108877690_917195550_pdc_pt_tra=
ck&H=3DKVPMEEQKDCKNEZFWYBLC3E75A84A&ref_=3Dpe_108877690_917195550_pdc_pt_tr=
ack" style=3D"background-color: #FFD814; border: 1px solid #FCD200; color: =
#0f1111; text-decoration: none; border-radius: 24px;padding: 6px 16px;displ=
ay: inline-block;box-shadow: 1px 2px 4px rgba(153, 153, 153, 0.2);font-size=
: 13px;line-height: 18px;white-space: nowrap;"><span style=3D"text-decorati=
on: none">Track package</span></a></td></tr></table></td></tr></tbody></tab=
le></td></tr></tbody></table></div><!--[if mso | IE]></td><td style=3D"alig=
n:left;width:284px;" ><![endif]--><div class=3D"mj-column-per-50 mj-outlook=
-group-fix" style=3D"font-size:0;line-height:0;text-align:left;display:inli=
ne-block;width:100%;direction:ltr;"><!--[if mso | IE]><table border=3D"0" c=
ellpadding=3D"0" cellspacing=3D"0" role=3D"presentation" ><tr></tr></table>=
<![endif]--></div><!--[if mso | IE]></td></tr></table><![endif]--></div><!-=
-[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody></table></di=
v><!--[if mso | IE]></td></tr></table></td></tr><![endif]--> <!--[if mso | =
IE]></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]><=
/td></tr></table></td></tr><![endif]--> <!--[if mso | IE]></table><![endif]=
--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table><![e=
ndif]--><!--[if mso | IE]><table align=3D"center" border=3D"0" cellpadding=
=3D"0" cellspacing=3D"0" class=3D"rootContent-outlook" role=3D"presentation=
" style=3D"width:600px;" width=3D"600" bgcolor=3D"#D5D9D9" ><tr><td style=
=3D"line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-=
-><div class=3D"rootContent" style=3D"background:#D5D9D9;background-color:#=
D5D9D9;margin:0px auto;max-width:600px;"><table align=3D"center" border=3D"=
0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentation" style=3D"backg=
round:#D5D9D9;background-color:#D5D9D9;width:100%;"><tbody><tr><td style=3D=
"direction:ltr;font-size:0px;padding:4px 8px 4px 8px;text-align:left;"><!--=
[if mso | IE]><table role=3D"presentation" border=3D"0" cellpadding=3D"0" c=
ellspacing=3D"0"><![endif]--> <!--[if mso | IE]><tr><td align=3D"left=
" class=3D"sonar-informational-outlook sonar-informational-v1-outlook" widt=
h=3D"600px" ><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspa=
cing=3D"0" class=3D"rio-card-outlook rio-card-72-outlook" role=3D"presentat=
ion" style=3D"width:584px;" width=3D"584" bgcolor=3D"#ffffff" ><tr><td styl=
e=3D"line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]=
--><div class=3D"rio-card rio-card-72" style=3D"background:#ffffff;backgrou=
nd-color:#ffffff;margin:0px auto;border-radius:4px;max-width:584px;"><table=
align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"=
presentation" style=3D"background:#ffffff;background-color:#ffffff;width:10=
0%;border-radius:4px;"><tbody><tr><td style=3D"direction:ltr;font-size:0px;=
padding:12px 8px 12px 8px;text-align:left;"><!--[if mso | IE]><table role=
=3D"presentation" border=3D"0" cellpadding=3D"0" cellspacing=3D"0"><tr><td =
align=3D"left" class=3D"" width=3D"584px" ><table align=3D"center" border=
=3D"0" cellpadding=3D"0" cellspacing=3D"0" class=3D"" role=3D"presentation"=
style=3D"width:568px;" width=3D"568" ><tr><td style=3D"line-height:0px;fon=
t-size:0px;mso-line-height-rule:exactly;"><![endif]--><div style=3D"margin:=
0px auto;max-width:568px;"><table align=3D"center" border=3D"0" cellpadding=
=3D"0" cellspacing=3D"0" role=3D"presentation" style=3D"width:100%;"><tbody=
><tr><td style=3D"direction:ltr;font-size:0px;padding:0;text-align:left;"><=
!--[if mso | IE]><table role=3D"presentation" border=3D"0" cellpadding=3D"0=
" cellspacing=3D"0"><tr><td align=3D"left" class=3D"" style=3D"vertical-ali=
gn:top;width:568px;" ><![endif]--><div class=3D"mj-column-per-100 mj-outloo=
k-group-fix" style=3D"font-size:0px;text-align:left;direction:ltr;display:i=
nline-block;vertical-align:top;width:100%;"><table border=3D"0" cellpadding=
=3D"0" cellspacing=3D"0" role=3D"presentation" width=3D"100%"><tbody><tr><t=
d style=3D"vertical-align:top;padding:0;"><table border=3D"0" cellpadding=
=3D"0" cellspacing=3D"0" role=3D"presentation" width=3D"100%"><tbody> </tbo=
dy></table></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></ta=
ble><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr=
></table></td></tr><tr><td align=3D"left" class=3D"" width=3D"584px" ><![en=
dif]--> <!--[if mso | IE]><table align=3D"center" border=3D"0" cellpad=
ding=3D"0" cellspacing=3D"0" class=3D"" role=3D"presentation" style=3D"widt=
h:568px;" width=3D"568" ><tr><td style=3D"line-height:0px;font-size:0px;mso=
-line-height-rule:exactly;"><![endif]--><div style=3D"margin:0px auto;max-w=
idth:568px;"><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspa=
cing=3D"0" role=3D"presentation" style=3D"width:100%;"><tbody><tr><td style=
=3D"direction:ltr;font-size:0px;padding:0;text-align:left;"><!--[if mso | I=
E]><table role=3D"presentation" border=3D"0" cellpadding=3D"0" cellspacing=
=3D"0"><tr><td align=3D"left" class=3D"" style=3D"vertical-align:top;width:=
568px;" ><![endif]--><div class=3D"mj-column-per-100 mj-outlook-group-fix" =
style=3D"font-size:0px;text-align:left;direction:ltr;display:inline-block;v=
ertical-align:top;width:100%;"><table border=3D"0" cellpadding=3D"0" cellsp=
acing=3D"0" role=3D"presentation" width=3D"100%"><tbody><tr><td style=3D"ve=
rtical-align:top;padding:0;"><table border=3D"0" cellpadding=3D"0" cellspac=
ing=3D"0" role=3D"presentation" width=3D"100%"><tbody><tr><td align=3D"left=
" style=3D"font-size:0px;padding:0;word-break:break-word;"><div style=3D"fo=
nt-family:Ember,'Amazon Ember',Arial,sans-serif;font-size:15px;font-weight:=
400;line-height:20px;text-align:left;color:#0F1111;"><span class=3D"rio-tex=
t rio-text-129">Return or replace items in=20
<a href=3D"https://www.amazon.com/gp/r.html?C=3D1Z6HAGN0Q74SS&K=3D1PCIPA854=
472X&M=3Durn:rtn:msg:20240816010133af409d4398414b769006b814f360p0na&R=3D2U7=
FVGB1HZ63S&T=3DC&U=3Dhttps%3A%2F%2Fwww.amazon.com%2Fyour-orders%2Forders%3F=
ref_%3Dpe_108877690_917195550_pdc_yo&H=3DMRR6NURCHGXV0VKPAM2B9LRNHGKA&ref_=
=3Dpe_108877690_917195550_pdc_yo" rel=3D"nofollow">Your Orders</a>.</span><=
/div></td></tr></tbody></table></td></tr></tbody></table></div><!--[if mso =
| IE]></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if=
mso | IE]></td></tr></table><![endif]--> <!--[if mso | IE]></td></t=
r></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></t=
d></tr></table></td></tr><![endif]--> <!--[if mso | IE]></table><![endif]-=
-></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table><![en=
dif]--><!--[if mso | IE]><table align=3D"center" border=3D"0" cellpadding=
=3D"0" cellspacing=3D"0" class=3D"rootContent-outlook" role=3D"presentation=
" style=3D"width:600px;" width=3D"600" bgcolor=3D"#D5D9D9" ><tr><td style=
=3D"line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-=
-><div class=3D"rootContent" style=3D"background:#D5D9D9;background-color:#=
D5D9D9;margin:0px auto;max-width:600px;"><table align=3D"center" border=3D"=
0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentation" style=3D"backg=
round:#D5D9D9;background-color:#D5D9D9;width:100%;"><tbody><tr><td style=3D=
"direction:ltr;font-size:0px;padding:4px 0px 0px 0px;text-align:left;"><!--=
[if mso | IE]><table role=3D"presentation" border=3D"0" cellpadding=3D"0" c=
ellspacing=3D"0"><![endif]--> <!--[if mso | IE]><tr><td align=3D"left" clas=
s=3D"sonar-footer-outlook sonar-footer-v1-outlook" width=3D"600px" ><table =
align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" class=3D"=
footerCard-outlook" role=3D"presentation" style=3D"width:600px;" width=3D"6=
00" bgcolor=3D"#F0F2F2" ><tr><td style=3D"line-height:0px;font-size:0px;mso=
-line-height-rule:exactly;"><![endif]--><div class=3D"footerCard" style=3D"=
background:#F0F2F2;background-color:#F0F2F2;margin:0px auto;max-width:600px=
;"><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0"=
role=3D"presentation" style=3D"background:#F0F2F2;background-color:#F0F2F2=
;width:100%;"><tbody><tr><td style=3D"direction:ltr;font-size:0px;padding:0=
;text-align:left;"><!--[if mso | IE]><table role=3D"presentation" border=3D=
"0" cellpadding=3D"0" cellspacing=3D"0"><tr><td align=3D"left" class=3D"foo=
terText-outlook" width=3D"600px" ><table align=3D"center" border=3D"0" cell=
padding=3D"0" cellspacing=3D"0" class=3D"footerText-outlook" role=3D"presen=
tation" style=3D"width:600px;" width=3D"600" ><tr><td style=3D"line-height:=
0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div class=3D"=
footerText" style=3D"margin:0px auto;max-width:600px;"><table align=3D"cent=
er" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentation" =
style=3D"width:100%;"><tbody><tr><td style=3D"direction:ltr;font-size:0px;p=
adding:20px 16px 0;text-align:left;"><!--[if mso | IE]><table role=3D"prese=
ntation" border=3D"0" cellpadding=3D"0" cellspacing=3D"0"><tr><td align=3D"=
left" class=3D"" style=3D"vertical-align:top;width:568px;" ><![endif]--><di=
v class=3D"mj-column-per-100 mj-outlook-group-fix" style=3D"font-size:0px;t=
ext-align:left;direction:ltr;display:inline-block;vertical-align:top;width:=
100%;"><table border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"pres=
entation" width=3D"100%"><tbody><tr><td style=3D"vertical-align:top;padding=
:0;"><table border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presen=
tation" width=3D"100%"><tbody><tr><td align=3D"left" style=3D"font-size:0px=
;padding:0;word-break:break-word;"><div style=3D"font-family:Ember,'Amazon =
Ember',Arial,sans-serif;font-size:14px;font-weight:400;line-height:20px;tex=
t-align:left;color:#494D4D;">=C2=A92024 Amazon.com, Inc. or its affiliates.=
Amazon and all related marks are trademarks of Amazon.com, Inc. or its aff=
iliates, Amazon.com, Inc. 410 Terry Avenue N., Seattle, WA 98109.</div></td=
></tr></tbody></table></td></tr></tbody></table></div><!--[if mso | IE]></t=
d></tr></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE=
]></td></tr></table></td></tr><![endif]--> <!--[if mso | IE]><tr><td align=
=3D"left" class=3D"" width=3D"600px" ><table align=3D"center" border=3D"0" =
cellpadding=3D"0" cellspacing=3D"0" class=3D"" role=3D"presentation" style=
=3D"width:600px;" width=3D"600" ><tr><td style=3D"line-height:0px;font-size=
:0px;mso-line-height-rule:exactly;"><![endif]--><div style=3D"margin:0px au=
to;max-width:600px;"><table align=3D"center" border=3D"0" cellpadding=3D"0"=
cellspacing=3D"0" role=3D"presentation" style=3D"width:100%;"><tbody><tr><=
td style=3D"direction:ltr;font-size:0px;padding:14px 16px 4px 0;text-align:=
left;"><!--[if mso | IE]><table role=3D"presentation" border=3D"0" cellpadd=
ing=3D"0" cellspacing=3D"0"><tr><td align=3D"left" class=3D"" style=3D"vert=
ical-align:top;width:584px;" ><![endif]--><div class=3D"mj-column-per-100 m=
j-outlook-group-fix" style=3D"font-size:0px;text-align:left;direction:ltr;d=
isplay:inline-block;vertical-align:top;width:100%;"><table border=3D"0" cel=
lpadding=3D"0" cellspacing=3D"0" role=3D"presentation" width=3D"100%"><tbod=
y><tr><td style=3D"vertical-align:top;padding:0;"><table border=3D"0" cellp=
adding=3D"0" cellspacing=3D"0" role=3D"presentation" width=3D"100%"><tbody>=
<table class=3D"lightFooterImg" style=3D"height:43px;width:86px;"><tr><td><=
img src=3D"https://m.media-amazon.com/images/G/01/outbound/OutboundTemplate=
s/Smile_Logo_Light._BG240,242,242_.png" alt=3D"Amazon.com" height=3D"43" wi=
dth=3D"86" align=3D"left"></td></tr></table><!--[if !mso]><!--><table class=
=3D"darkFooterImg" style=3D"height:43px;width:86px;display:none;"><tr><td><=
img src=3D"https://m.media-amazon.com/images/G/01/outbound/OutboundTemplate=
s/Smile_Logo_Dark.png" alt=3D"Amazon.com" height=3D"43" width=3D"86" align=
=3D"left"></td></tr></table><!--<![endif]--></tbody></table></td></tr></tbo=
dy></table></div><!--[if mso | IE]></td></tr></table><![endif]--></td></tr>=
</tbody></table></div><!--[if mso | IE]></td></tr></table></td></tr></table=
><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></=
table></td></tr><![endif]--> <!--[if mso | IE]></table><![endif]--></td></t=
r></tbody></table></div><!--[if mso | IE]></td></tr></table><![endif]--></d=
iv><img width=3D"1" height=3D"1" src=3D"https://www.amazon.com/gp/r.html?C=
=3D1Z6HAGN0Q74SS&K=3D1PCIPA854472X&M=3Durn:rtn:msg:20240816010133af409d4398=
414b769006b814f360p0na&R=3D1RGIUEL0NLUB0&T=3DE&U=3Dhttps%3A%2F%2Fimages-na.=
ssl-images-amazon.com%2Fimages%2FG%2F01%2Fnav%2Ftransp.gif&H=3DHTTPRSBYHYPR=
XR0HOLWRVI18LHCA&ref_=3Dpe_108877690_917195550_open" /></body></html>
------=_Part_533333_1464710557.1723770094034--
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use sanitized fixture data; avoid committing real-looking personal email records.

Lines 1–1000 contain real-looking personal identifiers (email addresses, message metadata, order details) in a publicly served test asset. Please replace this fixture with anonymized/synthetic data.

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

In `@browser_test/public/amazon.eml` around lines 1 - 1000, This fixture contains
real-looking PII (Delivered-To, From: "Amazon.com" <order-update@amazon.com>,
Message-ID, order number 111-3061760-5513056 and other headers/body links);
replace them with anonymized synthetic values across the file (e.g. redact
Delivered-To, replace order-update@amazon.com and any real email addresses with
test@example.com, remove or replace Message-ID and ARC/SPF metadata, and convert
the order Id and shipmentId to a clearly fake token like ORDER-TEST-0000) so the
public test asset contains no real personal identifiers while preserving the
same structure for tests.

Comment on lines +1 to +75
Delivered-To: yogeshshahi120@gmail.com
Received: by 2002:a05:7108:5e85:b0:47f:4051:d657 with SMTP id ev5csp1218074gdb;
Fri, 22 Aug 2025 23:59:07 -0700 (PDT)
X-Google-Smtp-Source: AGHT+IFLl4l7A6SKSW7/X6M4sCbyCp6E/LtBnXgQitc3mXhmvPpLD9A15zKAm4Hv1x3ACxHbyy9r
X-Received: by 2002:a05:622a:209:b0:4b2:8ac4:ef6b with SMTP id d75a77b69052e-4b2aab28ce1mr69583081cf.78.1755932346900;
Fri, 22 Aug 2025 23:59:06 -0700 (PDT)
ARC-Seal: i=1; a=rsa-sha256; t=1755932346; cv=none;
d=google.com; s=arc-20240605;
b=d0GamXf3hsBhtc6AO7TIZ5CPJMHh9iMnwtWtm4yJx72YNrw3dhgDUgdOU0EEfHvQRY
nOGpejLM54/1JgQtcrkQU/qPr5EwSWzspHh6R4OTZj5I2g9QVS+A6NVor9VtfdxevkDx
eN7j6A7NSYsqH8Ml1k9ZZ9FmeNf1jNeWaQ2JFkQoZMCsyzlG46IUMJRQ19/SDfR9++QG
kYIa8vqtE779vdBsWI/tV0mh4fJ3349bx9SWK/xsdoEwsR8ScJNFmjlmVHoYCXu5DUba
1LHeoKsO0GszHnJjeiBzcFXn8HLmSptPkHJYxSnN5tpd9jPCeN1vDZyTeWN4xFlRkl9e
bSYQ==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605;
h=content-transfer-encoding:mime-version:subject:message-id:to:from
:date:dkim-signature;
bh=VHgcAWi2xRXZm0DA4ihT7cYtF7t6AYCtpVyB8R4WYMY=;
fh=MrUDigLt8TyE79/Vsak8GkhPoyKFErE0pW3gJJgnYJQ=;
b=ISTi3pvc8IB6i9cnJFpLAlcdiXUS3fzaNcCMG4qCXpCvGh0sl7M9tMbfXFwvJuCzZf
KXIQHW3xkvKpYA7qPrrLYRDj89Poar6Lx9AxRnY3p4LVc6zSYmqsGujsaarceZvckFsa
7VtsM6YzT2jd9tKSJv8DmWoc0616i8yU+XUPBOlpkVD5FElN+p9WYggbrdZLDeboiPqb
25qyk5hyEo5RFCTSADJxHeiSKqUXMIHj0It1Lo5CrlhIfAC/BoQPGHGz6VlWmkfyozBj
c/vuFVqKkwL4x1L5LGOu5ouvql/XOt8Nu4dQmZxT58mwhQyhPMtpYSPt/58N3mrDAoOw
mDaw==;
dara=google.com
ARC-Authentication-Results: i=1; mx.google.com;
dkim=pass header.i=@github.com header.s=pf2023 header.b=Juy8nwl8;
spf=pass (google.com: domain of noreply@github.com designates 192.30.252.211 as permitted sender) smtp.mailfrom=noreply@github.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=github.com
Return-Path: <noreply@github.com>
Received: from out-28.smtp.github.com (out-28.smtp.github.com. [192.30.252.211])
by mx.google.com with ESMTPS id d75a77b69052e-4b2b8e43c0dsi7393621cf.846.2025.08.22.23.59.06
for <yogeshshahi120@gmail.com>
(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
Fri, 22 Aug 2025 23:59:06 -0700 (PDT)
Received-SPF: pass (google.com: domain of noreply@github.com designates 192.30.252.211 as permitted sender) client-ip=192.30.252.211;
Authentication-Results: mx.google.com;
dkim=pass header.i=@github.com header.s=pf2023 header.b=Juy8nwl8;
spf=pass (google.com: domain of noreply@github.com designates 192.30.252.211 as permitted sender) smtp.mailfrom=noreply@github.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=github.com
Received: from github.com (hubbernetes-node-531b44e.ash1-iad.github.net [10.56.208.50])
by smtp.github.com (Postfix) with ESMTPA id 9B1239206DD;
Fri, 22 Aug 2025 23:59:06 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=github.com;
s=pf2023; t=1755932346;
bh=VHgcAWi2xRXZm0DA4ihT7cYtF7t6AYCtpVyB8R4WYMY=;
h=Date:From:To:Subject:From;
b=Juy8nwl8FUv8aQ1x1rEOCiikwzic8OqoCCOjBHaWe2hC46AFbFHQzQSFKc2lmi4H0
2gwbBlJQ9FU2DzNyY/l425GX+JeZMlBoJlztrmew8KoypcOHV4BqX5LhLcFAra+s5e
bDTWDfRN31+F9XhRBNpqL3jc8ybSV2rY0TWoIQEs=
Date: Fri, 22 Aug 2025 23:59:06 -0700
From: GitHub <noreply@github.com>
To: Yogesh Shahi <yogeshshahi120@gmail.com>
Message-ID: <68a966ba9905b_fa10b81089f3@lowworker-74c7b88f6d-m2b9v.mail>
Subject: [GitHub] A third-party OAuth application has been added to your
account
Mime-Version: 1.0
Content-Type: text/plain;
charset=UTF-8
Content-Transfer-Encoding: 7bit
X-Auto-Response-Suppress: All

Hey BornPsych!

A third-party OAuth application (ETHGlobal) with read:user and user:email scopes was recently authorized to access your account.
Visit https://github.com/settings/connections/applications/7f0a42d8039f524b6a28 for more information.

To see this and other security events for your account, visit https://github.com/settings/security-log

If you run into problems, please contact support by visiting https://github.com/contact

Thanks,
The GitHub Team

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't commit real account-security emails under public/.

This fixture exposes a real email address and a GitHub OAuth authorization event. Since it sits in browser_test/public/, the entire raw message is also directly accessible to the browser test app. Please swap this for a redacted or synthetic fixture.

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

In `@browser_test/public/github.eml` around lines 1 - 75, The committed EML
fixture (github.eml) contains real PII and an actual GitHub OAuth event; replace
it with a redacted or synthetic fixture: remove or replace real email addresses
(From/To), strip or genericize security URLs and message-id, and replace the
body with a clearly synthetic message (e.g., placeholder sender, recipient, and
fake OAuth app name/scopes) so tests exercise parsing without exposing real
data; ensure the new fixture preserves the same MIME structure/headers your
tests expect and update any tests that assert exact strings to match the
redacted placeholders.

Comment on lines +1 to +127
Delivered-To: adityabisht64@gmail.com
Received: by 2002:a05:6504:1313:b0:279:6670:9162 with SMTP id s19csp1104512lto;
Thu, 5 Sep 2024 14:04:50 -0700 (PDT)
X-Forwarded-Encrypted: i=2; AJvYcCXmuTgC70UQT5Ts4OfBGnVnadr0JW74IPaiOJ5gDpOACNVP8eAJFJP4SA3ch7rvoG+htp/hPClBdbQKTZEu@gmail.com
X-Google-Smtp-Source: AGHT+IH76QMWC15Dg/+ThxNbcE5CDLb7cpojHoNsy/oAHrJx5dOAKtaNl1myXNjuD8Du2en5yyVL
X-Received: by 2002:a05:6870:63a9:b0:261:19a6:41ae with SMTP id 586e51a60fabf-27b82fab31bmr675802fac.30.1725570290222;
Thu, 05 Sep 2024 14:04:50 -0700 (PDT)
ARC-Seal: i=1; a=rsa-sha256; t=1725570290; cv=none;
d=google.com; s=arc-20160816;
b=LdXMuzrUNuNI+nDJ2bE4y4i/UNkay6ACfqFs51rtQOgUxDIYAuGZhqPw/monlbxkoN
NGihGo/EItYDoFyz2YenzxBQjpdSic9/g7qp6xz2O00PkGS43a1yOpd+ZVPlGaK5WZx1
LhKdW5Fk7Jx/xJtl5rKnhBgH1SaWkv2pECv5SGasqct9PROtQeg+MMv1kwGqxFBHZ8d9
4ZrRPsKrp04FiG8hbMLUEV5xd1qwsdmDSqHLvmjhX82mN/ohEM4p/laq1OGzqMnM9SuC
5vSNPUEx1wO49BEEqdQGVZ0qu8RQbAg6njk4bKUZoWSbDW1l/WU3Im+WbuCynOi8JMVw
pP6w==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
h=cc:subject:message-id:date:from:mime-version:dkim-signature;
bh=ZPnp2Ot4Oq1yml0gwfVCa+vOkRh/AoPZcUFDo94M5cA=;
fh=rt5+XsSKgJXCbjLkZz2YEwXmYMAytcEAXMTatOTpOfs=;
b=qVd1nk6MrHSij4tsjZ6Lr+Ymptz0OlsUw3AMcXtBlrNhD0U+HLQPSTScNrBaX0i3uX
or7VZjCPegexDy+ovr56hI6M0878rRWyBvW8LRJVn2IsLmImIsePhDPYjU+8YGyHnJ9T
DPz0QtdM6Tzu/WeyWeYsA+B/ab+lIfZ910dtUmOjokCSa2JxGgLaTD/x75L91FpG4KSb
T+FumRRMXfEFx5I6uwMcHQmR1LE22Krkk9D/fs7WyqmNfSBL2eruURXcjOku3zjqK3Hs
mnOqjSUXqEeJLvI+DTPcZ6xqywiq5Pq8I1sCyBi4EVprdq06eDlB7yz+L4ECLB97offc
4byg==;
dara=google.com
ARC-Authentication-Results: i=1; mx.google.com;
dkim=pass header.i=@succinct.xyz header.s=google header.b=PbaIBUZo;
spf=pass (google.com: domain of leland@succinct.xyz designates 209.85.220.41 as permitted sender) smtp.mailfrom=leland@succinct.xyz;
dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=succinct.xyz;
dara=pass header.i=@gmail.com
Return-Path: <leland@succinct.xyz>
Received: from mail-sor-f41.google.com (mail-sor-f41.google.com. [209.85.220.41])
by mx.google.com with SMTPS id 586e51a60fabf-277c34333casor4311308fac.9.2024.09.05.14.04.49
for <adityabisht64@gmail.com>
(Google Transport Security);
Thu, 05 Sep 2024 14:04:50 -0700 (PDT)
Received-SPF: pass (google.com: domain of leland@succinct.xyz designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41;
Authentication-Results: mx.google.com;
dkim=pass header.i=@succinct.xyz header.s=google header.b=PbaIBUZo;
spf=pass (google.com: domain of leland@succinct.xyz designates 209.85.220.41 as permitted sender) smtp.mailfrom=leland@succinct.xyz;
dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=succinct.xyz;
dara=pass header.i=@gmail.com
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=succinct.xyz; s=google; t=1725570289; x=1726175089; dara=google.com;
h=cc:subject:message-id:date:from:mime-version:from:to:cc:subject
:date:message-id:reply-to;
bh=ZPnp2Ot4Oq1yml0gwfVCa+vOkRh/AoPZcUFDo94M5cA=;
b=PbaIBUZod+MtWJh6mm0xQWE6KEJbub66abuVn6S+JQySDWT1c/wbaWs+Fe39s1svy3
GTiD7JdDSgJ0XOQxdeO6vTbyFLI8lFp5UknjFxhIi9TecwN/oBgCVQpjXaQ8hdIh5fA2
rZLURZB6/XnRnc1/I2DTrdegCDztjVty94pNZjKP1iRVCBQTfURoM4/r7ke1ez7P+qKG
uhKrt0XlPsEIbgeViKZpzlcjgfsbwEN0syimGnt+ccfmBx5r5lU9wG7HfW2i+EMOgOiu
+D4cX6uTOfYmBhCtH+HeErPenwgcQ0mTHxzcnAgPipVqTXA9VcQbgaCWB4hQtriMhYNU
B0ZA==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=1e100.net; s=20230601; t=1725570289; x=1726175089;
h=cc:subject:message-id:date:from:mime-version:x-gm-message-state
:from:to:cc:subject:date:message-id:reply-to;
bh=ZPnp2Ot4Oq1yml0gwfVCa+vOkRh/AoPZcUFDo94M5cA=;
b=kSvDsE08OCPix+MrHQsGps+9FNVPep2YruDdHlAU9kN7KHlWYFEunPmEqxghhqMMqW
vR2nT+a+FgXLidImEorIK+DEdoIIAEPJwHnuY7C4jsTgqktXSQvYBrNqGgl0e7oEgs3X
X4OQdV/U9zQkXRF1MTBxy+OJMX1e9MQmm2Pv1FyfqWJA2x3wFo1dtmCocvjjJU7mzgl9
2t4ijcPtdZxsW8U9CRvwN3KzFPtdbT6c6uzUVNGhBmBSFi7HfGKbW3V2+uzRZIBAfoR9
4sqmGVYf5ycQAN2e5r2QwUT6oEca47vEB5IDMRadOM3caymamT2+SLR3sq5JrKm/2/j1
C3kQ==
X-Forwarded-Encrypted: i=1; AJvYcCXxd8p4U8kra+dRtLXrArCb0RI+tf/okMTeOygvHmONJnKDrNbiVCZPinPL4j7RNT8h7+HhiwSoWhuKI1Mb@gmail.com
X-Gm-Message-State: AOJu0YxZgyTji/ChYSYLy8R8e+cfUgPmc5ZnDATk5epwZp5EqaRpnZzP
wXv1FS+cySFJJTaopk69+6+98CWXZ2JzXBqWFpLnGwLKkW6aEp7DwMSz2oXlDBdYXt+xWXULZ96
pL4fvwj3nWBaCvEQc1dHauElTv9Bqq4rH8EwEYg==
X-Received: by 2002:a05:6870:af47:b0:278:8b:57b1 with SMTP id
586e51a60fabf-27b82fab4a7mt675702fac.35.1725570289543; Thu, 05 Sep 2024
14:04:49 -0700 (PDT)
MIME-Version: 1.0
From: Leland Lee <leland@succinct.xyz>
Date: Thu, 5 Sep 2024 14:04:38 -0700
Message-ID: <CAPPv_w=DjyLg38_-aUvy7Lk-KP-YUaJROWForsmR6iaAgZCUVw@mail.gmail.com>
Subject: Welcome to the Succinct ZK Residency!
Cc: Uma Roy <uma@succinct.xyz>, Eli Yang <eli@succinct.xyz>
Content-Type: multipart/alternative; boundary="000000000000263095062165a40c"
Bcc: adityabisht64@gmail.com

--000000000000263095062165a40c
Content-Type: text/plain; charset="UTF-8"

Hi All,

We're thrilled to collaborate with you at the Succinct ZK residency in
three weeks. We can't wait to build together on a daily basis and bounce
ideas off each other.

To help us support your journey, please *complete the attached form
<https://docs.google.com/forms/d/e/1FAIpQLSfszQDE3DY5Tg4k3r-v9y3z8wnJy0g_mJKaB_4TRAEx-PnlpQ/viewform>*
with details about your project and a brief bio. Every resident must
complete the form. Your project information and bio will be shared with
other residents as we encourage collaboration and building in the open.

*Logistics*

- Please join residency telegram (https://t.me/+GHNdBiwW89VhOTkx)

We'll be sharing more information soon regarding logistics and schedules
through both email and telegram!

See you soon,
Succinct Team

--000000000000263095062165a40c
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div>Hi All,</div><div><br></div><div>We&#39;re thrilled t=
o collaborate with you at the Succinct ZK residency in three weeks. We can&=
#39;t wait to build together on a daily basis and bounce ideas off each oth=
er.</div><div><br></div><div>To help us support your journey, please <b><a =
href=3D"https://docs.google.com/forms/d/e/1FAIpQLSfszQDE3DY5Tg4k3r-v9y3z8wn=
Jy0g_mJKaB_4TRAEx-PnlpQ/viewform" target=3D"_blank">complete the attached f=
orm</a></b> with details about your project and a brief bio. Every resident=
must complete the form. Your project information and bio will be shared wi=
th other residents as we encourage collaboration and building in the open.<=
br></div><div><p><b>Logistics</b><br></p></div><div><ul><li>Please join res=
idency telegram (<a href=3D"https://t.me/+GHNdBiwW89VhOTkx" target=3D"_blan=
k">https://t.me/+GHNdBiwW89VhOTkx</a>)</li></ul><div>We&#39;ll be sharing m=
ore information soon regarding logistics and schedules through both email a=
nd telegram!</div><div><br></div><div>See you soon,<br></div></div><div>Suc=
cinct=C2=A0Team</div></div>

--000000000000263095062165a40c--
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Replace this public fixture with sanitized data.

This .eml contains real personal addresses, names, and live message content, and placing it under browser_test/public/ makes the raw email directly servable by the browser app. Please replace it with fully anonymized/synthetic data before merging.

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

In `@browser_test/public/residency.eml` around lines 1 - 127, The public .eml
fixture contains real PII (e.g., "From: Leland Lee <leland@succinct.xyz>", "Cc:
Uma Roy <uma@succinct.xyz>, Eli Yang <eli@succinct.xyz>", "Bcc:
adityabisht64@gmail.com", Message-ID and the real subject "Welcome to the
Succinct ZK Residency!"); replace the file contents with a fully sanitized,
synthetic email preserving the MIME structure and boundaries but using fake
names/addresses (e.g., "From: Test Sender <sender@example.com>"), anonymized
Subject and Message-ID, non-sensitive placeholder links (example.com), and
remove any real telegram/invite or personal content so the fixture can remain in
browser_test/public without exposing real data.

Comment on lines +13 to +19
init()
.then(() => {
relayerUtilsResolver(null);
})
.catch((err) => {
console.error("Failed to initialize wasm for relayer-utils: ", err);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reject relayerUtilsInit when WASM initialization fails.

The catch branch only logs, so every later await relayerUtilsInit hangs forever after an init error instead of failing fast.

Proposed fix
-let relayerUtilsResolver: (value: any) => void;
-const relayerUtilsInit: Promise<void> = new Promise((resolve) => {
-  relayerUtilsResolver = resolve;
+let relayerUtilsResolver!: () => void;
+let relayerUtilsRejecter!: (reason?: unknown) => void;
+const relayerUtilsInit: Promise<void> = new Promise((resolve, reject) => {
+  relayerUtilsResolver = resolve;
+  relayerUtilsRejecter = reject;
 });
@@
   .catch((err) => {
     console.error("Failed to initialize wasm for relayer-utils: ", err);
+    relayerUtilsRejecter(err);
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_test/src/circomProver.ts` around lines 13 - 19, The current init()
catch only logs and leaves relayerUtilsInit unresolved; update the promise setup
so you capture both resolve and reject (e.g., relayerUtilsResolve and
relayerUtilsReject) instead of only relayerUtilsResolver, and in the catch
handler call relayerUtilsReject(err) (or otherwise reject relayerUtilsInit with
the error) so awaiting relayerUtilsInit fails fast on WASM init errors; locate
the init() call and the relayerUtilsResolver (promise creation for
relayerUtilsInit) and add the corresponding relayerUtilsReject usage in the
catch branch.

Comment thread src/circuit/noir/utils.rs
Comment on lines +326 to +356
pub fn trim_sha256_padding(data: &[u8]) -> &[u8] {
// SHA256 padding starts with 0x80 byte followed by zeros
// Find the last 0x80 byte (padding marker)
if let Some(padding_start) = data.iter().rposition(|&b| b == 0x80) {
// Check if everything after 0x80 is zeros or length encoding
let after_padding = &data[padding_start + 1..];

// If we find 0x80 and it's followed by zeros/length, return data before padding
if after_padding.iter().rev().take(8).all(|&b| b != 0x80) {
return &data[..padding_start];
}
}

// Fallback: find the last non-zero byte that isn't part of length encoding
// SHA256 padding ends with 8-byte length, so check if last 8 bytes look like length
if data.len() >= 8 {
let (content, _potential_length) = data.split_at(data.len() - 8);
// If last 8 bytes represent a reasonable length, trim from there
if let Some(last_nonzero) = content.iter().rposition(|&b| b != 0) {
if content[last_nonzero] == 0x80 {
return &content[..last_nonzero];
}
}
}

// Ultimate fallback: trim trailing zeros
if let Some(last_nonzero) = data.iter().rposition(|&b| b != 0) {
&data[..=last_nonzero]
} else {
data
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't infer SHA padding from the last 0x80 byte.

A real header/body can legitimately contain 0x80, especially before UTF-8 decoding. In that case this heuristic truncates real content before src/circuit/noir/mod.rs:306-325 converts the bytes back to text. The trim point needs to be validated against the SHA-256 padding layout and length footer, not guessed from rposition(0x80).

Comment thread src/circuit/utils.rs
Comment on lines +29 to +31
if selector_start_index < position_map.len() && selector_end_index < position_map.len() {
let original_start_index = position_map[selector_start_index];
let original_end_index = position_map[selector_end_index];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how position_map is constructed to understand the mapping semantics
rg -n "position_map" --type rust -B 3 -A 10

Repository: zkemail/relayer-utils

Length of output: 7315


🏁 Script executed:

rg -n "remove_quoted_printable_soft_breaks" --type rust -A 30

Repository: zkemail/relayer-utils

Length of output: 23294


Off-by-one error: selector_end_index bounds check and slicing may fail for matches at the end of cleaned content.

When the regex match ends at the last byte of the cleaned content, selector_end_index equals the actual cleaned length. Accessing position_map[selector_end_index] retrieves a padded usize::MAX value, causing the function to return an error even though the match is valid. Additionally, when in bounds, position_map[selector_end_index] provides the original index of the byte after the match, not the exclusive end needed for slicing the original body correctly.

🔧 Suggested fix for correct end index mapping
-        if selector_start_index < position_map.len() && selector_end_index < position_map.len() {
+        if selector_start_index < position_map.len() && selector_end_index > 0 && selector_end_index <= position_map.len() {
             let original_start_index = position_map[selector_start_index];
-            let original_end_index = position_map[selector_end_index];
+            // m.end() is exclusive, so map the last matched index and add 1
+            let original_end_index = position_map[selector_end_index - 1] + 1;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if selector_start_index < position_map.len() && selector_end_index < position_map.len() {
let original_start_index = position_map[selector_start_index];
let original_end_index = position_map[selector_end_index];
if selector_start_index < position_map.len() && selector_end_index > 0 && selector_end_index <= position_map.len() {
let original_start_index = position_map[selector_start_index];
// m.end() is exclusive, so map the last matched index and add 1
let original_end_index = position_map[selector_end_index - 1] + 1;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/circuit/utils.rs` around lines 29 - 31, The current bounds check and
mapping for selector_end_index are wrong: allow selector_end_index ==
position_map.len() (use <=) and compute the exclusive original end correctly by
mapping selector_end_index to the original byte after the match; specifically,
keep original_start_index = position_map[selector_start_index], but set
original_end_index = if selector_end_index == position_map.len() {
original_body.len() } else { position_map[selector_end_index - 1] + 1 }; update
the if-condition from selector_end_index < position_map.len() to
selector_end_index <= position_map.len() and use these computed values for
slicing to avoid usize::MAX and off-by-one errors when matches end at the
cleaned content end.

Comment thread src/cryptos.rs
Comment on lines +523 to +533
let body_str = {
// Undo SHA padding
let mut trimmed_body = body.clone();
while !(trimmed_body.last() == Some(&10)
&& trimmed_body.get(trimmed_body.len() - 2) == Some(&13))
{
trimmed_body.pop();
}

String::from_utf8(trimmed_body)?
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Return an error when the body never reaches \r\n.

Both loops can panic on short or malformed input: once trimmed_body.len() < 2, trimmed_body.len() - 2 underflows before you have a chance to return Err. That turns these Result-returning helpers into abort paths instead of parse failures.

Proposed fix
-            while !(trimmed_body.last() == Some(&10)
-                && trimmed_body.get(trimmed_body.len() - 2) == Some(&13))
-            {
-                trimmed_body.pop();
-            }
+            while !trimmed_body.ends_with(b"\r\n") {
+                if trimmed_body.pop().is_none() {
+                    return Err(Box::new(std::io::Error::new(
+                        std::io::ErrorKind::InvalidData,
+                        "Body does not contain a trailing CRLF before SHA padding",
+                    )));
+                }
+            }

Also applies to: 589-599

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

In `@src/cryptos.rs` around lines 523 - 533, The loop that strips SHA padding in
the body_str construction (working on variable trimmed_body) can underflow when
trimmed_body.len() < 2; change the logic to loop only while trimmed_body.len()
>= 2 and the last two bytes are not b"\r\n", popping bytes as you go, and after
the loop return an Err (parse error) if trimmed_body.len() < 2 or the final two
bytes are not CRLF; apply the same fix to the analogous loop around lines
589-599. Ensure you use trimmed_body.last() and trimmed_body.get(len-2) safely
by checking len >= 2 before accessing len-2 and preserve the existing Result
error type when returning the parse failure.

Comment thread src/regex/patterns.rs
Comment on lines +24 to +38
static ref FROM_ADDR_CONFIG: DecomposedRegexConfig = DecomposedRegexConfig {
parts: vec![
RegexPart::Pattern(r"(?i)from:.*<".to_string()),
RegexPart::PublicPattern((r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}".to_string(), 256)),
RegexPart::Pattern(r">".to_string()),
],
};

/// Pattern for extracting To header
static ref TO_ADDR_CONFIG: DecomposedRegexConfig = DecomposedRegexConfig {
parts: vec![
RegexPart::Pattern(r"(?i)to:.*<".to_string()),
RegexPart::PublicPattern((r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}".to_string(), 256)),
RegexPart::Pattern(r">".to_string()),
],
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle unbracketed From: / To: headers too.

These configs only match Display Name <addr> forms. Valid headers like From: alice@example.com and To: bob@example.com will return NoMatch, which then propagates into downstream parsing via src/parse_email.rs:6-9.

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

In `@src/regex/patterns.rs` around lines 24 - 38, Update FROM_ADDR_CONFIG and
TO_ADDR_CONFIG so they match both display-name-with-brackets and bare-address
forms; replace the surrounding fixed Pattern parts with versions that allow
optional angle-bracketed display names (e.g. change
RegexPart::Pattern(r"(?i)from:.*<") to something like r"(?i)from:\s*(?:.*<)?",
and change the trailing RegexPart::Pattern(r">") to an optional r"(?:>)?"), and
do the analogous change for the TO config; keep the existing
RegexPart::PublicPattern (the email capture) unchanged so DecomposedRegexConfig
still extracts the same group.

Comment thread src/regex/patterns.rs
Comment on lines +42 to +46
static ref SUBJECT_CONFIG: DecomposedRegexConfig = DecomposedRegexConfig {
parts: vec![
RegexPart::Pattern(r"(?i)subject:\s*".to_string()),
RegexPart::PublicPattern((r".+?(?=\r?\n\S|\r?\n\r?\n)".to_string(), 512)),
],
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Folded Subject: lines won't be captured completely.

.+? stops at the first physical line because . does not match \r / \n. RFC 5322 continuation lines (\r\n / \r\n\t) will be dropped even though this helper is named extract_subject_all_idxes.

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

In `@src/regex/patterns.rs` around lines 42 - 46, The SUBJECT_CONFIG's public
pattern (RegexPart::PublicPattern currently using r".+?(?=\r?\n\S|\r?\n\r?\n)")
stops at physical newlines and thus drops RFC5322 folded continuation lines;
update that pattern to allow CRLF followed by SP/HTAB as part of the subject
(for example replace the dot-plus with a construct that matches either
non-newline chars or CRLF followed by a space/tab, e.g. a repeated group like
(?:[^\r\n]|\r?\n[ \t])+ with the same lookahead and keep the existing 512 length
cap) so extract_subject_all_idxes captures folded Subject: lines fully in
SUBJECT_CONFIG / DecomposedRegexConfig / RegexPart::PublicPattern.

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.

5 participants