Skip to content

fix(coding-agent): verify pinned fd, ripgrep, and uv downloads - #2173

Draft
kevinjosethomas wants to merge 1 commit into
mainfrom
eng-5343-helper-uv-verification
Draft

fix(coding-agent): verify pinned fd, ripgrep, and uv downloads#2173
kevinjosethomas wants to merge 1 commit into
mainfrom
eng-5343-helper-uv-verification

Conversation

@kevinjosethomas

@kevinjosethomas kevinjosethomas commented Sep 9, 2026

Copy link
Copy Markdown
Member

Context

tools-manager.ts resolved the latest fd/ripgrep release from the GitHub API and installed whatever the asset contained into ~/.prime/agent/bin with no digest check; an interrupted download left a partial <asset>.tar.gz in the bin dir, and archive containment relied on GNU tar's defaults. bootstrap.ts installed uv with sh -c "curl -LsSf https://astral.sh/uv/install.sh | sh". Supply-chain exposure, not evidence of upstream compromise.

Root cause: no pinned versions, no integrity check, download and extraction happened directly in the install directory.

Changes

  • src/utils/helper-tool-releases.ts (new): pins fd 10.5.0, ripgrep 15.2.0, uv 0.12.9 with bundled SHA-256 digests per supported asset. ripgrep and uv digests cross-checked against their published .sha256 files; fd publishes none, so its digests were computed from the GitHub release assets. uv 0.12.9 chosen over 0.12.10-12 to respect the 7-day release age.
  • src/utils/helper-tool-install.ts (new): shared installer. Downloads into a staging dir under the destination, hashes the stream and rejects on mismatch before extraction, lists tar members (tar -tPzf) and validates each (no absolute paths, no ..), applies the same check plus symlink rejection to zip entries via extract-zip's onEntry, verifies the extracted binary is a regular file inside the staging dir, runs the caller's --version check on the staged binary, then renames it into place. The staging dir is removed on every exit path, so failures leave nothing behind. Also enforces HTTPS and a size cap.
  • src/utils/tools-manager.ts: uses the installer; no more GitHub API "latest" lookup.
  • src/core/kernel/bootstrap.ts: ensureUv downloads the pinned uv release archive through the same installer into ~/.prime/agent/bin/uv (also checked on later runs, alongside PATH and ~/.local/bin). No shell is spawned. PRIME_AGENT_INSTALL_UV=1/0 and the interactive confirmation keep their semantics; prompt and error text now name the pinned version.
  • scripts/pin-helper-tools.ts (new): maintainer script to recompute digests for a new version and cross-check upstream checksums.
  • docs/rlm-runtime.md: documents the uv/helper provisioning path.
  • Tests: test/helper-tool-install.test.ts, test/kernel-bootstrap-uv.test.ts (new, stubbed fetch, hand-built tar.gz/zip fixtures in test/archive-fixtures.ts), test/tools-manager.test.ts updated for the pinned flow.

Model-facing surface unchanged.

Validation

Local: npm run check clean; tools-manager, helper-tool-install, kernel-bootstrap-uv, kernel-bootstrap, kernel-bootstrap-windows test files pass (57 tests). Real download on darwin/arm64 through the new path installed fd 10.5.0 and rg 15.2.0 with digests verified.

Prime Sandbox (node:24-bookworm, GNU tar 1.34, user tester, fresh HOME):

  • Before (main @ 427ea4c, ENG-5343 fixture eng5343-helper.test.ts): 4/4 vulnerable assertions pass — tampered archive installed as fd (echo 'tampered-fd 9.9.9 MARKER-5343'), requests were api.github.com/.../releases/latest + asset with no checksum fetch, interrupted download left fd-v9.9.9-x86_64-unknown-linux-gnu.tar.gz in the bin dir, uv bootstrap ran sh -c curl -LsSf https://astral.sh/uv/install.sh | sh.
  • After (this branch, same fixture): 3/4 fail as intended — tampered asset rejected with SHA-256 mismatch for fd-v10.5.0-x86_64-unknown-linux-gnu.tar.gz, interrupted download leaves [], fake sh never invoked; traversal containment still holds.
  • After: the five test files above pass in the sandbox (57 passed, 1 skipped).
  • After, real network: ensureToolWithStatus("fd"/"rg") installed fd 10.5.0 and ripgrep 15.2.0 into ~/.prime/agent/bin; ensureKernelPython with no uv on PATH and PRIME_AGENT_INSTALL_UV=1 downloaded uv 0.12.9 (uv 0.12.9 (x86_64-unknown-linux-gnu)), verified it, and bootstrapped the kernel venv (Python 3.11.16, rlm importable).

