Skip to content

feat(archive): unify TAR parsing in Rust and WASM - #229

Open
steipete wants to merge 3 commits into
mainfrom
steipete/amazing-hypatia-a3f17b
Open

feat(archive): unify TAR parsing in Rust and WASM#229
steipete wants to merge 3 commits into
mainfrom
steipete/amazing-hypatia-a3f17b

Conversation

@steipete

@steipete steipete commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Unify native and guarded JavaScript TAR parsing on one Rust core, compiled into the native binding and a bundled, import-free WASM module. TypeScript retains extraction policy and guarded JavaScript filesystem I/O; off, auto, and require retain their availability/containment meanings.

The old printable-ASCII PAX restriction compensated for genuine disagreement between node-tar and Rust tar: chunk-split UTF-8 could lose a pathname override, and embedded newlines could make effective payload sizes diverge. Both executors now consume byte-counted admitted member identities and payload ranges rather than asking a second TAR parser to reinterpret them.

Changes

  • Move framing, PAX/GNU metadata, path validation, mode decoding and member offsets into archive-core; use that same crate from native and archive-wasm.
  • Remove the independent TypeScript TAR interpreters and production tar::Archive traversal. npm tar and Rust tar remain development/fixture dependencies only.
  • Accept strict UTF-8/newline PAX paths and inert byte-counted xattrs, preserving BOM and numeric-looking filenames. Keep malformed records, duplicate/unknown/mixed metadata, structural NUL/invalid UTF-8, traversal, unsafe raw fallback fields, blocked links, collisions, limits and complete EOF validation fail-closed.
  • Stream admitted payload ranges through existing private staging/pinned writers and guarded publication. No whole decoded archive buffer or spool. WASM has a fixed 64 KiB inbox, one pending member event, isolated instances and a 256 MiB linear-memory ceiling.
  • Bundle the generated WASM asset and update build, prepack, CI, release-build prerequisites and omitted-optional consumer checks. Consumers need no compiler, install script or runtime download. ZIP and native-only zstd/bzip2 availability are unchanged.
  • Standardize malformed TAR mode fields on the former native zero fallback; preserve ordinary octal, absent/zero and supported GNU binary modes. Document the compatibility change.

Real producer compatibility and security

The live producer proof exposed another boundary: BSD tar's -czf - . stdout can include zero gzip-container padding, while file output omits it. Native now accepts that padding only after fully validated gzip members and only when every remaining physical byte is zero. The JavaScript route rejects nonzero bytes hidden after padding as well.

Independent review found an additional cross-chunk case: Node's final bytesWritten is a cumulative count, not a physical offset after a skipped padding gap. Serialized input tracking now preserves the first unused physical offset, and both pipelines join teardown on failure. Regression fixtures cover an empty following member in a later chunk, plus legitimate concatenation at the same boundaries. CRC, ISIZE, headers, truncation, physical input budgets and decoded TAR EOF checks remain enforced.

The producer smoke uses the actual bound-cwd/device/inode worker and unchanged /usr/bin/tar -czf - . stdout, not a recompressed or metadata-suppressed substitute. Valid PAX/GNU metadata still does not excuse a raw fallback field truncated inside a UTF-8 sequence.

Validation

  • Full Node 24 check: macOS arm64 7,756 passed / 80 platform-specific skips; fresh AWS Linux x64 7,755 passed / 81 skips. Builds, boundary lint, docs examples and package/public API checks passed.
  • Shared-core and native Rust suites passed on macOS and Linux, with normalized ASCII/Unicode boundary coverage. Clippy passed.
  • Independent black-box installed-package harness: 215/215 in off and 215/215 in require, on both macOS and Linux. Includes real system/npm producers, exact names and payloads, bounded reads, valid/invalid metadata, traversal, links, collisions, limits, ZIP smoke and empty destinations on pre-publication validation rejection. Native loading is verified; WASM runs from a neutral consumer environment.
  • Root-only npm/pnpm package smoke passes with normal optionals and all optionals omitted. Fresh matching native artifacts and bundled WASM are used; no generated binaries are committed.
  • Independent review: one concrete gzip-offset finding reproduced against the packed candidate, fixed, and re-reviewed; no remaining actionable P0–P2 findings.
  • git diff --check and workflow lint passed.

