LLM-native vulnerability scanning.
Traditional SAST tools use pattern matching and taint analysis โ rule engines that flag code based on signatures written by security engineers. They are fast, deterministic, and brittle. A new vulnerability class means new rules. An unusual framework means no coverage.
The AI Scanner takes a different approach: it uses a generative language model to read code and reason about its security properties, the same way a senior security engineer would. It understands context, control flow semantics, and the relationship between a code pattern and the class of attack it enables โ without being constrained to a predefined rule set.
Model ID: AquilaX-AI/Ai Scanner โ available on HuggingFace. Base: unsloth/Qwen2.5-Coder-3B-Instruct. Fine-tuning: LoRA rank 512, 4-bit quantization. Trained on 100K source files, 5 epochs. Max sequence length: 8192 tokens.
Architecture and training.
The AI Scanner is built on unsloth/Qwen2.5-Coder-3B-Instruct โ a 3 billion parameter code-specialised language model. The base model's instruction-following capability is retained and extended via LoRA fine-tuning specifically for vulnerability detection tasks.
Key training configuration:
- Base model:
unsloth/Qwen2.5-Coder-3B-Instruct(3B parameters) - Fine-tuning method: LoRA (Low-Rank Adaptation)
- LoRA rank: 512 โ substantially higher than typical LoRA applications, enabling the model to deeply adapt its code analysis behaviour
- Quantization: 4-bit (NF4) โ enables deployment on standard GPU hardware without large VRAM requirements
- Training corpus: 100,000 source files across Python, JavaScript, TypeScript, Java, Go, PHP, Ruby, and C/C++
- Training epochs: 5
- Max sequence length: 8,192 tokens โ handles files up to ~600 lines of code in a single pass
Training data composition: The 100K training files are drawn from real-world open-source repositories, manually reviewed and annotated with ground-truth vulnerability labels. Vulnerable files include confirmed CVEs, CWE-labelled code patterns, and AquilaX scan findings validated by security engineers.
Output schema.
The AI Scanner outputs a JSON array of vulnerability objects. Each finding in the array contains the following fields:
| Field | Type | Description |
|---|---|---|
| vulnerability | string | Vulnerability class name (e.g. "SQL Injection", "Path Traversal", "XSS") |
| cwe_id | string | CWE identifier as a string (e.g. "CWE-89", "CWE-22", "CWE-79") |
| description | string | Plain-English description of why this code pattern is vulnerable and what attack it enables |
| vuln_code | string | The specific line or expression identified as the vulnerability source |
| code_snipped | string | Broader code context surrounding the vulnerability (ยฑ5 lines) |
Note on field name: The output field is code_snipped (not code_snippet) โ this is intentional for backwards compatibility with the AquilaX findings schema.
Input / Output example.
The model receives source code as input and returns a JSON array of findings. Below is a full example:
import sqlite3
from flask import request
def get_user(user_id):
conn = sqlite3.connect('app.db')
cursor = conn.cursor()
query = (
"SELECT * FROM users "
"WHERE id = " + user_id
)
cursor.execute(query)
return cursor.fetchone()
def upload_file():
filename = request.form['name']
path = '/uploads/' + filename
with open(path, 'wb') as f:
f.write(request.data)
return path
[
{
"vulnerability":
"SQL Injection",
"cwe_id": "CWE-89",
"description":
"User-controlled input
concatenated directly
into SQL query string.
Enables full DB
compromise.",
"vuln_code":
"'WHERE id = '+user_id",
"code_snipped":
"query = (\"SELECT *
FROM users WHERE id
= \" + user_id)\n
cursor.execute(query)"
},
{
"vulnerability":
"Path Traversal",
"cwe_id": "CWE-22",
"description":
"Unsanitised filename
from HTTP form data
used to construct
filesystem path.",
"vuln_code":
"'/uploads/'+filename",
"code_snipped":
"filename = request
.form['name']\npath
= '/uploads/'+filename"
}
]
Batch inference.
For scanning entire repositories, the AI Scanner supports batch mode โ processing multiple files in a single model session. The AquilaX pipeline uses batch inference to scan all files in a repository concurrently across multiple worker threads.
from transformers import AutoTokenizer, AutoModelForCausalLM import json model_id = "AquilaX-AI/Ai Scanner" tokenizer = AutoTokenizer.from_pretrained(model_id) model = AutoModelForCausalLM.from_pretrained( model_id, load_in_4bit=True, device_map="auto" ) SYSTEM_PROMPT = """You are a security scanner. Analyse the provided source code and return a JSON array of vulnerability findings. Each finding must include: vulnerability, cwe_id, description, vuln_code, code_snipped. Return an empty array [] if no vulnerabilities are found.""" def scan_file(source_code: str) -> list: messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": f"Scan this code:\n\n{source_code}"} ] text = tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True ) inputs = tokenizer([text], return_tensors="pt").to("cuda") outputs = model.generate( **inputs, max_new_tokens=2048, temperature=0.1, do_sample=True, pad_token_id=tokenizer.eos_token_id ) response = tokenizer.decode( outputs[0][inputs["input_ids"].shape[-1]:], skip_special_tokens=True ) try: findings = json.loads(response) except json.JSONDecodeError: findings = [] return findings
Streaming inference.
For interactive use cases โ such as the AquilaX IDE extension scanning files on save โ the AI Scanner supports streaming token output. The JSON response is built incrementally and parsed once the full generation is complete.
from transformers import TextIteratorStreamer from threading import Thread streamer = TextIteratorStreamer( tokenizer, skip_prompt=True, skip_special_tokens=True ) generation_kwargs = { **inputs, "streamer": streamer, "max_new_tokens": 2048, "temperature": 0.1, "do_sample": True, "pad_token_id": tokenizer.eos_token_id } # Run generation in a background thread thread = Thread(target=model.generate, kwargs=generation_kwargs) thread.start() # Consume tokens as they are generated full_output = "" for token in streamer: full_output += token print(token, end="", flush=True) thread.join() findings = json.loads(full_output)
"We're not replacing rule-based scanners โ we're augmenting them with a model that can reason about code the way a security engineer does, catching vulnerabilities that patterns can never anticipate."
In the AquilaX pipeline, the AI Scanner runs in parallel with rule-based engines. Findings from all sources are deduplicated and passed through the Review model before reaching the dashboard โ ensuring that the AI Scanner's generative outputs are held to the same false-positive elimination standard as every other engine in the platform.