Skip to content

truncateBase64Content silently drops data-URI parameters due to repeated capture group #1819

Description

@Poojax21

Summary

In src/core/file/truncateBase64.ts, dataUriPattern captures data-URI parameters with a repeated capturing group (;[a-zA-Z0-9\-=]+)*. JavaScript regex semantics keep only the last iteration of a repeated capture group, so a data URI with two or more parameters loses all but the final one when the match is rebuilt in the replace callback. The truncation feature silently corrupts such URIs in the packed output instead of only shortening the base64 payload.

Static-analysis finding based on reading main; not executed here.

Location

  • File: src/core/file/truncateBase64.ts
  • Function: truncateBase64Content (pattern at lines 9-12, callback at lines 55-58)
  • Relevant code:
const dataUriPattern = new RegExp(
  `data:([a-zA-Z0-9\\/\\-\\+]+)(;[a-zA-Z0-9\\-=]+)*;base64,([A-Za-z0-9+/=]{40,})`,
  'g',
);
...
processedContent = processedContent.replace(dataUriPattern, (_match, mimeType, params, base64Data) => {
  const preview = base64Data.substring(0, TRUNCATION_LENGTH);
  return `data:${mimeType}${params || ''};base64,${preview}...`;
});

Problem

For input data:text/plain;a=1;b=2;base64,AAAA... (payload >= 40 chars):

  1. Group 1 (mimeType) = text/plain.
  2. The repeated group matches twice: first iteration consumes ;a=1, second consumes ;b=2. Per ECMAScript semantics, the group's captured value after the match is the last iteration: ";b=2". ;a=1 is matched but never retained anywhere.
  3. The literal ;base64, then matches, followed by the payload in group 3.
  4. The callback rebuilds the URI as data:text/plain;b=2;base64,<32 chars>... - ;a=1 has been deleted from the user's source content.

The function's contract (shorten long base64 payloads) implies everything outside the payload must be preserved byte-for-byte.

Trigger / Reproduction

Based on source reading:

  1. Run repomix with truncateBase64: true on any repository containing a file with a data URI carrying two parameters, e.g. data:image/png;charset=utf-8;name=x.png;base64,iVBORw0KGgoAAA....
  2. In the output bundle the URI becomes data:image/png;name=x.png;base64,... - the charset=utf-8 parameter is gone.

Expected Behavior

All parameters survive: data:image/png;charset=utf-8;name=x.png;base64,<truncated>....

Actual Behavior

Every parameter except the last is removed whenever more than one is present. Single-parameter URIs are unaffected, which explains why this has gone unnoticed.

Impact

Silent content corruption inside the generated pack: data URIs that consumers round-trip (e.g. embedded SVGs with charset + name, or URIs with vendor-specific flags) become invalid or semantically different after packing, with no warning. Since repomix output is often fed to LLMs/tools as ground truth, corrupted URIs propagate downstream.

Suggested Direction

Make the repeated group non-capturing - (?:;[a-zA-Z0-9\-=]+)* - and capture the full parameter run separately, e.g. wrap it: ((?:;[a-zA-Z0-9\-=]+)*). Then params holds every iteration concatenated, and no other change is needed.

Evidence

  • ECMAScript spec (RegExp [[Match]]): for a quantified capturing group, the final capture state is the one from the last successful iteration.
  • The rebuild path uses only mimeType, params, and base64Data; there is no other mechanism preserving the original prefix of the URI.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggood first issueGood for newcomers

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions