Skip to content

Repo optimization: lifecycle & security fixes, release readiness, roadmap wave 1 - #18

Open
Liohtml wants to merge 4 commits into
mainfrom
claude/repo-optimization-maintenance-izvium
Open

Repo optimization: lifecycle & security fixes, release readiness, roadmap wave 1#18
Liohtml wants to merge 4 commits into
mainfrom
claude/repo-optimization-maintenance-izvium

Conversation

@Liohtml

@Liohtml Liohtml commented Jul 29, 2026

Copy link
Copy Markdown
Owner

What & why

Maintenance batch from a full repo audit (code audit + OSS-maturity audit + ecosystem research), in three commits:

1. Critical fixes

  • Detach no longer destroys the sandbox. The container now runs detached with the entry script as PID 1; the terminal attaches via docker exec. Previously the tmux attach client was PID 1, so Ctrl+b d killed the lead, all teammates and the Observer, and --rm deleted the container — the opposite of the documented behavior.
  • Teammates appear as tmux panes again. The lead launches with --teammate-mode tmux; since Claude Code v2.1.179 the upstream default is in-process, which silently disabled agentbox's signature split-pane layout (verified against the Agent Teams docs).
  • Hardening: Observer stored-XSS escaping + EADDRINUSE handling, entry script moved to a per-user 0700 temp dir (tamper/TOCTOU), fail-fast validation for --port/--memory/--cpus/unknown flags, rebuild refuses while the box runs, down reports real failures, ~/.claude.json symlink/directory edge cases, shell-free launcher (Windows injection), doctor uid warning.

2. Release readiness

  • Package renamed to @liohtml/agentbox — the bare npm name agentbox is an npm security placeholder and can't be published to; npx agentbox outside the repo was a footgun (README fixed).
  • tsx moved to runtime dependencies (the published package was unrunnable for end users), publishConfig with provenance, tag-triggered release workflow.
  • CI: Node 22+24 matrix + npm pack tarball smoke test; SECURITY.md (incl. the ~/.claude settings/hooks container-to-host escape channel), CODE_OF_CONDUCT, PR/issue templates, Dependabot, CHANGELOG, RELEASING (replaces PUBLISH.md, which contained a personal email).

3. Roadmap wave 1 (from ecosystem research, see ROADMAP.md)

  • --yolo (opt-in --dangerously-skip-permissions for the lead), persistent npm/cargo/pip cache volumes, read-only ~/.gitconfig + ~/.config/gh forwarding with gh in the image, --gpus passthrough, doctor image/host arch check.

Scope

  • src/cli/ — arg parsing, help, config, command router
  • src/docker/ — image build, container run/exec, mounts
  • src/session/ — tmux entry script, TTY sizing
  • assets/ — Dockerfile / Observer
  • src/types.ts / CONTRACT.md — contract change (explain below!)
  • Docs / CI / meta

