Skip to main content

Zortium

Adversarial attack test suite for vision-language models.

Zortium runs a structured battery of adversarial attacks against any OpenAI-compatible VLM endpoint and reports a per-suite Attack Success Rate (ASR). Think promptfoo for vision models — systematic, repeatable, CI-ready.

$ zortium --base-url https://openrouter.ai/api/v1 --model google/gemma-3-27b-it --api-key $OPENROUTER_API_KEY \
          --judge-model gpt-4o-mini --judge-base-url https://api.openai.com/v1 --judge-api-key $OPENAI_API_KEY

███████╗ ██████╗ ██████╗ ████████╗██╗██╗   ██╗███╗   ███╗
╚══███╔╝██╔═══██╗██╔══██╗╚══██╔══╝██║██║   ██║████╗ ████║
  ███╔╝ ██║   ██║██████╔╝   ██║   ██║██║   ██║██╔████╔██║
 ███╔╝  ██║   ██║██╔══██╗   ██║   ██║██║   ██║██║╚██╔╝██║
███████╗╚██████╔╝██║  ██║   ██║   ██║╚██████╔╝██║ ╚═╝ ██║
╚══════╝ ╚═════╝ ╚═╝  ╚═╝   ╚═╝   ╚═╝ ╚═════╝ ╚═╝     ╚═╝

  VLM adversarial attack scanner

  target  google/gemma-3-27b-it
endpoint  https://openrouter.ai/api/v1
   judge  gpt-4o-mini
    mode  fast
  suites  18

  ⠋ scanning ━━━━━━━━━━━━━━━━━━━━━━━━━━ 18/18 suites 0:08:47   ← fills live, then:

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━┓
┃ Attack Suite                         ┃ Severity ┃ Breach Rate ┃ Status ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━┩
│ Typographic Visual Prompt Injection  │   HIGH   │          0% │  PASS  │
│ FigStep Typographic Jailbreak        │   HIGH   │         25% │  WARN  │
│ Query-Relevant Typographic Jailbreak │   HIGH   │          8% │  PASS  │
│ Goal Hijacking                       │   MID    │         50% │  FAIL  │
│ …                                    │    …     │           … │   …    │
├──────────────────────────────────────┼──────────┼─────────────┼────────┤
│ Overall                              │          │         12% │  WARN  │
└──────────────────────────────────────┴──────────┴─────────────┴────────┘

  cases    137 evaluated · 17 breached
  elapsed  8m 47s

PASS  Overall ASR 12.3% within threshold 100%

No judge configured? Zortium falls back to the target grading itself and prints a warning — usable for a first run, but self-grading is lenient, so those breach rates are a floor. Pass --judge-model for trustworthy numbers.


Installation

pip install zortium

Requires Python 3.10+. On Linux you may need the system Cairo library for image rendering (apt-get install libcairo2 / dnf install cairo); macOS and Windows wheels bundle it.


Usage

The command in the example above has two halves — the target you're testing and the judge that grades whether the target complied:

Role Flags What it is
Target --base-url · --model · --api-key the VLM under test — any OpenAI-compatible chat completions endpoint
Judge --judge-model · --judge-base-url · --judge-api-key a separate model that grades harmful-content compliance (see LLM judge)

That single command is the whole interface. Everything below is a convenience on top of it:

  • Keys off the command line — every flag has a ZORTIUM_* environment variable (ZORTIUM_API_KEY, ZORTIUM_JUDGE_API_KEY, ZORTIUM_MODEL, …), so secrets never appear in your shell history or ps.
  • Config file instead of a long line — run zortium --init to drop a documented zortium.yaml in the current directory, edit it, then zortium --config zortium.yaml. A flag or env var still overrides the file; blank values fall through to env/defaults.
  • Save every response — the CLI keeps no database, so add --output report.json to write a full report: run metadata, headline stats, and per-suite → per-case entries with the full model response, the judge verdict, and its reasoning.
  • Deeper audit — --no-fast runs full per-suite case coverage instead of the reduced fast set (see Scan modes).
  • Go faster on high-limit endpoints — --tps N (env ZORTIUM_TPS) runs N suites in parallel. Default is 1 (sequential — safe for rate-limited or shared-key endpoints); raise it for in-house models that can absorb the extra requests-per-second. Rate limits are still handled per the 429 policy, so a too-high value degrades gracefully rather than failing.

CI integration

Exit with code 1 if overall ASR exceeds a threshold:

zortium --max-asr 20
# GitHub Actions example
- name: Run VLM security scan
  run: zortium --max-asr 20
  env:
    ZORTIUM_BASE_URL: ${{ secrets.VLM_BASE_URL }}
    ZORTIUM_MODEL: ${{ secrets.VLM_MODEL }}
    ZORTIUM_API_KEY: ${{ secrets.VLM_API_KEY }}
    ZORTIUM_FAST: "true"

