From 6c3efdef002da70b93565cf53be3125240ea2872 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 16 Jun 2026 04:06:35 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[Medium]=20?= =?UTF-8?q?Fix=20missing=20OWASP=20mapping=20and=20agent=20integration=20v?= =?UTF-8?q?ia=20pre-commit=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/sentinel.md | 5 +++ scanner/cli/vibesec.py | 88 +++++++++++++++++++++++++++++++----------- 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 9bdaa4e..eeb6c60 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -31,3 +31,8 @@ **Vulnerability:** Regular Expression Denial of Service (ReDoS) vulnerability caused by tracking capturing group matches `(...)` during line-by-line file scanning in `SCAN_RULES`. **Learning:** In a highly repetitive inner loop (line-by-line file scanning), tracking backreferences and capturing group matches incurs unnecessary regex engine overhead. While unbounded quantifiers are the primary ReDoS cause, capturing groups exacerbate the tracking state, increasing scan time significantly on long lines or adversarial payloads. **Prevention:** Always use non-capturing groups `(?:...)` instead of capturing groups `(...)` when adding or modifying regular expressions in `SCAN_RULES` to prevent unnecessary performance overhead and ReDoS vulnerabilities. + +## 2026-06-16 - Add OWASP mapping and pre-commit hook integration +**Vulnerability:** The VibeSec scanner lacked explicit mapping to standard vulnerability frameworks (like OWASP Top 10) and relied on manual invocation, meaning vulnerabilities could easily bypass detection and be committed by developers or AI agents (like Claude Code or Codex). +**Learning:** To enforce security guardrails effectively, static analysis tools should intercept the workflow at commit time. Mapping findings to OWASP categories improves the clarity and actionability of the scanner output. +**Prevention:** Updated `SCAN_RULES` messages to include relevant OWASP classifications (e.g., A01, A03). Added a `vibesec hook` command that automatically installs a `pre-commit` script to block commits if critical or high vulnerabilities are detected. diff --git a/scanner/cli/vibesec.py b/scanner/cli/vibesec.py index f1a9137..5fb6e4e 100644 --- a/scanner/cli/vibesec.py +++ b/scanner/cli/vibesec.py @@ -13,6 +13,7 @@ init Install security rules into your project scan Run a lightweight security scan on a directory review Generate an AI review prompt for your stack + hook Install a pre-commit hook to block vulnerabilities Options: --tool AI coding tool: cursor, claude-code, windsurf, lovable (default: cursor) @@ -118,14 +119,14 @@ "id": "hardcoded-stripe-secret", "pattern": re.compile(r'sk_(?:live|test)_[A-Za-z0-9]{24,}'), "severity": "CRITICAL", - "message": "Hardcoded Stripe secret key detected. Rotate this key immediately.", + "message": "Hardcoded Stripe secret key detected. Rotate this key immediately. [OWASP A07:2021 - Identification and Authentication Failures]", "extensions": None, }, { "id": "hardcoded-openai-key", "pattern": re.compile(r'sk-[A-Za-z0-9]{32,}'), "severity": "CRITICAL", - "message": "Possible hardcoded OpenAI API key detected.", + "message": "Possible hardcoded OpenAI API key detected. [OWASP A07:2021 - Identification and Authentication Failures]", "extensions": None, }, { @@ -135,27 +136,21 @@ re.IGNORECASE, ), "severity": "CRITICAL", - "message": ( - "Secret environment variable uses NEXT_PUBLIC_ prefix — " - "this exposes it to the browser bundle." - ), + "message": "Secret environment variable uses NEXT_PUBLIC_ prefix — this exposes it to the browser bundle. [OWASP A05:2021 - Security Misconfiguration]", "extensions": None, }, { "id": "supabase-service-role-client", "pattern": re.compile(r'NEXT_PUBLIC_.*SERVICE_ROLE', re.IGNORECASE), "severity": "CRITICAL", - "message": "Supabase service role key exposed to the client via NEXT_PUBLIC_ prefix.", + "message": "Supabase service role key exposed to the client via NEXT_PUBLIC_ prefix. [OWASP A05:2021 - Security Misconfiguration]", "extensions": [".ts", ".tsx", ".js", ".jsx", ".env", ".env.local", ".env.production"], }, { "id": "firebase-allow-all", "pattern": re.compile(r'allow\s+(?:read|write|read,\s*write)\s*:\s*if\s+true'), "severity": "CRITICAL", - "message": ( - "Firebase/Firestore rule allows unrestricted read/write access. " - "Add authentication and ownership checks." - ), + "message": "Firebase/Firestore rule allows unrestricted read/write access. Add authentication and ownership checks. [OWASP A01:2021 - Broken Access Control]", "extensions": [".rules"], }, { @@ -164,17 +159,14 @@ r'(?i)(?:todo|fixme|hack|temp)[^\n]{0,50}(?:auth|security|permission|check|protect)', ), "severity": "HIGH", - "message": ( - "Comment suggests auth/security check was deferred. " - "Verify this is not deployed to production." - ), + "message": "Comment suggests auth/security check was deferred. Verify this is not deployed to production. [OWASP A01:2021 - Broken Access Control]", "extensions": [".ts", ".tsx", ".js", ".jsx", ".py"], }, { "id": "dangerous-cors", "pattern": re.compile(r"Access-Control-Allow-Origin['\",\s]*[*]"), "severity": "HIGH", - "message": "CORS set to allow all origins (*). Restrict to known domains.", + "message": "CORS set to allow all origins (*). Restrict to known domains. [OWASP A05:2021 - Security Misconfiguration]", "extensions": [".ts", ".tsx", ".js", ".jsx", ".py"], }, { @@ -183,7 +175,7 @@ r'(?i)(?:DATABASE_URL|POSTGRES_URL)\s*[=:]\s*["\x27](?:postgres|postgresql|mysql)://\S+', ), "severity": "CRITICAL", - "message": "Hardcoded database connection string detected.", + "message": "Hardcoded database connection string detected. [OWASP A07:2021 - Identification and Authentication Failures]", "extensions": None, }, { @@ -192,14 +184,14 @@ r'(?i)(?:JWT_SECRET|NEXTAUTH_SECRET)\s*[=:]\s*["\x27][^"\x27\s]{8,}["\x27]', ), "severity": "CRITICAL", - "message": "Hardcoded JWT/NextAuth secret detected.", + "message": "Hardcoded JWT/NextAuth secret detected. [OWASP A02:2021 - Cryptographic Failures]", "extensions": None, }, { "id": "stripe-webhook-no-verify", "pattern": re.compile(r'constructEvent\s*\([^)]*(?:undefined|""|\'\')\s*\)'), "severity": "CRITICAL", - "message": "Stripe constructEvent called with empty/undefined webhook secret.", + "message": "Stripe constructEvent called with empty/undefined webhook secret. [OWASP A08:2021 - Software and Data Integrity Failures]", "extensions": [".ts", ".tsx", ".js", ".jsx"], }, { @@ -208,21 +200,21 @@ r'const\s+(?:session|user)\s*=\s*\{\s*(?:user\s*:\s*)?\{\s*id\s*:\s*["\x27]', ), "severity": "HIGH", - "message": "Hardcoded mock session/user in what may be a production handler. Verify this is test-only code.", + "message": "Mock or hardcoded session/user object detected in route handler. [OWASP A01:2021 - Broken Access Control]", "extensions": [".ts", ".tsx", ".js", ".jsx"], }, { "id": "dangerous-eval", "pattern": re.compile(r'\beval\s*\('), "severity": "CRITICAL", - "message": "Use of eval() detected. This is a critical risk for arbitrary code execution and injection attacks.", + "message": "Use of eval() detected. This is a critical risk for arbitrary code execution and injection attacks. [OWASP A03:2021 - Injection]", "extensions": [".js", ".jsx", ".ts", ".tsx", ".py"], }, { "id": "react-dangerously-set-inner-html", "pattern": re.compile(r'dangerouslySetInnerHTML\s*='), "severity": "HIGH", - "message": "Use of dangerouslySetInnerHTML detected. This can lead to Cross-Site Scripting (XSS) if input is not sanitized.", + "message": "Use of dangerouslySetInnerHTML detected. This can lead to Cross-Site Scripting (XSS) if input is not sanitized. [OWASP A03:2021 - Injection]", "extensions": [".jsx", ".tsx"], }, { @@ -231,7 +223,7 @@ r'(?i)(?:query|execute|raw)\s*\(\s*(?:`[^`]*\$\{[^}]+\}[^`]*`|["\'].*?["\']\s*\+\s*[a-zA-Z0-9_]+)' ), "severity": "CRITICAL", - "message": "Potential SQL injection detected: string concatenation or template literal in database query.", + "message": "Potential SQL injection detected: string concatenation or template literal in database query. [OWASP A03:2021 - Injection]", "extensions": [".ts", ".tsx", ".js", ".jsx", ".py"], }, ] @@ -430,6 +422,50 @@ def cmd_scan(args): return 1 if any(f["severity"] in ("CRITICAL", "HIGH") for f in findings) else 0 +def cmd_hook(args): + """Install a pre-commit hook to block commits with vulnerabilities.""" + project_root = Path(".").resolve() + git_dir = project_root / ".git" + + if not git_dir.is_dir(): + print("Error: Not a git repository. Run 'git init' first.", file=sys.stderr) + return 1 + + hooks_dir = git_dir / "hooks" + # SECURITY: Prevent Arbitrary File Write via symlink path traversal + if not hooks_dir.resolve().is_relative_to(project_root): + print(f"Error: Target path {hooks_dir} escapes the project root. Aborting.", file=sys.stderr) + return 1 + + hooks_dir.mkdir(parents=True, exist_ok=True) + pre_commit_file = hooks_dir / "pre-commit" + + if pre_commit_file.is_symlink(): + pre_commit_file.unlink() + + hook_content = """#!/bin/sh +# VibeSec Pre-Commit Hook + +echo "\\nšŸ” Running VibeSec scan..." +vibesec scan . + +if [ $? -ne 0 ]; then + echo "\\nāŒ VibeSec scan failed! Critical or high vulnerabilities found." + echo "Please fix the issues or use '--no-verify' to bypass (not recommended)." + exit 1 +fi + +echo "āœ… VibeSec scan passed." +""" + + pre_commit_file.write_text(hook_content) + pre_commit_file.chmod(pre_commit_file.stat().st_mode | stat.S_IEXEC) + + print("\nāœ… VibeSec pre-commit hook installed successfully at .git/hooks/pre-commit!\n") + print("This will run 'vibesec scan .' before every commit and block commits if vulnerabilities are found.") + return 0 + + # ⚔ Bolt: Cache applicable rules per file extension to avoid redundant list # comprehensions and pre-extract the search method to avoid dictionary and # attribute lookups in the tight scanning loop. @@ -665,6 +701,10 @@ def main(): review_parser.add_argument("--db", help="Database/backend (e.g. supabase, firebase)") review_parser.add_argument("--payments", help="Payment provider (e.g. stripe)") + # hook + hook_parser = subparsers.add_parser("hook", help="Install a pre-commit hook to block commits with vulnerabilities") + + args = parser.parse_args() if args.command == "init": @@ -673,6 +713,8 @@ def main(): sys.exit(cmd_scan(args)) elif args.command == "review": cmd_review(args) + elif args.command == "hook": + sys.exit(cmd_hook(args)) else: parser.print_help() sys.exit(0)