Not validated live: Windows zip assets (digests for ripgrep/uv cross-checked against upstream .sha256; fd computed from GitHub), macOS x86_64 and Linux aarch64 assets (same provenance).

Linear: ENG-5343 — https://linear.app/primeintellect/issue/ENG-5343

Note

Verify pinned fd, ripgrep, and uv downloads with SHA-256 and safe extraction

  • Replaces dynamic latest-release API lookups and the shell-piped uv installer with a static pinned release registry covering fd 10.5.0, rg 15.2.0, and uv 0.12.9 across six supported Rust targets.
  • Adds a shared installer in helper-tool-install.ts that streams HTTPS downloads with a size cap, verifies SHA-256 during download, rejects unsafe archive members (absolute paths, parent traversal, zip symlinks), validates the extracted binary runs, then moves it into the managed bin directory and cleans staging on any failure.
  • Updates bootstrap.ts and tools-manager.ts to route uv, fd, and rg provisioning through this shared installer instead of bespoke download logic.
  • Adds pin-helper-tools.ts to recompute digests for all supported assets and cross-check against upstream checksum files.
  • Behavioral Change: unsupported platforms now fail fast via the shared UnsupportedPlatformError; uv is no longer installed by piping a remote shell script, and tools-manager no longer queries the GitHub latest-release API at runtime.

Macroscope summarized 2b69894.

Helper binaries were resolved from the latest GitHub release and installed
without any digest check, partial downloads stayed in ~/.prime/agent/bin, and
archive containment relied on the system tar. uv was installed by piping
https://astral.sh/uv/install.sh into sh.

Pin fd 10.5.0, ripgrep 15.2.0, and uv 0.12.9 with bundled SHA-256 digests,
download into a staging directory, verify the digest before extraction,
validate tar and zip member paths, run the version check on the staged binary,
and only then move it into place. uv now comes from the pinned GitHub release
archive through the same path; PRIME_AGENT_INSTALL_UV and the interactive
confirmation keep their semantics.

Linear: ENG-5343
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

Prime Agent performance — completed

PR 2b698944 compared with main f9c7e06b.
↓ improved · ↑ regressed · ≈ no clear change · — unavailable

Metric Main This PR Change Change % Result
Cold startup 3,488.6 ms 3,527.7 ms ≈ +39.1 ms +1.12% no clear change
Warm startup 2,004.7 ms 2,096.3 ms ≈ +91.6 ms +4.57% no clear change
Installation 36.37 s 36.80 s ≈ +0.42 s +1.16% no clear change
Compressed release artifacts 11.10 MB 11.11 MB ≈ +0.01 MB +0.10% no clear change
Installed footprint 597.35 MB 596.99 MB ≈ -0.36 MB -0.06% no clear change
Idle memory, summed RSS 1,133.14 MB 1,116.32 MB ≈ -16.82 MB -1.48% no clear change

Python runtime

