230 lines
7.7 KiB
Bash
Executable File
230 lines
7.7 KiB
Bash
Executable File
#!/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 allowed lib files... "
|
|
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/dbcore.go" | grep -v "lib/stubs.go" | grep -v "lib/migrate" | grep -v "_test.go" || 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 allowed lib files... "
|
|
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/dbcore.go" | grep -v "lib/stubs.go" | grep -v "lib/migrate" | grep -v "_test.go" || 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 allowed lib files... "
|
|
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/dbcore.go" | grep -v "lib/stubs.go" | grep -v "lib/migrate" | grep -v "_test.go" || 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" | grep -v "snpDB" || 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" | grep -v "_test.go" || 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 "=== Unexported DB Function Check ==="
|
|
echo ""
|
|
|
|
# Now that Query/Save/Load/Delete/Count are unexported (dbQuery, dbSave, etc.),
|
|
# no code outside lib/ should reference them. The Go compiler enforces this,
|
|
# but this check catches it earlier (before build).
|
|
ALL_DIRS="portal api viewer mcp-client import-genome cmd find_dossiers tools test-prompts doc-processor"
|
|
for fn in "lib\.Query(" "lib\.Save(" "lib\.Load(" "lib\.Delete(" "lib\.Count("; do
|
|
name=$(echo "$fn" | sed 's/lib\\.//;s/($//')
|
|
echo -n "Checking for $fn outside lib/... "
|
|
MATCHES=""
|
|
for dir in $ALL_DIRS; do
|
|
if [ -d "$dir" ]; then
|
|
FOUND=$(grep -rn "$fn" --include="*.go" "$dir" 2>/dev/null || true)
|
|
# Filter out comments and string literals (rough heuristic: lines with // before the match)
|
|
if [ -n "$FOUND" ]; then
|
|
REAL=$(echo "$FOUND" | grep -v "^\s*//" | grep -v "^\s*\*" || true)
|
|
MATCHES="$MATCHES$REAL"
|
|
fi
|
|
fi
|
|
done
|
|
if [ -n "$MATCHES" ]; then
|
|
echo -e "${RED}FAILED${NC}"
|
|
echo "$MATCHES"
|
|
echo " lib.$name() is unexported — use RBAC-checked functions instead"
|
|
ERRORS=$((ERRORS + 1))
|
|
else
|
|
echo -e "${GREEN}OK${NC}"
|
|
fi
|
|
done
|
|
|
|
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 RBAC-checked lib functions."
|
|
echo "Raw DB functions (dbQuery, dbSave, etc.) are unexported — only callable from lib/."
|
|
exit 1
|
|
fi
|