Skip to content

extract: private cache dir and per-file integrity manifest#74

Merged
JAORMX merged 2 commits intomainfrom
jaosorior/extract-cache-integrity
Apr 17, 2026
Merged

extract: private cache dir and per-file integrity manifest#74
JAORMX merged 2 commits intomainfrom
jaosorior/extract-cache-integrity

Conversation

@JAORMX
Copy link
Copy Markdown
Contributor

@JAORMX JAORMX commented Apr 17, 2026

Summary

Phase 6 of the hardening series. Two changes to the extract cache
(extract/bundle.go), which holds the spawned runner binary and
its shared libraries.

  1. Cache directory mode 0o700. Previously 0o755. The cache
    holds an executable that will later be spawned; world/group
    readability exposes the bundle and a writable sibling permits
    local code injection. Private-by-default is the correct posture.

  2. Per-file manifest replaces the .version marker. The old
    marker stored only the expected aggregate bundle hash, which
    isValid compared against a recomputed value. It did not
    re-read the extracted files — a cached binary that had been
    silently replaced or corrupted after extraction still looked
    valid on every subsequent Ensure.

    The new .manifest.json records the bundle version, the
    aggregate hash, and a SHA-256 per extracted artifact. isValid
    walks the manifest and re-hashes each file on disk; any
    mismatch triggers a re-extract. Re-extract removes the stale
    target dir first because os.Rename cannot replace an existing
    directory.

Commits are self-contained with tests. task verify green at
every commit.

Test plan

  • task verify green at every commit
  • New tests:
    • TestBundle_Ensure_CacheDirIsPrivate — cache dir mode is 0o700.
    • TestBundle_IsValid_TamperedFileTriggersReextract — a file
      modified after extraction is detected; a follow-up Ensure
      restores the original content.
  • Updated existing isValid tests that wrote .version files
    directly; they now go through Ensure to produce a valid
    manifest.
  • End-to-end via brood-box local replace: rebuilt bbox,
    VM boots, SSH + hooks round-trip clean.

🤖 Generated with Claude Code

JAORMX and others added 2 commits April 17, 2026 09:36
The extract cache holds the runner binary and its libraries — an
executable that will later be spawned as the VM runner subprocess.
A world-readable or group-writable cache directory permits a
same-host local attacker to read the bundle (minor) or, worse,
replace it before the next spawn (local code execution in the
invoking user's session). 0o700 keeps the cache contents
readable and writable only by the invoking user.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous .version marker only stored the expected aggregate
bundle hash; isValid compared the stored string to the recomputed
one. It did not re-read the extracted files, so a cached binary
or library that had been replaced or silently corrupted after
extraction was treated as valid on every subsequent Ensure.

Replace the marker with a JSON manifest listing the bundle
version, the aggregate hash, and a per-file SHA-256 of every
extracted artifact. On Ensure, isValid now walks the manifest
and re-hashes each file on disk; any mismatch triggers a
re-extract, which removes the stale target dir first since
os.Rename cannot replace an existing directory.

Tests cover the happy path (Ensure -> isValid), wrong bundle
hash, missing manifest, and — the new guarantee — a cached
file modified after extraction causes isValid to return false
and a follow-up Ensure to re-extract the original content.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JAORMX JAORMX merged commit f98a5da into main Apr 17, 2026
7 checks passed
@JAORMX JAORMX deleted the jaosorior/extract-cache-integrity branch April 17, 2026 06:41
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.

1 participant