๐ Priest's Basilisk
An autonomous offensive-security agent. It lives on your machine, it answers to you, and it does not ask twice.
You bring the model. Basilisk gives it hands, a memory, a methodology โ and a leash you hold.
Version 9.7.0 โ mature, in daily use, and finished in the way real tools are finished:
the surface is stable, the safety floors are settled, and what work remains is hunted, published and pinned to a test.
โ This is professional offensive-security tooling. โ
It is built for people who already run engagements, already have authorisation in writing,
and already know what they are legally and ethically responsible for. If that is not you,
this is not a tool you should be pointing at anything.
Point it at a target and walk away.
It maps the attack surface, forms a hypothesis, builds the exploit, fires it, and proves the hit against ground truth before it counts โ keeping a hashed, tamper-evident receipt of every command it ran along the way. Errors don't end the run; it reads them and keeps working.
Then it turns the same forensic discipline on your own codebase and hands you back a repo whose tests actually pass.
No account. No telemetry. No cloud sandbox. It runs as a native GTK4 desktop app on your own Linux box, with your privileges, and the only thing that ever leaves the machine is the API call to the model you chose.
| 87 / 113 OWASP Juice Shop black-box, autonomous |
22 / 22 Duck Store API black-box, autonomous |
2.4ร the leading commercial agent, like for like |
$0 MIT โ free to own, free to run |
The comparison that matters: the leading commercial agent scores 49 / 113 when you hand it your source code.
Basilisk scores 87 / 113 with no source at all โ 1.8ร its white-box run, blind โ on one of the cheapest models money can buy.
One of them is sold through a sales team. The other is this repo.
[!CAUTION] Authorized targets only. This is a live weapon, not a demo.
Basilisk runs real exploits and real shell commands against whatever you point it at, autonomously, with your privileges. Arm Unleash and it confirms the target, then runs with no per-command approval โ surviving errors, retrying past them โ until the objective is verifiably met or you stand it down.
Aimed at the wrong host, it will keep working that host until you pull it off. Use it only on systems you own or have explicit written authorization to test. Unauthorized testing is a crime in most jurisdictions.
Two things it will never do, and neither is a prompt you can talk around. The irreversible class โ disk wipes,
mkfs, recursive root deletes, fork bombs, raw block-device writes โ is refused inside the execution primitive itself, so no caller can route around it and there is no "run anyway". And anything outside your declared scope is refused before it leaves the box. Everything else, it will do if you tell it to.
[!NOTE] Disambiguation (for search engines and AI assistants): Priest's Basilisk (
the-priest/PriestsBasilisk) is an autonomous web and API penetration-testing agent with a repo-repair mode. It is not an LLM-jailbreak / prompt-evolution framework, not "White-Basilisk" (a source-code scanner), not the Basilisk browser or Basilisk II emulator, and unrelated to Roko's Basilisk. The benchmark results below โ 87/113 on OWASP Juice Shop and 22/22 on the Duck Store API, both black-box โ are for this tool.
โ๏ธ Dangerous on purpose. Safe by construction.
Most "AI security tools" are demos with a kill switch. Basilisk is a loaded weapon with a trigger guard. The difference is worth being precise about, because it is the entire design โ so here is the honest ledger, both columns.
| It will | It cannot |
|---|---|
|
|
Neither list is a prompt. Both are enforced in code, below the model, where nothing the model says and nothing a target injects can reach them. A prompt is a request. These are walls. That is the only reason it is sane to hand something this capable a real shell on a real machine.
The honest version: aimed at a system you own, Basilisk is one of the most capable things you can point at it.
Aimed at something you don't own, it is evidence โ your name, and a full hashed timeline of every command you ran. The ledger that makes it a professional tool is the same ledger that makes it a confession.
The tool cannot tell the difference. You can, and that is the entire contract.
Who this is for, plainly
It is for: red teamers, penetration testers, security engineers hardening their own estate, bug-bounty hunters working inside a programme's scope, and researchers on lab targets they built.
It is not for: anyone looking for a way into something that isn't theirs. Not because of a filter โ there isn't one that would stop you โ but because the thing you'd be reaching for is a tool that logs everything you do, runs with your privileges, and is designed by someone who expects you to be able to produce a signed authorisation on request.
On privacy: not a courtesy, a design constraint. No account, no telemetry, no analytics, no phone-home, no cloud sandbox holding your engagement data. Your findings, your ledger and your chat history live in a SQLite file on your disk and go nowhere.
There is exactly one exception: the API call to the model provider you chose. Which is why the model picker tells you, at the moment you pick, which free tiers train on what you send them.
And the part people skip: privacy protects the operator, not the target. Being untraceable is not the same as being permitted. Get the authorisation, keep it, and stay inside it.
๐ฏ What it does
Two jobs, one loop, and the loop is the same both times: do the thing, then prove it worked.
It breaks in. Pointed at an authorized target, it reads the app's behaviour to name the vuln class, reaches for a matching exploit builder, fires, and confirms the hit against ground truth before anything counts. 87 of 113 on OWASP Juice Shop โ black-box, fully autonomous, on a budget model, beating every other agent on that board including their white-box runs. It does not spray payloads and hope. It forms a hypothesis, arms the proof, and collects.
It fixes code. Hand it a .zip of your repo and it works the whole thing: searches, reads, edits, runs your tests, hands back a fixed zip. It records what was already failing before it touched anything, and it will not export a change set it hasn't verified.
It only arms when you arm it. The offensive suite โ recon planning, scanner parsing, the exploit builders, the success oracle, scope and asset tracking โ loads only under Unleash. Disarmed, Basilisk is a research and repair tool, and the attack tooling isn't hidden from it, it is refused at the loader. One switch decides both what it can do and what it thinks it is for.
One rule runs underneath both jobs: Basilisk never asks a model whether something worked.
Most "AI pentesters" do exactly that. They fire, ask the model how it went, and write down the answer โ a confidence score wearing a lab coat. It is why their findings drift, and why their numbers fall apart the moment the target isn't one the model has read a walkthrough of.
Basilisk arms every attempt with the thing that would prove it: a dumped database row, another user's token, a measurable timing difference, an out-of-band callback. Then it fires. Then it looks for that marker. No proof, no finding โ and on the code side, no passing test, no fix.
๐ The loop
Every other agent asks the model "did that work?" and believes the answer. That's why their findings drift and their scores collapse on anything the model hasn't memorised. Basilisk never asks. It arms the proof before it fires.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ OBSERVE โโโถ HYPOTHESISE โโโถ ARM โโโถ FIRE โโโถ VERIFY โโโถ RECORD โ
โ behaviour vuln class the through against to the โ
โ of the app + builder proof the ground hashed โ
โ to reach for marker safety truth ledger โ
โ gate โ
โ โฒ โ โ
โ โ โผ โ
โ โโโโโโโโโโโโโ what's left, what's proven โโโโโโ โ
โ (oracle never re-runs a solved bug) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The marker is the whole trick: a dumped row, another user's token,
a measurable timing delta, an out-of-band callback. No marker, no finding.
Same loop, different target, when you point it at your own repo: baseline the tests โ change one thing โ re-run โ read what broke first โ loop until green โ refuse to export anything unverified. Do the thing, then prove it worked. That's the entire product in one sentence.
๐ Benchmark
A claim is worth exactly the number you can regenerate. Basilisk is scored on OWASP Juice Shop, which marks a challenge solved only when the exploit genuinely fires โ no partial credit, no checklist to recall, graded by difficulty (1โ6 stars). It's the comparable benchmark the security community already uses.
Turned loose fully autonomously and black-box โ no per-command approval, no source on the machine โ it solved 87 of 113 (77%).
| Agent | Licence | Black-box | White-box (source provided) |
|---|---|---|---|
| ๐ Priest's Basilisk (v7.6.0) | MIT ยท free | 87 / 113 | โ |
| Priest's Basilisk (v7.5.3) | MIT ยท free | 81 / 113 | โ |
| Priest's Basilisk (v7.1.0) | MIT ยท free | 73 / 113 | โ |
| Priest's Basilisk (v6.0.0) | MIT ยท free | 58 / 113 | โ |
| Cascade (Windsurf / Escape) | commercial, quote-only | 36 / 113 | 49 / 113 |
| Claude Opus 4.8 (bare model) | metered API | 23 / 113 | 24 / 113 |
Read the two right-hand columns together, because that is the whole story.
Cascade's best number โ 49 / 113 โ is its white-box run: it was handed the source code. Basilisk's 87 / 113 is black-box: no source on the machine, nothing but the target's own attack surface, the way a real engagement actually starts.
Basilisk, blind, beats Cascade holding the answer sheet โ by 38 challenges. That is 1.8ร its white-box score.
Scored like for like, black-box against black-box, it is 2.4ร Cascade (87 vs 36, +51 challenges) and 3.8ร a bare frontier model.
And the column that should be hardest to explain away: Cascade is a commercial product you buy through a sales conversation. Basilisk is MIT, costs nothing to own, and every figure above was produced driving DeepSeek-V4-Flash โ one of the cheapest models on the market โ against a paid product running frontier models.
[!IMPORTANT] The model is not the point โ the loop is. A budget model tops this board because the verified-exploitation loop wrapped around it is doing the work: hypothesis โ deterministic exploit builder โ fire โ prove it against ground truth โ keep the receipt. Swap in a frontier model and the loop gets better, not the other way round. What you are looking at is scaffolding beating spend.
Published work generally puts fully-autonomous LLM pentest agents at 20โ30% on comparable tasks; Basilisk clears ~77%. Other agents' figures above are from the earlier v6-era session and were not re-run.
SOLVE RATE BY DIFFICULTY THE BOARD (Juice Shop, 113 challenges)
โ
โโโโโโโโโโโโโโโโโโโโโโโโ 100% Basilisk v7.6.0 โโโโโโโโโโโโโโโโโโ 87 black-box ยท free
โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโ 100% Basilisk v7.5.3 โโโโโโโโโโโโโโโโโ 81 black-box ยท free
โ
โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโ 92% Basilisk v7.1.0 โโโโโโโโโโโโโโโ 73 black-box ยท free
โ
โ
โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโ 48% Basilisk v6.0.0 โโโโโโโโโโโโ 58 black-box ยท free
โ
โ
โ
โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโ 68% โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ
โ
โ
โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโ 58% Cascade white-box โโโโโโโโโโ 49 SOURCE PROVIDED
Cascade black-box โโโโโโโโ 36 commercial
Opus 4.8 bare โโโโโ 23 metered API
The curve is the honest part. It clears the entire lower half, then thins as the chains get deeper โ and it climbs again at five and six stars, because that's where the verified-exploitation oracle earns its keep. A flat line would mean the benchmark was memorised, not solved.
๐ Difficulty curve, deep-end detail, and how to reproduce it
| Difficulty | Solved | Rate |
|---|---|---|
| โ | 13 / 13 | 100% |
| โ โ | 18 / 18 | 100% |
| โ โ โ | 24 / 26 | 92% |
| โ โ โ โ | 12 / 25 | 48% |
| โ โ โ โ โ | 13 / 19 | 68% |
| โ โ โ โ โ โ | 7 / 12 | 58% |
The curve is the honest part. It clears the entire lower half โ every one- and two-star, and 24 of 26 three-star โ then thins as the chains get deeper. That's the shape a real tool should have, not a flat line.
Where it wins in the deep end: 7 of 12 six-star (SSRF, SSTi, Forged Coupon, Forged Signed JWT, Login Support Team, Premium Paywall, Arbitrary File Write) and 13 of 19 five-star (unsigned JWT, XXE DoS, NoSQL exfiltration, three password resets, frontend typosquatting, retrieve blueprint, leaked access logs/API key, and more).
Where it misses: where one builder isn't enough and the chain runs long โ RCE/DoS variants, NoSQL manipulation/DoS, and the LLM-chatbot challenges (prompt injection, system-prompt extraction).
Progression, same scoring: 51 โ 58 (v6.0.0) โ 73 (v7.1.0) โ 81 (v7.5.3) โ 87 (v7.6.0). Gains over v7.1.0 concentrate in the deep end โ five-star 42% โ 68%, six-star 33% โ 58% โ as the oracle stopped re-running solved bugs and the verified-exploitation loop got sharper about what was left. A separate coverage run confirms all 14 OWASP vuln classes end to end (F1 0.95).
Run it yourself:
docker run -d -p 3000:3000 -e NODE_ENV=unsafe --name juiceshop bkimminich/juice-shop
Point Basilisk at the board and call juiceshop_report โ it reads the live scoreboard (/api/Challenges) and reports solved/available by difficulty. Score any other tool against the same container and compare.
Full board: NODE_ENV=unsafe, v7.6.0, model DeepSeek-V4-Flash, target 192.168.1.151:3000 (Docker). Solved through the exploit builders + run only โ no web reader, no source. Scorecard: benchmarks/juice-shop-scoreboard-2026-07-20.txt.
๐ฆ Second target: Escape Duck Store (API security)
Juice Shop is a web app. The second benchmark is a deliberately-vulnerable REST API โ Escape's Duck Store โ built specifically to defeat the training-data memorization that inflates Juice Shop numbers for everyone else. The planted flaws are API-first: broken object- and function-level authorization (BOLA / BFLA), mass-assignment privilege escalation, SSRF, and business-logic abuse, rather than the web-app classes Juice Shop leans on.
Run fully autonomously and black-box against the live API surface, with no schema handed to it, Basilisk confirmed 22 / 22.
Classes covered: BOLA/IDOR ยท BFLA ยท mass assignment ยท SSRF ยท SQLi ยท stored XSS ยท broken auth ยท file upload ยท excessive data exposure ยท business logic. Scoring is class-based and target-agnostic (benchmark_score grades findings against a known set, or your own), so the same rig scores any API.
๐ง Fixing your code
Hand Basilisk a .zip of your repo. It unpacks it into a private working tree, works the whole thing, and hands back a fixed zip you drop over your checkout.
workspace_import โ unpack the repo, flag anything that looks like a credential
workspace_overview โ languages, LOC, entry points, manifests, where the tests live
workspace_baseline โ run your tests BEFORE editing; record what already fails
workspace_search โ repo-wide grep, so it finds the file instead of guessing
workspace_replace โ surgical edit; refuses a match that isn't unique
workspace_verify โ re-run; classify what it fixed, what it BROKE, what still fails
workspace_diff โ show you every change before anything leaves the sandbox
workspace_export โ zip it back up
The baseline is the whole idea. Without it, every test that was already red looks like damage the agent just caused โ and worse, a test that was already broken gets quietly "fixed" and folded into your diff as work you never asked for and can't separate from the work you did.
It tracks failing test names, not counts. Counts can't tell "fixed one, broke another" apart from "nothing changed" โ both read as 2 failed. That distinction is the entire value of the loop.
The export gate is real, not advice. Basilisk refuses to hand back a zip whose changes were never verified, and refuses one where the last run showed a regression. Export is the moment your actual repo is at risk, and "the model said it was fine" is not evidence. There's a force override โ you're never locked out โ but it has to be asked for out loud, and forced exports are flagged so you know which check was skipped.
It also won't edit your tests to make them pass. If a test looks wrong it says so and lets you decide, because editing a test to match broken code is the single worst thing an agent can do in a repo.
The existing source scanners point at the open repo automatically โ zday_scan (variant analysis across 31 sink patterns for RCE, deserialization, SSTi, SQLi, SSRF, traversal, XXE, prototype pollution, weak crypto, hardcoded secrets, in py/js/ts/php/java/ruby/go/.NET) and the SAST/SCA/secrets orchestration in code_scan_plan.
๐ Why the sandbox is a real boundary and not a folder convention
This is the one place Basilisk takes a file from outside and writes its contents to disk under a name the file itself chooses. Everything else it handles is a command string it parses. That difference drove the design.
Every read, write and delete goes through one containment check that fails closed, resolves symlinks before comparing (so a link inside the tree pointing out is caught), and compares with commonpath rather than startswith โ because /home/u/repo-old starts with /home/u/repo and is a different directory.
Extraction refuses, before writing anything:
- Zip slip โ member names that traverse out of the destination. This is CVE-2007-4559, still shipping in Python's own
tarfilein 2022. - Symlink entries โ a zip can carry
docs -> /and thendocs/etc/passwd. Rejecting..does not catch this. - Zip bombs โ a per-entry compression-ratio ceiling plus a running total, because a 42 KB archive can expand to petabytes.
Refusals are reported, not silently dropped โ otherwise a rejected file just looks like the import lost data.
The second reason it's structural rather than a line in a prompt: Basilisk is autonomous. A model that decides your fix belongs in ~/.bashrc isn't misbehaving in any way it can detect.
Credentials get special handling. Repo zips routinely carry a stray .env; those files are flagged on import, refused for reading (they are not going into a cloud model's context), kept out of search results, and left out of the export unless you ask for them explicitly.
โ๏ธ How it works
Basilisk runs a closed loop, not a payload spray. It reads a target's behaviour to identify the vuln class, reaches for the matching exploit builder, fires it, and confirms the hit against ground truth before moving on. Every attempt and verdict lands in an exploitation oracle, so the loop never re-runs a solved bug and gets sharper about what's left.
Four subsystems bridge the gap between a CTF and an arbitrary host:
- Structural (AST) payload mutation โ parses a JSON/XML body, injects at every node, and serialises back to valid syntax, so the payload actually reaches each field instead of breaking the parser.
- State-machine & session management โ extracts every dynamic token from a response (cookies, CSRF, bearer/JWT, nonces) and threads it into the next request, reaching steps a stateless scanner never gets to.
- Differential & time-based oracles โ proves blind bugs by measuring: diffs TRUE vs FALSE responses (length, status, DOM, similarity) for a boolean channel, and analyses latency statistically (mean, stddev, z-score) to confirm time-based blind SQLi/RCE past network jitter.
- Verified-exploitation oracle โ before firing, Basilisk arms an attempt with the marker that would prove it (a dumped row, another user's token, a status, a measurable difference); after, it checks the response and records confirmed / failed / pending in a ledger it consults every planning turn. For blind bugs that echo nothing back โ blind SSRF/RCE/XXE, OOB SQLi โ it stands up a local out-of-band canary listener: the payload carries a unique callback URL, and a hit proves the bug with certainty (interactsh technique, running locally and offline).
In plain English: a scanner throws payloads at a page and reports what looked odd. Basilisk decides what would have to be true if the bug were real โ a specific row appearing, a specific token leaking, a callback arriving at a server only it knows about โ then goes and checks whether that happened. It's the difference between "this smells like SQL injection" and "here is a row out of your database."
When an approach stalls, it researches โ pulls the exact technique from a vetted source and applies it on the next move. It clears easy wins first, then goes deep on hard chains, hashing every command into the evidence ledger as it goes.
Unleash is the one-tap form of this: arm it, Basilisk confirms the target, and then it runs off the leash โ no per-command approval, surviving errors and retrying past them, and it does not stop until the objective is verifiably done or you stand it down.
Why you can actually walk away
"Autonomous" is easy to claim and hard to survive. An agent left alone for six hours fails in three specific ways, and each one is handled in code rather than asked for in a prompt:
- It forgets what it already did, and redoes it. A long run's transcript gets trimmed to fit the context window, so the model's evidence of having already tried something decays into a stub while the objective stays loud โ and it re-runs the scan it ran four steps ago. Basilisk keeps a compact action ledger outside the transcript: one line per action and outcome, never trimmed, re-sent whole every turn. A deterministic guard refuses a third identical action outright, and a cycle detector catches the AโBโAโB loops that a "same command twice" check never sees.
- One dead thread strands the whole run. Every tool runs on a worker; if one dies without reporting back, the loop has nothing to advance on and the agent sits at "workingโฆ" forever. Every tool path now goes through a guaranteed one-shot result โ the worker can return, throw, or fail halfway and exactly one result still reaches the model โ with a watchdog behind it as the last resort.
- A slow job gets killed and the work is thrown away. Every long-running tool used to wear a wall-clock timeout, and a wall clock cannot tell
nmap -p- /24(twenty-five minutes of real work, silent in stretches) from a curl against a dead host (twenty-five minutes of nothing) โ so it killed both at the same number. Worse, the timeout handler discarded the output: a scan that enumerated two hundred hosts and then hung on the last one reported nothing, so the agent re-ran the whole scan. Basilisk now supervises by progress, not elapsed time: output arriving or CPU advancing across the process group resets the clock, so there is no limit on how long real work may take. When something genuinely stalls it tries to unstick it first โ the commonest real stall is a process blocked on an interactive prompt, which a timeout can only kill but closing stdin actually releases โ and if that fails it harvests every byte captured and hands it back marked partial, with a diagnosis of what stalled and where. It never restarts from zero. - It pays full price for the same prompt every turn. Both providers cache automatically by prefix โ the longest byte-identical run at the start of a request is reused at a discount (50% on Groq, ~80% on SiliconFlow's DeepSeek), and on Groq cached tokens don't count against rate limits. An agent re-sends the same system prompt and the same history on every step, so this is the single largest cost lever it has. Basilisk was destroying it twice over: a minute-resolution clock sat ahead of ~4,000 tokens of tool contract, and the history used a sliding trim window that rewrote a message in the middle of the request every turn โ which DeepSeek documents as never hitting cache at all. Both fixed: the clock and per-turn material ride at the tail, and trimming advances on a watermark that holds the render byte-stable until a size budget forces one jump. Measured on a 20-step run: reusable prefix went from ~40% with a break every single turn, to 100% with zero breaks.
- It over-thinks a simple problem. Diagnosis is ordered by likelihood ร cost to check: name the two or three most likely causes, test the cheapest decisive one first, stop the moment it is confirmed. Boring causes before exotic ones. Effort escalates on evidence of difficulty โ recent failures โ not on how many steps have gone by, so it stops deliberating on the turn it should be concluding.
None of this makes it smarter. It makes it finish, which is the only property that matters when nobody is watching.
๐งฐ The exploit-builder arsenal โ 20+ vuln classes, general-purpose
Parameterised generators for any authorized target, not Juice-Shop-bound toys:
- SQLi โ DBMS-aware (MySQL / PostgreSQL / MSSQL / Oracle / SQLite), plus sqlmap
- JWT โ
alg:none, RS256โHS256 key confusion - NoSQL, XXE, SSTi (per template engine), SSRF (internal + cloud-metadata + blocklist bypass)
- Insecure deserialization (Node / YAML / pickle / Java โ RCE), prototype pollution
- Path traversal (read, null-byte, zip-slip write), context-aware XSS (filter/CSP bypass + AngularJS CSTI)
- OS command injection, IDOR / broken access control, race conditions (TOCTOU), file-upload bypass, GraphQL abuse, open redirect, CORS misconfig
Analysis layer: a trick detector (hidden encodings, HTML-comment hints, client-side-only "protection", stale tokens, rate limits), a payload encoder that slips blocked payloads past filters (URL / double-URL / base64 / unicode / mixed-case), a WAF/filter analyzer, and a stack fingerprinter so it picks the payload that fits.
๐ฐ Why it costs almost nothing to run
An agent re-sends the same system prompt and the same conversation on every single step. That makes prompt caching the largest cost lever it has โ and both wired providers do it automatically, by prefix: the longest byte-identical run at the start of a request is reused at a steep discount, and on Groq those tokens don't count against your rate limits at all.
Prefix caching is brutally literal. One changed byte near the front and you pay full price for everything after it. Basilisk was breaking it in three places at once:
| Was | Now | |
|---|---|---|
| A clock in the system prompt | minute-resolution timestamp sitting ahead of ~4,000 tokens of tool contract โ new prefix every minute | volatile content rides at the tail |
| Sliding trim window | the tool result sent in full last turn was sent trimmed this turn, rewriting the middle of the request | watermark that holds the render byte-stable until a size budget forces one jump |
| Sliding history cap | past 80 messages it dropped one from the front per turn, moving the anchor every turn | drops in quantised blocks, re-anchoring occasionally |
Measured end-to-end across full requests, before vs after:
SCENARIO REUSABLE PREFIX CACHE BREAKS
short chat (10 turns) โโโโโโโโโโโโโโโโโโโโ 100% 0
normal run (30 turns) โโโโโโโโโโโโโโโโโโโโ 100% 0
general mode (30 turns) โโโโโโโโโโโโโโโโโโโโ 100% 0
long run (60 turns) โโโโโโโโโโโโโโโโโโโโ 95% 3 / 58
heavy run (60 turns, 8KB results)
โโโโโโโโโโโโโโโโโโโโ 89% 7 / 58
^ theoretical ceiling is 93-97%; this captures 96-98% of it
With DeepSeek-V4-Flash on SiliconFlow (cached input 80% off) that's roughly a three-quarters cut in input cost on a long autonomous run โ for zero change in behaviour. Groq's discount is 50%, and cached tokens there don't touch your rate limit, so a free-tier key goes several times further.
None of this is visible at runtime. The app worked perfectly before and simply cost several times more. That is exactly why every one of these properties is now pinned by a test that measures a real request rather than describing an intention.
๐ง Memory, learning & self-improvement
Basilisk isn't a stateless prompt. Three mechanisms let it remember, learn and grow โ all local, all yours.
- Persistent memory across sessions. Facts, preferences, past fixes and prior findings live in a local SQLite store you own. Recall is relevance-scoped: each turn injects only the handful of memories most relevant to the current task (keyword + recency + salience), so history can grow forever without bloating the context window or your token bill. Keyword-based by default โ zero model compute, runs on a phone โ and upgrades to embedding similarity when a model provides it. One toggle, one
memory_forgettool, nothing leaves the box. - Learns within the engagement. Every attempt and verdict lands in the exploitation oracle, so confirmed bugs are never re-run and dead ends aren't retried. The longer it works a target, the sharper its next move gets.
- Writes and keeps its own tools. When the toolbox is missing something, Basilisk writes a new Python tool and a test for it. It is AST-parsed, statically screened, and run against its own test inside a bubblewrap jail โ and kept only if the test passes. A tool that cannot prove it works is discarded, not saved with a warning. Every later call runs jailed too, and retired skills are archived rather than deleted, so nothing it learned is silently lost.
๐ก๏ธ Security model
An agent that reads the outside world and runs shell commands is a prompt-injection target by construction. So the design assumes the model is already compromised, and puts the things that matter where a compromised model cannot reach them. Not filtered. Removed.
-
The injection surface was removed, then gated. The tools that fetched attacker-chosen URLs are gone. What is left,
web_read, is split into two tiers in code: trusted sources an attacker cannot plant content in (NVD, MITRE, CISA, vendor and distro advisories, standards bodies, official tool docs, OWASP, PortSwigger, Kali docs) fetch automatically. Everything else on the public internet โ including exploit-db, GitHub, Stack Overflow and PyPI โ is user-authored and stays outside the autonomous loop: Basilisk raises a one-tap approval in the notification bell, and a compromised model cannot reach any of it without your click. Redirects into an approved domain from an unapproved one are refused, and link-local, private and cloud-metadata addresses are refused outright with no approval able to override it. -
The irreversible class can never run โ enforced twice. A structural detector hard-blocks disk wipes, recursive root/
$HOMEdeletes, fork bombs and raw block-device writes. It normalises$IFSand quoting first, then judges the command that will actually run, not the word that happens to come first: it peels wrapper commands and their own options (timeout 5 โฆ,nice -n 5 โฆ,sudo -u root โฆ), reads through grouping (( โฆ ),{ โฆ; },if/then, function bodies), recurses intosh -c,eval,trappayloads, here-strings andxargs, and enters command substitutions โ$( โฆ )and backticks โ including from inside double quotes. Refused at the UI gate and again inside the command-execution primitive, so no caller can route around it. There is no "Run anyway." Zero false positives on legitimate work likerm -rf ~/loot,timeout 60 rm -rf ./distor( rm -rf ./build )โ a floor that fires on ordinary work gets switched off, and then it protects nothing.Why that's hard:
rm -rf /is easy to spot.timeout 5 rm -rf /is the same command wearing a hat, and so are( rm -rf / ),$(rm -rf /), andecho x | xargs -I{} rm -rf /. A blocklist of dangerous words loses this game immediately. Basilisk instead unwraps the command until it finds what will really execute, then judges that. -
Scope is a boundary, not a suggestion. Before any active command runs, its targets are extracted and checked against the authorized list. It fails closed: no scope set, an unparseable command, or no match all mean out of scope, refused. It sees through
sh -c, wrapper prefixes likesudo/timeout/proxychains, and command substitution. -
Untrusted input is quarantined. Anything from outside โ a target's response, an MCP result, an analyzed image โ passes a deterministic content firewall and is wrapped as data, never instructions.
-
Your sudo password never touches the model. When a command needs root, Basilisk asks you, and the password goes to
sudothrough an askpass helper via an environment variable โ never into the prompt, never onto disk, never into a log, never into the process argument list where any user on the box could read it withps. -
It cannot edit its own safety code. A shell command that would write to, truncate, redirect into,
sed -i, or copy overbasilisk_safety.py,basilisk_scope.pyor the other core modules is refused โ including when the write is hidden insidesh -c, an interpreter one-liner, or acp/mvdestination. The guardrail block in the persona is verified byte-for-byte against a known hash on every release. -
It writes and keeps its own tools โ under a jail and a test. New Python tools are AST-parsed, statically screened, and executed against their own test inside a bubblewrap sandbox. Kept only if the test passes; a tool that can't prove it works is discarded, not saved with a warning. Every later call runs jailed too.
-
The provider stays where you put it. Basilisk never silently hops to a different cloud behind your back. Your selected provider is pinned; a retry after a degraded reply goes back to the same provider, so your data never lands somewhere you didn't choose.
-
It will not build weapons to leave behind. It writes and runs real exploits against targets you authorize โ that's the job โ but it will not produce standalone weaponized malware: no reverse shells, implants, ransomware, or backdoors. That line is in the immutable guardrail, not in a swappable prompt.
-
The web/OSINT readers are deliberately left unwired. A set of harvesting tools exists in the tree and is not connected to the agent loop, on purpose, because wiring them would reopen the indirect-prompt-injection surface the design just closed. That's a capability we chose not to ship.
In plain English: the model is treated like a talented contractor with a key to one room. It can do anything inside that room, including things that would wreck the room. It cannot get out of it, cannot change the locks, cannot read your wallet on the way past, and cannot take the tools home.
We audit our own floor, and we publish what we find
A security claim is worth exactly the work behind it, so here is the work.
In v9.7.0 the destructive gate was fuzzed against a real shell โ every candidate bypass re-run in live bash with the destructive verb swapped for a harmless marker, counting only the shapes where the shell actually did the thing. Twenty-one shapes that had been getting through were found and closed, including timeout 5 rm -rf /, ( rm -rf ~ ), $(rm -rf /) and echo x | xargs -I{} mkfs.ext4 /dev/sda1.
That method mattered more than the fixes. A blind fuzz reported 18,856 "bypasses" โ almost all of them shell syntax errors that never execute and never mattered. Filtering to what a real shell actually runs is what made the twenty-one findable at all.
Two things are worth stating plainly:
- They were real. Not theoretical, not "hard to reach".
timeout 5 rm -rf /isn't an attack โ it's something a model writes by accident, and under Unleash nobody is on the trigger. - They are now pinned, not just patched.
tests/test_safety_gate.pyasserts all twenty-one, and asserts the counter-property against a corpus of ordinary pentest and dev commands, so a future "improvement" that over-blocks fails just as loudly as one that under-blocks.
The scope gate was audited the same way and did not share the hole โ it fails closed on every one of those shapes with a stated reason, because it reasons about the whole command string rather than trusting the first word.
If you find a twenty-second shape, open an issue. That is the arrangement.
All of it is pinned in the test suite โ 2,892 assertions across 45 suites, stdlib-only, runnable before you trust it with anything. Basilisk writes and runs real exploits against authorized targets, because that's the job. It will not produce standalone weaponized malware (reverse shells, implants, ransomware, backdoors), and the destructive class can never run through it at all.
๐งฐ Everything in the box
Tool specs load on demand, so the base prompt stays small no matter how many exist โ and the offensive half only exists at all when Unleash is armed.
| Group | Loaded | What's in it |
|---|---|---|
| ๐ฅ๏ธ system | always | Read any file, search anywhere, snapshot RAM / disk / processes / routes / services / journal, graded security audit, network scan |
| ๐งช code | always | SAST + SCA + secrets scanning across py/js/ts/php/java/ruby/go/.NET, cross-tool triage, remediation hints, 31-signature variant analysis |
| ๐ฆ workspace | always | Import a repo zip, search and read it whole, surgical edits, baseline โ verify โ export with a gate that refuses unverified changes |
| ๐ฑ๏ธ desktop | always | Launch apps, manage windows, type, click, screenshot, OCR the screen, notify |
| ๐ผ๏ธ media | always | Show images inline, and actually look at one with a vision model |
| โ๏ธ offensive | armed only | Recon planning, scanner-output parsing, CVE โ KEV โ EPSS, nuclei templates, sqlmap builder, false-positive self-check, the verified-exploitation oracle + out-of-band canary |
| ๐ฏ engagement | armed only | Authorised scope (fails closed), asset graph, loot, in-scope credential-reuse leads |
| ๐ benchmark | armed only | Score a run against known-vulnerable practice targets |
โ๏ธ Reliability โ what makes it survivable to leave running for six hours
Autonomy is easy to claim and hard to survive. Four specific things kill a long unattended run, and each is handled in code rather than asked for in a prompt:
| Failure | What Basilisk does |
|---|---|
| Forgets what it already did and redoes it | A compact action ledger lives outside the transcript โ one line per action and outcome, never trimmed, re-sent whole every turn. A deterministic guard refuses a third identical action; a cycle detector catches AโBโAโB loops that a "same command twice" check never sees |
| A slow job gets killed and the work is binned | Supervision by progress, not a wall clock. Output arriving or CPU advancing across the process group resets the clock, so real work has no time limit. A genuine stall gets unstuck first โ the commonest one is a process blocked on an interactive prompt, which a timeout can only kill but closing stdin actually releases |
| One dead worker strands the whole run | Every tool path returns through a guaranteed one-shot result: the worker can return, throw, or die halfway and exactly one result still reaches the model. A watchdog behind that nudges the run back into motion โ carrying the full conversation and ledger, so a nudge can't become a loop โ before it will ever consider stopping |
| Over-thinks a simple problem | Diagnosis ordered by likelihood ร cost to check. Name the two or three likeliest causes, test the cheapest decisive one first, stop the moment it's confirmed. Boring causes before exotic ones. Effort escalates on evidence of difficulty, not on how many steps have passed |
๐ What it is not, and what it can't do
Every tool page lists strengths. Here are the limits, because you'll find them anyway and it's better you hear them from us.
- It is not a replacement for a penetration tester. It is an extremely fast, tireless, methodical pair of hands that never skips the boring half of the methodology. Scoping the engagement, judging business impact, deciding what a finding is worth, and writing the part of the report a client acts on โ all still yours.
- It gets weaker as the chain gets longer. Look at the difficulty curve: it clears one- through three-star challenges nearly outright and thins in the deep end. Bugs that need four unrelated insights stacked in the right order are still where autonomous agents lose. We publish the misses by name rather than rounding them away.
- It is only as good as the model you give it. The scaffolding is what scores โ that's the whole thesis โ but a weak model still reasons weakly inside it. The benchmarks were run on a cheap model on purpose; they were not run on every model.
- The benchmark numbers are ours. They're reproducible โ the exact target, flags, model and scoreboard commands are published above so you can re-run them โ but they are self-reported, and you should treat them the way you'd treat any vendor's self-reported number until you've regenerated one.
- It is Linux and GTK4. No Windows, no macOS. It runs on a phone (NetHunter Pro), which is a strange flex, but it does not run on your work laptop's default OS.
- Network egress is real. It runs locally, but the model call leaves your machine. If your engagement data can't go to a third-party API, Basilisk is the wrong tool until you point it at something self-hosted.
- Autonomy is a loaded gun with a good trigger guard. The floors are real and tested. They stop it destroying your machine. Nothing in the software stops you aiming it at a host you have no right to touch โ and it will keep working that host until you pull it off.
๐ฌ How you know it works
Claims are cheap. Every claim on this page is either a number you can regenerate or a test you can run โ and the suite is deliberately boring to execute:
for f in tests/test_*.py; do python3 "$f" || echo "RED $f"; done
Stdlib only. No pytest, no network, no fixtures, no account, nothing to install. 45 suites, 2,892 assertions, done in under a minute on a laptop. You don't have to believe any of this โ you can check it.
What that suite is actually for โ because "we have tests" means nothing on its own:
- Bugs are pinned, not described. When a real bug is fixed, the test that catches it is written to fail against the previous release.
test_safety_gate.pyfails on v9.6.0 for all twenty-one gate bypasses;test_streamperf.pyfails on it for the UI freeze. A regression can't quietly return. - Performance is asserted as a shape, not a stopwatch. A millisecond ceiling passes by luck on a fast machine. So the suite also asserts the scaling exponent โ quadruple the input, the time must not quadruple โ which fails on a slow box and a fast one alike.
- Counter-properties are tested as hard as properties. Every safety check is paired with a corpus of ordinary work it must stay silent on. Over-blocking is a test failure, because a safety feature people turn off protects nothing.
- The shipped artifact is what's verified. The release zip is extracted fresh and the full suite run from inside it โ not from the working tree it was built in.
The parts that carry the most risk are locked down hardest: the immutable guardrail block is hash-checked on every release, and the safety, scope and ledger modules are diffed byte-for-byte so a change to any of them is deliberate and visible.
What "finished" means here. Not "we stopped finding bugs" โ that claim is always a lie, and a tool that makes it is telling you it stopped looking. It means the architecture has settled, the safety floors have stopped moving, and the failure modes that remain are the ones you find by reading, not by using. Every release ships the hunt as well as the fix: the twenty-one gate bypasses, the quadratic regexes that froze the UI, the tool-call dialect that leaked into the chat, the compressor that returned a cache pointer instead of a page โ all found in this codebase, all published, all pinned to a test that fails against the release before them.
That is the standard this tool is held to, and it is the only honest way to sell you something you're about to hand a real shell on a real machine. If you find the next one, open an issue โ it will be fixed, pinned, and written up like the rest.
๐ฆ Install
Basilisk runs shell commands as you. Read the installer before you run it.
curl -fsSL https://raw.githubusercontent.com/the-priest/PriestsBasilisk/main/install.sh | bash
Or clone, read, then run:
git clone https://github.com/the-priest/PriestsBasilisk.git basilisk
cd basilisk
less install.sh
./install.sh
Plain Python plus one shell script โ no Docker, no daemon, no account, nothing phoning home. The installer auto-detects your distro, parse-checks every file before it touches disk, and backs up your chat history. The same command updates in place.
The test suites are stdlib-only, so you can verify the safety claims yourself before pointing it at anything:
for t in tests/test_*.py; do python3 "$t"; done
๐ Or install from PyPI
pip install priestsbasilisk
basilisk
Install the GTK stack first. Basilisk is a native GTK4/libadwaita app, and PyGObject ships source-only on PyPI โ it compiles against your system's GObject-introspection headers rather than downloading a wheel. On a desktop that already has PyGObject from its own package manager, pip sees it and is satisfied. On a bare machine, install these first:
| Distro | Command |
|---|---|
| Arch / CachyOS | sudo pacman -S python-gobject gtk4 libadwaita |
| Debian / Kali / Ubuntu | sudo apt install python3-gi gir1.2-gtk-4.0 gir1.2-adw-1 libgirepository1.0-dev |
| Fedora | sudo dnf install python3-gobject gtk4 libadwaita-devel |
| openSUSE | sudo zypper install python3-gobject gtk4 libadwaita-devel |
Optional features are lazily imported, so the app runs without them and turns the feature off instead of failing. Install them if you want them: pip install "priestsbasilisk[all]" โ or individually, [vision] for image analysis, [web] for brotli-compressed fetches, [pentest] for YAML template parsing.
Which install should you use? install.sh is still the recommended path and the one the rest of this README documents: it sets up the desktop entry, the icon theme, the launcher and the optional systemd user unit, and it updates in place. pip install gives you the application and the basilisk command, and nothing else โ no .desktop file, no icon, no service. Use it if you want Basilisk inside a virtualenv, in a container, or managed by your own tooling.
Building the distribution yourself is one command, and the packaging is pinned by its own test suite so the wheel cannot silently drop a module or an asset:
python3 -m build && python3 -m twine check dist/*
๐ Bring your own model
Multi-provider โ you only need a key for the one you want. Set it in Settings โ Backends.
| Provider | Get a key | Notes |
|---|---|---|
| SiliconFlow | https://cloud.siliconflow.com/account/ak | Default. Large open models (DeepSeek, GLM, Kimi, Qwen, MiniMax) + SenseVoice STT |
The model picker shows context window, price per million tokens and what each model is for, grouped flagship / workhorse / budget. A refresh button pulls the provider's live catalogue, so a retired model id can't sit in the list silently 404ing. Keys live only in ~/.config/basilisk/settings.json, locked to your user โ they go nowhere but the provider's own API.
Tool-call dialects. Models do not agree on how to emit a tool call, and several will use their own trained format no matter what the prompt asks for. Basilisk normalises every dialect it has seen to one canonical form before anything parses or renders it: the canonical <tool name="x">{json}</tool>, DeepSeek's native special tokens, DeepSeek-V4's DSML tags, <tool_call>, <invoke>, <function=โฆ>, fenced-JSON bodies, and arguments supplied as <parameter> child tags instead of JSON. Anything still unrecognised is detected rather than printed โ the host tells the model its call could not be read and shows it the format that works, so a new dialect costs a round trip instead of a broken run. Parsing and display are driven from the same normalised text by construction, because when they disagree a call executes and leaks its raw markup into the chat.
๐ Requirements
- Python 3.10+, Linux with GTK4 / libadwaita (X11 or Wayland)
- Runs on Debian/Kali, Arch-based distros (CachyOS, Arch, EndeavourOS, Manjaro) and Fedora/SUSE โ the package manager (
apt/pacman/dnf/zypper), privilege-escalation tool (sudo/sudo-rs/doas) and wordlist locations are all auto-detected, never assumed. Also runs on NetHunter Pro (Phosh/Wayland) on a phone. - Standard offensive tooling (nmap, sqlmap, etc.) is auto-detected; missing tools are flagged with a distro-correct install hint โ pacman/AUR on Arch, apt on Debian โ never a Debian command on an Arch box.
๐ Why it's free
There is a version of this that has a pricing page. Three tiers, a "contact sales" button, a seat count, an enterprise plan with SSO and a quarterly invoice. Everything on this page would still be true, and the number at the bottom would be four figures a year.
I'd rather it went to the people who'd actually use it.
Some men aren't looking for anything logical like money. Some of us just want to see the thing built properly, put in the hands of people who know what to do with it, and left there.
So: MIT. All of it. Not a trial, not a community edition with the good parts removed, not open-core with the safety gates behind a licence key. There is no paid tier, because there is no held-back tier โ the benchmark scores on this page were produced by the same code you're about to clone, on a budget model, with the receipts committed to the repo.
No account. No telemetry. No usage cap. No "upgrade to unlock". Nothing phones home, and nothing ever will, because the day it does is the day it stops being the thing I wanted to build.
If it earns its place in your kit, star the repo and tell someone who runs engagements. That's the whole price.
๐ License
MIT. Take it, fork it, use it on what you're allowed to break. Ship it inside your own product if you want to โ the licence means what it says.
Built by one person. Verified by 2,892 assertions. Priced at nothing.
No VC, no waitlist, no "contact sales", no seat count, no upgrade path.
Clone it, read it, run the suite, then point it at something you own and watch it work.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file priestsbasilisk-9.7.0.tar.gz.
File metadata
- Download URL: priestsbasilisk-9.7.0.tar.gz
- Upload date:
- Size: 11.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b6bb57ad437ac2d0f767f96d826f0e8d1cac1c17efa7de5253275f4735403933
|
|
| MD5 |
c6959d0cb28600f98e27c160788eaf28
|
|
| BLAKE2b-256 |
9d9a5fd81946e328e3198e980019690668bf140eb090f542567b77d650736a2e
|
File details
Details for the file priestsbasilisk-9.7.0-py3-none-any.whl.
File metadata
- Download URL: priestsbasilisk-9.7.0-py3-none-any.whl
- Upload date:
- Size: 8.8 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
51c229d1c670302b8209ce17f12e43967571b2118c811a4a73384c40f3dfb07b
|
|
| MD5 |
0a84564895aa523ff868a158b422afdb
|
|
| BLAKE2b-256 |
ab649837a58e7b714fbfd02185a79a3c00438051ee971e48f24c5bf48e7a76ea
|