Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ bin/ security & operations scripts
setup-arch.sh Arch Linux droplet: prereqs + setup + tests
lib/ shared shell helpers sourced by CLI/release scripts
shell-common.sh strict mode + shared logging/error/root-check helpers
paths-common.sh shared path constants (bb_init_paths, bb_refresh_release_paths)
release-common.sh shared update/rollback helpers
deploy-common.sh deploy/runtime helper functions
doctor-common.sh doctor status/check formatting helpers
Expand Down Expand Up @@ -181,7 +182,7 @@ Add new test files to `vitest.config.mjs` (and shell wrappers under `test/` as n
- Security functions must be pure, testable modules (no side effects, no env vars at module scope).
- All security code must have tests before merging.
- Run `bin/security-audit.sh --deep` after any security-relevant changes.
- Keep shell CLIs thin: move reusable logic to `bin/lib/*.sh`, and source shared helpers (`shell-common.sh`, `release-common.sh`, `deploy-common.sh`, `doctor-common.sh`) instead of duplicating logging/error/root-check patterns.
- Keep shell CLIs thin: move reusable logic to `bin/lib/*.sh`, and source shared helpers (`shell-common.sh`, `paths-common.sh`, `release-common.sh`, `deploy-common.sh`, `doctor-common.sh`) instead of duplicating logging/error/root-check patterns.
- For shell scripts, standardize on `bb_enable_strict_mode` and shared helper functions (`bb_log`, `bb_die`, etc.) rather than ad-hoc wrappers.
- Protected files (`tool-guard.ts`, `security.mjs`, their tests) are deployed read-only. The agent cannot modify them at runtime.
- New integrations get their own subdirectory (e.g. `discord-bridge/`).
Expand Down
7 changes: 5 additions & 2 deletions bin/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,21 @@

# Auto-detect source repo from this script's location
BAUDBOT_SRC="${BAUDBOT_SRC:-$(cd "$(dirname "$0")/.." && pwd)}"
BAUDBOT_HOME="${BAUDBOT_HOME:-/home/baudbot_agent}"
AGENT_USER="baudbot_agent"
DRY_RUN=0
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

# shellcheck source=bin/lib/shell-common.sh
source "$SCRIPT_DIR/lib/shell-common.sh"
# shellcheck source=bin/lib/paths-common.sh
source "$SCRIPT_DIR/lib/paths-common.sh"
# shellcheck source=bin/lib/json-common.sh
source "$SCRIPT_DIR/lib/json-common.sh"
# shellcheck source=bin/lib/deploy-common.sh
source "$SCRIPT_DIR/lib/deploy-common.sh"
bb_enable_strict_mode
bb_init_paths

AGENT_USER="${AGENT_USER:-$BAUDBOT_AGENT_USER}"

# Helper: run a command as baudbot_agent
as_agent() {
Expand Down
22 changes: 12 additions & 10 deletions bin/doctor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=bin/lib/shell-common.sh
source "$SCRIPT_DIR/lib/shell-common.sh"
# shellcheck source=bin/lib/paths-common.sh
source "$SCRIPT_DIR/lib/paths-common.sh"
# shellcheck source=bin/lib/doctor-common.sh
source "$SCRIPT_DIR/lib/doctor-common.sh"
bb_enable_strict_mode
bb_init_paths

for arg in "$@"; do
case "$arg" in
Expand All @@ -20,7 +23,6 @@ for arg in "$@"; do
esac
done

BAUDBOT_HOME="/home/baudbot_agent"
doctor_init_counters
IS_ROOT=0
if [ "$(id -u)" -eq 0 ]; then
Expand All @@ -42,10 +44,10 @@ fi
# ── User ─────────────────────────────────────────────────────────────────────

echo "User:"
if id baudbot_agent &>/dev/null; then
pass "baudbot_agent user exists"
if id "$BAUDBOT_AGENT_USER" &>/dev/null; then
pass "$BAUDBOT_AGENT_USER user exists"
else
fail "baudbot_agent user does not exist (run: baudbot setup)"
fail "$BAUDBOT_AGENT_USER user does not exist (run: baudbot setup)"
fi

# ── Dependencies ─────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -90,10 +92,10 @@ else
fi

if command -v gh &>/dev/null; then
if sudo -u baudbot_agent gh auth status &>/dev/null; then
if sudo -u "$BAUDBOT_AGENT_USER" gh auth status &>/dev/null; then
pass "gh cli authenticated"
else
warn "gh cli installed but not authenticated (run: sudo -u baudbot_agent gh auth login)"
warn "gh cli installed but not authenticated (run: sudo -u $BAUDBOT_AGENT_USER gh auth login)"
fi
else
fail "gh cli not found"
Expand Down Expand Up @@ -136,10 +138,10 @@ if [ -f "$ENV_FILE" ]; then
else
fail ".env has $PERMS permissions (should be 600)"
fi
if [ "$OWNER" = "baudbot_agent" ]; then
pass ".env owned by baudbot_agent"
if [ "$OWNER" = "$BAUDBOT_AGENT_USER" ]; then
pass ".env owned by $BAUDBOT_AGENT_USER"
else
fail ".env owned by $OWNER (should be baudbot_agent)"
fail ".env owned by $OWNER (should be $BAUDBOT_AGENT_USER)"
fi

# LLM key validation: require at least one valid key, and flag malformed configured keys.
Expand Down Expand Up @@ -369,7 +371,7 @@ if bb_has_systemd; then
fi
else
# No systemd — check for pi process
if pgrep -u baudbot_agent -f "pi --session-control" &>/dev/null; then
if pgrep -u "$BAUDBOT_AGENT_USER" -f "pi --session-control" &>/dev/null; then
pass "agent is running (direct mode)"
else
warn "agent is not running"
Expand Down
67 changes: 67 additions & 0 deletions bin/lib/paths-common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/bin/bash
# Shared path constants for baudbot shell scripts.

# shellcheck disable=SC2120 # Optional args are used by callers in other scripts.
bb_refresh_release_paths() {
local release_root="${BAUDBOT_RELEASE_ROOT:-/opt/baudbot}"
local force="0"

if [ "$#" -ge 1 ]; then
release_root="$1"
fi
if [ "$#" -ge 2 ]; then
force="$2"
fi

BAUDBOT_RELEASE_ROOT="$release_root"

if [ "$force" = "1" ]; then
BAUDBOT_RELEASES_DIR="$BAUDBOT_RELEASE_ROOT/releases"
BAUDBOT_CURRENT_LINK="$BAUDBOT_RELEASE_ROOT/current"
BAUDBOT_PREVIOUS_LINK="$BAUDBOT_RELEASE_ROOT/previous"
BAUDBOT_SOURCE_URL_FILE="$BAUDBOT_RELEASE_ROOT/source.url"
BAUDBOT_SOURCE_BRANCH_FILE="$BAUDBOT_RELEASE_ROOT/source.branch"
else
: "${BAUDBOT_RELEASES_DIR:=$BAUDBOT_RELEASE_ROOT/releases}"
: "${BAUDBOT_CURRENT_LINK:=$BAUDBOT_RELEASE_ROOT/current}"
: "${BAUDBOT_PREVIOUS_LINK:=$BAUDBOT_RELEASE_ROOT/previous}"
: "${BAUDBOT_SOURCE_URL_FILE:=$BAUDBOT_RELEASE_ROOT/source.url}"
: "${BAUDBOT_SOURCE_BRANCH_FILE:=$BAUDBOT_RELEASE_ROOT/source.branch}"
fi
}

bb_init_paths() {
: "${BAUDBOT_AGENT_USER:=baudbot_agent}"

if [ -n "${BAUDBOT_HOME:-}" ] && [ -z "${BAUDBOT_AGENT_HOME:-}" ]; then
BAUDBOT_AGENT_HOME="$BAUDBOT_HOME"
fi

if [ -z "${BAUDBOT_AGENT_HOME:-}" ]; then
BAUDBOT_AGENT_HOME="$(bb_resolve_user_home "$BAUDBOT_AGENT_USER" 2>/dev/null || true)"
fi
: "${BAUDBOT_AGENT_HOME:=/home/$BAUDBOT_AGENT_USER}"

if [ -z "${BAUDBOT_HOME:-}" ]; then
BAUDBOT_HOME="$BAUDBOT_AGENT_HOME"
fi

: "${BAUDBOT_RUNTIME_DIR:=$BAUDBOT_AGENT_HOME/runtime}"
: "${BAUDBOT_PI_DIR:=$BAUDBOT_AGENT_HOME/.pi}"
: "${BAUDBOT_AGENT_DIR:=$BAUDBOT_PI_DIR/agent}"
: "${BAUDBOT_AGENT_EXT_DIR:=$BAUDBOT_AGENT_DIR/extensions}"
: "${BAUDBOT_AGENT_SKILLS_DIR:=$BAUDBOT_AGENT_DIR/skills}"
: "${BAUDBOT_AGENT_SETTINGS_FILE:=$BAUDBOT_AGENT_DIR/settings.json}"
: "${BAUDBOT_VERSION_FILE:=$BAUDBOT_AGENT_DIR/baudbot-version.json}"
: "${BAUDBOT_MANIFEST_FILE:=$BAUDBOT_AGENT_DIR/baudbot-manifest.json}"
: "${BAUDBOT_ENV_FILE:=$BAUDBOT_AGENT_HOME/.config/.env}"

bb_refresh_release_paths

export BAUDBOT_AGENT_USER BAUDBOT_AGENT_HOME BAUDBOT_HOME
export BAUDBOT_RUNTIME_DIR BAUDBOT_PI_DIR BAUDBOT_AGENT_DIR
export BAUDBOT_AGENT_EXT_DIR BAUDBOT_AGENT_SKILLS_DIR BAUDBOT_AGENT_SETTINGS_FILE
export BAUDBOT_VERSION_FILE BAUDBOT_MANIFEST_FILE BAUDBOT_ENV_FILE
export BAUDBOT_RELEASE_ROOT BAUDBOT_RELEASES_DIR BAUDBOT_CURRENT_LINK BAUDBOT_PREVIOUS_LINK
export BAUDBOT_SOURCE_URL_FILE BAUDBOT_SOURCE_BRANCH_FILE
}
15 changes: 4 additions & 11 deletions bin/rollback-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=bin/lib/shell-common.sh
source "$SCRIPT_DIR/lib/shell-common.sh"
# shellcheck source=bin/lib/paths-common.sh
source "$SCRIPT_DIR/lib/paths-common.sh"
bb_enable_strict_mode

BAUDBOT_RELEASE_ROOT="${BAUDBOT_RELEASE_ROOT:-/opt/baudbot}"
BAUDBOT_RELEASES_DIR="${BAUDBOT_RELEASES_DIR:-$BAUDBOT_RELEASE_ROOT/releases}"
BAUDBOT_CURRENT_LINK="${BAUDBOT_CURRENT_LINK:-$BAUDBOT_RELEASE_ROOT/current}"
BAUDBOT_PREVIOUS_LINK="${BAUDBOT_PREVIOUS_LINK:-$BAUDBOT_RELEASE_ROOT/previous}"
bb_init_paths

BAUDBOT_ROLLBACK_DEPLOY_CMD="${BAUDBOT_ROLLBACK_DEPLOY_CMD:-}"
BAUDBOT_ROLLBACK_RESTART_CMD="${BAUDBOT_ROLLBACK_RESTART_CMD:-}"
Expand All @@ -24,9 +23,6 @@ BAUDBOT_ROLLBACK_SKIP_VERSION_CHECK="${BAUDBOT_ROLLBACK_SKIP_VERSION_CHECK:-0}"
BAUDBOT_ROLLBACK_SKIP_CLI_LINK="${BAUDBOT_ROLLBACK_SKIP_CLI_LINK:-0}"
BAUDBOT_ROLLBACK_ALLOW_NON_ROOT="${BAUDBOT_ROLLBACK_ALLOW_NON_ROOT:-0}"

BAUDBOT_AGENT_USER="${BAUDBOT_AGENT_USER:-baudbot_agent}"
BAUDBOT_AGENT_HOME="${BAUDBOT_AGENT_HOME:-/home/baudbot_agent}"

log() { bb_log "$1"; }
die() { bb_die "$1"; }

Expand All @@ -50,10 +46,7 @@ while [ "$#" -gt 0 ]; do
case "$1" in
--release-root)
[ "$#" -ge 2 ] || die "--release-root requires a value"
BAUDBOT_RELEASE_ROOT="$2"
BAUDBOT_RELEASES_DIR="$BAUDBOT_RELEASE_ROOT/releases"
BAUDBOT_CURRENT_LINK="$BAUDBOT_RELEASE_ROOT/current"
BAUDBOT_PREVIOUS_LINK="$BAUDBOT_RELEASE_ROOT/previous"
bb_refresh_release_paths "$2" 1
shift 2
;;
--skip-restart)
Expand Down
2 changes: 1 addition & 1 deletion bin/scan-extensions.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ describe("scan-extensions: new rules", () => {
await writeFile(join(dir, "bad.js"), `
const mod = require(userInput);
`);
const { output, exitCode } = runScanner(dir);
const { output } = runScanner(dir);
// info severity = exit 0, but should appear in output
assert.ok(output.includes("dynamic") || output.includes("Dynamic") || output.includes("require"), output);
});
Expand Down
20 changes: 11 additions & 9 deletions bin/security-audit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=bin/lib/shell-common.sh
source "$SCRIPT_DIR/lib/shell-common.sh"
# shellcheck source=bin/lib/paths-common.sh
source "$SCRIPT_DIR/lib/paths-common.sh"
bb_enable_strict_mode
bb_init_paths

