- Chat mode: AIUI loads in sandboxed iframe at /dashboard/chat with transparent bg - Mode switcher: Easy + Pro tabs only, Chat is a launcher button - Keyboard shortcuts: Cmd+1 (Easy), Cmd+2 (Pro), Cmd+3 (Chat), Cmd+M (cycle) - Directional transitions: chat slides from/to left, dashboard from/to right - Context broker: postMessage protocol for quarantined AIUI communication - AI permissions store: user-controlled toggles for data access categories - Settings UI: AI Data Access section with per-category toggles - AIUI container manifest and nginx proxy config for /aiui/ - Deploy script builds AIUI with /aiui/ base path - Overnight loop infrastructure (loop.sh, prepare.sh, plan.md, prompt.md) - Security hooks for autonomous overnight runs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
77 lines
2.6 KiB
Bash
Executable File
77 lines
2.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# PreToolUse Bash guard: block dangerous shell commands.
|
|
# Denies: rm -rf, git reset --hard, git push -f, git clean -fd, chmod -R 777,
|
|
# fork bombs, block device overwrites, mkfs, building Rust on macOS for Linux.
|
|
set -euo pipefail
|
|
|
|
INPUT=$(cat)
|
|
CMD=$(python3 -c "
|
|
import json, sys
|
|
try:
|
|
data = json.loads(sys.stdin.read())
|
|
print(data.get('tool_input', {}).get('command', ''))
|
|
except: pass
|
|
" <<< "$INPUT")
|
|
BASE="${CLAUDE_PROJECT_DIR:-}"
|
|
[[ -z "$BASE" ]] && BASE=$(python3 -c "
|
|
import json, sys
|
|
try:
|
|
data = json.loads(sys.stdin.read())
|
|
print(data.get('cwd', ''))
|
|
except: pass
|
|
" <<< "$INPUT")
|
|
[[ -z "$BASE" ]] && BASE="$(pwd)"
|
|
|
|
# Normalize: collapse whitespace, strip leading/trailing
|
|
CMD_NORM=$(echo "$CMD" | tr -s '[:space:]' ' ' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
|
|
deny() {
|
|
local reason="$1"
|
|
python3 -c "
|
|
import json
|
|
print(json.dumps({
|
|
'hookSpecificOutput': {
|
|
'hookEventName': 'PreToolUse',
|
|
'permissionDecision': 'deny',
|
|
'permissionDecisionReason': '$reason'
|
|
}
|
|
}))
|
|
"
|
|
exit 0
|
|
}
|
|
|
|
# Dangerous patterns
|
|
case "$CMD_NORM" in
|
|
*"rm -rf"*|*"rm -fr"*|*"rm -f -r"*|*"rm -r -f"*) deny "Destructive rm -rf blocked by security hook" ;;
|
|
*"git reset --hard"*) deny "git reset --hard would lose uncommitted work" ;;
|
|
*"git push --force"*|*"git push -f"*|*"git push -f "*) deny "git push --force would rewrite history" ;;
|
|
*"git clean -fd"*|*"git clean -f -d"*) deny "git clean -fd deletes untracked files" ;;
|
|
*"chmod -R 777"*|*"chmod -R 0777"*) deny "chmod -R 777 is a security risk" ;;
|
|
*":(){ :"*"};:"*) deny "Fork bomb pattern blocked" ;;
|
|
*"> /dev/sd"*|*">/dev/sd"*) deny "Block device overwrite blocked" ;;
|
|
*"mkfs "*|*"mkfs."*) deny "Disk format command blocked" ;;
|
|
esac
|
|
|
|
# Block building Rust locally on macOS (should always build on dev server)
|
|
if [[ "$(uname)" == "Darwin" ]]; then
|
|
if echo "$CMD_NORM" | grep -qE '^\s*cargo\s+build'; then
|
|
# Allow if it's clearly an SSH command (building on remote)
|
|
if ! echo "$CMD_NORM" | grep -qE 'ssh|sshpass'; then
|
|
deny "NEVER build Rust on macOS — use ./scripts/deploy-to-target.sh --live or build on dev server via SSH"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Check for path traversal escaping project root
|
|
if [[ -n "$BASE" ]] && [[ -d "$BASE" ]]; then
|
|
if echo "$CMD_NORM" | grep -qE '\.\./|/\.\.'; then
|
|
if echo "$CMD_NORM" | grep -qE '(rm|mv|cp|cat|chmod|chown)\s+.*\.\.'; then
|
|
if echo "$CMD_NORM" | grep -qE '\brm\b.*\.\.'; then
|
|
deny "Path traversal with rm blocked"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
exit 0
|