Lab: Scanning with Garak
Install and run the Garak LLM vulnerability scanner against local models. Learn to configure probes, generators, and evaluators for comprehensive scanning.
Prerequisites
- Completed Environment Setup
- Completed Building a Test Harness (for comparison)
- Ollama running with at least one model installed
- Garak installed (
pip install garak)
Background
Garak (Generative AI Red-teaming and Assessment Kit) is the most widely used open-source tool for automated LLM vulnerability scanning. It provides a structured framework with hundreds of pre-built attack probes organized by vulnerability category.
For a broader overview of red teaming tools, see Tool Landscape and Automated Scanning.
Lab Exercises
Verify Garak Installation
Check that Garak is installed and accessible:
garak --version garak --list_probes | head -20Expected output:
garak v0.9.x probes: atkgen continuation dan encoding gcg glitch goodside knowledgegraph leakreplay lmrc malwaregen ...If
garakis not found, install it:pip install garakUnderstand Garak's Architecture
Before running scans, understand how the four components interact:
Component Role Example Generator Interface to the target model ollamagenerator for local modelsProbe Attack payload or test case dan.DANsends DAN-style jailbreaksDetector Analyzes model responses Checks if safety refusal was present Evaluator Makes pass/fail decisions Determines if the probe succeeded List available components:
# List all probe categories garak --list_probes # List all available generators garak --list_generators # List all detectors garak --list_detectorsRun Your First Scan
Run a basic scan against a local Ollama model using a small set of probes:
garak --model_type ollama --model_name llama3.2 \ --probes dan.DAN_Jailbreak \ -vThis command:
- Targets
llama3.2via the Ollama backend - Runs the
DAN_Jailbreakprobe (a classic jailbreak test) - Uses verbose mode (
-v) to show detailed output
Expected output:
garak LLM vulnerability scanner v0.9.x Running probes: dan.DAN_Jailbreak Target: ollama:llama3.2 dan.DAN_Jailbreak: 100%|████████████████| 10/10 Results ┌───────────────────────┬────────┬────────┐ │ Probe │ Passed │ Failed │ ├───────────────────────┼────────┼────────┤ │ dan.DAN_Jailbreak │ 7 │ 3 │ └───────────────────────┴────────┴────────┘ Report saved to: garak_runs/...- Targets
Run a Multi-Category Scan
Run a broader scan covering multiple vulnerability categories:
garak --model_type ollama --model_name llama3.2 \ --probes dan,encoding,continuation,leakreplay \ -vThis tests four categories:
- dan: DAN-style jailbreaks and persona attacks
- encoding: Base64, ROT13, and other encoding bypasses
- continuation: Prompt continuation attacks
- leakreplay: System prompt extraction attempts
The scan may take several minutes depending on your hardware.
Interpret Scan Results
Garak generates an HTML report and a JSONL log file. Open the HTML report:
# Find the latest report ls -la garak_runs/ # Open in browser (Linux) xdg-open garak_runs/garak.*.html # Or examine the JSONL log programmatically python3 -c " import json import sys results = [] with open(sys.argv[1]) as f: for line in f: results.append(json.loads(line)) # Count results by probe from collections import Counter probes = Counter() for r in results: probe = r.get('probe', 'unknown') status = r.get('status', 'unknown') probes[f'{probe}:{status}'] += 1 for key, count in sorted(probes.items()): print(f'{key}: {count}') " garak_runs/garak.*.jsonlKey things to look for in the results:
- Failure rate by category: Which vulnerability types had the highest failure rate?
- Specific failing probes: Which exact prompts caused the model to fail?
- Consistency: Does the model fail consistently or intermittently on the same probe?
Configure Custom Scans
Create a Garak configuration file for targeted scanning. Save as
garak_config.yaml:# Custom Garak scan configuration plugins: generators: - ollama probes: - dan.DAN_Jailbreak - dan.ChatGPT_Developer_Mode - encoding.InjectBase64 - encoding.InjectROT13 - leakreplay.LiteralSequenceReplay - continuation.ContinueSlur extended: generations: 5 # Number of attempts per probe seed: 42 # For reproducibilityRun with your custom configuration:
garak --model_type ollama --model_name llama3.2 \ --config garak_config.yamlCompare with Manual Results
Compare Garak's automated results with the findings from your manual test harness (built in the previous lab):
#!/usr/bin/env python3 """Compare Garak results with manual test harness results.""" import json import csv import os from collections import defaultdict def load_garak_results(jsonl_path): """Load results from a Garak JSONL log.""" results = defaultdict(lambda: {"pass": 0, "fail": 0}) with open(jsonl_path) as f: for line in f: data = json.loads(line) probe = data.get("probe", "unknown") if data.get("status") == "pass": results[probe]["pass"] += 1 else: results[probe]["fail"] += 1 return dict(results) def load_harness_results(csv_path): """Load results from the manual test harness CSV.""" results = defaultdict(lambda: {"pass": 0, "fail": 0}) with open(csv_path) as f: reader = csv.DictReader(f) for row in reader: category = row.get("category", "unknown") if row.get("success", "").lower() == "true": results[category]["fail"] += 1 # Attack success = defense failure else: results[category]["pass"] += 1 return dict(results) def compare(garak, harness): print(f"\n{'Category':<30} {'Garak ASR':>10} {'Harness ASR':>12}") print("-" * 55) all_categories = set(list(garak.keys()) + list(harness.keys())) for cat in sorted(all_categories): g = garak.get(cat, {"pass": 0, "fail": 0}) h = harness.get(cat, {"pass": 0, "fail": 0}) g_total = g["pass"] + g["fail"] h_total = h["pass"] + h["fail"] g_asr = 100 * g["fail"] / g_total if g_total > 0 else 0 h_asr = 100 * h["fail"] / h_total if h_total > 0 else 0 print(f"{cat:<30} {g_asr:>9.1f}% {h_asr:>11.1f}%") if __name__ == "__main__": # Update these paths to match your actual output files garak_path = "garak_runs/garak.latest.jsonl" harness_path = "results/test_run_latest.csv" if os.path.exists(garak_path) and os.path.exists(harness_path): garak = load_garak_results(garak_path) harness = load_harness_results(harness_path) compare(garak, harness) else: print("Run both Garak and the manual harness first to compare.")
Troubleshooting
| Issue | Solution |
|---|---|
garak: command not found | Ensure your virtual environment is activated and run pip install garak |
| Ollama connection refused | Start Ollama with ollama serve and verify it is running on port 11434 |
| Scan hangs or is very slow | Reduce the number of probes or set generations: 1 in config |
| JSONL file is empty | Check the Garak log output for errors; ensure the model is responding |
| HTML report not generated | Update Garak: pip install --upgrade garak |
Related Topics
- Building a Test Harness - The custom harness you built, which follows the same architectural pattern as Garak
- PyRIT Campaigns - Microsoft's PyRIT framework for orchestrated red teaming campaigns
- Promptfoo Regression Testing - Another automated testing tool that complements Garak
- Tool Landscape - Comprehensive overview of all available red teaming tools and frameworks
References
- "Garak: A Framework for LLM Vulnerability Scanning" - NVIDIA/garak (2024) - Official documentation and probe catalog for the Garak scanner
- "Red Teaming Language Models to Reduce Harms" - Ganguli et al. (2022) - Research on systematic vulnerability scanning methodologies
- "OWASP Top 10 for LLM Applications" - OWASP (2025) - The vulnerability categories that Garak probes are designed to test
- "Garak GitHub Repository" - NVIDIA (2024) - Source code, probe definitions, and contribution guidelines for extending Garak
In Garak's output, what does 'Passed' mean?
What are the four modular components of Garak's architecture?