Protect host-run files from the agent; trust registry; git audit

- read-only in the sandbox: .opencode-sandbox/, .cc-safety-net/, .git/hooks, .git/config,
  an in-project core.hooksPath, the egress rules and the safety-net home; .git is pinned
  by a bind onto itself so it cannot be swapped
- only trusted projects are loaded (init / trust): a .opencode-sandbox/ in a worktree or a
  subdirectory is no longer sourced on the host
- run (before and after the session) and doctor report planted .git entries, redirected
  worktrees and per-worktree overrides
- hide Firefox, Claude, VS Code, Cursor and Codex configs
This commit is contained in:
2026-10-30 01:31:36 -07:00
parent 5a7567849e
commit 01fc1627c1
8 changed files with 143 additions and 8 deletions
+31 -5
View File
@@ -55,19 +55,44 @@ ocsb_defaults() {
OCSB_HIDE_HOME=(.gnupg .aws .azure .config/gcloud .kube/config .docker/config.json .netrc
.git-credentials .config/gh/hosts.yml .password-store .local/share/keyrings .mozilla
.config/google-chrome .config/chromium .config/BraveSoftware .claude .codex
.config/opencode .local/share/opencode .Xauthority)
.config/opencode .local/share/opencode .Xauthority .config/mozilla .config/Claude .config/Code
.config/Cursor .config/Codex)
OCSB_SKILLS=(opencode-sandbox) # skills mounted for the agent (names of skill directories)
OCSB_SKILL_DIRS=() # extra directories to look for skills in
OCSB_PLUGINS=(skill-compact-resurrect/skill-compact-resurrect.v2.ts) # files in plugins/
OCSB_ENV=() # extra KEY=VALUE for OpenCode
}
# Trusted projects (init / trust), one physical path per line. Only these are loaded:
# sandbox.conf is bash run on the host, and the agent can write anywhere in the project
# tree, so a .opencode-sandbox/ found in a worktree or a subdirectory proves nothing.
ocsb_trust_file() { echo "${XDG_CONFIG_HOME:-${HOME}/.config}/opencode-sandbox/projects"; }
ocsb_trusted() { [[ -f $(ocsb_trust_file) ]] && grep -Fxq -- "$1" "$(ocsb_trust_file)"; }
ocsb_trust() {
local dir f
dir=$(cd "${1:-.}" && pwd -P) || ocsb_die "no such directory: ${1}"
[[ -f ${dir}/.opencode-sandbox/sandbox.conf ]] || ocsb_die "no ${dir}/.opencode-sandbox/sandbox.conf"
[[ -z ${OCSB_IN_SANDBOX:-} ]] || ocsb_die "trust is a host command"
ocsb_trusted "${dir}" && { echo "already trusted: ${dir}"; return 0; }
f=$(ocsb_trust_file)
mkdir -p "$(dirname "${f}")"
echo "${dir}" >>"${f}"
echo "✓ trusted: ${dir}"
}
# Nearest trusted project at or above the directory
ocsb_find_project() {
local dir=${1:-$PWD}
local dir untrusted=""
dir=$(cd "${1:-.}" && pwd -P)
while [[ ${dir} != / ]]; do
[[ -f ${dir}/.opencode-sandbox/sandbox.conf ]] && { echo "${dir}"; return 0; }
if [[ -f ${dir}/.opencode-sandbox/sandbox.conf ]]; then
ocsb_trusted "${dir}" && { echo "${dir}"; return 0; }
untrusted=${untrusted:-${dir}}
fi
dir=$(dirname "${dir}")
done
[[ -z ${untrusted} ]] ||
ocsb_warn "${untrusted} is not a trusted project: read its .opencode-sandbox/ (sandbox.conf and hooks run on the host), then: opencode-sandbox trust ${untrusted}"
return 1
}
@@ -82,12 +107,13 @@ ocsb_load_project() {
OCSB_PROJECT_DIR=""
if [[ -n ${OCSB_PROJECT:-} ]]; then
OCSB_PROJECT_DIR=$(cd "${OCSB_PROJECT}" && pwd)
OCSB_PROJECT_DIR=$(cd "${OCSB_PROJECT}" && pwd -P)
ocsb_trusted "${OCSB_PROJECT_DIR}" || ocsb_die "OCSB_PROJECT=${OCSB_PROJECT} is not a trusted project (opencode-sandbox trust)"
else
OCSB_PROJECT_DIR=$(ocsb_find_project) || true
fi
if [[ -z ${OCSB_PROJECT_DIR} || ! -f ${OCSB_PROJECT_DIR}/.opencode-sandbox/sandbox.conf ]]; then
[[ ${optional} == 1 ]] || ocsb_die "no .opencode-sandbox/sandbox.conf here or above (run: opencode-sandbox init)"
[[ ${optional} == 1 ]] || ocsb_die "no trusted .opencode-sandbox/ here or above (new project: opencode-sandbox init)"
fi
export OCSB_PROJECT_DIR