GC-301h · Module 3
Security Scanning
3 min read
AI-powered security scanning complements static analysis tools (SAST) by catching semantic vulnerabilities that pattern-matching tools miss. A regex can find hardcoded passwords. An LLM can find business logic flaws — an authorization check that verifies the user is authenticated but not that they own the resource, a rate limiter that checks per-IP but not per-account, a file upload handler that validates the extension but not the content type. These are the vulnerabilities that ship to production because they pass every static checker.
Security scanning prompts must be specific about the vulnerability classes to check. A generic "find security issues" prompt produces vague results. A prompt that says "Check for OWASP Top 10 categories: injection, broken auth, sensitive data exposure, XXE, broken access control, security misconfiguration, XSS, insecure deserialization, known vulnerable components, insufficient logging" produces actionable findings. Include your application's technology stack in the prompt so Gemini can focus on framework-specific vulnerabilities.
#!/bin/bash
# Security scanning with categorized findings
set -euo pipefail
SCAN_PROMPT=$(cat <<'PROMPT'
Perform a security audit of this code diff. Check specifically for:
1. SQL/NoSQL injection
2. Broken authentication or authorization
3. Sensitive data exposure (credentials, PII in logs)
4. Cross-site scripting (XSS)
5. Insecure deserialization
6. Missing input validation
7. Path traversal
8. SSRF (Server-Side Request Forgery)
For each finding, provide:
- OWASP category
- Severity (critical/high/medium/low)
- File and line number
- Description
- Remediation
Output JSON: {findings: [{owasp, severity, file, line, description, remediation}], clean: boolean}
PROMPT
)
DIFF=$(git diff origin/main...HEAD)
RESULT=$(gemini -p "$SCAN_PROMPT\n\n$DIFF" --output-format json 2>/dev/null)
# Check for critical/high findings
CRIT_COUNT=$(echo "$RESULT" | jq '[.response.findings[]? | select(.severity == "critical" or .severity == "high")] | length')
if [ "$CRIT_COUNT" -gt 0 ]; then
echo "::error::Security scan found $CRIT_COUNT critical/high vulnerabilities"
echo "$RESULT" | jq '.response.findings[] | select(.severity == "critical" or .severity == "high")'
exit 1
fi
echo "Security scan passed"