Python client library for the Garak AI Security Platform
The Garak SDK provides programmatic access to the Garak AI Security Platform for running security scans against AI models, discovering vulnerabilities, and integrating security testing into your CI/CD pipelines.
- 🔒 Security Scanning - Run comprehensive security scans against AI models
- 🤖 Multiple Generators - Support for OpenAI, Anthropic, HuggingFace, and more
- 🎯 Probe Categories - Test for jailbreaks, harmful content, privacy violations, and more
- 📊 Detailed Reports - Download JSON, JSONL, and HTML reports
- ⚡ CI/CD Integration - Built for automation and continuous integration
- 🔄 Async Support - Efficient polling and waiting mechanisms
- 🛡️ Type Safe - Full Pydantic model support
pip install garak-sdkFor development with .env file support:
pip install garak-sdk[dotenv]from garak_sdk import GarakClient
import os
# Initialize client
client = GarakClient(api_key=os.getenv("GARAK_API_KEY"))
# Create a security scan using REST generator
scan = client.scans.create(
generator="rest",
model_name="https://api.example.com/v1/chat/completions",
probe_categories=["jailbreak", "harmful"],
name="rest-example-scan",
description="Security scan of REST API endpoint",
rest_config={
"uri": "https://api.example.com/v1/chat/completions",
"method": "post",
"req_template_json_object": {
"model": "gpt-4",
"messages": [
{
"role": "user",
"content": "$INPUT" # Put $INPUT where your API expects the prompt
}
],
"temperature": 0.7,
"max_tokens": 256
},
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer fake-api-key-12345" # Replace with your actual auth header
},
"response_json": True, # Set to True if your API returns JSON
"response_json_field": "$.choices[0].message.content", # JSONPath to extract the response text
"verify_ssl": True # Set to False to disable SSL verification (not recommended for production)
}
)
scan_id = scan['metadata']['scan_id']
# Wait for completion
scan = client.scans.wait_for_completion(scan_id)
# Get results
results = client.scans.get_results(scan_id)
# Calculate security score
overall_score = results['overallMetrics']['overallScore']
security_score = overall_score * 100
print(f"Security Score: {security_score:.1f}/100")The Garak SDK supports two authentication methods:
- Firebase JWT - For web users (interactive access)
- API Keys - For CI/CD automation (programmatic access)
API keys are the recommended method for automation, CI/CD pipelines, and programmatic access.
- Log in to the dashboard at scans.garaksecurity.com
- Navigate to Settings → API Keys
- Click "Create API Key"
- Configure your key:
- Name: Descriptive name (e.g., "GitHub Actions - Production")
- Description: Purpose of the key
- Rate Limit: Requests per minute (default: 100, max: 200)
- Expiration: Days until expiration (default: 90, max: 365)
- Save the key securely - it will only be shown once!
To create API keys, your account must meet these requirements:
- ✅ Email address verified
- ✅ Account at least 24 hours old
- ✅ Maximum 5 active keys per account
- ✅ Maximum 20 total keys (including revoked)
User-created API keys have the following permissions:
- ✅ Read: List scans, get results, download reports
- ✅ Write: Create scans, update scan metadata
- ❌ Admin: Cannot create more API keys (prevents privilege escalation)
Set your API key as an environment variable:
export GARAK_API_KEY="garak_abc123..."Or use a .env file:
# .env
GARAK_API_KEY=garak_abc123...
GARAK_API_BASE_URL=https://scans.garaksecurity.com
# Model API keys
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...scan = client.scans.create(
generator="rest",
model_name="https://api.example.com/v1/chat/completions",
probe_categories=["jailbreak", "harmful", "privacy"],
name="Production Security Scan",
description="Weekly security audit",
rest_config={
"uri": "https://api.example.com/v1/chat/completions",
"method": "post",
"req_template_json_object": {
"model": "gpt-4",
"messages": [
{
"role": "user",
"content": "$INPUT" # Put $INPUT where your API expects the prompt
}
],
"temperature": 0.7,
"max_tokens": 256
},
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer fake-api-key-12345" # Replace with your actual auth header
},
"response_json": True, # Set to True if your API returns JSON
"response_json_field": "$.choices[0].message.content", # JSONPath to extract the response text
"verify_ssl": True # Set to False to disable SSL verification (not recommended for production)
}
)from garak_sdk import ScanStatus
# List all scans
scans = client.scans.list(page=1, per_page=20)
# Filter by status
completed = client.scans.list(status=ScanStatus.COMPLETED)
# Search scans
results = client.scans.list(search="production")def on_progress(status):
if status.get('progress'):
print(f"Progress: {status['progress']['progress_percent']}%")
scan = client.scans.wait_for_completion(
scan_id,
timeout=3600, # 1 hour
poll_interval=10, # Check every 10 seconds
on_progress=on_progress
)# Download a specific report
client.reports.download(
scan_id,
report_type="jsonl",
output_path="./scan_report.jsonl"
)
# Download all reports
client.reports.download_all(
scan_id,
output_dir="./security-reports/"
)# List available generators
generators = client.metadata.list_generators()
for gen in generators:
print(f"{gen.name}: {gen.description}")
# List available models for a generator
models = client.metadata.list_models("openai")
print(f"OpenAI models: {models}")
# List probe categories
categories = client.metadata.list_probe_categories()
for cat in categories:
print(f"{cat.name}: {len(cat.probes)} probes")Create separate API keys for each environment and pipeline:
# Example key naming convention:
- "GitHub Actions - Production" (for main branch deployments)
- "GitHub Actions - Staging" (for staging branch)
- "Jenkins - Integration Tests" (for integration testing)
- "Local Development - John" (for local testing)GitHub Actions:
- Go to repository Settings → Secrets and variables → Actions
- Click "New repository secret"
- Name:
GARAK_API_KEY - Value: Your API key (
garak_...) - Click "Add secret"
GitLab CI:
- Go to Project Settings → CI/CD → Variables
- Add variable:
GARAK_API_KEY - Check "Protect variable" and "Mask variable"
- Save variable
Jenkins:
- Go to Manage Jenkins → Manage Credentials
- Add Credentials → Secret text
- Secret: Your API key
- ID:
garak-api-key
CircleCI:
- Project Settings → Environment Variables
- Add Variable:
GARAK_API_KEY
name: Security Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Garak SDK
run: pip install garak-sdk
- name: Run Security Scan
env:
GARAK_API_KEY: ${{ secrets.GARAK_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GARAK_MIN_SCORE: 80
run: |
python - <<'EOF'
from garak_sdk import GarakClient
import os
import sys
client = GarakClient(api_key=os.getenv("GARAK_API_KEY"))
# Create scan
scan = client.scans.create(
name=f"GitHub Actions - {os.getenv('GITHUB_REF_NAME', 'unknown')}",
description=f"Security scan for commit {os.getenv('GITHUB_SHA', '')[:8]}",
generator="openai",
model_name="gpt-4",
probe_categories=["jailbreak", "harmful"],
api_keys={"OPENAI_API_KEY": os.getenv("OPENAI_API_KEY")}
)
scan_id = scan['metadata']['scan_id']
print(f"Created scan: {scan_id}")
# Wait for completion
scan = client.scans.wait_for_completion(
scan_id,
timeout=3600,
poll_interval=10
)
# Get results
results = client.scans.get_results(scan_id)
# Check threshold
min_score = float(os.getenv("GARAK_MIN_SCORE", "80"))
overall_score = results['overallMetrics']['overallScore']
actual_score = overall_score * 100
print(f"\n{'='*60}")
print(f"Security Score: {actual_score}/100")
print(f"Threshold: {min_score}/100")
print(f"{'='*60}\n")
if actual_score < min_score:
print(f"❌ FAILED: Security score {actual_score} below threshold {min_score}")
sys.exit(1)
print(f"✅ PASSED: Security scan passed with score {actual_score}/100")
EOF
- name: Upload Reports
if: always()
uses: actions/upload-artifact@v4
with:
name: security-reports
path: ./security-reports/name: Multi-Model Security Scan
on:
schedule:
- cron: '0 2 * * *' # Daily at 2 AM
workflow_dispatch:
jobs:
security-scan:
runs-on: ubuntu-latest
strategy:
matrix:
model:
- generator: openai
model_name: gpt-4
probe_categories: jailbreak,harmful,privacy
- generator: anthropic
model_name: claude-3-opus-20240229
probe_categories: jailbreak,harmful
- generator: openai
model_name: gpt-3.5-turbo
probe_categories: jailbreak
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Garak SDK
run: pip install garak-sdk
- name: Run Security Scan - ${{ matrix.model.generator }}/${{ matrix.model.model_name }}
env:
GARAK_API_KEY: ${{ secrets.GARAK_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
python - <<EOF
from garak_sdk import GarakClient
import os
import json
client = GarakClient(api_key=os.getenv("GARAK_API_KEY"))
# Create scan
scan = client.scans.create(
name="${{ matrix.model.generator }}/${{ matrix.model.model_name }}",
generator="${{ matrix.model.generator }}",
model_name="${{ matrix.model.model_name }}",
probe_categories="${{ matrix.model.probe_categories }}".split(','),
api_keys={
"OPENAI_API_KEY": os.getenv("OPENAI_API_KEY"),
"ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"),
}
)
scan_id = scan['metadata']['scan_id']
# Wait and get results
scan = client.scans.wait_for_completion(scan_id)
results = client.scans.get_results(scan_id)
# Save results
with open("scan_results.json", "w") as f:
json.dump(results, f, indent=2)
overall_score = results.get('overallMetrics', {}).get('overallScore', 0)
security_score = overall_score * 100
print(f"Security Score: {security_score:.1f}/100")
EOF
- name: Upload Results
uses: actions/upload-artifact@v4
with:
name: scan-results-${{ matrix.model.generator }}-${{ matrix.model.model_name }}
path: scan_results.json
### GitLab CI
```yaml
# .gitlab-ci.yml
security_scan:
stage: test
image: python:3.11-slim
before_script:
- pip install garak-sdk
script:
- |
python - <<'EOF'
from garak_sdk import GarakClient
import os
import sys
client = GarakClient(api_key=os.getenv("GARAK_API_KEY"))
scan = client.scans.create(
name=f"GitLab CI - {os.getenv('CI_COMMIT_REF_NAME')}",
description=f"Commit {os.getenv('CI_COMMIT_SHORT_SHA')}",
generator="openai",
model_name="gpt-4",
probe_categories=["jailbreak", "harmful"],
api_keys={"OPENAI_API_KEY": os.getenv("OPENAI_API_KEY")}
)
scan_id = scan['metadata']['scan_id']
scan = client.scans.wait_for_completion(scan_id)
results = client.scans.get_results(scan_id)
overall_score = results['overallMetrics']['overallScore']
security_score = overall_score * 100
if security_score < 80:
sys.exit(1)
EOF
artifacts:
paths:
- security-reports/
when: always// Jenkinsfile
pipeline {
agent any
environment {
GARAK_API_KEY = credentials('garak-api-key')
OPENAI_API_KEY = credentials('openai-api-key')
}
stages {
stage('Install') {
steps {
sh 'pip install garak-sdk'
}
}
stage('Security Scan') {
steps {
sh '''
python - <<'EOF'
from garak_sdk import GarakClient
import os
import sys
client = GarakClient(api_key=os.getenv("GARAK_API_KEY"))
scan = client.scans.create(
name=f"Jenkins - {os.getenv('BRANCH_NAME')}",
generator="openai",
model_name="gpt-4",
probe_categories=["jailbreak", "harmful"],
api_keys={"OPENAI_API_KEY": os.getenv("OPENAI_API_KEY")}
)
scan_id = scan['metadata']['scan_id']
scan = client.scans.wait_for_completion(scan_id)
results = client.scans.get_results(scan_id)
overall_score = results.get('overallMetrics', {}).get('overallScore', 0)
score = overall_score * 100
print(f"Security Score: {score:.1f}/100")
if score < 80:
print("Security scan failed!")
sys.exit(1)
EOF
'''
}
}
}
post {
always {
archiveArtifacts artifacts: 'security-reports/**', allowEmptyArchive: true
}
}
}#!/usr/bin/env python3
"""
Basic CI/CD security scan script.
Usage: python cicd_scan.py
"""
from garak_sdk import GarakClient
import os
import sys
# Initialize client
client = GarakClient(api_key=os.getenv("GARAK_API_KEY"))
# Create and run scan
print("Creating security scan...")
scan = client.scans.create(
generator="openai",
model_name="gpt-4",
probe_categories=["jailbreak", "harmful"],
api_keys={"OPENAI_API_KEY": os.getenv("OPENAI_API_KEY")}
)
scan_id = scan['metadata']['scan_id']
print(f"Scan created: {scan_id}")
print("Waiting for completion...")
# Wait for completion with progress
def on_progress(status):
if status.get('progress'):
print(f"Progress: {status['progress']['progress_percent']}%")
scan = client.scans.wait_for_completion(
scan_id,
timeout=3600,
poll_interval=10,
on_progress=on_progress
)
# Get results
results = client.scans.get_results(scan_id)
# Download reports
print("\nDownloading reports...")
client.reports.download_all(scan_id, output_dir="./security-reports/")
# Check threshold
min_score = float(os.getenv("GARAK_MIN_SCORE", "80"))
overall_score = results.get('overallMetrics', {}).get('overallScore', 0)
actual_score = overall_score * 100
print(f"\n{'='*60}")
print(f"Security Score: {actual_score}/100")
print(f"Threshold: {min_score}/100")
print(f"{'='*60}\n")
if actual_score < min_score:
print(f"❌ FAILED: Security score {actual_score} below threshold {min_score}")
sys.exit(1)
print(f"✅ PASSED: Security scan passed with score {actual_score}/100")#!/usr/bin/env python3
"""
Advanced CI/CD script with parallel scanning of multiple models.
"""
from garak_sdk import GarakClient
import os
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
def run_scan(client, config):
"""Run a single scan and return results."""
print(f"Starting scan: {config['name']}")
scan = client.scans.create(**config)
scan = client.scans.wait_for_completion(scan.metadata.scan_id, timeout=3600)
results = client.scans.get_results(scan.metadata.scan_id)
return {
'name': config['name'],
'scan_id': scan.metadata.scan_id,
'score': results.get('security_score', 0),
'results': results
}
# Initialize client
client = GarakClient(api_key=os.getenv("GARAK_API_KEY"))
# Define scan configurations
scan_configs = [
{
'name': 'GPT-4 Security Scan',
'generator': 'openai',
'model_name': 'gpt-4',
'probe_categories': ['jailbreak', 'harmful', 'privacy'],
'api_keys': {'OPENAI_API_KEY': os.getenv('OPENAI_API_KEY')}
},
{
'name': 'GPT-3.5 Security Scan',
'generator': 'openai',
'model_name': 'gpt-3.5-turbo',
'probe_categories': ['jailbreak', 'harmful'],
'api_keys': {'OPENAI_API_KEY': os.getenv('OPENAI_API_KEY')}
},
{
'name': 'Claude Security Scan',
'generator': 'anthropic',
'model_name': 'claude-3-opus-20240229',
'probe_categories': ['jailbreak', 'harmful'],
'api_keys': {'ANTHROPIC_API_KEY': os.getenv('ANTHROPIC_API_KEY')}
},
]
# Run scans in parallel
print(f"Running {len(scan_configs)} scans in parallel...")
results = []
with ThreadPoolExecutor(max_workers=3) as executor:
futures = [executor.submit(run_scan, client, config) for config in scan_configs]
for future in as_completed(futures):
try:
result = future.result()
results.append(result)
print(f"✓ Completed: {result['name']} - Score: {result['score']}/100")
except Exception as e:
print(f"✗ Failed: {e}")
# Check if all scans passed
min_score = float(os.getenv("GARAK_MIN_SCORE", "80"))
failed_scans = [r for r in results if r['score'] < min_score]
print(f"\n{'='*60}")
print(f"Results Summary:")
for result in results:
status = "✅ PASS" if result['score'] >= min_score else "❌ FAIL"
print(f"{status} {result['name']}: {result['score']}/100")
print(f"{'='*60}\n")
if failed_scans:
print(f"❌ {len(failed_scans)} scan(s) failed!")
sys.exit(1)
print(f"✅ All scans passed!")-
Create separate keys for each use case
- One key per CI/CD pipeline
- One key per environment (prod, staging, dev)
- One key per team member for local development
-
Use descriptive names
- ✅ "GitHub Actions - Production Pipeline"
- ✅ "Jenkins - Integration Tests"
- ✅ "Local Development - John Doe"
- ❌ "my-key" or "test-key"
-
Rotate keys regularly
- Set expiration dates (default: 90 days)
- Use the key rotation endpoint to update without downtime
- Remove old keys after rotation
-
Store keys in secret managers
- GitHub Secrets
- AWS Secrets Manager
- Azure Key Vault
- HashiCorp Vault
- Google Secret Manager
-
Set appropriate rate limits
- Production: 100-200 requests/minute
- Development: 50-100 requests/minute
- Testing: 20-50 requests/minute
-
Monitor usage in the dashboard
- Check the "API Keys" page regularly
- Review "Last Used" timestamps
- Look for unusual IP addresses
-
Revoke keys immediately when:
- Key is compromised or leaked
- Team member leaves
- Pipeline is decommissioned
- Suspicious activity is detected
-
Don't share keys between people
- Each person should create their own key
- Sharing makes it impossible to track who did what
-
Don't commit keys to git repositories
- Keys in git history are permanently exposed
- Use
.envfiles and add them to.gitignore
-
Don't use keys with overly broad permissions
- User keys are limited to read+write (good!)
- Never share admin keys
-
Don't reuse keys across environments
- Production keys ≠ Staging keys ≠ Development keys
- Environment isolation prevents accidents
-
Don't leave keys active indefinitely
- Always set an expiration date
- Review and renew keys periodically
-
Don't ignore security alerts
- Check your email for security notifications
- Review the dashboard for warnings
-
Immediately revoke the key
# Via dashboard: Settings → API Keys → Revoke # Or via API: curl -X POST https://scans.garaksecurity.com/api/v1/keys/{key_id}/revoke \ -H "Authorization: Bearer YOUR_FIREBASE_TOKEN"
-
Create a new key
- Log into dashboard
- Create replacement key with a new name
- Update your CI/CD secrets
-
Review audit logs
- Check "Last Used" timestamp
- Look for suspicious IP addresses
- Review recent scans created with the key
-
Rotate all other keys as a precaution
- If one key was compromised, others might be too
- Use the rotation endpoint for zero-downtime updates
from garak_sdk import GarakClient
import os
client = GarakClient(api_key=os.getenv("GARAK_API_KEY"))
# Rotate your key programmatically
new_key_response = client.keys.rotate(
key_id=123,
name="GitHub Actions - Production (Rotated)",
expires_days=90
)
# Save the new key securely
new_key = new_key_response['new_api_key']
print(f"New key created: {new_key}")
print(f"Old key revoked: {new_key_response['old_key_id']}")
# Update your CI/CD secrets with the new keySee the examples/ directory for complete examples:
basic_scan.py- Simple security scancicd_integration.py- CI/CD pipeline integrationbatch_scanning.py- Parallel batch scanning
GarakClient(
base_url: str = "https://scans.garaksecurity.com",
api_key: str = None,
timeout: int = 30,
verify_ssl: bool = True
)client.scans.create()- Create a new scanclient.scans.list()- List scans with paginationclient.scans.get(scan_id)- Get scan detailsclient.scans.get_status(scan_id)- Get scan statusclient.scans.wait_for_completion(scan_id)- Wait for scan to completeclient.scans.update(scan_id)- Update scan metadataclient.scans.cancel(scan_id)- Cancel a running scanclient.scans.get_results(scan_id)- Get scan resultsclient.scans.get_quota()- Get quota information
client.metadata.list_generators()- List available generatorsclient.metadata.get_generator(name)- Get generator detailsclient.metadata.list_models(generator)- List models for generatorclient.metadata.list_probe_categories()- List probe categoriesclient.metadata.list_probes(category)- List probes in categoryclient.metadata.health_check()- Check API healthclient.metadata.get_api_info()- Get API information
client.reports.list(scan_id)- List available reportsclient.reports.download(scan_id, report_type, output_path)- Download a reportclient.reports.download_all(scan_id, output_dir)- Download all reports
from garak_sdk import (
GarakSDKError,
AuthenticationError,
QuotaExceededError,
ScanNotFoundError,
ScanTimeoutError
)
try:
scan = client.scans.create(...)
except AuthenticationError:
print("Invalid API key")
except QuotaExceededError:
print("Quota exceeded, please upgrade")
except ScanTimeoutError:
print("Scan timed out")
except GarakSDKError as e:
print(f"Error: {e}")# Clone repository
git clone https://github.com/Garak-inc/garak-python-sdk.git
cd garak-python-sdk
# Install in development mode
pip install -e ".[dev]"
# Run tests
pytest
# Format code
black garak_sdk/
isort garak_sdk/
# Type checking
mypy garak_sdk/# Run all tests
pytest
# Run with coverage
pytest --cov=garak_sdk --cov-report=html
# Run specific test
pytest tests/test_client.pyContributions are welcome! Please see CONTRIBUTING.md for guidelines.
This project is licensed under the MIT License - see the LICENSE file for details.
- 📧 Email: support@getgarak.com
Made with ❤️ by Garak Security