From: "Michael S. Tsirkin" <mst@redhat.com>
To: Konstantin Ryabitsev <mricon@kernel.org>
Cc: users@kernel.org, tools@kernel.org
Subject: Re: b4 review available in master
Date: Sat, 28 Feb 2026 16:04:24 -0500 [thread overview]
Message-ID: <20260228155515-mutt-send-email-mst@kernel.org> (raw)
In-Reply-To: <20260228-sceptical-fierce-swift-db217c@lemur>
On Sat, Feb 28, 2026 at 03:53:40PM -0500, Konstantin Ryabitsev wrote:
> On Sat, Feb 28, 2026 at 03:47:51PM -0500, Michael S. Tsirkin wrote:
> > > Bah, that "--" was needed for Claude. /o\
> >
> > at least for me, claude works without --. shrug.
>
> Yes, but that's without passing --allowedTools, which takes a really complex
> format with a ton of spaces. I may deprecate that in lieu of shipping policy
> files instead.
Since we are discussing this, I thought I'd share the following, written
a while ago with help from claude. I know I sleep better with this than
with running random stuff from the intertubes directly.
-->
review: add sandbox wrappers and docs for AI review agents
Add bwrap and firejail wrapper scripts that sandbox review agents
against prompt injection in untrusted patches. Document agent
configuration including a bunch of agents.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
diff --git a/misc/bwrap-review-agent.sh b/misc/bwrap-review-agent.sh
new file mode 100755
index 0000000..460a21d
--- /dev/null
+++ b/misc/bwrap-review-agent.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+# bwrap-review-agent.sh -- bubblewrap sandbox for b4 review agents
+#
+# Full filesystem isolation: the entire filesystem is mounted
+# read-only, real HOME is replaced with a temporary copy containing
+# only agent auth config, and .git/b4-review/ is the sole writable
+# path. Agent binaries installed under HOME (npm, pip, cargo) are
+# re-exposed read-only.
+#
+# Requires: bubblewrap (bwrap)
+#
+# Usage -- set as your review-agent-command in git config:
+#
+# [b4]
+# review-agent-command = misc/bwrap-review-agent.sh cursor-agent --yolo
+# review-agent-prompt-path = .git/agent-reviewer.md
+
+set -euo pipefail
+
+mydir="$(cd "$(dirname "$0")" && pwd)"
+# shellcheck source=review-agent-sandbox-lib.sh
+. "$mydir/review-agent-sandbox-lib.sh"
+
+if ! command -v bwrap >/dev/null 2>&1; then
+ echo >&2 "bwrap-review-agent: bwrap not found, running agent without sandbox"
+ export HOME="$sandbox_home"
+ exec "$@"
+fi
+
+# Mount layout (later mounts override earlier ones):
+#
+# / read-only entire host filesystem
+# /dev read-write minimal private devtmpfs
+# /proc read-only procfs
+# /tmp read-write private tmpfs (empty, ephemeral)
+# $real_home read-write sandbox_home replaces real HOME;
+# ~/.ssh, ~/.gnupg, etc. are invisible
+# $real_home/.local/ read-only agent binaries (pip, pipx installs)
+# $real_home/.npm-*/ read-only agent binaries (npm global installs)
+# $real_home/.cargo/ read-only agent binaries (cargo installs)
+# $real_home/.nvm|... read-only node version managers
+# $topdir read-only the git repository
+# $review_dir read-write .git/b4-review/ -- review output
+#
+bwrap_args=(
+ --ro-bind / / # base: everything read-only
+ --dev /dev # private /dev
+ --proc /proc # procfs
+ --tmpfs /tmp # private /tmp
+ --bind "$sandbox_home" "${real_home}" # replace HOME with sandbox copy
+ --ro-bind "$topdir" "$topdir" # repo read-only
+ --bind "$review_dir" "$review_dir" # review output writable
+)
+
+# Re-expose directories under real HOME that contain agent binaries
+# and their libraries/runtimes (symlinks often point into lib dirs).
+# These are mounted read-only on top of the sandbox HOME.
+for d in \
+ .local/bin .local/share/cursor-agent \
+ .npm-global \
+ .cargo/bin \
+ .nvm .fnm .volta \
+; do
+ p="${real_home}/$d"
+ if [ -d "$p" ]; then
+ bwrap_args+=(--ro-bind "$p" "$p") # agent binary dir, read-only
+ fi
+done
+
+exec bwrap "${bwrap_args[@]}" -- "$@"
diff --git a/misc/firejail-review-agent.sh b/misc/firejail-review-agent.sh
new file mode 100755
index 0000000..aa8093f
--- /dev/null
+++ b/misc/firejail-review-agent.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+# firejail-review-agent.sh -- firejail sandbox for b4 review agents
+#
+# The repository and real HOME are mounted read-only. HOME env is
+# redirected to a temporary copy containing only agent auth config.
+# .git/b4-review/ is the sole writable path in the repository.
+#
+# Requires: firejail
+#
+# Note: firejail cannot fully hide the real HOME from the agent
+# (read-only != invisible). For stronger isolation, prefer
+# bwrap-review-agent.sh which replaces HOME entirely.
+#
+# Usage -- set as your review-agent-command in git config:
+#
+# [b4]
+# review-agent-command = misc/firejail-review-agent.sh cursor-agent --yolo
+# review-agent-prompt-path = .git/agent-reviewer.md
+
+set -euo pipefail
+
+mydir="$(cd "$(dirname "$0")" && pwd)"
+# shellcheck source=review-agent-sandbox-lib.sh
+. "$mydir/review-agent-sandbox-lib.sh"
+
+if ! command -v firejail >/dev/null 2>&1; then
+ echo >&2 "firejail-review-agent: firejail not found, running agent without sandbox"
+ export HOME="$sandbox_home"
+ exec "$@"
+fi
+
+# Mount layout:
+#
+# $real_home read-only real HOME (still visible but not writable;
+# ~/.ssh, ~/.gnupg are readable -- use bwrap
+# wrapper if this is a concern)
+# $topdir read-only the git repository
+# $review_dir read-write .git/b4-review/ -- review output
+# /tmp read-write private tmpfs (empty, ephemeral)
+# /dev read-write minimal private devtmpfs
+# HOME env sandbox_home agent looks here for config by default
+#
+# Capabilities: all dropped. No new privileges, no root.
+#
+export HOME="$sandbox_home"
+firejail --noprofile \
+ --read-only="${real_home}" \
+ --read-only="$topdir" \
+ --read-write="$review_dir" \
+ --private-tmp \
+ --private-dev \
+ --caps.drop=all \
+ --nonewprivs \
+ --noroot \
+ -- "$@"
diff --git a/misc/review-agent-sandbox-lib.sh b/misc/review-agent-sandbox-lib.sh
new file mode 100644
index 0000000..df588ff
--- /dev/null
+++ b/misc/review-agent-sandbox-lib.sh
@@ -0,0 +1,44 @@
+# review-agent-sandbox-lib.sh -- shared setup for sandbox wrappers
+#
+# Sourced by firejail-review-agent.sh and bwrap-review-agent.sh.
+# Not executable on its own.
+#
+# After sourcing, the following variables are set:
+# topdir -- repository top-level directory
+# review_dir -- .git/b4-review/ (writable review output)
+# real_home -- the user's real HOME
+# sandbox_home -- temporary HOME with only agent auth config
+#
+# A trap is registered to clean up sandbox_home on exit.
+
+topdir="$(git rev-parse --show-toplevel)"
+review_dir="$topdir/.git/b4-review"
+mkdir -p "$review_dir"
+real_home="${HOME}"
+
+# Create a temporary HOME containing only agent auth config.
+# The agent process sees this as $HOME, so ~/.ssh, ~/.gnupg, etc.
+# simply do not exist.
+mkdir -p "$review_dir/.sandbox"
+sandbox_home="$(mktemp -d --tmpdir="$review_dir/.sandbox")"
+trap 'rm -rf "$sandbox_home"' EXIT INT TERM
+
+# Copy top-level config files (auth, settings) for known agents.
+# Only files under 1 MB are copied -- large session logs and caches
+# are skipped since the agent only needs credentials.
+for d in \
+ .config/cursor .config/Cursor .cursor \
+ .config/claude .claude .local/share/claude .local/state/claude \
+ .codex \
+ .gemini .config/gemini \
+; do
+ src="${real_home}/$d"
+ if [ -d "$src" ]; then
+ mkdir -p "$sandbox_home/$d"
+ find "$src" -maxdepth 1 -type f -size -1024k \
+ -exec cp -a {} "$sandbox_home/$d/" \;
+ elif [ -e "$src" ] || [ -L "$src" ]; then
+ mkdir -p "$(dirname "$sandbox_home/$d")"
+ cp -a "$src" "$sandbox_home/$d"
+ fi
+done
next prev parent reply other threads:[~2026-02-28 21:04 UTC|newest]
Thread overview: 69+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-27 19:53 b4 review available in master Konstantin Ryabitsev
2026-02-28 15:12 ` Mark Brown
2026-02-28 15:47 ` Konstantin Ryabitsev
2026-02-28 16:00 ` Konstantin Ryabitsev
2026-02-28 18:12 ` Mark Brown
2026-02-28 15:53 ` Mark Brown
2026-02-28 21:11 ` Mark Brown
2026-03-03 5:14 ` Konstantin Ryabitsev
2026-03-03 12:42 ` Mark Brown
2026-03-03 18:21 ` Konstantin Ryabitsev
2026-03-12 17:21 ` Alexandre Belloni
2026-03-13 15:42 ` Konstantin Ryabitsev
2026-03-13 15:55 ` Alexandre Belloni
2026-03-21 10:01 ` Alexandre Belloni
2026-03-12 17:35 ` Mark Brown
2026-03-13 15:42 ` Konstantin Ryabitsev
2026-02-28 16:36 ` Conor Dooley
2026-02-28 16:45 ` Konstantin Ryabitsev
2026-02-28 16:48 ` Conor Dooley
2026-02-28 16:57 ` Konstantin Ryabitsev
2026-02-28 17:00 ` Conor Dooley
2026-02-28 17:05 ` Konstantin Ryabitsev
2026-02-28 17:12 ` Conor Dooley
2026-02-28 17:21 ` Konstantin Ryabitsev
2026-02-28 17:34 ` Konstantin Ryabitsev
2026-02-28 18:37 ` Conor Dooley
2026-02-28 22:16 ` Conor Dooley
2026-02-28 22:32 ` Conor Dooley
2026-03-03 5:16 ` Konstantin Ryabitsev
2026-03-04 21:38 ` Conor Dooley
2026-03-04 22:40 ` Konstantin Ryabitsev
2026-03-04 22:55 ` Conor Dooley
2026-03-05 3:26 ` Konstantin Ryabitsev
2026-03-05 6:17 ` Konstantin Ryabitsev
2026-02-28 18:31 ` Michael S. Tsirkin
2026-02-28 20:06 ` Konstantin Ryabitsev
2026-02-28 20:23 ` Michael S. Tsirkin
2026-02-28 20:37 ` Konstantin Ryabitsev
2026-02-28 20:47 ` Michael S. Tsirkin
2026-02-28 20:53 ` Konstantin Ryabitsev
2026-02-28 21:04 ` Michael S. Tsirkin [this message]
2026-03-02 9:30 ` Michael S. Tsirkin
2026-03-02 10:33 ` Michael S. Tsirkin
2026-03-03 1:58 ` Junio C Hamano
2026-03-03 4:26 ` Konstantin Ryabitsev
2026-03-03 11:20 ` Matthieu Baerts
2026-03-04 20:56 ` range-diff hangs Marc Kleine-Budde
2026-03-14 4:20 ` Konstantin Ryabitsev
2026-03-14 9:27 ` Marc Kleine-Budde
2026-03-16 23:28 ` b4 review available in master Jonathan Corbet
2026-03-16 23:41 ` Jonathan Corbet
2026-03-17 0:15 ` Konstantin Ryabitsev
2026-03-17 14:11 ` Jonathan Corbet
2026-03-17 14:23 ` Konstantin Ryabitsev
2026-03-17 20:30 ` Konstantin Ryabitsev
2026-03-17 21:46 ` Jonathan Corbet
2026-03-17 22:39 ` Konstantin Ryabitsev
2026-03-17 23:37 ` Konstantin Ryabitsev
2026-03-18 7:56 ` Geert Uytterhoeven
2026-03-18 13:00 ` Mark Brown
2026-03-18 13:26 ` Konstantin Ryabitsev
2026-03-18 16:47 ` Jonathan Corbet
2026-03-18 18:31 ` Laurent Pinchart
2026-03-18 19:22 ` Konstantin Ryabitsev
2026-03-17 0:12 ` Konstantin Ryabitsev
2026-03-18 13:43 ` Johannes Thumshirn
2026-03-20 16:53 ` Louis Chauvet
2026-03-20 19:31 ` Konstantin Ryabitsev
2026-03-20 21:14 ` Alexandre Belloni
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260228155515-mutt-send-email-mst@kernel.org \
--to=mst@redhat.com \
--cc=mricon@kernel.org \
--cc=tools@kernel.org \
--cc=users@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.