Files
5990ff0324 feat(artifacts): read WeChat mini-program wxapkg containers (#8148)
* feat(artifacts): read WeChat mini-program wxapkg containers

Add a read-only reader for plaintext wxapkg packages: the 0xBE/0xED
18-byte big-endian header, the bounded name/offset/size member table,
and verbatim member bytes at absolute carrier offsets.

- Members retain declared names, absolute offsets and sizes, so
  source_location reports real carrier positions.
- V1MMWX PC-client packages are detected as encrypted coverage with
  null member facts; extraction refuses with a typed error and no
  decryption is attempted.
- Bounded table and directory budgets are admitted before allocation;
  malformed marks, truncated tables, invalid UTF-8 names, member
  extents inside the header or beyond the file, and counts that cannot
  fit their table are typed format or limit failures.
- Member reads stream in bounded 64 KiB chunks with carrier freshness
  checks; the reader joins outstanding streams before close and bounds
  active streams and repeated member I/O.

Refs #8121.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(artifacts): bound wxapkg members to the declared data region

Normalize the two indexLength conventions before the table bounds check
and the table read. Member ranges must then lie in that declared data
region, which has to fit in the file.

Record wxapkg format metadata as not compared: header facts repeat the
package bytes, and entry source paths keep a producer root prefix.

* fix(artifacts): choose the wxapkg index after member ranges fit

Four trailing bytes made the rows-only layout end on the file and dropped
the count-inclusive layout. Keep every admitted layout until the parsed
rows and member ranges select one.

---------

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-11-05 00:52:20 +08:00

4.4 KiB

WeChat mini-program wxapkg archives

inspect-artifact and extract-artifact read plaintext WeChat mini-program package (.wxapkg) containers without running WeChat or an unpacker. The plaintext form starts with a 0xBE mark and an 18-byte big-endian header: an unknown info field producers leave zero, the member-table byte length, the member-data byte length, a 0xED mark and the member count. Producers either count only the name/offset/size rows in that table length or include the four-byte member count as well; both declarations are accepted. The table region then carries one name/offset/size row per member; member bytes are stored verbatim at absolute file offsets.

Inspection retains the header facts: info, index and data lengths, member count and directory status. Members retain declared source paths, absolute carrier offsets and declared sizes, so source_location reports real file positions. Member reads only examine their declared range in bounded 64 KiB chunks. Carrier freshness is checked when a member is opened and after its stream completes. Reader close cancels and joins outstanding streams before releasing its descriptor.

Encrypted PC-client packages start with the V1MMWX magic instead of the plain header. They keep their format identity but report encrypted directory status with unknown coverage: member identities and bytes remain unknown and no decryption is attempted, so extraction refuses with a typed error rather than producing ciphertext. Declared tables or data regions that exceed the file, member extents outside the declared data region, truncated rows, invalid UTF-8 names, and counts that cannot fit their table are malformed-input errors.

Resource bounds

These are resource policies, not format maxima. The encoded member table is limited to 16 MiB and its buffer, parsed rows, retained entries and escaped path projections share a conservative 64 MiB admission budget, reserved before allocation; a declared member name is limited to 64 KiB. A reader admits four active member streams. The shared archive plaintext-work ceiling is the larger of 256 MiB and 500 times carrier size: every declared member extent is charged, including aliases that overlap one stored range. A separate member-I/O budget allows four times the larger of 1,024 and the sum of the member count and declared 64 KiB plaintext chunks, bounding repeated tiny-member reads independently of output bytes.

CLI

rea inspect-artifact app.wxapkg --detail summary \
  --evidence_output evidence/package.json --json
rea extract-artifact app.wxapkg --json

Detection is byte-based on the 0xBE/0xED marks or the V1MMWX magic, so .wxapkg, __APP__.wxapkg and extensionless cache files all classify as wxapkg containers. Compact delivery keeps complete Evidence in the requested bundle.

Verification scope

Synthetic fixtures cover plaintext headers, multiple members, empty members, chunked reads, unicode and dotted names, malformed marks, truncated tables, invalid UTF-8, table and directory-budget admission, member extents inside the header, the index, or beyond the declared data region, a data region declared past the file, a one-byte member whose table length includes the member count, an overstated first row consuming its successor, V1MMWX packages, cancellation inside member reads, paused iteration, close during I/O, cleanup retry, stream capacity and carrier freshness. Compiled full/compact MCP tests cover inventory and extraction.

Real producer verification used plaintext 0xBE packages from a local macOS WeChat mini-program cache (__APP__.wxapkg main packages and __PLUGINCODE__.wxapkg plugin packages across three distinct app directories): every member extracted byte-identical to its declared carrier range, including __plugin__/-namespaced members and the producer's root-prefixed (/name) member convention. A cached V1MMWX PC package reports the encrypted directory state with unknown member facts and refuses extraction.

wxml/wxss source recovery, compiled bundle semantics, subpackage relationships and runtime behavior are outside this container reader's verification scope.

Artifact comparison pairs members by their package-relative paths and compares their bytes. It does not compare wxapkg or wxapkg-entry metadata: header fields repeat the package bytes, index is layout, and source_path keeps a producer root prefix that the occurrence path strips.