An initial concurrent macOS consumer run hit three 10-second extraction deadlines. The unchanged harness passed when rerun serially; no timeout, limit or assertion was widened.

CI follow-ups install the WASM target in all package-build jobs and provide the expected system-tar path in Alpine. A near-limit ASCII manifest case now avoids redundant Unicode normalization: the identical 32-member/33rd-member-rejection probe improved from a 1.70-second median to 0.17 seconds without changing limits or deadlines.

Final head 7bd7540ce0f016f4b4d23ec6fb4eec4f537836cc passed cross-platform CI and merged coverage, including Node 22/24, native Windows/macOS/Linux, musl and packed consumer checks. Benchmarks, CodeQL and the Windows hash-identity proof also passed.

Repository proof entrypoints:

CI=1 FS_SAFE_PAX_REQUIRE_NATIVE=1 pnpm check
pnpm native:test
pnpm package:smoke
pnpm archive:producer-smoke ./consumer off
pnpm archive:producer-smoke ./consumer require

Scope

This does not change OpenClaw's directory projection, archive-authorization listing, wire schemas or active checkouts. It proves real producers into the installed fs-safe API, not a live Gateway/model session. Cross-platform CI remains the source of Windows and other-target proof. No version bump, publication or release is included.

Share bounded TAR/PAX/GNU admission across native and portable WASM paths,
then replay admitted ranges through existing guarded filesystem writers.
Accept strict UTF-8 and newline PAX paths without downstream reinterpretation
while preserving raw-field, traversal, link, collision, byte and EOF checks.

Bundle the import-free WASM asset and keep native-off archives usable without
optional dependencies or a consumer compiler. Preserve gzip member integrity
while admitting only zero physical padding from system-tar stdout, retaining
the first unused input offset across chunks.

Verify real producer and installed-package behavior on macOS and Linux,
including the cross-chunk gzip regression, native/WASM parity, and omitted
optional-dependency installs. Document the common malformed-mode fallback.
@steipete
steipete requested a review from a team as a code owner September 5, 2026 00:14
@clawsweeper

clawsweeper Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

Add the portable parser target before benchmark, coverage collection and
Windows hash-proof builds. Keep runtime, permissions, checks and coverage
policy unchanged.
@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Sep 5, 2026
@clawsweeper

clawsweeper Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed September 4, 2026, 11:55 PM ET / September 5, 2026, 03:55 UTC.

ClawSweeper review

What this changes

The PR shares one Rust TAR parser between native and bundled WebAssembly extraction, expands supported metadata, validates gzip padding, and updates packaging, documentation, and tests.

Merge readiness

Blocked before merge - 6 items remain

This remains useful work that current main and v0.8.1 do not implement. No discrete blocking code defect was found, but compatibility acceptance and the previously requested behavior evidence remain unresolved.