BAUDBOT_HOME="${BAUDBOT_HOME:-/home/baudbot_agent}"
# Source repo — auto-detect from this script's location, or use env override
BAUDBOT_SRC="${BAUDBOT_SRC:-$(cd "$SCRIPT_DIR/.." && pwd)}"

Expand Down Expand Up @@ -97,12 +99,12 @@ echo ""
# ── Docker group ─────────────────────────────────────────────────────────────

echo "Docker Access"
if id baudbot_agent 2>/dev/null | grep -q '(docker)'; then
finding "CRITICAL" "baudbot_agent is in docker group" \
if id "$BAUDBOT_AGENT_USER" 2>/dev/null | grep -q '(docker)'; then
finding "CRITICAL" "$BAUDBOT_AGENT_USER is in docker group" \
"Can bypass baudbot-docker wrapper via /usr/bin/docker directly"
fix_skip "Remove from docker group" "Requires root: sudo gpasswd -d baudbot_agent docker"
fix_skip "Remove from docker group" "Requires root: sudo gpasswd -d $BAUDBOT_AGENT_USER docker"
else
ok "baudbot_agent not in docker group"
ok "$BAUDBOT_AGENT_USER not in docker group"
fi

if [ -f /usr/local/bin/baudbot-docker ]; then
Expand Down Expand Up @@ -191,7 +193,7 @@ echo "Source Isolation & Integrity"
# Source repo lives outside agent's home — agent should not be able to read it
if [ -r "$BAUDBOT_SRC/setup.sh" ] 2>/dev/null; then
# If we're running as admin, this is expected — check agent can't
agent_can_read=$(sudo -u baudbot_agent test -r "$BAUDBOT_SRC/setup.sh" 2>/dev/null && echo "yes" || echo "no")
agent_can_read=$(sudo -u "$BAUDBOT_AGENT_USER" test -r "$BAUDBOT_SRC/setup.sh" 2>/dev/null && echo "yes" || echo "no")
if [ "$agent_can_read" = "yes" ]; then
finding "WARN" "Agent can read source repo at $BAUDBOT_SRC" \
"Ensure admin home is 700: chmod 700 $(dirname "$BAUDBOT_SRC")"
Expand Down Expand Up @@ -566,10 +568,10 @@ echo ""
echo "Extension & Skill Safety"

