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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox