STATIC SCANNER · OFFLINE · NO API KEY

Type-safe isn't correct.
Your guardrail still fails open.

Jev can't hallucinate a field and can't throw a type error. Neither means the answer is right. A guardrail that returns a confident noul: 0.02 for rm -rf / still lets the call through. jev-guard scans your code for the ways a Jev-based guardrail fails open.

See it beat your SAST Source $ pip install jevg

The trap shipped straight from the docs

LangChain's AutoModeMiddleware uses Jev to block dangerous tool calls. The documented example gates a shell on the model's default decision, with no threshold. That is the exact shape jev-guard was built to catch.

# straight from the AutoModeMiddleware blog
guardrail = AutoModeMiddleware(tools=["bash"])   # no threshold. no escalation. no abstain.
agent = create_agent("openai:gpt-5.6-luna", middleware=[guardrail])

Your SAST passes this file. jev-guard finds 4 HIGH.

A 31-line code-review agent that gates bash with Jev, feeds an untrusted PR diff into state, and acts on the decision. Same file, three tools. bandit and ruff are not wrong — there's no eval, no SQL string, no subprocess. The danger is trusting an un-abstaining classifier on a control path, which pattern-based SAST can't see.

bandit

No issues identified.

ruff (S rules)

All checks passed!

jev-guard

8 findings · 4 HIGH
$ jev-guard scan review_agent.py
L8   HIGH JG001  guardrail gates bash with no confidence threshold
L8   HIGH JG009  Jev is the terminal judge of a dangerous action, no escalation
L18  HIGH JG008  forced-choice question has no abstain option
L15  MED  JG004  untrusted PR diff flows into guardrail state
L26  MED  JG003  decision consumed without checking confidence
     INFO JG005  Jev used as a security control
And it doesn't cry wolf. Run it on 94 real Python files that don't touch Jev — my copy of the anthropic-cookbook — and it prints no findings. Zero false positives. It's scoped to Jev guardrail patterns and silent everywhere else, so it drops into CI without adding noise.

What it catches

Nine rules, each with a fix. Static AST analysis that follows variable assignments, so indirection is caught too.

JG001 HIGH

Guardrail gates actions with no configured confidence threshold.

JG002 CRIT

Dangerous tool wired to an agent with no guardrail present.

JG003 MED

Decision consumed without ever checking calibrated confidence.

JG004 MED

Untrusted content flows into the guardrail's state.

JG005 INFO

Jev on a security path — validate thresholds on your own data.

JG006 MED

Threshold set in an unsafe band — blocks only near-certain danger.

JG007 HIGH

Untrusted text interpolated into a question's instructions.

JG008 HIGH

Forced-choice question with no abstain option — the model guesses.

JG009 HIGH

Jev as terminal judge of a dangerous action, no escalation path.

Router, not judge

Jev is a forced-choice model. Give it three wrong options and it picks one, sometimes at high confidence. It doesn't abstain.

So use it to route, never to judge. Its job is to decide whether to wake something smarter — a stronger model or a human — not to be the last word on an irreversible action. Give every forced choice an escape option. Escalate on low confidence. And remember the triage gate is itself a control surface an attacker will shape their input to slip. jev-guard encodes all three as rules.

Use it

Command line and CI gate:

pip install jevg
jev-guard scan src/                 # scan a tree
jev-guard scan src/ --format sarif  # GitHub code scanning
jev-guard scan src/ --fail-on HIGH  # exit 1 on HIGH+

GitHub Action and pre-commit:

# .github/workflows/ci.yml
- uses: ppradyoth/jev-guard@v0.4.1
  with:
    path: src/
    fail-on: HIGH