#!/usr/bin/env bash # Base64 obfuscation scanner — extracts base64 blobs from changed files, # decodes them, and checks decoded content for prompt injection patterns. # # Catches obfuscated directives that would bypass docs-prompt-injection-scan.sh, # which only scans raw text in markdown files. # # Usage: # scripts/base64-scan.sh # scan staged files (pre-commit mode) # scripts/base64-scan.sh --diff origin/main # scan diff vs branch (CI mode) # scripts/base64-scan.sh --file path # scan a specific file # # Works on macOS (BSD grep) and Linux (GNU grep) — uses only ERE patterns. set -euo pipefail RED='\033[0;31m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' IGNOREFILE=".base64scanignore" EXIT_CODE=0 FINDINGS=0 # Blobs shorter than this have too many false positives. # 40 base64 chars decodes to ~30 bytes — minimum length for a meaningful directive. MIN_BLOB_LEN=40 # ── Prompt injection patterns to match against decoded content ──────── # Format: "Label:::flags:::regex" # Mirrors the patterns in docs-prompt-injection-scan.sh but applied to # base64-decoded content across all file types. DECODED_PATTERNS=( # System prompt markers "System prompt marker:::i:::" "System prompt marker:::i:::<\|im_start\|>system" "System prompt marker:::i:::\[SYSTEM\][[:space:]]*:" # Role injection / override "Role injection:::i:::you are now [a-z]" "Instruction override:::i:::ignore (all )?previous instructions" "Instruction override:::i:::ignore (all )?prior instructions" "Instruction override:::i:::disregard (all )?(above|previous|prior)" "Instruction override:::i:::forget (all )?(above|previous|prior) (instructions|context|rules)" "Instruction override:::i:::new instructions:" "Instruction override:::i:::override (all )?instructions" "Instruction override:::i:::your new role is" "Instruction override:::i:::from now on,? (you (are|will|must|should)|act as)" # Hidden HTML directives "Hidden directive::::::