Automated fix generation for security vulnerabilities detected by taint analysis.
Table of Contents¶
- Overview
- Quick Start
- How It Works
- Supported Vulnerability Types
- Confidence Scoring
- Configuration Reference
- Approval Flow
- Extending with Domain Templates
- Examples
Overview¶
CodeGraph’s AutofixEngine takes detected vulnerabilities (taint paths from untrusted sources to dangerous sinks) and generates concrete code patches. Three strategies are used in order of priority:
- SSR (Structural Search & Replace) — AST-aware rules via
SSRAutofixBridge, highest confidence - Template-based — regex pattern-matching fix templates via
AutofixGenerator(high confidence, >0.8) - LLM-powered — fallback to LLM generation via
PromptBuilderwhen no template matches (lower confidence, <=0.59)
All fixes are validated by DiffValidator and presented as unified diffs. In read-only / dry-run mode (default), no files are modified.
Quick Start¶
CLI¶
# Run security audit with autofix suggestions
python -m src.cli audit --db data/projects/postgres.duckdb --autofix
# Non-interactive CI mode
python -m src.cli exec --prompt "Fix all SQL injection findings" \
--sandbox read-only --output-file results.json
REST API¶
curl -X POST http://localhost:8000/api/v1/security/autofix \
-H "Content-Type: application/json" \
-d '{"method_name": "process_query"}'
MCP (AI Assistant)¶
codegraph_autofix(method_name="process_query", cwe="CWE-89")
How It Works¶
TaintPath
|
v
AutofixEngine
|
+---> Parse sink_location -> (file_path, line_number)
|
+---> Read source code window (configurable context lines)
|
+---> Infer vulnerability_type from sink_category
|
+---> Try SSR fix (SSRAutofixBridge — AST-aware rules via gocpg)
| |
| +---> Match? Return FixSuggestion (strategy="ssr")
| |
| +---> No match? Fall through to template
|
+---> Try template-based fix (AutofixGenerator)
| |
| +---> Match? Return FixSuggestion (strategy="template")
| |
| +---> No match? Fall through to LLM
|
+---> LLM fallback (PromptBuilder + LLM provider)
| |
| +---> Build structured prompt with taint flow context
| +---> Call configured LLM provider
| +---> Parse code from response
| +---> Return FixSuggestion (strategy="llm")
|
+---> DiffValidator
| |
| +---> Verify original code exists in file
| +---> Check change ratio is minimal
| +---> Generate unified diff
|
v
AutofixResult (diff, confidence, strategy, validated)
Supported Vulnerability Types¶
| Vulnerability | CWE | Languages | Templates |
|---|---|---|---|
| SQL Injection | CWE-89 | C, Python, Go | sprintf->snprintf, %-format->params, fstring->params, concat->params, Sprintf->$1 |
| Buffer Overflow | CWE-120 | C, Python, Go | strcpy->strncpy, sprintf->snprintf, ctypes size limit, CGO strncpy |
| Command Injection | CWE-78 | C, Python, Go | system->execv, shell=True->shlex, bash -c->direct |
| Null Dereference | CWE-476 | C | NULL check if, NULL check Assert |
| Use-After-Free | CWE-416 | C | NULL after free |
| Info Disclosure | CWE-200 | C | Sanitize error messages |
| Integer Overflow | CWE-190 | C | palloc size check (domain-specific, e.g. PostgreSQL mul_size()) |
Domain plugins may define additional templates in src/domains/{name}/config/security.yaml.
Confidence Scoring¶
| Strategy | Confidence Range | Meaning |
|---|---|---|
| SSR (AST-aware) | 0.8 - 1.0 | Highest confidence, AST-verified |
| Template (simple pattern) | 0.8 - 1.0 | High confidence, safe to apply |
| Template (complex transform) | 0.6 - 0.8 | Good confidence, review recommended |
| LLM-generated | <= 0.59 | Lower confidence, manual review required |
| Failed validation | 0.0 | Diff does not apply cleanly |
Confidence calculation:
- Base: autofix_base (default 0.7)
- Simple patterns boost: +autofix_pattern_boost (default +0.2)
- Complex transforms penalty: +autofix_complexity_penalty (default -0.1)
- Short code bonus: +autofix_context_boost (default +0.1)
Configuration Reference¶
In config.yaml:
autofix:
enabled: true # Enable autofix engine
context_lines_before: 5 # Lines of context before target line
context_lines_after: 5 # Lines of context after target line
llm_max_confidence: 0.6 # Max confidence for LLM fixes
llm_temperature: 0.1 # LLM temperature (low for deterministic)
llm_max_tokens: 2048 # Max tokens for LLM response
max_fixes_per_run: 10 # Limit fixes per invocation
require_approval: true # Require approval before applying
Confidence tuning in config.yaml under workflows.handlers.confidence_settings:
confidence_settings:
autofix_base: 0.7
autofix_pattern_boost: 0.2
autofix_complexity_penalty: -0.1
autofix_context_boost: 0.1
Approval Flow¶
When require_approval: true (default), fixes are never applied silently:
- Interactive mode (CLI): Each fix is presented with its diff for accept/decline
- CI mode (
exec): Fixes are returned as structured JSON without applying - REST API / MCP server: Always read-only, returns diffs only
Approval decisions:
- accept — apply this fix
- accept_for_session — auto-approve similar fixes (same CWE class)
- decline — skip this fix
- cancel — abort entire autofix run
Extending with Domain Templates¶
Domain-specific autofix patterns are loaded from the active domain plugin’s YAML config. To add templates for a new domain:
- Add patterns to
src/domains/{name}/config/security.yamlunderautofix_patterns:
autofix_patterns:
sql_injection:
- pattern_name: "domain_specific_fix"
pattern_regex: "dangerous_func\\((.+?)\\)"
replacement_template: "safe_func({param})"
explanation: "Use safe_func() instead of dangerous_func()"
- The
AutofixGeneratorautomatically loads these viaget_autofix_patterns_from_plugin().
Examples¶
SQL Injection Fix (C)¶
Before:
sprintf(query, "SELECT * FROM t WHERE id = %s", user_input);
After:
snprintf(query, sizeof(query), "SELECT * FROM t WHERE id = %s", user_input);
Buffer Overflow Fix (C)¶
Before:
strcpy(dest, src);
After:
strncpy(dest, src, sizeof(dest) - 1); dest[sizeof(dest) - 1] = '\0';
SQL Injection Fix (Python)¶
Before:
cursor.execute("SELECT * FROM users WHERE id = %s" % user_id)
After:
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
SQL Injection Fix (Go)¶
Before:
db.Query(fmt.Sprintf("SELECT * FROM users WHERE id = %s", id))
After:
db.Query("SELECT * FROM users WHERE id = $1", id)