# Check pi extensions for suspicious patterns (deployed copies only)
AGENT_USER="${BAUDBOT_AGENT_USER:-baudbot_agent}"
AGENT_USER="$BAUDBOT_AGENT_USER"
suspicious_extension_patterns="(eval\s*\(|new\s+Function\s*\(|child_process|execSync|execFile|spawn\s*\(|writeFileSync.*\/etc|writeFileSync.*\/home\/(?!${AGENT_USER}))"
ext_dirs=(
"$BAUDBOT_HOME/.pi/agent/extensions"
"$BAUDBOT_AGENT_EXT_DIR"
)
ext_findings=0
for ext_dir in "${ext_dirs[@]}"; do
Expand All @@ -588,7 +590,7 @@ fi

# Check skills for dangerous tool instructions (deployed copies only)
skill_dirs=(
"$BAUDBOT_HOME/.pi/agent/skills"
"$BAUDBOT_AGENT_SKILLS_DIR"
)
skill_findings=0
for skill_dir in "${skill_dirs[@]}"; do
Expand Down
19 changes: 11 additions & 8 deletions bin/setup-firewall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,25 @@
# - Bind to ports (no inbound listeners/backdoors)
# - Do DNS tunneling over non-53 UDP

set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=bin/lib/shell-common.sh
source "$SCRIPT_DIR/lib/shell-common.sh"
# shellcheck source=bin/lib/paths-common.sh
source "$SCRIPT_DIR/lib/paths-common.sh"
bb_enable_strict_mode
bb_init_paths

if [ "$(id -u)" -ne 0 ]; then
echo "❌ Must run as root (sudo $0)"
exit 1
fi
bb_require_root "setup-firewall"

UID_BAUDBOT=$(id -u baudbot_agent 2>/dev/null)
UID_BAUDBOT=$(id -u "$BAUDBOT_AGENT_USER" 2>/dev/null)
if [ -z "$UID_BAUDBOT" ]; then
echo "❌ baudbot_agent user not found"
echo "❌ $BAUDBOT_AGENT_USER user not found"
exit 1
fi

CHAIN="BAUDBOT_OUTPUT"

echo "🔒 Setting up firewall rules for baudbot_agent (uid $UID_BAUDBOT)..."
echo "🔒 Setting up firewall rules for $BAUDBOT_AGENT_USER (uid $UID_BAUDBOT)..."

# Clean up any existing rules first
iptables -w -D OUTPUT -m owner --uid-owner "$UID_BAUDBOT" -j "$CHAIN" 2>/dev/null || true
Expand Down
Loading