Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bde56b8
remove sed/awk/tar/env from VFS allowlist, block ANSI-C quoting
vlad-activeloop May 27, 2026
769fbc0
eliminate shell fallback in VFS hook, escape Grep pattern
vlad-activeloop May 27, 2026
9c47809
pin auto-update to exact discovered npm version
vlad-activeloop May 27, 2026
9dd2f4c
use npm ci in CI to enforce lockfile
vlad-activeloop May 27, 2026
445c912
mark backend notifications user-visible-only to prevent model injection
vlad-activeloop May 27, 2026
8b9ef58
scope 1password clawhub token to login step only
vlad-activeloop May 27, 2026
91e4469
edits
vlad-activeloop May 27, 2026
de7654f
edits
vlad-activeloop Jun 1, 2026
7c146e8
guide unroutable/unconfigured memory commands instead of host passthr…
vlad-activeloop Jun 1, 2026
c183f24
drop sed/awk from VFS retry guidance — they're no longer in the isSaf…
vlad-activeloop Jun 2, 2026
ecfca6b
deny unserviceable Read instead of command-shaped guidance (Read need…
vlad-activeloop Jun 2, 2026
0b1cf4e
codex: block unsafe memory commands instead of guide — guide exits 0 …
vlad-activeloop Jun 2, 2026
6480c90
pin npx/unknown manual-install messages to the verified version inste…
vlad-activeloop Jun 2, 2026
d2625e7
rewrite only the memory-path argument in the interpreter fast-path, n…
vlad-activeloop Jun 2, 2026
4a9a66f
anchor the find -name matcher to the exact supported shape so suffixe…
vlad-activeloop Jun 2, 2026
299a5b8
assert the npm-install-failure manual hint is pinned, not @latest
vlad-activeloop Jun 2, 2026
d4c25b3
set persist-credentials:false on read-only checkout jobs so GITHUB_TO…
vlad-activeloop Jun 2, 2026
b874c1a
match the memory mount on path boundaries so siblings like memory-bac…
vlad-activeloop Jun 2, 2026
665efee
pin clawhub CLI to 0.18.0 before it consumes CLAWHUB_TOKEN
vlad-activeloop Jun 2, 2026
d0c65d9
stop rewriting interpreter reads to a host cat (leaked host files via…
vlad-activeloop Jun 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
with:
# Read-only job: don't leave GITHUB_TOKEN in .git/config.
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@v6.4.0
with:
node-version: 22

- name: Install dependencies
run: npm install
run: npm ci

- name: Run jscpd
# Threshold 7% is the current baseline (see .jscpd.json). The job
Expand Down Expand Up @@ -95,14 +98,17 @@ jobs:
# `git diff origin/<base>...HEAD` to detect touched src/ files.
# Default shallow checkout (depth=1) produces "no merge base".
fetch-depth: 0
# Read-only job (diff is local once history is fetched): don't leave
# GITHUB_TOKEN in .git/config.
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@v6.4.0
with:
node-version: 22

- name: Install dependencies
run: npm install
run: npm ci

- name: Build (typecheck + emit bundle artefacts)
# `build` runs `tsc && esbuild`, which is a strict superset of
Expand Down Expand Up @@ -357,6 +363,9 @@ jobs:
HIVEMIND_STRICT_POSTINSTALL: "1"
steps:
- uses: actions/checkout@v6.0.2
with:
# Read-only job: don't leave GITHUB_TOKEN in .git/config.
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@v6.4.0
Expand All @@ -369,7 +378,7 @@ jobs:
# tree-sitter.mjs exit 1 here, failing the job at the earliest
# possible step (instead of swallowing the warning and failing
# later in tsc with a confusing "Cannot find module" error).
run: npm install
run: npm ci

- name: Build (typecheck + emit bundle artefacts)
# Second backstop: if the strict-postinstall path somehow doesn't
Expand Down
18 changes: 14 additions & 4 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@ jobs:
# commit the reviewer never approved.
ref: ${{ needs.release.outputs.sha }}
fetch-depth: 1
# Publish reads a pinned SHA and never pushes; don't leave
# GITHUB_TOKEN in .git/config.
persist-credentials: false
Comment on lines +234 to +236
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Release checkout still persists the write-scoped token.

This hardens the publish checkout, but the release job's checkout at Lines 33-36 still leaves secrets.GITHUB_TOKEN in .git/config for the whole npm ci/build/commit sequence. That keeps the highest-privilege checkout in this workflow outside the hardening and widens exposure if any install/build step is compromised. Please set persist-credentials: false there too and inject auth only for the final git push.

Based on learnings, every actions/checkout step in .github/workflows should set persist-credentials: false explicitly.

🤖 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.yaml around lines 234 - 236, The release job's
actions/checkout step currently leaves secrets.GITHUB_TOKEN persisted in
.git/config during the entire npm ci/build/commit sequence; update the release
job's actions/checkout invocation to include persist-credentials: false (same
hardening as the publish checkout) and ensure every other actions/checkout in
this workflow also sets persist-credentials: false; then add a dedicated step
right before the final git push that temporarily injects auth (e.g., set up auth
or re-run actions/checkout with credentials or configure git remote using the
token) so only the push step has write access.


- name: Setup Node.js
uses: actions/setup-node@v6.4.0
Expand All @@ -246,9 +249,10 @@ jobs:
cache: "npm"

- name: Load secrets from 1Password
id: op_secrets
uses: 1Password/load-secrets-action@v4.0.0
with:
export-env: true
export-env: false
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
CLAWHUB_TOKEN: "op://GitHub Actions/hivemind/CLAWHUB_TOKEN"
Expand Down Expand Up @@ -316,13 +320,19 @@ jobs:
npm publish --provenance --access public

- name: Install ClawHub CLI
run: npm install -g clawhub
# Pin the CLI version: it runs immediately before `clawhub login --token`
# with the real CLAWHUB_TOKEN in env, so a floating `@latest` would let an
# unexpected publish read/exfiltrate the token. Bump deliberately.
run: npm install -g clawhub@0.18.0
Comment on lines +323 to +326
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mirror the ClawHub pin into the smoke-test workflow too.

This step is pinned now, but .github/workflows/publish-smoke-test.yaml:77-84 still does npm install -g clawhub immediately before clawhub login --token "$CLAWHUB_TOKEN". That leaves the same pre-auth supply-chain window open in the dry-run canary and makes the two publish paths diverge. Please pin the same clawhub@0.18.0 there as well.

🤖 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.yaml around lines 323 - 326, Update the smoke-test
workflow step that currently runs "npm install -g clawhub" (the step just before
clawhub login) to pin the CLI to the same version used in release.yaml by
changing the install to "npm install -g clawhub@0.18.0"; locate the install step
in publish-smoke-test.yaml (the run command immediately before the clawhub login
--token invocation) and replace the floating install with the fixed version to
ensure both workflows use the same pinned CLI.


- name: Authenticate ClawHub CLI
# `clawhub login --token` writes a credential file inside the
# runner's $HOME, which is ephemeral and discarded when the job
# ends. The token only ever appears as ${{ secrets.* }}, which
# GitHub auto-masks in logs.
# ends. The token is scoped to this step only (not exported to all
# steps via export-env) to limit the blast radius if any build step
# is compromised.
env:
CLAWHUB_TOKEN: ${{ steps.op_secrets.outputs.CLAWHUB_TOKEN }}
run: clawhub login --token "$CLAWHUB_TOKEN" --no-browser

- name: Publish openclaw bundle to ClawHub
Expand Down
159 changes: 159 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 9 additions & 5 deletions src/cli/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ export async function runUpdate(opts: UpdateOptions = {}): Promise<number> {
switch (detected.kind) {
case "npm-global": {
if (opts.dryRun) {
log(`(dry-run) Would run: npm install -g ${PKG_NAME}@latest`);
log(`(dry-run) Would run: npm install -g ${PKG_NAME}@${latest}`);
log(`(dry-run) Would re-run: hivemind install --skip-auth`);
return 0;
}
Expand All @@ -297,10 +297,14 @@ export async function runUpdate(opts: UpdateOptions = {}): Promise<number> {
try {
log(`Upgrading via npm…`);
try {
spawn("npm", ["install", "-g", `${PKG_NAME}@latest`]);
// Pin to the exact version fetched from the registry rather than
// re-resolving `@latest` at install time — avoids a race where a
// new (potentially malicious) publish lands between the version
// check and the install.
spawn("npm", ["install", "-g", `${PKG_NAME}@${latest}`]);
} catch (e: any) {
warn(`npm install failed: ${e.message}`);
warn(`Try running it manually: npm install -g ${PKG_NAME}@latest`);
warn(`Try running it manually: npm install -g ${PKG_NAME}@${latest}`);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return 1;
}
log(``);
Expand Down Expand Up @@ -336,7 +340,7 @@ export async function runUpdate(opts: UpdateOptions = {}): Promise<number> {
log(``);
log(`Or install globally so future updates are one command:`);
log(``);
log(` npm install -g ${PKG_NAME}@latest`);
log(` npm install -g ${PKG_NAME}@${latest}`);
return 0;
}

Expand All @@ -358,7 +362,7 @@ export async function runUpdate(opts: UpdateOptions = {}): Promise<number> {
return 0;
}
warn(`Could not determine how hivemind was installed (path: ${detected.installDir}).`);
warn(`Update manually: npm install -g ${PKG_NAME}@latest`);
warn(`Update manually: npm install -g ${PKG_NAME}@${latest}`);
return 1;
}
}
Expand Down
Loading
Loading