#!/bin/bash # check-db-access.sh - Verify no direct database access to inou.db outside db_queries.go # Run: make check-db or ./scripts/check-db-access.sh # # This checks the CORE CODEBASE that accesses inou.db: # - lib/ # - portal/ # - api/ # - viewer/ # - mcp-client/ # # Excluded (have their own separate databases): # - snpedia-genotypes/ (genotypes.db) # - scrape_mychart/ (scrape.db) # - main.go at root (old/unused) # - import-genome/ (reads genotypes.db, uses lib for inou.db) # - docs/, backups/ set -e RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color ERRORS=0 # Core directories that access inou.db CORE_DIRS="lib portal api viewer mcp-client" echo "=== Database Access Check (inou.db) ===" echo "Checking: $CORE_DIRS" echo "" # Check for lib.DB() usage (should not exist anywhere in core) echo -n "Checking for lib.DB() usage... " MATCHES="" for dir in $CORE_DIRS; do if [ -d "$dir" ]; then FOUND=$(grep -r "lib\.DB()" --include="*.go" "$dir" 2>/dev/null || true) MATCHES="$MATCHES$FOUND" fi done if [ -n "$MATCHES" ]; then echo -e "${RED}FAILED${NC}" echo "$MATCHES" ERRORS=$((ERRORS + 1)) else echo -e "${GREEN}OK${NC}" fi # Check for db.Exec outside allowed files (in lib only) echo -n "Checking for db.Exec() outside db_schema.go/db_queries.go... " MATCHES=$(grep -r "db\.Exec" --include="*.go" lib 2>/dev/null | grep -v "lib/db_queries.go" | grep -v "lib/db_schema.go" | grep -v "lib/migrate" || true) if [ -n "$MATCHES" ]; then echo -e "${RED}FAILED${NC}" echo "$MATCHES" ERRORS=$((ERRORS + 1)) else echo -e "${GREEN}OK${NC}" fi # Check for db.Query outside allowed files (in lib only) echo -n "Checking for db.Query() outside db_schema.go/db_queries.go... " MATCHES=$(grep -r "db\.Query" --include="*.go" lib 2>/dev/null | grep -v "lib/db_queries.go" | grep -v "lib/db_schema.go" | grep -v "lib/migrate" || true) if [ -n "$MATCHES" ]; then echo -e "${RED}FAILED${NC}" echo "$MATCHES" ERRORS=$((ERRORS + 1)) else echo -e "${GREEN}OK${NC}" fi # Check for db.QueryRow outside allowed files (in lib only) echo -n "Checking for db.QueryRow() outside db_schema.go/db_queries.go... " MATCHES=$(grep -r "db\.QueryRow" --include="*.go" lib 2>/dev/null | grep -v "lib/db_queries.go" | grep -v "lib/db_schema.go" | grep -v "lib/migrate" || true) if [ -n "$MATCHES" ]; then echo -e "${RED}FAILED${NC}" echo "$MATCHES" ERRORS=$((ERRORS + 1)) else echo -e "${GREEN}OK${NC}" fi # Check for sql.Open in portal/api/viewer (should only be rateDB exception in portal) echo -n "Checking for sql.Open() in portal/api/viewer... " MATCHES=$(grep -r "sql\.Open" --include="*.go" portal api viewer 2>/dev/null | grep -v "rateDB" | grep -v "ratelimit" || true) if [ -n "$MATCHES" ]; then echo -e "${RED}FAILED${NC}" echo "$MATCHES" ERRORS=$((ERRORS + 1)) else echo -e "${GREEN}OK${NC}" fi # Check for deprecated DB wrapper usage in core dirs echo -n "Checking for deprecated DB wrapper usage... " MATCHES="" for dir in $CORE_DIRS; do if [ -d "$dir" ]; then FOUND=$(grep -rE "\b(DBExec|DBInsert|DBUpdate|DBDelete|DBQuery|DBQueryRow)\b" --include="*.go" "$dir" 2>/dev/null || true) MATCHES="$MATCHES$FOUND" fi done if [ -n "$MATCHES" ]; then echo -e "${RED}FAILED${NC}" echo "$MATCHES" ERRORS=$((ERRORS + 1)) else echo -e "${GREEN}OK${NC}" fi echo "" echo "=== Schema Safety Check ===" echo "" # Check for CREATE TABLE statements in code (schema is documented in docs/schema.sql) # Allowed: lib/db_queries.go (ORM layer), rateDB (separate rate-limiting database) echo -n "Checking for CREATE TABLE in code... " MATCHES="" for dir in $CORE_DIRS; do if [ -d "$dir" ]; then FOUND=$(grep -ri "CREATE TABLE" --include="*.go" "$dir" 2>/dev/null | grep -v "lib/db_queries.go" | grep -v "rateDB" || true) MATCHES="$MATCHES$FOUND" fi done if [ -n "$MATCHES" ]; then echo -e "${RED}FAILED${NC}" echo "$MATCHES" echo " Schema creation is forbidden. Schema is documented in docs/schema.sql" ERRORS=$((ERRORS + 1)) else echo -e "${GREEN}OK${NC}" fi echo "" echo "=== RBAC Enforcement Check ===" echo "" # Check that internal checkAccess() is only called within lib/ # The checkAccess function is internal to lib/access.go and should only be called from lib/v2.go echo -n "Checking for checkAccess() calls outside lib/... " MATCHES="" for dir in portal api viewer mcp-client; do if [ -d "$dir" ]; then FOUND=$(grep -r "checkAccess(" --include="*.go" "$dir" 2>/dev/null || true) MATCHES="$MATCHES$FOUND" fi done if [ -n "$MATCHES" ]; then echo -e "${RED}FAILED${NC}" echo "$MATCHES" echo " checkAccess() is an internal function - use data layer functions that enforce RBAC" ERRORS=$((ERRORS + 1)) else echo -e "${GREEN}OK${NC}" fi # Check that lib.CheckAccess is only called from lib/ or allowed API files # Allowed: api/auth.go (RBAC helper functions), api/api_rbac.go (RBAC management API) echo -n "Checking for lib.CheckAccess() outside lib/ and RBAC helpers... " MATCHES="" for dir in portal viewer mcp-client; do if [ -d "$dir" ]; then FOUND=$(grep -r "lib\.CheckAccess" --include="*.go" "$dir" 2>/dev/null || true) MATCHES="$MATCHES$FOUND" fi done # Also check api/ but exclude auth.go and api_rbac.go if [ -d "api" ]; then FOUND=$(grep -r "lib\.CheckAccess" --include="*.go" api 2>/dev/null | grep -v "api/auth.go" | grep -v "api/api_rbac.go" || true) MATCHES="$MATCHES$FOUND" fi if [ -n "$MATCHES" ]; then echo -e "${RED}FAILED${NC}" echo "$MATCHES" echo " RBAC checks should be in data layer (lib/) or RBAC helpers (api/auth.go, api/api_rbac.go)" ERRORS=$((ERRORS + 1)) else echo -e "${GREEN}OK${NC}" fi echo "" # Summary if [ $ERRORS -eq 0 ]; then echo -e "${GREEN}All checks passed!${NC}" echo "No direct database access detected in core codebase." exit 0 else echo -e "${RED}$ERRORS check(s) failed!${NC}" echo "" echo "Direct database access is FORBIDDEN without Johan's express consent." echo "All DB operations must go through lib/db_queries.go functions:" echo " - Save(), Load(), Query(), Delete(), Count()" exit 1 fi