SAST finding triage model — fine-tuned on real production security scan data to classify Static Application Security Testing findings as true positives, false positives, or uncertain, with CWE labels, confidence scores, and remediation guidance.
Overview
Rule-based SAST tools generate enormous volumes of findings, a significant portion of which are false positives. Security analysts spend hours triaging noise instead of fixing real vulnerabilities. teptez-ai is a 7B-parameter LLM fine-tuned specifically to automate this triage step.
Given a SAST finding (title, CWE, severity, code snippet, taint flow), teptez-ai returns a structured JSON verdict:
true_positive — finding is real, exploit path exists
false_positive — finding is noise, safe to suppress
uncertain — insufficient context, escalate to analyst
Fine-tuned on production findings from the Teptez security platform — real codebases, real scan data, real analyst labels.
Key specs
Property
Value
Base model
Qwen2.5-Coder-7B-Instruct
Quantization
GGUF Q4_K_M
Model size
~4.7 GB
Inference speed
~100 tok/s (RTX 3090 24GB)
Context window
8192 tokens
License
Apache 2.0
Benchmark Results
Evaluated against OWASP Benchmark v1.2 — the standard industry benchmark for SAST tools — using the official Youden's J statistic (J = TPR − FPR).
J = 0.0 is random. J = 1.0 is perfect. Open-source SAST tools typically score 0.30–0.45.
Head-to-head vs base model
Model
TPR
FPR
Youden J
vs base
teptez-ai (Q4_K_M)
0.68
0.57
0.109
+0.048 (+79%)
qwen2.5-coder-7b (base)
0.66
0.60
0.061
—
Fine-tuning delivers a 79% relative improvement in Youden's J over the base model, primarily by cutting the false positive rate from 0.60 to 0.57 across the full benchmark.
Per-category breakdown
CWE Category
teptez-ai J
base J
Delta
Command Injection (CWE-78)
0.40
0.22
+0.18
SQL Injection (CWE-89)
0.33
0.18
+0.15
XSS (CWE-79)
0.28
0.12
+0.16
Path Traversal (CWE-22)
0.22
0.09
+0.13
Weak Randomness (CWE-330)
0.13
0.05
+0.08
Crypto/Hash (CWE-327/328)
0.00
0.01
-0.01
Auth/Authz (CWE-862/639)
0.02
0.01
+0.01
Timing (CWE-208)
0.05
0.04
+0.01
Secure Cookie (CWE-614)
-0.08
0.52
-0.60 ⚠️
Strong on injection classes. teptez-ai significantly outperforms the base model across all injection-type CWEs (cmdi/sqli/xss/path/weakrand). These are the highest-volume SAST categories in real codebases.
Securecookie regression. CWE-614 (missing HttpOnly/Secure flags) shows a severe regression vs the base model. Do not use teptez-ai to triage cookie security findings. This is a known training artifact being fixed in the next round.
Dead categories. Crypto, authz, and timing categories have near-zero Youden J on both models — 7B parameters are insufficient for these without full class context. Escalate to frontier models or human analysts.
Production run
On 369 real production SAST findings from live codebases:
17% rejected as false positive (~63 findings suppressed)
Injection-class findings: majority of suppressions, generally accurate
Authz/crypto findings: some wrong suppressions (do not enable for these categories)
Usage
Recommended architecture
Use teptez-ai as a gated FP suppressor, not a confirmer:
Rule-engine finding
│
▼
Is CWE in injection classes? ──No──▶ Keep finding (don't run model)
│ Yes
▼
Run teptez-ai with full function + taint context
│
├── verdict: false_positive, confidence > 0.75 ──▶ Suppress finding
├── verdict: true_positive ──▶ Keep finding
└── verdict: uncertain / confidence < 0.75 ──▶ Escalate to frontier model / analyst
Only suppress on false_positive — never on true_positive. The model is biased toward flagging (FPR 0.57), so a false_positive verdict is rare and higher-precision.
Injection-class CWEs only (where Youden J ≥ 0.13):
CWE-78 Command Injection
CWE-79 Cross-Site Scripting
CWE-89 SQL Injection
CWE-22 Path Traversal
CWE-330 Weak Randomness
Never auto-suppress:
CWE-614 Secure Cookie (regression — model worse than random)
CWE-327/328 Weak Crypto/Hash (near-zero J)
CWE-862/639 Auth/Authz/IDOR (near-zero J)
CWE-208 Timing Attacks (near-zero J)
Running with llama.cpp / Ollama
bash
1# Pull via Ollama2ollama pull hf.co/Kuyash/teptez-ai:Q4_K_M
34# Or run directly with llama.cpp5./llama-cli -m teptez-ai-Q4_K_M.gguf \6 --temp 0.1\7 --top-p 0.9\8 -n 512\9 -p "<prompt>"
Python integration
python
1import json
2import requests
34deftriage_finding(finding:dict)->dict:5 prompt =f"""<|im_start|>system
6You are a SAST triage expert. Analyze this finding and return JSON with keys:
7verdict (true_positive|false_positive|uncertain), confidence (0.0-1.0),
8cwe (string), explanation (string), remediation (string).
9<|im_end|>
10<|im_start|>user
11Finding: {finding['title']}12CWE: {finding.get('cwe_id','unknown')}13Severity: {finding.get('severity','MEDIUM')}14Code:
15{finding.get('code_snippet','')}1617Taint flow: {finding.get('data_flow','not available')}18<|im_end|>
19<|im_start|>assistant
20"""21 response = requests.post("http://localhost:11434/api/generate", json={22"model":"teptez-ai",23"prompt": prompt,24"stream":False,25"options":{"temperature":0.1}26})27 text = response.json()["response"].strip()28# Strip markdown fences if present29if text.startswith("```"):30 text = text.split("```")[1]31if text.startswith("json"):32 text = text[4:]33return json.loads(text)3435# Gate: only run on injection CWEs36INJECTION_CWES ={"CWE-78","CWE-79","CWE-89","CWE-22","CWE-330"}3738defshould_suppress(finding:dict)->bool:39 cwe = finding.get("cwe_id","")40if cwe notin INJECTION_CWES:41returnFalse# don't touch non-injection42 result = triage_finding(finding)43return(44 result.get("verdict")=="false_positive"45and result.get("confidence",0)>=0.7546)
Input / Output Format
Prompt template
<|im_start|>system
You are a SAST triage expert. Analyze this finding and return JSON.
<|im_end|>
<|im_start|>user
Finding: {title}
CWE: {cwe_id}
Severity: {severity}
Code:
{code_snippet}
Taint flow: {data_flow}
<|im_end|>
<|im_start|>assistant
Tips for best results:
Provide the full function, not just the flagged line — avoids "insufficient context" errors
Include taint flow when available (source → sink path from your SAST tool)
Keep code under 2048 tokens; truncate from the bottom if needed
Output schema
json
1{2"verdict":"true_positive" | "false_positive" | "uncertain",3"confidence":0.85,4"cwe":"CWE-89",5"explanation":"User input from request.getParameter() flows directly into a string-concatenated SQL query with no parameterization or escaping.",6"remediation":"Replace string concatenation with a PreparedStatement: `conn.prepareStatement(\"SELECT * FROM users WHERE id = ?\")` and bind the parameter with `stmt.setString(1, userId)`."7}
Field
Type
Description
verdict
string
true_positive, false_positive, or uncertain
confidence
float
0.0–1.0; scores < 0.75 should be treated as uncertain
cwe
string
Classified CWE identifier
explanation
string
Why the model reached this verdict
remediation
string
Concrete fix recommendation
Limitations
Known issues (as of current release)
CWE-614 Secure Cookie — severe regression.
teptez-ai scores Youden J = −0.08 on secure cookie findings, compared to 0.52 for the base model. This is a catastrophic regression caused by training data imbalance. Do not use teptez-ai for HttpOnly/Secure flag findings until this is fixed.
High overall FPR (0.57).
The model over-flags — it sees vulnerability in safe code, especially in crypto, auth, and cookie-related code patterns. A false_positive verdict is more reliable than a true_positive verdict because it swims against the model's bias.
Dead categories (crypto/authz/timing).
CWE-327/328/614/862/639/208 have near-zero Youden J. The model lacks sufficient training signal for these categories. Use a frontier model (Claude, GPT-4o, Gemini) or a human analyst for these.
7B parameter ceiling.
Subtle IDOR, broken access control, and privilege escalation patterns require understanding class hierarchy, authentication flow, and business logic across multiple files. A 7B model with single-function context cannot reliably detect these.
GGUF Q4_K_M quantization.
~4-bit quantization introduces slight accuracy loss vs fp16. For maximum accuracy, use the Q8_0 variant (if available) or the full fp16 model.
Snippet-only inputs fail.
If you pass only the flagged 1–3 lines without the surrounding function, the model frequently returns uncertain with "insufficient context." Always include the full function body.
Reproducing the Benchmark
bash
1# 1. Clone OWASP Benchmark2git clone https://github.com/OWASP-Benchmark/BenchmarkJava
3cd BenchmarkJava && mvn package -DskipTests
45# 2. Run evaluation (requires teptez-ai running on Ollama)6python salad/eval/owasp_eval.py # stratified sample → results.jsonl7python salad/eval/owasp_score.py # Youden J + per-category table8python salad/eval/owasp_compare.py # head-to-head vs base model
The eval script uses a stratified sample of OWASP BenchmarkJava test cases, covering all CWE categories proportionally. Scoring follows the official OWASP methodology (Youden's J = TPR − FPR).
Roadmap
The following improvements are planned for the next fine-tuning round:
Round 2 targets
Improvement
Target metric
Flood training with safe-code negatives (50/50 balance)
FPR < 0.30
Fix securecookie regression (restore CWE-614 training data)
Root cause of FPR problem: Current training set is vuln-heavy (more vulnerable examples than safe ones). The model learned to flag aggressively. Rebalancing to 50/50 with explicit safe variants (parameterized SQL, escaped HTML, validated paths, compare_digest timing-safe comparisons, role-checked endpoints, strong ciphers) is the single highest-leverage fix.
Securecookie fix: Restore the original training examples for CWE-614 that were accidentally dropped. Mix with new negative examples. Lower learning rate for this category to avoid forgetting again.
No catastrophic forgetting: Round 2 will mix old injection data with new negatives and use a lower learning rate on the balanced set, following standard continual learning practice.
teptez-ai is a security research model. Results may vary across codebases and languages. Always have a human analyst review suppressed findings in critical security contexts.