Checklist

  • npm run typecheck passes
  • npm test passes (45 → 76 tests)
  • No build step; tsx intentionally became a runtime dependency so the published package works via npx (CONTRIBUTING's no-build constraint is unchanged)
  • Contract friction described: DockerModule gained execOk/imageArchitecture; runContainer is now detached; the entry script idles as PID 1 instead of exec tmux attach; new config fields yolo/gpus/gitconfigPath/ghConfigDir. CONTRACT.md §5/§6 updated accordingly.

Notes

Touches all modules because it consolidates an audit; the three commits are separable if needed. The weekly "Research digest" routine will file follow-up issues; remaining roadmap items are tracked in ROADMAP.md.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RfjQjysq5JF7gQCJB3RQtP


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added GPU passthrough and optional permission-skipping mode.
    • Added read-only Git and GitHub configuration mounts and persistent tool caches.
    • Improved tmux session handling, including reliable reattachment and detaching without stopping the sandbox.
    • Added architecture and workspace permission checks to doctor.
    • Added automated package publishing and release workflows.
  • Bug Fixes

    • Improved input and configuration validation.
    • Hardened Observer output against injected content and improved port-error handling.
  • Documentation

    • Updated README, changelog, security policy, usage contracts, roadmap, and contribution guidance.

claude added 4 commits July 29, 2026 17:40
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfjQjysq5JF7gQCJB3RQtP
…x + pack smoke test

- Rename package to @liohtml/agentbox (bare 'agentbox' on npm is a security
  placeholder and cannot be claimed); bin command stays 'agentbox'.
- Move tsx to dependencies so the published package runs via npx/global
  install without dev setup; add prepublishOnly guard + publishConfig
  (public access, provenance).
- CI: Node 22+24 matrix, npm pack smoke test that installs the tarball and
  runs the CLI as an end user; narrow push triggers to main.
- Add tag-triggered release workflow (npm publish --provenance).
- Add SECURITY.md (threat model + private reporting), CODE_OF_CONDUCT.md,
  PR template, issue-template config, dependabot config, CHANGELOG.md.
- Replace one-time PUBLISH.md (contained personal email) with generic
  RELEASING.md; README: badges + corrected npx instructions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfjQjysq5JF7gQCJB3RQtP
…launcher hardening

Consolidated fixes from a code audit + upstream research:

- Run the container detached with the entry script as PID 1 and attach via
  'docker exec tmux attach'. Previously the tmux attach client was PID 1, so
  Ctrl+b d (detach) killed the lead, all teammates and the Observer, and
  --rm deleted the container — the opposite of the documented behavior.
- Launch the lead with --teammate-mode tmux: since Claude Code v2.1.179 the
  default teammateMode is 'in-process', which silently disabled the
  split-pane team layout agentbox is built around.
- Fail fast with clear errors on invalid --port/--memory/--cpus, unknown
  flags, stray positionals, ':' in mount paths, and a nonexistent explicitly
  requested workspace (no more silently created empty mounts).
- rebuild refuses while the box is running (used to rebuild then silently
  reattach to the old container); reattach warns when tuning flags are
  ignored; stale stopped containers are removed before docker run.
- down surfaces real 'docker rm -f' failures instead of claiming success.
- ensureHostMounts: clear error when ~/.claude.json is a directory; follow
  symlinked configs (dotfile managers); benign EEXIST race handling.
- Entry script moves to a per-user 0700 temp subdir (was a fixed
  world-shared /tmp path: tamper/TOCTOU + collision); never write through a
  pre-existing symlink.
- Observer: HTML-escape project/session names (stored XSS), handle
  EADDRINUSE with a readable message, remain-on-exit for the observer tmux
  window, port sanity fallback, drop dead tail() code.
- Launcher: invoke local tsx via node directly (no shell) — survives paths
  with spaces, removes cmd.exe metacharacter injection on Windows.
- doctor: warn on host uid != 1000 (bind-mount write failures).
- Dockerfile: CLAUDE_CODE_VERSION/TSX_VERSION build args for reproducible
  builds.
- Contract & README updated; tests: 45 → 65 (new cli router suite, config
  validation, observer + session assertions).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfjQjysq5JF7gQCJB3RQtP
…rwarding, --gpus, arch check

- --yolo: launch the lead with --dangerously-skip-permissions (teammates
  inherit it). Strictly opt-in with a visible warning; documented under
  Security & trust.
- Persistent tool-cache named volumes (agentbox-npm-cache,
  agentbox-cargo-registry, agentbox-pip-cache): boxes stay --rm-disposable,
  package downloads survive. Mount points pre-created agent-owned in the
  image so first use isn't root-owned.
- Host identity forwarding: ~/.gitconfig and ~/.config/gh bind-mounted
  READ-ONLY when present; gh CLI installed in the image. Same philosophy as
  Claude auth: borrow host identity, never store it.
- --gpus passthrough to docker run.
- doctor: image-vs-host architecture check (warns about qemu-emulated
  images on e.g. Apple Silicon).
- docker run argv construction extracted as pure buildRunArgs() and covered
  by a new Docker-free test suite (the audit's top missing-coverage item).
- Docs: README flags/mounts/security, CONTRACT mounts + cache volumes,
  ROADMAP wave 1 marked done, CHANGELOG. Tests: 65 → 76, all green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfjQjysq5JF7gQCJB3RQtP
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR hardens Agentbox configuration, Docker startup, tmux session management, Observer rendering, and CLI validation. It also adds package publishing metadata, CI and release workflows, dependency automation, security documentation, repository templates, and project documentation.

Changes

Agentbox runtime

Layer / File(s) Summary
Session and configuration contracts
src/types.ts, src/cli/config.ts, src/session/index.ts, tests/config.test.ts, tests/session.test.ts, CONTRACT.md, README.md
Adds GPU and yolo configuration, optional identity mounts, cache volumes, per-user entry-script paths, stricter validation, and a detached PID 1 tmux lifecycle with Agent Teams support.
Docker execution and CLI flow
src/cli/*, src/docker/index.ts, tests/cli.test.ts, tests/docker.test.ts
Adds strict CLI input handling, Docker argument construction, conditional mounts, safer host-file preparation, startup polling, stale-container cleanup, and architecture and UID diagnostics.
Runtime assets and package execution
assets/Dockerfile, assets/observer.mjs, bin/agentbox.mjs, package.json, tests/observer.test.ts
Adds versioned image tools and GitHub CLI, prepares cache directories, changes local tsx invocation, hardens Observer port and HTML handling, and configures scoped npm publishing.

Repository delivery

Layer / File(s) Summary
Repository automation and release documentation
.github/*, CHANGELOG.md, SECURITY.md, RELEASING.md, ROADMAP.md, CODE_OF_CONDUCT.md
Adds issue and PR templates, Dependabot, Node 22/24 CI coverage, package smoke testing, tag-based provenance publishing, security reporting guidance, release instructions, changelog entries, and project roadmap documentation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AgentboxCLI
  participant Docker
  participant Tmux
  User->>AgentboxCLI: Run agentbox up with validated options
  AgentboxCLI->>Docker: Start detached container
  Docker->>Tmux: Create or reuse session
  AgentboxCLI->>Tmux: Poll for session readiness
  AgentboxCLI->>Tmux: Attach client to session
  User->>Tmux: Detach client
  Tmux-->>Docker: Keep container PID 1 alive while session exists
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main themes of the PR: lifecycle/security hardening, release readiness, and roadmap updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/repo-optimization-maintenance-izvium

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 24-25: Disable persisted checkout credentials for every checkout
step: add persist-credentials: false to .github/workflows/ci.yml lines 24-25 and
46-47, and .github/workflows/release.yml lines 15 and 32.

In @.github/workflows/release.yml:
- Around line 11-22: The release gate must validate the packed artifact before
publication. In .github/workflows/release.yml lines 11-22, add the existing
clean-install npm pack smoke-test step after the source checks and before the
publish job or step; in RELEASING.md lines 25-26, retain the “all checks”
statement only after the release workflow’s smoke test is included.
- Around line 3-5: Update the release workflow’s tag-triggered publishing flow
to compare the pushed tag with v${package.json.version} before publishing. Add a
pre-publish assertion that fails on stale, mistyped, or otherwise mismatched
tags, while preserving the existing v* trigger.
- Around line 39-41: Align the release authentication strategy across
.github/workflows/release.yml (lines 39-41) and RELEASING.md (lines 34-35):
configure the npm publish step to use Trusted Publishing exclusively and update
the documentation accordingly, or remove/clarify the Trusted Publishing
documentation as unsupported while retaining NODE_AUTH_TOKEN-based publishing.
- Around line 16-20: Disable npm caching in both setup-node steps at
.github/workflows/release.yml lines 16-20 and 33-38. Remove the cache
configuration entirely, or configure caching without restore/write, while
preserving lockfile-based npm ci installation and the existing typecheck/test
and publish flows.

In `@assets/Dockerfile`:
- Around line 48-50: Update the CLAUDE_CODE_VERSION and TSX_VERSION
build-argument defaults in the Dockerfile to reviewed exact immutable versions
instead of latest, while preserving the arguments so upgrades remain intentional
overrides for the npm install command.

In `@bin/agentbox.mjs`:
- Around line 28-31: Remove the Windows shell-based global fallback from the
spawn logic in bin/agentbox.mjs, so process.argv.slice(2) is never passed with
shell: true or routed through cmd.exe. Use the local tsx runtime dependency
directly and fail clearly when it is unavailable, while preserving inherited
stdio and environment behavior.

In `@CHANGELOG.md`:
- Around line 61-64: Update the “Git identity + gh auth forwarding” changelog
entry to replace “never entering the image” with wording that clarifies
credentials are not baked into the image, while explicitly stating that
runtime-mounted credentials may remain readable inside the running container.

In `@CODE_OF_CONDUCT.md`:
- Around line 30-35: Update the “Enforcement” section of CODE_OF_CONDUCT.md to
name a maintainer contact or dedicated private channel for sensitive conduct
complaints, while retaining the GitHub issue option for non-sensitive matters.
Reserve the GitHub Security Advisories link specifically for security
vulnerability reports.

In `@src/cli/index.ts`:
- Around line 106-117: The reattach warning in the `up`/`rebuild` flow uses
invocation-time `config.yolo` instead of the existing container’s state. Persist
the startup yolo mode as a container label or environment marker when creating
the container, inspect that marker before reattaching, and base the warning on
the persisted state so both `up --yolo` and reattaching without `--yolo` report
accurately; update the related logic around `tuningFlagsGiven` as well.
- Around line 85-104: Restrict the startup validation block around
validateConfig to only the up and rebuild commands, so down remains usable with
invalid runtime settings. Update cmdDoctor to call validateConfig explicitly and
report every returned problem while retaining its checklist behavior. Keep the
explicit workspace existence check within the up/rebuild validation flow.

In `@src/cli/parser.ts`:
- Line 11: Update the argument parsing flow around VALUE_FLAGS to reject any
value-consuming flag, including --gpus, when it has no following value before
resolveConfig runs. Ensure bare flags produce the existing invalid-argument
behavior rather than being recorded as true and converted to undefined, while
preserving valid values for all flags in VALUE_FLAGS.

In `@src/docker/index.ts`:
- Around line 281-291: Update the symbolic-link handling block to inspect the
followed target with statSync and require isFile() before returning. If the
target exists but is not a regular file, reject it using the same corrective
guidance already used for invalid config targets; retain dangling-link creation
through fs.writeFileSync with flag "w".
- Around line 124-127: The Docker run configuration in src/docker/index.ts lines
124-127 must stop using automatic container removal so failed startup containers
and their logs remain available. Update the polling logic around the CLI startup
flow in src/cli/index.ts lines 214-222 to detect an early container exit, report
diagnostics from the retained container, and avoid waiting for the full timeout;
apply these changes using the relevant Docker argument construction and
container startup/polling symbols.

In `@src/session/index.ts`:
- Around line 136-147: The writeEntryScript path must not rely on a pre-existing
predictable temporary directory. Update the directory setup around mkdirSync so
it atomically creates a unique directory, or validates an existing directory is
owned by the current UID and has no group/world permissions before writing
entryScriptPath.

In `@src/types.ts`:
- Around line 75-82: Update the global flag surface in CONTRACT.md to include
the AgentboxConfig options yolo (--yolo) and gpus (--gpus), matching their
existing types and semantics. Keep the prose and machine-readable contract
entries consistent with each other and with the declarations in AgentboxConfig.

In `@tests/docker.test.ts`:
- Around line 59-65: Extend the “mounts workspace, claude dir/json, entry script
and observer” test to assert that the observer asset bind mount is included in
the mounts returned by buildRunArgs. Use the expected observerAsset source and
its corresponding container destination, preserving the existing assertions.

In `@tests/observer.test.ts`:
- Around line 60-62: Strengthen the test in “falls back to the default port when
OBSERVER_PORT is not a valid port” to verify the complete contract: integer
validation, inclusive 1–65535 bounds, and fallback to port 4317. Either match
the full validation expression in src or add cases covering invalid, zero, and
out-of-range values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 775f86f3-e039-4799-a1c8-57ca7b43ef3c

📥 Commits

Reviewing files that changed from the base of the PR and between 4f9cfc9 and f97c386.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (29)
  • .github/ISSUE_TEMPLATE/config.yml
  • .github/PULL_REQUEST_TEMPLATE.md
  • .github/dependabot.yml
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • CHANGELOG.md
  • CODE_OF_CONDUCT.md
  • CONTRACT.md
  • PUBLISH.md
  • README.md
  • RELEASING.md
  • ROADMAP.md
  • SECURITY.md
  • assets/Dockerfile
  • assets/observer.mjs
  • bin/agentbox.mjs
  • package.json
  • src/cli/config.ts
  • src/cli/help.ts
  • src/cli/index.ts
  • src/cli/parser.ts
  • src/docker/index.ts
  • src/session/index.ts
  • src/types.ts
  • tests/cli.test.ts
  • tests/config.test.ts
  • tests/docker.test.ts
  • tests/observer.test.ts
  • tests/session.test.ts
💤 Files with no reviewable changes (1)
  • PUBLISH.md

Comment thread .github/workflows/ci.yml
Comment on lines 24 to 25
- name: Checkout
uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Disable persisted checkout credentials across all workflows.

Each checkout retains the GitHub token while later steps execute repository or package code.

  • .github/workflows/ci.yml#L24-L25: set persist-credentials: false.
  • .github/workflows/ci.yml#L46-L47: set persist-credentials: false.
  • .github/workflows/release.yml#L15-L15: set persist-credentials: false.
  • .github/workflows/release.yml#L32-L32: set persist-credentials: false.
🧰 Tools
🪛 zizmor (1.28.0)

[warning] 24-25: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

📍 Affects 2 files
  • .github/workflows/ci.yml#L24-L25 (this comment)
  • .github/workflows/ci.yml#L46-L47
  • .github/workflows/release.yml#L15-L15
  • .github/workflows/release.yml#L32-L32
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 24 - 25, Disable persisted checkout
credentials for every checkout step: add persist-credentials: false to
.github/workflows/ci.yml lines 24-25 and 46-47, and
.github/workflows/release.yml lines 15 and 32.

Source: Linters/SAST tools

Comment on lines +3 to +5
on:
push:
tags: ["v*"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate the tag against the package version before publishing.

v* accepts arbitrary tags, and the workflow never checks that the tag equals v${package.json.version}. Add a pre-publish assertion to prevent stale or mistyped tags from publishing the wrong version.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 3 - 5, Update the release
workflow’s tag-triggered publishing flow to compare the pushed tag with
v${package.json.version} before publishing. Add a pre-publish assertion that
fails on stale, mistyped, or otherwise mismatched tags, while preserving the
existing v* trigger.

Comment on lines +11 to +22
checks:
name: typecheck & test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
- run: npm ci
- run: npm run typecheck
- run: npm test

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make the release artifact smoke test part of the release gate.

The release workflow publishes after source tests but does not validate the packed tarball.

  • .github/workflows/release.yml#L11-L22: add the clean-install npm pack smoke test before publication.
  • RELEASING.md#L25-L26: keep the “all checks” statement only after the release workflow runs that smoke test.
🧰 Tools
🪛 zizmor (1.28.0)

[warning] 15-15: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 16-16: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): this step

(cache-poisoning)

📍 Affects 2 files
  • .github/workflows/release.yml#L11-L22 (this comment)
  • RELEASING.md#L25-L26
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 11 - 22, The release gate must
validate the packed artifact before publication. In
.github/workflows/release.yml lines 11-22, add the existing clean-install npm
pack smoke-test step after the source checks and before the publish job or step;
in RELEASING.md lines 25-26, retain the “all checks” statement only after the
release workflow’s smoke test is included.

Comment on lines +16 to +20
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
- run: npm ci

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== release workflow =="
cat -n .github/workflows/release.yml

echo
echo "== cache-related entries =="
rg -n "cache: npm|actions/setup-node|npm ci|npm publish|npm_config_userconfig|NPM_TOKEN|NODE_AUTH_TOKEN|registry-url" .github/workflows/release.yml || true

echo
echo "== package files if present =="
fd -a '^(package\.json|package-lock\.json|npm-shrinkwrap\.json)$' . | sed 's#^\./##' | while read -r f; do echo "--- $f"; sed -n '1,160p' "$f"; done

Repository: Liohtml/agentbox

Length of output: 7654


🌐 Web query:

GitHub Actions actions/setup-node cache npm cache scope public fork pull request documentation

💡 Result:

When using GitHub Actions, pull requests from public forks present specific security considerations regarding cache access and modification [1][2]. By default, workflows triggered by a pull request—including those from public forks—can restore caches created in the base branch (such as the default main branch), but they cannot write to the base branch's cache [1][2]. Caches created by a workflow run triggered by a pull request are scoped to the merge ref (refs/pull/.../merge) [1][2]. This ensures that these caches are isolated and can only be restored by re-runs of that specific pull request [1][2]. Regarding actions/setup-node specifically: 1. Cache Poisoning Prevention: Because untrusted pull requests could theoretically modify caches to inject malicious dependencies, it is recommended to disable automatic dependency caching in publishing workflows or workflows with elevated privileges [3][4][5]. This is done by setting the input package-manager-cache to false [6][7][8]. 2. Read-Only Caching (Restore-Only): As of March 2026, an open pull request (#1508) for actions/setup-node introduces a cache-write input [9]. This input is designed to allow workflows to restore dependencies from the cache without writing back to it at the end of the run, effectively enabling a read-only cache mode [9]. When cache-write is set to false, the post-step cache save is skipped, which prevents untrusted pull requests from potentially poisoning the cache while still allowing them to benefit from existing cached dependencies [9]. Example usage provided in the pull request: - uses: actions/setup-node@v6 with: node-version: 20 cache: npm cache-write: ${{ github.event_name!= 'pull_request' }} If you are not using this proposed feature, standard practice for securing sensitive workflows is to explicitly disable automatic caching to avoid cache poisoning risks [3][4].

Citations:


Disable npm caching in the release workflow steps.

Both .github/workflows/release.yml#L16-L20 and #L33-L38 rely on actions/setup-node npm caching for jobs that install before typecheck/test and npm publish. Pin trusted dependencies via the lockfile and avoid cache restore/write in these sensitive jobs; with this version, use a cached npm setup without restore/write if needed, or remove caching entirely.

🧰 Tools
🪛 zizmor (1.28.0)

[error] 16-16: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): this step

(cache-poisoning)

📍 Affects 1 file
  • .github/workflows/release.yml#L16-L20 (this comment)
  • .github/workflows/release.yml#L33-L38
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 16 - 20, Disable npm caching in
both setup-node steps at .github/workflows/release.yml lines 16-20 and 33-38.
Remove the cache configuration entirely, or configure caching without
restore/write, while preserving lockfile-based npm ci installation and the
existing typecheck/test and publish flows.

Source: Linters/SAST tools

Comment on lines +39 to +41
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files matching release docs/workflows:\n'
git ls-files | rg '(^\.github/workflows/.*release.*\.ya?ml$|RELEASING\.md|package\.json|npm-shrinkwrap\.json|package-lock\.json)$' || true

printf '\nrelease workflow excerpt:\n'
if [ -f .github/workflows/release.yml ]; then
  nl -ba .github/workflows/release.yml | sed -n '1,180p'
fi

printf '\nRELEASING excerpt:\n'
if [ -f RELEASING.md ]; then
  nl -ba RELEASING.md | sed -n '1,90p'
fi

printf '\nSearch for NPM_TOKEN/Trusted Publishing/permissions in relevant files:\n'
rg -n "NPM_TOKEN|Trusted Publishing|id-token|provenance|npm publish|registry-url|publish" .github/workflows/release.yml RELEASING.md package.json package-lock.json npm-shrinkwrap.json 2>/dev/null || true

printf '\nPackage publishing metadata relevant to Trusted Publishing prerequisites:\n'
python3 - <<'PY'
import json
try:
    with open('package.json') as f:
        p=json.load(f)
    print(json.dumps({k:p.get(k) for k in ['name','version','repository','packageManager']}, indent=2))
except Exception as e:
    print("package.json missing/unreadable:", e)
PY

Repository: Liohtml/agentbox

Length of output: 334


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'release.yml excerpt:\n'
awk '{printf "%6d\t%s\n",NR,$0}' .github/workflows/release.yml | sed -n '1,180p'

printf '\nRELEASING.md excerpt:\n'
awk '{printf "%6d\t%s\n",NR,$0}' RELEASING.md | sed -n '1,90p'

printf '\nSearch relevant terms:\n'
grep -n "NPM_TOKEN|Trusted Publishing|id-token|provenance|npm publish|registry-url|publish" .github/workflows/release.yml RELEASING.md package.json package-lock.json npm-shrinkwrap.json 2>/dev/null || true

printf '\npackage.json metadata:\n'
python3 - <<'PY'
import json
try:
    with open('package.json') as f:
        p=json.load(f)
    print(json.dumps({k:p.get(k) for k in ['name','version','repository','packageManager','publishConfig','engines'] if k in p}, indent=2))
except Exception as e:
    print("package.json missing/unreadable:", e)
PY

Repository: Liohtml/agentbox

Length of output: 3243


Align release auth with the documented path.

RELEASING.md says Trusted Publishing is supported, but .github/workflows/release.yml still always seeds NODE_AUTH_TOKEN before npm publish. Either configure the workflow to use npm Trusted Publishing exclusively and update the docs, or keep Trusted Publishing documentation removed/clarified as unsupported.

📍 Affects 2 files
  • .github/workflows/release.yml#L39-L41 (this comment)
  • RELEASING.md#L34-L35
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 39 - 41, Align the release
authentication strategy across .github/workflows/release.yml (lines 39-41) and
RELEASING.md (lines 34-35): configure the npm publish step to use Trusted
Publishing exclusively and update the documentation accordingly, or
remove/clarify the Trusted Publishing documentation as unsupported while
retaining NODE_AUTH_TOKEN-based publishing.

Comment thread src/docker/index.ts
Comment on lines +281 to +291
if (target?.isSymbolicLink()) {
// Dotfile managers often symlink ~/.claude.json. Follow it: if the
// target exists we're done; if it dangles, create the target through
// the symlink (flag "w" — "wx" would fail with a confusing EEXIST on
// the link itself).
try {
fs.statSync(config.claudeJson);
} catch {
fs.writeFileSync(config.claudeJson, "{}\n", { flag: "w" });
}
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Verify that the symlink target is a regular file.

A symlink targeting a directory makes statSync succeed and then returns early, allowing Docker to mount a directory at ~/.claude.json. Check isFile() on the followed target and fail with the same corrective guidance otherwise.

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 288-288: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.writeFileSync(config.claudeJson, "{}\n", { flag: "w" })
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/docker/index.ts` around lines 281 - 291, Update the symbolic-link
handling block to inspect the followed target with statSync and require isFile()
before returning. If the target exists but is not a regular file, reject it
using the same corrective guidance already used for invalid config targets;
retain dangling-link creation through fs.writeFileSync with flag "w".

Comment thread src/session/index.ts
Comment on lines 136 to 147
function writeEntryScript(config: AgentboxConfig, tty: TtySize): string {
const script = buildEntryScript(config, tty);
mkdirSync(dirname(config.entryScriptPath), { recursive: true, mode: 0o700 });
// Never write through a pre-existing symlink at the target path.
try {
if (lstatSync(config.entryScriptPath).isSymbolicLink()) {
rmSync(config.entryScriptPath);
}
} catch {
// does not exist — fine
}
writeFileSync(config.entryScriptPath, script, { encoding: "utf8" });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not trust an existing predictable temp directory.

Recursive mkdirSync leaves a pre-created /tmp/agentbox-<uid> unchanged. Its owner could replace or race entry.sh, which is then executed inside the container. Atomically create a unique directory, or verify the existing directory is owned by the current UID and has no group/world permissions before writing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/session/index.ts` around lines 136 - 147, The writeEntryScript path must
not rely on a pre-existing predictable temporary directory. Update the directory
setup around mkdirSync so it atomically creates a unique directory, or validates
an existing directory is owned by the current UID and has no group/world
permissions before writing entryScriptPath.

Comment thread src/types.ts
Comment on lines +75 to +82
/**
* Launch the Claude lead with --dangerously-skip-permissions (teammates
* inherit it). Opt-in via --yolo — the disposable, resource-capped box is
* the environment that flag exists for, but it stays off by default.
*/
yolo: boolean;
/** Optional `docker run --gpus` value (e.g. "all", "device=0"). flag: --gpus. */
gpus?: string;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the locked CLI contract for --yolo and --gpus.

AgentboxConfig now exposes both flags, but CONTRACT.md’s global flag surface omits them. The prose and machine-readable contract explicitly require agreement.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/types.ts` around lines 75 - 82, Update the global flag surface in
CONTRACT.md to include the AgentboxConfig options yolo (--yolo) and gpus
(--gpus), matching their existing types and semantics. Keep the prose and
machine-readable contract entries consistent with each other and with the
declarations in AgentboxConfig.

Comment thread tests/docker.test.ts
Comment on lines +59 to +65
test("mounts workspace, claude dir/json, entry script and observer", () => {
const mounts = valuesOf(buildRunArgs(baseConfig(), TTY, NONE), "-v");
assert.ok(mounts.includes(`/home/u/proj:${CONTAINER_WORKSPACE}`));
assert.ok(mounts.includes("/home/u/.claude:/home/agent/.claude"));
assert.ok(mounts.includes("/home/u/.claude.json:/home/agent/.claude.json"));
assert.ok(mounts.includes(`/tmp/agentbox-1000/entry.sh:${CONTAINER_ENTRY}`));
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Assert the observer bind mount named by this test.

The current assertions do not verify observerAsset, so removing that mount would go undetected.

Proposed assertion
   assert.ok(mounts.includes(`/tmp/agentbox-1000/entry.sh:${CONTAINER_ENTRY}`));
+  assert.ok(mounts.includes("/repo/assets/observer.mjs:/agentbox/observer.mjs"));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test("mounts workspace, claude dir/json, entry script and observer", () => {
const mounts = valuesOf(buildRunArgs(baseConfig(), TTY, NONE), "-v");
assert.ok(mounts.includes(`/home/u/proj:${CONTAINER_WORKSPACE}`));
assert.ok(mounts.includes("/home/u/.claude:/home/agent/.claude"));
assert.ok(mounts.includes("/home/u/.claude.json:/home/agent/.claude.json"));
assert.ok(mounts.includes(`/tmp/agentbox-1000/entry.sh:${CONTAINER_ENTRY}`));
});
test("mounts workspace, claude dir/json, entry script and observer", () => {
const mounts = valuesOf(buildRunArgs(baseConfig(), TTY, NONE), "-v");
assert.ok(mounts.includes(`/home/u/proj:${CONTAINER_WORKSPACE}`));
assert.ok(mounts.includes("/home/u/.claude:/home/agent/.claude"));
assert.ok(mounts.includes("/home/u/.claude.json:/home/agent/.claude.json"));
assert.ok(mounts.includes(`/tmp/agentbox-1000/entry.sh:${CONTAINER_ENTRY}`));
assert.ok(mounts.includes("/repo/assets/observer.mjs:/agentbox/observer.mjs"));
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/docker.test.ts` around lines 59 - 65, Extend the “mounts workspace,
claude dir/json, entry script and observer” test to assert that the observer
asset bind mount is included in the mounts returned by buildRunArgs. Use the
expected observerAsset source and its corresponding container destination,
preserving the existing assertions.

Comment thread tests/observer.test.ts
Comment on lines +60 to +62
test("falls back to the default port when OBSERVER_PORT is not a valid port", () => {
assert.match(src, /Number\.isInteger\(RAW_PORT\)/);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the complete fallback contract.

This only checks for Number.isInteger(RAW_PORT), so it passes if the 1–65535 bounds or the 4317 fallback are broken. Match the full validation expression or exercise invalid, zero, and out-of-range inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/observer.test.ts` around lines 60 - 62, Strengthen the test in “falls
back to the default port when OBSERVER_PORT is not a valid port” to verify the
complete contract: integer validation, inclusive 1–65535 bounds, and fallback to
port 4317. Either match the full validation expression in src or add cases
covering invalid, zero, and out-of-range values.

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.

2 participants