Priority: P2
Reviewed head: 7bd7540ce0f016f4b4d23ec6fb4eec4f537836cc
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🦐 gold shrimp (3/6) The implementation is coherent and extensively covered, but the outstanding behavior and upgrade evidence limits readiness.
Proof confidence 🦐 gold shrimp (3/6) Needs stronger real behavior proof before merge: The captured body reports positive installed macOS/Linux off/require extraction and rejection through extractArchive/readArchiveEntry. It does not establish the requested v0.8.1-to-candidate permission observations for files and directories under clamp and preserve; aggregate producer results also remain short of the requested output. Unreachable Actions logs remain unknown, not evidence of absent execution. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Needs proof Needs stronger real behavior proof before merge: The captured body reports positive installed macOS/Linux off/require extraction and rejection through extractArchive/readArchiveEntry. It does not establish the requested v0.8.1-to-candidate permission observations for files and directories under clamp and preserve; aggregate producer results also remain short of the requested output. Unreachable Actions logs remain unknown, not evidence of absent execution. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed 12 items Policy and revision verified: Read the complete root AGENTS.md; no nested AGENTS.md or maintainer-notes directory was found. HEAD matches the pinned original PR head, whose raw recorded parent is 6896472. The checkout remained clean.
Still necessary on current main: Current main retains the optional npm TAR parser, separate TypeScript PAX admission, and native tar::Archive traversal. Its PAX validator still rejects Unicode structural values and embedded newlines; no equivalent shared implementation is established.
Latest release retains the previous parser: The supplied latest release, v0.8.1, still lists tar 7.5.22 as an optional runtime dependency. This PR's bundled-parser implementation is not established as shipped.
Findings None None.
Security None None.

How this fits together

fs-safe accepts untrusted archives and produces files beneath a guarded destination or returns a bounded entry buffer. The shared parser supplies validated names and payload ranges to the existing extraction policy and filesystem publication helpers.

flowchart TD
  A[Untrusted archive] --> B[Private staged input]
  B --> C[Native or WebAssembly TAR parser]
  C --> D[Path, filter and byte checks]
  D --> E[Reject invalid archive]
  D --> F[Replay admitted payload ranges]
  F --> G[Guarded filesystem publication]
  F --> H[Bounded entry buffer]
Loading

Decision needed

Question Recommendation
Should JavaScript TAR extraction adopt the native zero fallback for malformed modes despite changing permissions for previously accepted archives? Accept the common fallback after upgrade proof: Retain one decoding contract after reviewing installed old/new permission observations and explicitly accepting the documented transition.

Why: The change is documented and intentional, but code correctness cannot determine whether that compatibility cost is acceptable.

Before merge

  • Add real behavior proof - Needs stronger real behavior proof before merge: The captured body reports positive installed macOS/Linux off/require extraction and rejection through extractArchive/readArchiveEntry. It does not establish the requested v0.8.1-to-candidate permission observations for files and directories under clamp and preserve; aggregate producer results also remain short of the requested output. Unreachable Actions logs remain unknown, not evidence of absent execution. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • Resolve merge risk (P1) - Existing JavaScript consumers of malformed-mode archives can receive inaccessible files or directories under preserve, or lose file execute permission under clamp; the transition needs explicit compatibility acceptance.
  • Resolve merge risk (P1) - The linked Actions logs could not be inspected, leaving their detailed producer and upgrade coverage unknown.
  • Resolve merge risk (P1) - GitHub reports merge conflicts, so the eventual merged result still needs review.
  • Complete next step (P2) - Resolve the reported merge conflict and record the maintainer's malformed-mode compatibility decision after reviewing the permission comparison.
  • Resolve maintainer decision - Resolve the maintainer decision shown above before merge.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production and test LOC Production −287; tests +508 Parser consolidation reduces runtime source while adding coverage; counts include Rust test sections and exclude scripts, configuration, and documentation.

Merge-risk options

Maintainer options:

  1. Validate and accept the permission transition (recommended)
    Review installed v0.8.1/candidate file and directory results under clamp and preserve before accepting the common zero fallback.
  2. Preserve compatibility pending a separate decision
    Defer malformed-mode standardization if existing consumer permission behavior must remain unchanged.

Technical review

Best possible solution:

Keep one bounded TAR admission core and the existing publication guards, with demonstrated upgrade behavior and an explicitly accepted malformed-mode permission contract.

Do we have a high-confidence way to reproduce the issue?

Not applicable as a single bug reproduction: this is a parser consolidation and capability expansion. The permission transition is source-visible, but no reviewer-side runtime reproduction was executed.

Is this the best way to solve the issue?

