Files
rea/docs/wxapkg.md
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

85 lines
4.4 KiB
Markdown

# 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
```sh
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.