#!/usr/bin/env bash # Deterministic compliance checker # Runs against a diff or directory and checks all requirement categories # Usage: ./check.sh [--diff ] set -euo pipefail REPO="${1:-.}" DIFF_MODE=false BASE_BRANCH="" VIOLATIONS=0 WARNINGS=0 if [[ "${2:-}" == "--diff" ]]; then DIFF_MODE=true BASE_BRANCH="${3:-main}" fi RED='\033[0;31m' YELLOW='\033[1;33m' GREEN='\033[0;32m' NC='\033[0m' pass() { echo -e " ${GREEN}✓${NC} $1"; } warn() { echo -e " ${YELLOW}⚠${NC} $1"; WARNINGS=$((WARNINGS + 1)); } fail() { echo -e " ${RED}✗${NC} $1"; VIOLATIONS=$((VIOLATIONS + 1)); } get_changed_files() { if $DIFF_MODE; then git -C "$REPO" diff --name-only "$BASE_BRANCH" -- '*.java' '*.go' '*.js' '*.ts' '*.py' '*.kt' '*.sql' '*.yaml' '*.yml' '*.tf' '*.json' 2>/dev/null || true else find "$REPO" -type f \( -name '*.java' -o -name '*.go' -o -name '*.js' -o -name '*.ts' -o -name '*.py' -o -name '*.kt' \) ! -path '*/test/*' ! -path '*/node_modules/*' ! -path '*/.git/*' 2>/dev/null fi } FILES=$(get_changed_files) echo "═══════════════════════════════════════" echo " AI SDLC Standards Compliance Check" echo "═══════════════════════════════════════" echo "" # ── SEC-001: Input Validation ── echo "▸ SEC-001: Input Validation" RAW_ACCESS=$(echo "$FILES" | xargs grep -ln 'request\.getParameter\|req\.body\b\|request\.body\b' 2>/dev/null | grep -v test || true) if [[ -n "$RAW_ACCESS" ]]; then for f in $RAW_ACCESS; do fail "Raw request access: $f"; done else pass "No raw request body access found" fi # ── SEC-002: No Raw SQL ── echo "▸ SEC-002: No Raw SQL" SQL_CONCAT=$(echo "$FILES" | xargs grep -Pln '(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE).*["\x27]\s*\+\s*|String\.format.*?(SELECT|INSERT|UPDATE|DELETE)' 2>/dev/null | grep -v test || true) if [[ -n "$SQL_CONCAT" ]]; then for f in $SQL_CONCAT; do fail "SQL concatenation: $f"; done else pass "No SQL string concatenation found" fi # ── SEC-003: Auth Annotations ── echo "▸ SEC-003: Authentication Annotations" ENDPOINTS=$(echo "$FILES" | xargs grep -ln '@RequestMapping\|@GetMapping\|@PostMapping\|@PutMapping\|@DeleteMapping' 2>/dev/null || true) for f in $ENDPOINTS; do MISSING=$(grep -B5 '@\(Request\|Get\|Post\|Put\|Delete\)Mapping' "$f" | grep -c '@ReltioSecured\|@PublicEndpoint\|@PreAuthorize' || true) TOTAL=$(grep -c '@\(Request\|Get\|Post\|Put\|Delete\)Mapping' "$f" || true) if [[ "$MISSING" -lt "$TOTAL" ]]; then warn "Endpoint without explicit auth: $f ($MISSING/$TOTAL annotated)" fi done if [[ -z "$ENDPOINTS" ]]; then pass "No endpoint files in scope"; fi # ── SEC-004: Secrets in Code ── echo "▸ SEC-004: Secrets in Code" SECRETS=$(echo "$FILES" | xargs grep -Piln '(password|secret|api_key|apikey|token)\s*=\s*["\x27][^"\x27]{8,}' 2>/dev/null | grep -v test | grep -v '\.env\.example' || true) if [[ -n "$SECRETS" ]]; then for f in $SECRETS; do fail "Possible hardcoded secret: $f"; done else pass "No hardcoded secrets detected" fi # ── OPS-002: Structured Logging ── echo "▸ OPS-002: Structured Logging" RAW_LOG=$(echo "$FILES" | xargs grep -ln 'System\.out\.print\|System\.err\.print' 2>/dev/null | grep -v test || true) if [[ -n "$RAW_LOG" ]]; then for f in $RAW_LOG; do warn "Raw stdout/stderr logging: $f"; done else pass "No raw stdout/stderr logging" fi # ── OPS-003: Resource Limits ── echo "▸ OPS-003: Resource Limits" K8S_DEPLOYS=$(find "$REPO" -name '*.yaml' -o -name '*.yml' | xargs grep -l 'kind: Deployment' 2>/dev/null || true) for f in $K8S_DEPLOYS; do if ! grep -q 'resources:' "$f"; then fail "K8s deployment missing resource limits: $f" else pass "Resource limits present: $f" fi done # ── OPS-004: Rollback Safety ── echo "▸ OPS-004: Rollback Safety" MIGRATIONS=$(find "$REPO" -path '*/migration*' -name '*.sql' 2>/dev/null || true) for f in $MIGRATIONS; do DANGEROUS=$(grep -Pic 'DROP\s+COLUMN|RENAME\s+COLUMN|ALTER\s+.*TYPE' "$f" || true) if [[ "$DANGEROUS" -gt 0 ]]; then fail "Destructive migration: $f (DROP/RENAME/ALTER TYPE)" fi done if [[ -z "$MIGRATIONS" ]]; then pass "No migration files in scope"; fi # ── COST-001: Resource Tagging ── echo "▸ COST-001: Resource Tagging" TF_FILES=$(find "$REPO" -name '*.tf' 2>/dev/null || true) for f in $TF_FILES; do if grep -q 'resource\s' "$f" && ! grep -q 'tags' "$f"; then warn "Terraform resource without tags: $f" fi done if [[ -z "$TF_FILES" ]]; then pass "No Terraform files in scope"; fi echo "" echo "═══════════════════════════════════════" if [[ $VIOLATIONS -gt 0 ]]; then echo -e " Result: ${RED}FAIL${NC} — $VIOLATIONS violation(s), $WARNINGS warning(s)" exit 1 elif [[ $WARNINGS -gt 0 ]]; then echo -e " Result: ${YELLOW}WARN${NC} — $WARNINGS warning(s)" exit 0 else echo -e " Result: ${GREEN}PASS${NC} — all checks passed" exit 0 fi