Solutions · AI Teams → Solutions · Banking → Benchmarks → Integrations → Docs → Pricing → Sign in Get Early Access

Zortium documentation

Zortium runs a structured battery of adversarial attacks against any OpenAI-compatible vision-language model and reports a per-suite Attack Success Rate. This is the reference for the open-source CLI.

$pip install zortium

Introduction #

Zortium is an adversarial testing tool for vision-language models (VLMs) — think promptfoo, but for image-based attacks. It points at any endpoint speaking the OpenAI chat-completions protocol, runs a battery of attack suites drawn from published research, and reports how often each attack got the model to comply.

The headline metric is Attack Success Rate (ASR) — the fraction of adversarial cases in a suite where the target produced harmful or hijacked output instead of refusing. Lower is safer. Each suite is scored independently, and an overall ASR summarises the run.

Zortium ships as a standalone CLI that never phones home. zortium.dev is an optional managed platform built on the same suites — web UI, saved history, model benchmarks, and team features — but nothing below requires it.

Installation #

pip install zortium

Requires Python 3.10 or newer. There are no system dependencies — every dependency ships as a pure-Python wheel, so a fresh machine needs nothing beyond pip.

Verify the install:

zortium --help

Quick start #

A scan needs a target (the model under test) and, for trustworthy numbers, a judge (a separate model that grades whether the target actually complied):

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

Every flag has a ZORTIUM_* environment variable, so keys never appear in your shell history or a process listing. That single command is the whole interface — everything else is a convenience on top of it.

!

No judge configured? Zortium falls back to the target model grading its own responses and prints a warning. That keeps a first run useful, but a model judging itself is lenient — treat those breach rates as a floor, not a trusted number. Configure a real judge for results you'll act on.

You get a per-suite breach-rate table and a headline ASR:

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━┓
┃ 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
PASS  Overall ASR 12.3% within threshold 100%

The scan command #

The command has two halves — the target you're testing and the judge that grades it:

RoleFlagsWhat 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. Base URL and key inherit the target's when omitted.

Everything else layers convenience on top:

  • Keys off the command line — every flag has a ZORTIUM_* env var (ZORTIUM_API_KEY, ZORTIUM_JUDGE_API_KEY, ZORTIUM_MODEL, …).
  • Deeper audit — --no-fast runs full per-suite coverage instead of the reduced fast set (see Scan modes).
  • Parallelism — --tps N runs N suites at once on high-limit endpoints (see Configuration).

Configuration #

Environment variables

Every flag has a ZORTIUM_* equivalent, so secrets stay out of shell history and CI logs. Precedence is flag > env var > config file > default.

Config file

Instead of a long command, generate a documented config and edit it:

# write a starter zortium.yaml in the current directory
zortium --init

# run from it (flags and env vars still override the file)
zortium --config zortium.yaml

Both YAML and JSON are accepted. Blank or null values in the file fall through to env vars and defaults.

Saving a report

The CLI keeps no database. Add --output to write a full JSON report — run metadata, headline stats, and per-suite → per-case entries with the model response, judge verdict, and its reasoning:

zortium --output report.json

Parallelism

--tps N (env ZORTIUM_TPS) runs N suites in parallel. The default is 1 — sequential, which is 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 too high a value degrades gracefully rather than failing.

CI integration #

Use --max-asr to set a threshold. The CLI exits 1 if the overall ASR exceeds it — a native CI gate, no plugin needed:

zortium --max-asr 20   # fail the build if overall ASR > 20%

A complete GitHub Actions job — secrets stay in CI variables:

# .github/workflows/vlm-safety.yml
name: VLM Safety Gate
on: [push]
jobs:
  zortium:
    runs-on: ubuntu-latest
    steps:
      - run: pip install zortium
      - 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 }}

The --max-asr gate uses the raw overall ASR. Diagnostic suites are always excluded from it.

Scan modes #

Both modes run the same 18 suites; fast runs fewer, highest-signal cases per suite, thorough runs full coverage. --fast is the default; use --no-fast for deep audits.

Fast (default)Thorough
Flag--fast--no-fast
Cases per image suiteReduced (highest-signal)Full
Good forDaily CI, first runsPre-release, deep audits
!

