Meet Palisade. Your AI's safety engineer.
AI can be tricked into leaking data, running commands, or hitting your database. Palisade is the engineer that finds those weaknesses, tests them, and blocks them before you ship - one command, right in your pipeline.
A sentence can turn your AI against you.
Feed a model untrusted text and its answer can reach the real world - your shell, your database, an outbound request - running with your privileges. It is the #1 risk on the industry's LLM list, it has already been exploited in the wild, and no prompt fully prevents it. The one place you can reliably catch it is your code, before it ships.
Ranked first, still unsolved
OWASP puts prompt injection at the top of the LLM Top-10, two editions running. The model vendors agree it can't be fully prevented at the model layer.
Real CVEs, this exact shape
Langflow's injection → exec path (CVE-2025-3248) is on the CISA KEV list, exploited in the wild. PandasAI (CVE-2024-12366, CVSS 9.8) and Vanna (CVE-2024-5565) shipped the same pattern.
Find it in code, before it ships
You can't fix the model, but you can find every place a manipulated answer could reach something dangerous - and block it in your pipeline before it ever reaches production.
It reads your code the way an attacker would.
Palisade follows the path from untrusted input, through the model, to anything dangerous - and only flags a real, complete path with no guardrail in between. Calling an AI is never a finding on its own. That one rule is why it almost never cries wolf.
Parse
Python via stdlib ast, JS/TS via tree-sitter. Source text only - scanned code is never executed.
Lower
Both languages compile to one normalized taint IR. Import aliases resolve, so sp.run is subprocess.run.
Propagate
Taint flows through f-strings, collections, awaits, class fields, and bounded cross-file calls - 3 hops deep.
Verify defenses
Real sanitizers suppress. Denylists, confirmation gates, and sanitizers-in-name-only downgrade - never silence.
Report
Full trace per finding: source, LLM, sink, attack, fix, CVE refs. Terminal, stable JSON, or markdown threat model.
Five taint rules, plus multi-agent handoffs. Each mapped to a real CVE class.
Rules are plain YAML - sources, LLM signatures, sinks, sanitizers. Adding coverage for your framework is a data change, never an engine change. The CVEs below name the vulnerability class each rule covers; the proof section shows what is verified end-to-end, including the one documented miss.
Injection → code execution
Model output executed as code. The most common real-world LLM vulnerability class.
new Function · vm.runIn*
Injection → OS command
Model output handed to a shell verbatim. Arg-list subprocess.run([...]) stays silent.
child_process.exec / execSync
Injection → raw SQL
Text-to-SQL executed non-parameterized. execute(q, params) stays silent.
pool.query · db.query
Framework wrapper → execution step
Agent frameworks hide the LLM behind wrappers. Palisade knows the shapes: submit_prompt, call_llm, generate_code…
Injection → SSRF / exfiltration
Model-chosen URLs fetched directly - cloud metadata, internal APIs, attacker hosts. Advisory: never gates CI.
Injection → agent handoff → dangerous tool
Multi-agent path: untrusted input runs an agent that hands off (≥1 hop) to an agent holding a dangerous-capability tool. Deterministic and offline.
Your framework, one YAML file
Your codebase routes LLM calls through self.inference()? Add one line to llm_signatures, pass --rules ./dir. Same id overrides a builtin.
It doesn't cry wolf.
A security tool that raises false alarms gets ignored, then uninstalled. So Palisade holds itself to precision as the product: every "won't flag" below is locked by a permanent test, and every false alarm anyone reports becomes one too.
- PASSConstant developer prompt → LLM → exec - no untrusted source, no finding
- PASS
subprocess.run([...])with an arg list and no shell - PASSParameterized SQL -
execute(q, params),pool.query(text, values) - PASSpydantic / marshmallow validation on the path
- PASSVerified project sanitizers - allowlists and guards that actually raise
- PASSLLM output that is only logged, printed, or returned
- MEDDenylists. LangChain PAL's
COMMAND_EXECUTION_FUNCTIONSdenylist was bypassed → CVE-2023-36258 - MEDConfirmation gates. "Are you sure?" is not a security boundary
- MEDSanitizers in name only. Vanna's
_sanitize_plotly_codestrippedfig.show()- and shipped CVE-2024-5565. Palisade verifies the body, not the name - HIGHMulti-hop paths. Source in one file, LLM in a second, sink in a third - traced across the call graph
- HIGHHidden flows.
json.loads(output)["cmd"], f-strings,parts.append(...),self.xclass fields
"A false positive is worse than a miss - noise trains developers to ignore all security warnings."Design philosophy #1, enforced by the test suite
Point it at the repo behind a real CVE.
We scanned the actual vulnerable releases of the projects that motivated Palisade - and published the misses alongside the hits.
The exact CVE line, nothing else
Builtin rules + library mode land on base.py:1998 - the sink NVD lists for CVE-2024-5565 - and correctly call out the cosmetic sanitizer that failed in the wild.
Zero false positives at scale
A pinned corpus of 26 third-party repos, 17,343 files, scored in CI: precision 1.000, recall 0.667. Not one wrong flag, and the single miss is labelled as a miss rather than deleted.
Misses are documented, not hidden
PandasAI's dynamic pipeline dispatch beats bounded static taint. We say so, and it defines the roadmap. Read the full proof-scan report →
Scan. Gate. Fix.
Baseline your existing debt, fail CI only on new findings, and generate a guardrail + regression test for every finding you burn down.
Gate CI on new findings
github actions# .github/workflows/security.yml - uses: astral-sh/setup-uv@v5 - run: | uvx palisade-sec scan . --ci \ --baseline .palisade/baseline.json
Fix with proof
palisade-sec fix$ palisade-sec fix . → palisade-fixes.md # per finding: a tailored guardrail # + a pytest proving it blocks the # canonical attack. Offline. Never # edits your code.
Built for AI agents
llms.txt · agents.md$ palisade-sec scan . --json { "schema_version": 1, ... } # stable schema · exit-code contract # remediation policy · fingerprint # diffing - the full agent contract
One engineer. The whole safety job.
Finding the risk is where Palisade starts. A real safety engineer also stress-tests the system, argues it is safe with evidence, gates the risk in your pipeline, and keeps a human in charge as AI writes more of the code. Palisade is growing into all of it - one honest tool across every facet of AI safety engineering. Each piece is marked for what it does today versus what is still being built.
The action boundary
Where a model reaches a real capability - exec, a shell, SQL, an outbound call. This is the loss-of-control surface as autonomy grows.
today · map agents + handoffs (openai · langgraph · crewai)
today · audit excessive agency
Exercise it adversarially
Attack the system with inputs built from its own map, and measure what lands - on a pinned, reproducible harness rather than a vibe.
today · gated red-team execution (HttpTarget + scorer)
today · pinned 26-repo precision gate in CI
Argue the residual risk
A structured, evidence-backed argument a decision-maker can accept - not a bare score. Every claim traces to a fact the engine can prove.
today · measured-not-asserted discipline
Make it enforceable
Turn safety practice into an organizational requirement a pipeline enforces, with an audit trail teams and regulators can read.
today · SARIF + GitHub code-scanning Action
today · stable JSON schema · disclosure policy
Keep a human in control
Keep AI-assisted work verifiable as models begin to exceed human review. Advisory by default; a person holds the gate on anything that acts.
today · approval gates · unverified-can't-act
today · never executes your code
Why the ambition stays honest
Every judgment is anchored to a deterministic finding the engine can prove. The floor under the whole roadmap is a real, measured detector.
today · precision 1.000 across 17,343 files
today · zero false positives
The questions people ask first.
A linter is the foundation, and that is deliberate: every safety claim is anchored to a deterministic finding Palisade can prove. On that ground truth it does more of an AI safety engineer's job - mapping the action surface, red-teaming it, arguing residual risk, and gating AI-assisted changes. It works the applied, verifiable end of the problem: safety for agentic systems at the deployment layer. It is not frontier alignment research, and it does not claim to be.
The taint core - single-agent paths and multi-agent handoff detection - is shipped and gated in CI at precision 1.000, and SARIF output plus gated red-team execution ship alongside it. The judged layer (audit, review, posture) runs today with a preliminary seed-corpus calibration but no full-benchmark precision/recall yet, so it is advisory and can never gate CI or raise a critical posture on its own. Deeper cross-agent taint, governance tags and SBOM/signing, and the oversight-of-AI-code mode are on the public roadmap, marked building. We would rather show the frontier than blur it.
No. scan is pure static analysis: it parses source text and never executes, imports, or evals it - a live test in the suite proves scanned code cannot run. There are no network calls, no telemetry, no accounts, and no API key. The only writes are .palisade/ and files you explicitly request. The optional judgment layer (audit, review) is the only part that reaches an endpoint, one you configure in .env; it is all MIT and free, it currently has a preliminary seed-corpus calibration and stays advisory, and the core never calls out.
Bandit flags exec() anywhere; Semgrep matches patterns you write. Palisade runs LLM-aware taint analysis: it only fires on a complete untrusted-input → LLM → sink data-flow path, understands LLM SDK response shapes, and judges defenses - denylists and cosmetic sanitizers downgrade instead of silencing. It's the layer those tools don't model, and it composes fine with both.
Zero across a pinned benchmark corpus of 26 third-party repos and 17,343 files, measured in CI rather than claimed. The mechanism: findings require the full path, safe shapes (arg-list subprocess, parameterized SQL, verified sanitizers, constant prompts) are recognized, and every reported FP becomes a permanent must-stay-silent test. Misses are documented publicly in the proof-scan report.
Yes - install with the extra: pip install "palisade-sec[js]" or uvx --from "palisade-sec[js]" palisade-sec scan .. A tree-sitter frontend lowers JS/TS into the same taint IR with zero engine changes, so the same YAML rules match Express req.body, eval, new Function, child_process.exec, and pool.query.
Library mode: --assume-params-untrusted treats the parameters of public functions as untrusted sources - a library's callers are the untrusted world. That's exactly how Palisade finds the real CVE-2024-5565 sink in vanna v0.5.5 with builtin rules and nothing else.
No, and we won't pretend otherwise. Palisade is one layer against one class of vulnerability - code-level injection-to-sink paths. Keep your runtime guardrails, permission boundaries, and sandboxes. Palisade complements them, before merge.