--fast (default true) runs one high-signal case per harm category for the image suites — the leanest useful scan, for CI and first runs. Use --no-fast for pre-release deep scans with full per-category coverage.

Rate-limited endpoints

By default a rate-limited (429) test case is recorded as unavailable and left out of the score — fast, and right for CI. For a one-off baseline against a heavily throttled endpoint, --wait (env ZORTIUM_WAIT) retries each rate-limited case until the limit clears so every case gets a real verdict. It respects the Retry-After header and can take much longer — use it for benchmarks, not CI.

zortium --no-fast --wait   # complete thorough baseline, waits out rate limits

Each suite is labelled with a severity — HIGH, MID, LOW, or DIAGNOSTIC — reflecting how real-world exploitable the attack is (distinct from how it's judge-gated). Diagnostic suites are always excluded from the headline ASR. In the web UI and benchmarks, severity also drives a weighted overall score (HIGH=3 / MID=2 / LOW=1 / DIAGNOSTIC=0), so high-severity breaches move the headline number more than low-severity ones. The CLI --max-asr gate uses the raw overall ASR.


LLM judge

Critical suites are graded by an LLM judge — it reads each (harmful request, model response) pair and decides whether the target actually complied. Configure it with --judge-model / --judge-base-url / --judge-api-key (or the ZORTIUM_JUDGE_* env vars). The base URL and key inherit the target's when omitted, so a judge on the same endpoint needs only --judge-model. Use a separate, stronger model than the one under test for better calibration.

No judge configured? Zortium falls back to the target model grading its own responses and prints a warning. This keeps a first run useful out of the box, but a model judging itself is lenient — the resulting breach rates are a floor, not a trusted number. Always configure a real judge for numbers you'll act on.


Attack suites

Critical — LLM judge required, high research backing

Suite Technique Paper
Typographic Visual Prompt Injection Embeds harmful instructions as text in images Goh et al. 2021
Split-Modality Injection Splits instruction across image + text channels Original
FigStep Numbered-list document completion framing Gong et al. 2023
Steganographic Text Injection Hides instructions in low-contrast image regions Li et al. 2024
Image Hijacks Behaviour-matching visual overlays Bailey et al. 2023
JailBreakV-28K Real benchmark — 7 adversarial format types Luo et al. 2024
Visual Jailbreak Transfer DAN / AIM / many-shot visual personas Wei et al. 2023
Query-Relevant Typographic Jailbreak Hides the harmful instruction in a rendered-text image Inspired by Liu et al. ECCV 2024
Many-Shot Text 24-example in-context compliance history Anil et al. 2024
Goal Hijacking Prompt injection with delimiter spoofing Perez & Ribeiro 2022
Structured Output Injection JSON schema / constrained-decoding exploitation Original

PAIR Agent (iterative attacker-LLM red-teaming loop, Chao et al. 2023) is implemented but disabled by default. It's a multi-turn attacker-agent loop built for targeted deep audits of a single model rather than routine scanning, so it isn't part of the standard suite — enable it with a one-line registry change when you want it.

JailBreakV-28K is not bundled — the suite fetches its 300 benchmark images on demand from the official MIT-licensed HuggingFace repo the first time it runs (python -m zortium.attacks.jailbreakv_28k --download). Every other suite works out of the box.

Medium — Keyword evaluation, limited effectiveness on modern models

Suite Technique
GCG Suffix Precomputed adversarial token suffixes (transfer variant)
Encoding Attack Base64 / ROT13 / hex payload obfuscation
Refusal Suppression Instruction-stack refusal bypass
Multi-Modal Linkage Cross-modal encryption-decryption framing
UI Spoofing Authoritative-surface behavioural deviation

Diagnostic — Robustness probes, reported separately from ASR

Suite Metric
Visual Perturbation Drift Cosine similarity drift under pixel noise
Stroop Interference Visual-linguistic conflict detection

Scan modes

Both modes run the same 18 suites; fast runs fewer cases per suite, thorough runs full per-suite coverage.

Fast (default) Thorough
Cases per image suite reduced (highest-signal) full
Query-Relevant Typo cases 13 (1 per scenario, TYPO) 26 (2 per scenario, TYPO)
Good for daily CI, first runs pre-release, deep audits

On runtime: wall-clock time is dominated by the target model's own latency, throughput, and rate limits — not by Zortium — so it varies widely by model. Expect anywhere from a few minutes to ~10+ minutes for a fast scan; a slow or heavily throttled endpoint (especially with --wait) can take considerably longer. If your endpoint has generous rate limits, --tps N runs suites in parallel to cut that time (see Usage).


zortium.dev — managed platform

This CLI is the open-source engine, free and fully standalone. zortium.dev is a managed platform built on top of the same attack suites, for teams that want more than a per-run terminal report:

  • Web UI, no install — run scans from the browser with results rendered live as each suite completes.
  • Persistent history & dashboards — every scan is stored and browsable; resume a scan by simply returning to the tab.
  • Scan-to-scan comparison — diff two runs to see which suites regressed or improved between model versions, with per-suite flip detection.
  • Weighted risk scoring — a severity-weighted headline score (HIGH/MID/LOW) and per-tier breakdown, so a critical breach outweighs a low-severity one.
  • Cross-model benchmarks — a leaderboard ranking models by the same methodology, for choosing or tracking a vision model.
  • Teams & enterprise — shared accounts, plus an on-prem deployment where scan content stays entirely on your own database and only aggregate metadata is reported.

The CLI never phones home and needs none of this — the platform is for teams that want persistence, collaboration, and benchmarking on top of the engine.


Supported providers

Any endpoint speaking the OpenAI chat completions protocol:

  • OpenRouter — pay-per-token access to hundreds of OSS models via one key (the easiest path for benchmarking OSS models)
  • OpenAI, Groq, Together AI, Google Gemini (via their OpenAI-compatible endpoints)
  • vLLM, Ollama (via /v1 shim), TGI adapter
  • In-house gateways and fine-tuned model deployments

Architecture

The engine is a src-layout package under src/zortium/:

src/zortium/
├── runner.py            TestRunner — judge enforcement, suite execution
├── constants.py         ScanMode, SuitePriority, SuiteSeverity, RateLimitPolicy, ScanStatus enums
├── attacks/             Attack suite implementations
│   ├── base.py          AttackSuite base class — configure_for_mode(), evaluate()
│   ├── __init__.py      SuiteRegistry — ACTIVE_SUITE_CLASSES, build_active_suites()
│   ├── typographic_injection.py
│   └── ...
├── providers/
│   ├── openai_compatible.py  Target and judge provider abstraction
│   └── ratelimit.py     RateLimitResolver — Retry-After/backoff, SKIP/WAIT, connection retry
├── evaluators/
│   ├── judge.py         LLM-as-judge evaluation
│   └── refusal_aware.py Keyword-based refusal detection
├── utils/               Render, Perturbations, SemanticSimilarity, ImageUtils, PayloadPack
└── config/
    ├── attacks.json     Suite config — priority, severity, harm categories, parameters
    └── harm_payloads.json  Harm category definitions and prompt library

Adding a suite: subclass AttackSuite in src/zortium/attacks/, add an entry to config/attacks.json, register it in ACTIVE_SUITE_CLASSES in attacks/__init__.py. Override configure_for_mode() if the suite needs different behaviour in fast vs thorough mode.


Intended use & safety

Zortium is a defensive security tool for evaluating the robustness of vision-language models you own or are authorized to test.

  • Authorized targets only. Do not run Zortium against models, endpoints, or systems you do not own or have explicit permission to test.
  • It sends adversarial and harmful content. By design, Zortium submits jailbreak prompts, harmful requests, and adversarial images to the target model to measure its refusal behaviour. The bundled payloads (config/harm_payloads.json) and the JailBreakV-28K benchmark contain offensive material.
  • Purpose is measurement, not exploitation. The output is a robustness report (Attack Success Rate per suite) to help you harden a model — not a toolkit for attacking third-party systems.

Report security issues responsibly and use results to improve model safety.


Citations

See CITATIONS.md for full attribution of all research papers and benchmark datasets.


License

See LICENSE.

Metadata

Release files for zortium 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zortium 0.2.0
File Size Uploaded
zortium-0.2.0.tar.gz 358.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for zortium 0.2.0
File Interpreter ABI Platform
zortium-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 627.9 kB

Release files / zortium-0.2.0.tar.gz

Download URL zortium-0.2.0.tar.gz
Size 358.1 kB
Tags Source
SHA-256 checksum
How to use checksums
68880b20f01fc91eec4d64bc90aebc897562d38ed9b6ea0256976dabc99cef29
BLAKE2b-256 checksum
How to use checksums
b0139d9fbea503344c8001eaf9a1336fcb6cd70d5e304993c9b03a063b074e28
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.9

Release files / zortium-0.2.0-py3-none-any.whl

Download URL zortium-0.2.0-py3-none-any.whl
Size 269.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
32414bdd4b1ad731cb7e4301b8367f1d7f402840bd8fb7234d5bc19921af19e6
BLAKE2b-256 checksum
How to use checksums
b928496497cbf26ecbd23f3da023b9d2ba909046f6bbc2e3cd7e28fbce4899b5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.9

Release history Release notifications | RSS feed

0.2.1

2 release files

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page