Metric Main This PR Change Change % Result
Python kernel startup 136.0 ms 141.0 ms ≈ +5.0 ms +3.71% no clear change
Python cell round trip 0.574 ms 0.569 ms ≈ -0.005 ms -0.79% no clear change
Empty bash command 12.3 ms 12.2 ms ≈ -0.1 ms -1.18% no clear change
Bash git status 19.2 ms 18.6 ms ≈ -0.6 ms -2.97% no clear change
Bash 32 KiB output 13.6 ms 13.1 ms ≈ -0.6 ms -4.16% no clear change
35 cells / 9 shell calls 188.5 ms 190.2 ms ≈ +1.7 ms +0.92% no clear change
Python interrupt to done 1.566 ms 1.497 ms ≈ -0.068 ms -4.37% no clear change
Python state snapshot 26.8 ms 29.2 ms ≈ +2.4 ms +8.98% no clear change
Python state restore 396.4 ms 396.6 ms ≈ +0.2 ms +0.06% no clear change
Python idle RSS 34.03 MB 33.77 MB ≈ -0.26 MB -0.76% no clear change
Python RSS after pandas workload 96.95 MB 96.06 MB ≈ -0.89 MB -0.92% no clear change

Sandbox cost: ~$0.1101 — no inference calls.
Run, logs, and downloadable raw results

Methodology and samples

Main resolved at 2026-09-09T23:53:55.403373+00:00. Harness f9c7e06b.
Linux x64, 4 vCPU, 8 GB RAM, 20 GB disk; region us.
Image: node:24-bookworm@sha256:be23f54a88d34e8824c741b19b91064094f92c1c97b194144bfc8b50d67258e2.
Stock tools, skills, daemon, and Python bootstrap enabled; fresh homes and a fixed Git fixture.
Onboarding is dismissed; the editor starts without a selected model or submitted prompt.
Medians shown. Arrows require a 20% timing/memory change plus absolute floors and IQR.
These practical noise floors are not a statistical significance test.
Cold means stopped Prime processes; OS filesystem caches are not flushed.
No model requests or credentials. Installation excludes build/setup time.
Installer tarballs use loopback; npm/Python downloads use the network with fresh caches.
Artifact size counts release tarballs; footprint after first use includes registry packages.
MB is decimal. Summed RSS can double-count shared pages; PSS is recorded when available.
Provisioning, setup, and build durations are recorded separately in the raw results.
Kernel probes use the installed JSONL runtime, outside the TUI/TypeScript host.
Per trial: 50 Python cells, 5 calls per shell case, and one 35-cell mix (9 git status calls).
Cell/shell values are batch means; other runtime timings are single operations.
State fixture: a 10,000-row × 8-column integer DataFrame and a 10,000-integer list.
Restore runs in a fresh kernel, including pandas imports; kernel startup is excluded.
Kernel RSS covers the isolated Python process; loaded RSS follows the pandas workload.
Costs estimate full sandbox lifetimes at configured rates, including setup and build.
Budget target: $1; not a billing cap. Checks are informational.

Metric Main successful/attempted PR successful/attempted Main spread PR spread
Cold startup 10/10 10/10 IQR 268.3 ms IQR 208.3 ms
Warm startup 10/10 10/10 IQR 186.5 ms IQR 125.6 ms
Installation 3/3 3/3 range 5.29 s range 3.85 s
Compressed release artifacts 1/1 1/1
Installed footprint 1/1 1/1
Idle memory, summed RSS 10/10 10/10 IQR 39.97 MB IQR 16.42 MB
Python kernel startup 10/10 10/10 IQR 11.1 ms IQR 18.3 ms
Python cell round trip 10/10 10/10 IQR 0.118 ms IQR 0.054 ms
Empty bash command 10/10 10/10 IQR 1.4 ms IQR 0.6 ms
Bash git status 10/10 10/10 IQR 1.2 ms IQR 0.7 ms
Bash 32 KiB output 10/10 10/10 IQR 1.3 ms IQR 1.0 ms
35 cells / 9 shell calls 10/10 10/10 IQR 13.3 ms IQR 12.5 ms
Python interrupt to done 10/10 10/10 IQR 0.129 ms IQR 0.124 ms
Python state snapshot 10/10 10/10 IQR 1.8 ms IQR 5.1 ms
Python state restore 10/10 10/10 IQR 23.8 ms IQR 32.8 ms
Python idle RSS 10/10 10/10 IQR 3.36 MB IQR 1.94 MB
Python RSS after pandas workload 10/10 10/10 IQR 3.33 MB IQR 2.23 MB

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