On runtime: wall-clock time is dominated by the target model's own latency and rate limits — not by Zortium — so it varies widely. Expect a few minutes to ~10+ for a fast scan; a throttled endpoint (especially with --wait) takes longer. Raise --tps on high-limit endpoints to cut it.

Rate limits #

By default a rate-limited (429) 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

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. Keyword matching produces false positives on jailbreak evaluation, so these suites require a judge rather than falling back to keywords.

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? The target grades its own responses and Zortium prints a warning. Self-grading 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

SuiteTechniqueReference
Typographic Visual Prompt InjectionEmbeds harmful instructions as text in imagesGoh et al. 2021
Split-Modality InjectionSplits instruction across image + text channelsOriginal
FigStepNumbered-list document completion framingGong et al. 2023
Steganographic Text InjectionHides instructions in low-contrast regionsLi et al. 2024
Image HijacksBehaviour-matching visual overlaysBailey et al. 2023
JailBreakV-28KReal benchmark — 7 adversarial format typesLuo et al. 2024
Visual Jailbreak TransferDAN / AIM / many-shot visual personasWei et al. 2023
Query-Relevant Typographic JailbreakHides the harmful instruction in a rendered-text imageInspired by Liu et al. 2024
Many-Shot Text24-example in-context compliance historyAnil et al. 2024
Goal HijackingPrompt injection with delimiter spoofingPerez & Ribeiro 2022
Structured Output InjectionJSON schema / constrained-decoding exploitationOriginal
i

JailBreakV-28K isn't bundled — the suite fetches its 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. PAIR Agent (iterative attacker-LLM red-teaming, Chao et al. 2023) is implemented but disabled by default — a multi-turn loop for targeted deep audits rather than routine scanning.

Medium — keyword evaluation, limited effectiveness on modern models

SuiteTechnique
GCG SuffixPrecomputed adversarial token suffixes (transfer variant)
Encoding AttackBase64 / ROT13 / hex payload obfuscation
Refusal SuppressionInstruction-stack refusal bypass
Multi-Modal LinkageCross-modal encryption-decryption framing
UI SpoofingAuthoritative-surface behavioural deviation

Diagnostic — robustness probes, reported separately from ASR

SuiteMetric
Visual Perturbation DriftCosine similarity drift under pixel noise
Stroop InterferenceVisual-linguistic conflict detection

Scoring & severity #

Each suite carries a severity reflecting how real-world exploitable the attack is — distinct from how it's evaluated internally:

SeverityMeaningWeight
HIGHStrong, real-world exploitable attacks3
MIDLimited effectiveness on modern models2
LOWWeak or marker-based techniques1
DIAGNOSTICRobustness probes, excluded from ASR0

Diagnostic suites are always excluded from the headline ASR. On zortium.dev, severity also drives a weighted overall score, so a high-severity breach moves the number more than a low-severity one. The CLI --max-asr gate uses the raw overall ASR.

Providers #

Any endpoint speaking the OpenAI chat-completions protocol works — no SDK changes, no instrumentation, just a base URL and an API key:

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

See Integrations for per-provider base URLs and model examples.

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 enums
├── attacks/             Attack suite implementations
│   ├── base.py          AttackSuite base — configure_for_mode(), evaluate()
│   ├── __init__.py      SuiteRegistry — build_active_suites()
│   └── ...
├── providers/
│   ├── openai_compatible.py  Target and judge provider abstraction
│   └── ratelimit.py     Retry-After / backoff, SKIP/WAIT
├── evaluators/
│   ├── judge.py         LLM-as-judge evaluation
│   └── refusal_aware.py Keyword-based refusal detection
├── utils/               Render, Perturbations, ImageUtils, PayloadPack
└── config/
    ├── attacks.json     Suite config — priority, severity, parameters
    └── harm_payloads.json  Harm categories and prompt library

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

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 or systems you don't 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 measure refusal behaviour. The bundled payloads and the JailBreakV-28K benchmark contain offensive material.
  • Purpose is measurement, not exploitation. The output is a robustness report 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.