CodeQL Analysis
Supported languages: Python, JavaScript/TypeScript, Go, Java/Kotlin, C/C++, C#, Ruby, Swift.
Skill resources: Reference files and templates are located at {baseDir}/references/ and {baseDir}/workflows/.
Essential Principles
- Database quality is non-negotiable. A database that builds is not automatically good. Always run quality assessment (file counts, baseline LoC, extractor errors) and compare against expected source files. A cached build produces zero useful extraction.
- Data extensions catch what CodeQL misses. Even projects using standard frameworks (Django, Spring, Express) have custom wrappers around database calls, request parsing, or shell execution. Skipping the create-data-extensions workflow means missing vulnerabilities in project-specific code paths.
- Explicit suite references prevent silent query dropping. Never pass pack names directly to
codeql database analyze— each pack'sdefaultSuiteFileapplies hidden filters that can produce zero results. Always generate a custom.qlssuite file.
- Zero findings needs investigation, not celebration. Zero results can indicate poor database quality, missing models, wrong query packs, or silent suite filtering. Investigate before reporting clean.
- macOS Apple Silicon requires workarounds for compiled languages. Exit code 137 is
arm64e/arm64mismatch, not a build failure. Try Homebrew arm64 tools or Rosetta before falling back tobuild-mode=none.
- Follow workflows step by step. Once a workflow is selected, execute it step by step without skipping phases. Each phase gates the next — skipping quality assessment or data extensions leads to incomplete analysis.
Output Directory
All generated files (database, build logs, diagnostics, extensions, results) are stored in a single output directory.
- If the user specifies an output directory in their prompt, use it as
OUTPUT_DIR. - If not specified, default to
./static_analysis_codeql_1. If that already exists, increment to_2,_3, etc.
In both cases, always create the directory with mkdir -p before writing any files.
# Resolve output directory
if [ -n "$USER_SPECIFIED_DIR" ]; then
OUTPUT_DIR="$USER_SPECIFIED_DIR"
else
BASE="static_analysis_codeql"
N=1
while [ -e "${BASE}_${N}" ]; do
N=$((N + 1))
done
OUTPUT_DIR="${BASE}_${N}"
fi
mkdir -p "$OUTPUT_DIR"
The output directory is resolved once at the start before any workflow executes. All workflows receive $OUTPUT_DIR and store their artifacts there:
$OUTPUT_DIR/
├── rulesets.txt # Selected query packs (logged after Step 3)
├── codeql.db/ # CodeQL database (dir containing codeql-database.yml)
├── build.log # Build log
├── codeql-config.yml # Exclusion config (interpreted languages)
├── diagnostics/ # Diagnostic queries and CSVs
├── extensions/ # Data extension YAMLs
├── raw/ # Unfiltered analysis output
│ ├── results.sarif
│ └── <mode>.qls
└── results/ # Final results (filtered for important-only, copied for run-all)
└── results.sarif
Database Discovery
A CodeQL database is identified by the presence of a codeql-database.yml marker file inside its directory. When searching for existing databases, always collect all matches — there may be multiple databases from previous runs or for different languages.
Discovery command:
# Find ALL CodeQL databases (top-level and one subdirectory deep)
find . -maxdepth 3 -name "codeql-database.yml" -not -path "*/\.*" 2>/dev/null \
| while read -r yml; do dirname "$yml"; done
- Inside
$OUTPUT_DIR:find "$OUTPUT_DIR" -maxdepth 2 -name "codeql-database.yml" - Project-wide (for auto-detection):
find . -maxdepth 3 -name "codeql-database.yml"— covers databases at the project top level (./db-name/) and one subdirectory deep (./subdir/db-name/). Does not search deeper.
Never assume a database is named codeql.db — discover it by its marker file.
When multiple databases are found:
For each discovered database, collect metadata to help the user choose:
# For each database, extract language and creation time
for db in $FOUND_DBS; do
CODEQL_LANG=$(codeql resolve database --format=json -- "$db" 2>/dev/null | jq -r '.languages[0]')
CREATED=$(grep '^creationMetadata:' -A5 "$db/codeql-database.yml" 2>/dev/null | grep 'creationTime' | awk '{print $2}')
echo "$db — language: $CODEQL_LANG, created: $CREATED"
done
Then use AskUserQuestion to let the user select which database to use, or to build a new one. Skip AskUserQuestion if the user explicitly stated which database to use or to build a new one in their prompt.
Quick Start
For the common case ("scan this codebase for vulnerabilities"):
# 1. Verify CodeQL is installed
if ! command -v codeql >/dev/null 2>&1; then
echo "NOT INSTALLED: codeql binary not found on PATH"
else
codeql --version || echo "ERROR: codeql found but --version failed (check installation)"
fi
# 2. Resolve output directory
BASE="static_analysis_codeql"; N=1
while [ -e "${BASE}_${N}" ]; do N=$((N + 1)); done
OUTPUT_DIR="${BASE}_${N}"; mkdir -p "$OUTPUT_DIR"
Then execute the full pipeline: build database → create data extensions → run analysis using the workflows below.
When to Use
- Scanning a codebase for security vulnerabilities with deep data flow analysis
- Building a CodeQL database from source code (with build capability for compiled languages)
- Finding complex vulnerabilities that require interprocedural taint tracking or AST/CFG analysis
- Performing comprehensive security audits with multiple query packs
When NOT to Use
- Writing custom queries - Use a dedicated query development skill
- CI/CD integration - Use GitHub Actions documentation directly
- Quick pattern searches - Use Semgrep or grep for speed
- No build capability for compiled languages - Consider Semgrep instead
- Single-file or lightweight analysis - Semgrep is faster for simple pattern matching
Rationalizations to Reject
These shortcuts lead to missed findings. Do not accept them:
- "security-extended is enough" - It is the baseline. Always check if Trail of Bits packs and Community Packs are available for the language. They catch categories
security-extendedmisses entirely. - "The database built, so it's good" - A database that builds does not mean it extracted well. Always run quality assessment and check file counts against expected source files.
- "Data extensions aren't needed for standard frameworks" - Even Django/Spring apps have custom wrappers that CodeQL does not model. Skipping extensions means missing vulnerabilities.
- "build-mode=none is fine for compiled languages" - It produces severely incomplete analysis. Only use as an absolute last resort. On macOS, try the arm64 toolchain workaround or Rosetta first.
- "The build fails on macOS, just use build-mode=none" - Exit code 137 is caused by
arm64e/arm64mismatch, not a fundamental build failure. See [macos-arm64e-workaround.md](references/macos-arm64e-workaround.md). - "No findings means the code is secure" - Zero findings can indicate poor database quality, missing models, or wrong query packs. Investigate before reporting clean results.
- "I'll just run the default suite" / "I'll just pass the pack names directly" - Each pack's
defaultSuiteFileapplies hidden filters and can produce zero results. Always use an explicit suite reference. - "I'll put files in the current directory" - All generated files must go in
$OUTPUT_DIR. Scattering files in the working directory makes cleanup impossible and risks overwriting previous runs. - "Just use the first database I find" - Multiple databases may exist for different languages or from previous