The shared parser is a maintainable way to eliminate downstream reinterpretation while retaining existing filesystem policy. Accepting its malformed-mode compatibility change remains a maintainer choice.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 899f91b97d26.

Labels

Label justifications:

  • P2: This is a bounded archive compatibility improvement without an established urgent production regression.
  • merge-risk: 🚨 compatibility: Standardizing malformed modes changes the permissions produced for some archives accepted by the previous JavaScript backend.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The captured body reports positive installed macOS/Linux off/require extraction and rejection through extractArchive/readArchiveEntry. It does not establish the requested v0.8.1-to-candidate permission observations for files and directories under clamp and preserve; aggregate producer results also remain short of the requested output. Unreachable Actions logs remain unknown, not evidence of absent execution. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Evidence

What I checked:

  • Policy and revision verified: Read the complete root AGENTS.md; no nested AGENTS.md or maintainer-notes directory was found. HEAD matches the pinned original PR head, whose raw recorded parent is 6896472. The checkout remained clean. (AGENTS.md:1, 7bd7540ce0f0)
  • Still necessary on current main: Current main retains the optional npm TAR parser, separate TypeScript PAX admission, and native tar::Archive traversal. Its PAX validator still rejects Unicode structural values and embedded newlines; no equivalent shared implementation is established. (src/archive-tar-runtime.ts:52, 899f91b97d26)
  • Latest release retains the previous parser: The supplied latest release, v0.8.1, still lists tar 7.5.22 as an optional runtime dependency. This PR's bundled-parser implementation is not established as shipped. (package.json:164, 5872f54f62a7)
  • Admission remains connected to guarded publication: The new fallback completes admission before applying the existing entry checker, copies accepted ranges through the pinned writer, and uses the existing guarded merge. Native extraction similarly admits the full manifest before replay. Raw-path, blocked-link, collision, and final destination checks remain in the inspected paths. (src/archive-tar-extract.ts:22, 7bd7540ce0f0)
  • Deliberate permission compatibility change: Malformed mode fields now decode to zero. The unchanged permission policy preserves that zero or clamps files to 0644. The documentation explicitly acknowledges replacing JavaScript's previous prefix parsing, defaulting, or rejection. (archive-core/src/tar_mode.rs:28, 7bd7540ce0f0)
  • Permission validation does not establish upgrade behavior: The added malformed-mode cases check candidate regular files under preserve. They do not compare installed v0.8.1 and candidate outputs for files and directories under both preserve and clamp, as requested previously. (test/archive-tar-mode-fields.test.ts:84, 7bd7540ce0f0)

Likely related people:

  • Peter Steinberger: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • Yiğit ERDOĞAN: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Identify existing installed-package producer/rejection output showing backend selection, payload results, and an empty destination on rejection; a precise artifact reference can satisfy this.
  • Provide installed v0.8.1/candidate permission observations for files and directories under clamp and preserve, and record acceptance of the transition.
  • Redact private details from evidence and update the PR body for automatic re-review; if it does not trigger, ask a maintainer to comment @clawsweeper re-review.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (3 earlier review cycles)
  • reviewed 2026-09-05T00:31:58.119Z sha 6896472 :: needs real behavior proof before merge. :: none
  • reviewed 2026-09-05T00:50:59.171Z sha 7bd7540 :: needs real behavior proof before merge. :: none
  • reviewed 2026-09-05T01:01:33.143Z sha 7bd7540 :: needs real behavior proof before merge. :: none

Keep ASCII component length validation equivalent to NFC/NFD without
performing redundant Unicode normalization. Preserve the 255-byte boundary,
Unicode normalization checks, path restrictions and manifest budgets.
The identical 32-member near-limit WASM probe retains its overflow point
while running about ten times faster.

Install GNU tar in the Alpine validation container and provide the expected
/usr/bin/tar executable path rather than skipping the bound producer proof.
No test timeout, resource limit or coverage threshold is increased.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P2 Normal priority bug or improvement with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant