Audit any Directus instance for the misconfigurations that actually leak data — public-role read exposure, user enumeration, unauthenticated version/schema leaks, GraphQL introspection, and the
search-param field-enumeration oracle — and prove each one live with an anonymous probe. Other checklists tell you what might be wrong; this fetches the bytes and shows you what is.
⚡ Run it in one line, no admin token, no install:
npx directus-security --url https://your-directus.example.com
🤝 Want it done for you? Fixed-scope audit — $99 / 24h: I verify each finding live and send a written report with the exact policy fixes.
$ npx directus-security --url https://directus.example.com
1 critical, 2 high, 1 medium — 4 CONFIRMED via anonymous probe
CRITICAL /items/posts public-role read enabled — rows reachable anonymously
HIGH /items/posts?search=… search-param field enumeration (CVE-2025-30352 class)
HIGH /items/directus_users user list exposed (name + email)
MEDIUM /server/specs/oas Directus version leaked unauthenticated (CVE-2025-53887 class)
Directus powers headless backends at Tripadvisor, Adobe and Mercedes, and the default access model makes one mistake very easy: leaving read enabled for the Public policy. The result is an API anyone can read. 2025 brought a cluster of unauthenticated CVEs that map exactly to anonymous probes:
- CVE-2025-30352 — the
searchquery parameter enumerates non-permitted fields, leaking emails and password hashes one character at a time. - CVE-2025-53887 — the Directus version is exposed unauthenticated via
/server/specs/oas, letting attackers match your build to known exploits. - CVE-2025-64749 — collection-existence leak via error-message diffing.
- CVE-2025-53889 — unauthenticated Flow trigger.
directus-security checks for these and confirms the real ones by issuing the
exact anonymous request an attacker would — so you triage facts, not maybes.
| Check | Severity | How it's confirmed |
|---|---|---|
| Public-role read on a collection | critical | anonymous GET /items/{collection} returns rows |
search-param field enumeration |
high | anonymous GET /items/{collection}?search=… answers (CVE-2025-30352 class) |
/items/directus_users user enumeration |
high | anonymous read returns the user list (name + email) |
| Unauthenticated version/schema leak | medium | /server/info or /server/specs/oas returns the version unauth (CVE-2025-53887 class) |
| GraphQL introspection in prod | medium | __schema query answered on /graphql or /graphql/system |
# Probe a live instance (guesses common collection names)
npx directus-security --url https://directus.example.com
# Probe specific collections
npx directus-security --url https://directus.example.com --collections posts,authors
# Learn your exact collection names from a Directus schema snapshot, then probe
npx directus-security --url https://directus.example.com --snapshot ./snapshot.json
# Write a shareable HTML report
npx directus-security --url https://directus.example.com --html report.html
# Static only (no requests sent)
npx directus-security --url https://directus.example.com --no-probeOutput is JSON on stdout (pipe it into CI) and a one-line summary on stderr.
Exit is non-zero only on usage errors — gate your pipeline on the JSON summary.
npm i -g directus-security
directus-security --url https://directus.example.comZero dependencies. Your data and credentials never leave your machine — every request goes straight from the tool to your Directus instance.
Same active-probe philosophy for the rest of the backend stack, all MIT:
strapi-security · supabase-security · pocketbase-security · firebase-security · appwrite-security · nhost-security
MIT © Renzo Madueno
📚 Part of Awesome Backend Security Auditors — the full collection of keyless active-probe auditors.