jevg 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: False positive
3
+ about: jev-guard flagged safe code
4
+ labels: false-positive
5
+ ---
6
+
7
+ **Rule:** JG0__
8
+ **Minimal code that triggers it:**
9
+
10
+ ```python
11
+ # paste the smallest snippet that reproduces
12
+ ```
13
+
14
+ **Why it's safe:**
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: New rule / guardrail failure
3
+ about: Propose a detection, ideally backed by a measured Jev failure
4
+ labels: rule
5
+ ---
6
+
7
+ **The failure mode:**
8
+
9
+ **Evidence (if measured):** state / config / decision / confidence
10
+
11
+ **Suggested severity:**
12
+
13
+ **Code pattern to detect:**
14
+
15
+ ```python
16
+ ```
@@ -0,0 +1,20 @@
1
+ name: ci
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ jobs:
7
+ test:
8
+ runs-on: ubuntu-latest
9
+ strategy:
10
+ matrix:
11
+ python-version: ["3.10", "3.12"]
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: astral-sh/setup-uv@v3
15
+ with:
16
+ python-version: ${{ matrix.python-version }}
17
+ - run: uv venv
18
+ - run: uv pip install -e ".[dev]"
19
+ - run: uv run ruff check .
20
+ - run: uv run pytest -q
@@ -0,0 +1,19 @@
1
+ name: release
2
+ on:
3
+ release:
4
+ types: [published]
5
+ workflow_dispatch:
6
+ jobs:
7
+ pypi:
8
+ runs-on: ubuntu-latest
9
+ environment: pypi
10
+ permissions:
11
+ id-token: write # trusted publishing; no API token needed
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: astral-sh/setup-uv@v3
15
+ with:
16
+ python-version: "3.12"
17
+ - run: uv build
18
+ - run: uvx twine check dist/*
19
+ - uses: pypa/gh-action-pypi-publish@release/v1
jevg-0.3.0/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ .DS_Store
11
+ .env
@@ -0,0 +1,11 @@
1
+ # Copy to .jev-guard.toml at your repo root.
2
+ [jev-guard]
3
+ disable = []
4
+
5
+ [jev-guard.severity]
6
+ # JG004 = "HIGH"
7
+
8
+ [jev-guard.extra]
9
+ dangerous_tools = []
10
+ import_roots = []
11
+ guardrail_calls = []
@@ -0,0 +1,8 @@
1
+ - id: jev-guard
2
+ name: jev-guard
3
+ description: Audit Jev / TypeSafe guardrail usage.
4
+ entry: jev-guard scan
5
+ language: python
6
+ types: [python]
7
+ pass_filenames: false
8
+ args: ["."]
@@ -0,0 +1,45 @@
1
+ # Changelog
2
+
3
+ All notable changes to jev-guard are documented here. Versioning is semver.
4
+
5
+ ## [0.3.0] - 2026-09-22
6
+
7
+ ### Added
8
+ - **Published to PyPI as `jevg`** (`pip install jevg`; the CLI command is
9
+ `jev-guard`, with `jevg` as an alias). The GitHub repo stays `jev-guard`.
10
+
11
+
12
+ ### Added
13
+ - **SARIF output** (`--format sarif`) for GitHub code-scanning / the Security tab.
14
+ - **`--output/-o`** to write a report to a file.
15
+ - **Composite GitHub Action** (`ppradyoth/jev-guard@v0.3.0`) and a **pre-commit
16
+ hook** (`.pre-commit-hooks.yaml`) for one-line CI/local adoption.
17
+ - `--version` flag.
18
+ - Contributor docs: `CONTRIBUTING.md`, `docs/adding-a-rule.md`, issue templates.
19
+ - Example consumer workflow that uploads SARIF to code scanning.
20
+
21
+ ### Note
22
+ - Generalization beyond Jev is via `.jev-guard.toml` (`extra.import_roots`,
23
+ `extra.guardrail_calls`, `extra.dangerous_tools`) — point jev-guard at any
24
+ System One-style guardrail without a code change.
25
+
26
+ ## [0.2.0] - 2026-09-22
27
+
28
+ ### Added
29
+ - Name-based **taint tracking** with fixed-point propagation: dangerous tools
30
+ and untrusted `state` are now caught through variable assignments and chains,
31
+ not just at the literal call site (JG002, JG004).
32
+ - **JG006** — guardrail threshold set in an unsafe band (blocks only on
33
+ near-certain danger, or almost never escalates).
34
+ - **JG007** — untrusted content interpolated into a question's `instructions`.
35
+ - **`.jev-guard.toml`** config: disable rules, override severities, and extend
36
+ the dangerous-tool / import-root / guardrail-call sets. Discovered by walking
37
+ up from the scan path.
38
+ - **Inline suppression**: `# jev-guard: ignore` (all) or `# jev-guard: ignore JG001,JG004`.
39
+ - Findings are de-duplicated by (rule, line).
40
+
41
+ ## [0.1.0] - 2026-09-21
42
+
43
+ ### Added
44
+ - Initial release: JG001–JG005, AST scanner, `scan`/`rules` CLI, text/JSON
45
+ output, CI gate via `--fail-on`, tests, docs, MIT license.
@@ -0,0 +1,36 @@
1
+ # Contributing to jev-guard
2
+
3
+ Thanks for helping map how Jev-based guardrails fail. This is a young tool for a
4
+ young model class; rules will change as the real failure modes get measured.
5
+
6
+ ## Setup
7
+
8
+ ```bash
9
+ git clone https://github.com/ppradyoth/jev-guard && cd jev-guard
10
+ uv venv && . .venv/bin/activate
11
+ uv pip install -e ".[dev]"
12
+ pytest -q && ruff check .
13
+ ```
14
+
15
+ ## Ground rules
16
+
17
+ - **Every rule ships with fixtures.** A vulnerable fixture that triggers it and a
18
+ safe fixture that must not. No fixture, no merge.
19
+ - **Prefer evidence over intuition.** If a rule is motivated by an observed Jev
20
+ failure, link it. If it's a pattern heuristic, say so in the rule's remediation
21
+ text — we are honest about what is measured vs. reasoned.
22
+ - **No false-positive land mines.** A rule that fires on idiomatic safe code is
23
+ worse than no rule; people stop trusting the whole tool.
24
+ - Keep the scanner offline. jev-guard never calls the model or the network.
25
+ - Match the existing style: `ruff` clean, no comments unless the *why* is
26
+ non-obvious.
27
+
28
+ ## Adding a rule
29
+
30
+ See [`docs/adding-a-rule.md`](docs/adding-a-rule.md) for the step-by-step.
31
+
32
+ ## Reporting a Jev guardrail failure you measured
33
+
34
+ Open an issue with the empirical evidence (state, config, decision, confidence).
35
+ Measured failures are how we grow the ruleset — they're the most valuable
36
+ contribution you can make.
jevg-0.3.0/GUIDE.md ADDED
@@ -0,0 +1,84 @@
1
+ # jev-guard: usage guide
2
+
3
+ A practical guide to what this tool is for, when it helps, when it doesn't, what to use instead, and how much to trust it.
4
+
5
+ ## The one idea
6
+
7
+ Jev's headline safety property is that it **cannot hallucinate and cannot emit a type error**. Both are true and both are about *structure*. Neither says the answer is *right*.
8
+
9
+ A guardrail's job is to be right about "is this action dangerous?" A type-safe wrong answer is still a wrong answer — and because it's well-formed, your code will act on it without hesitation. The failure is quieter than an LLM going off the rails, which makes it more dangerous in a system with latency guarantees or buried several layers deep.
10
+
11
+ `jev-guard` finds the places in your code where a well-formed-but-possibly-wrong decision is trusted more than it should be.
12
+
13
+ ## When to use it
14
+
15
+ - You use `langchain_typesafe`, `AutoModeMiddleware`, or `TypeSafeClassifier` anywhere a decision leads to an action — routing, tool gating, auto-approval, content moderation.
16
+ - A Jev decision sits on the path to something irreversible: shell, SQL, HTTP, money movement, sending mail, writing files.
17
+ - You're adding Jev to an agent and want a checklist before it ships.
18
+ - You want a cheap CI gate that fails the build when someone wires a dangerous tool behind a default-threshold guardrail.
19
+ - You're reviewing someone else's agent and want a fast map of where the trust boundaries are.
20
+
21
+ ## When NOT to use it
22
+
23
+ - **You want proof you're exploitable.** This tool points at code smells. It does not run the model or demonstrate a bypass. Use a live probe harness for that (see below).
24
+ - **You want to measure Jev's actual calibration.** That needs a labeled adversarial dataset and real API calls, not static analysis.
25
+ - **Your guardrail isn't in Python**, or is assembled dynamically / behind heavy abstraction. AST matching will miss it. A miss here is not a clean bill of health.
26
+ - **You're using Jev for non-safety classification** (analytics, feature extraction, map-reduce over data) where a wrong answer is a data-quality issue, not a security event. jev-guard's severities assume a security context; here they'll over-alarm.
27
+ - **As your only control.** A linter is a smoke detector, not a sprinkler. Passing jev-guard means "no obvious footguns," not "safe."
28
+
29
+ ## How to read the findings
30
+
31
+ - **JG002 (CRITICAL)** — a dangerous tool with no guardrail at all. Fix first; this is the unguarded case.
32
+ - **JG001 (HIGH)** — a guardrail exists but blocks on the model's default decision with no threshold. Set an explicit block threshold *and* an escalate-below-confidence path.
33
+ - **JG003 (MEDIUM)** — you're acting on the point estimate and throwing away the confidence, which is the entire reason to use Jev over a coin flip. Gate the branch on confidence.
34
+ - **JG004 (MEDIUM)** — untrusted text flows into `state`. If content in `state` can override the question `instructions`, the guardrail is injectable by the input it's inspecting. Keep untrusted state structurally separate and sanitized.
35
+ - **JG005 (INFO)** — informational marker that a security decision runs through Jev. Not a defect; a prompt to validate thresholds on *your* adversarial workload, because the published evals are self-graded on non-adversarial distributions.
36
+
37
+ ## Fixing the common case
38
+
39
+ The default `AutoModeMiddleware(tools=["bash"])` (JG001) becomes defensible when you make the decision boundary explicit and add an escalation path for low confidence:
40
+
41
+ ```python
42
+ guardrail = AutoModeMiddleware(
43
+ tools=["bash"],
44
+ block_threshold=0.2, # block generously; err toward blocking
45
+ escalate_below=0.85, # low confidence -> human, not silent allow
46
+ )
47
+ ```
48
+
49
+ And consume decisions with the confidence, not without it:
50
+
51
+ ```python
52
+ decision = resp.nouls["danger"]
53
+ if decision.confidence < 0.85:
54
+ return escalate_to_human()
55
+ return "block" if decision.noul > 0.2 else "allow"
56
+ ```
57
+
58
+ ## Alternatives and complements
59
+
60
+ `jev-guard` is a narrow static linter. Reach for these depending on what you actually need:
61
+
62
+ | Need | Use |
63
+ |------|-----|
64
+ | Prove a specific bypass exists | A live probe harness: send known-dangerous states through your real guardrail config and measure block rate. jev-guard tells you where; this tells you whether. |
65
+ | Measure calibration under attack | A labeled adversarial dataset + reliability diagram (clean vs. adversarial). This is research, not linting. |
66
+ | General agent/tool security review | OWASP Top 10 for Agentic Applications, MAESTRO threat modeling, or a manual review of the whole trust boundary — not just the Jev call. |
67
+ | Prompt-injection coverage for an LLM in the loop | A dedicated injection test suite. jev-guard only checks the *guardrail's* input surface (JG004), not the primary model's. |
68
+ | Broad Python security patterns | Semgrep / CodeQL. jev-guard is Jev-specific and deliberately shallow; general SAST catches the rest. |
69
+ | Runtime enforcement | jev-guard is build-time only. Pair it with a runtime allow/deny layer that doesn't depend on a probabilistic classifier for hard-stop actions. |
70
+
71
+ A defensible design uses Jev for *soft* routing and triage, and keeps a deterministic, non-probabilistic hard stop in front of anything irreversible. jev-guard is one check in that stack, not the stack.
72
+
73
+ ## Self-assessment: how much to trust this tool
74
+
75
+ Honest limitations, so you calibrate your own confidence in *it*:
76
+
77
+ - **It's heuristic, not sound.** It matches names and shapes in the AST. Rename a variable or wrap the call and it goes blind. Absence of findings is not evidence of safety.
78
+ - **It can raise false positives.** JG004 keys off variable names that *look* untrusted (`user_`, `message`, `payload`). A safe file that happens to use those names will alarm. Read every finding; don't auto-fail blindly on MEDIUM.
79
+ - **It can raise false negatives.** Dynamic construction, indirection, non-literal tool lists, config-driven wiring, and any non-Python guardrail are invisible to it.
80
+ - **The rules encode a point of view, not a standard.** There is no published secure-usage spec for Jev yet — it's a week old. These rules are one security engineer's reading of two blog posts and the documented API, and they will change as the real failure modes get mapped.
81
+ - **The severities assume a security context.** In a non-safety use of Jev, they over-state risk.
82
+ - **It does not touch the model.** Every claim about calibration and hallucination in this repo is reasoning about the vendor's own marketing, not independent measurement. Treat it as a hypothesis to test, not a result.
83
+
84
+ Use it as a fast first pass and a CI tripwire. Do not use it as the thing that lets you sign off.
jevg-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pradyoth P.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
jevg-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,149 @@
1
+ Metadata-Version: 2.5
2
+ Name: jevg
3
+ Version: 0.3.0
4
+ Summary: Static auditor for code that uses Jev / TypeSafe System One models as a security guardrail.
5
+ Project-URL: Homepage, https://github.com/ppradyoth/jev-guard
6
+ Project-URL: Issues, https://github.com/ppradyoth/jev-guard/issues
7
+ Author: Pradyoth P.
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: ai-security,guardrail,jev,llm,red-team,static-analysis,typesafe
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Security
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest>=8; extra == 'dev'
19
+ Requires-Dist: ruff>=0.6; extra == 'dev'
20
+ Description-Content-Type: text/markdown
21
+
22
+ # jev-guard
23
+
24
+ [![ci](https://github.com/ppradyoth/jev-guard/actions/workflows/ci.yml/badge.svg)](https://github.com/ppradyoth/jev-guard/actions/workflows/ci.yml) [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) ![python](https://img.shields.io/badge/python-3.10%2B-blue)
25
+
26
+ **A static code scanner (SAST-style linter).** It reads your Python source and flags insecure usage of [Jev / TypeSafe "System One" models](https://typesafe.ai) when they're used as a security guardrail.
27
+
28
+ > jev-guard is **not** a guardrail and it does **not** run at runtime, call the model, or touch the network. It is a build-time analysis tool — think `ruff`/`bandit`, scoped to Jev guardrail patterns. It tells you where your guardrail *code* is misconfigured; it does not do any guarding itself.
29
+
30
+ **Thesis: type-safe is not the same as correct.** Jev can't emit a type error and it can't hallucinate a field — but "no hallucination" is a guarantee about *shape*, not about *truth*. A guardrail that returns a confidently wrong `noul: 0.02` for a `rm -rf /` still lets the call through. If you gate `bash` on that number, the type safety bought you nothing.
31
+
32
+ `jev-guard` scans your code for the ways a Jev-based guardrail fails open. It runs entirely offline — it reads your source, not the model — so it needs no API key and costs nothing.
33
+
34
+ ## Why this exists
35
+
36
+ Two things shipped in September 2026:
37
+
38
+ - TypeSafe released **Jev**, marketed for "score, judge, verify, guardrail, and detect jailbreaks."
39
+ - LangChain shipped **`AutoModeMiddleware`**, which uses Jev to classify tool calls as dangerous and block them before they run — the classifier pattern that used to live inside the closed-source parts of Claude Code / Codex / Cursor, now open to every agent.
40
+
41
+ The documented example is literally `AutoModeMiddleware(tools=["bash"])` — a shell gate with **no threshold set**, relying on the model's default point decision. That is the exact shape `jev-guard` was built to catch.
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ pip install jevg # PyPI package name (the CLI command is still `jev-guard`)
47
+ # or, from source:
48
+ git clone https://github.com/ppradyoth/jev-guard && cd jev-guard
49
+ pip install -e .
50
+ ```
51
+
52
+ ## Use
53
+
54
+ ```bash
55
+ jev-guard scan path/to/agent.py # scan a file
56
+ jev-guard scan src/ # scan a tree
57
+ jev-guard scan src/ --format json # machine-readable
58
+ jev-guard scan src/ --fail-on HIGH # CI gate (default): exit 1 on HIGH+
59
+ jev-guard rules # list what it checks
60
+ ```
61
+
62
+ Exit code is non-zero when any finding is at or above `--fail-on`, so it drops straight into CI:
63
+
64
+ ```yaml
65
+ - run: jev-guard scan src/ --fail-on HIGH
66
+ ```
67
+
68
+ ## In CI (GitHub Action)
69
+
70
+ ```yaml
71
+ - uses: ppradyoth/jev-guard@v0.3.0
72
+ with:
73
+ path: src/
74
+ fail-on: HIGH
75
+ ```
76
+
77
+ Surface findings in the **Security** tab by emitting SARIF and uploading it —
78
+ see [`examples/github-workflow.yml`](examples/github-workflow.yml).
79
+
80
+ ## Pre-commit
81
+
82
+ ```yaml
83
+ repos:
84
+ - repo: https://github.com/ppradyoth/jev-guard
85
+ rev: v0.3.0
86
+ hooks:
87
+ - id: jev-guard
88
+ ```
89
+
90
+ ## What it flags
91
+
92
+ | Code | Severity | What |
93
+ |-------|----------|------|
94
+ | JG001 | HIGH | Jev guardrail gates actions with no configured confidence threshold (blocks on the model default). |
95
+ | JG002 | CRITICAL | A dangerous tool (`bash`/`sql`/`http`/...) is wired into an agent with no `AutoModeMiddleware` present. |
96
+ | JG003 | MEDIUM | A `.noul`/`.choice`/`.score` decision is used to branch without ever reading a confidence field. |
97
+ | JG004 | MEDIUM | Attacker-influenceable content flows straight into a guardrail's `state` (injection surface). |
98
+ | JG005 | INFO | Jev is being used as a security control — reminder to validate thresholds against your own adversarial data. |
99
+ | JG006 | MEDIUM | Threshold set in an unsafe band (blocks only near-certain danger / almost never escalates). |
100
+ | JG007 | HIGH | Untrusted content interpolated into a question's `instructions` — the inspected text can rewrite the question. |
101
+
102
+ Full rationale and remediation for each: [`GUIDE.md`](GUIDE.md).
103
+
104
+ ## Example
105
+
106
+ ```
107
+ $ jev-guard scan examples/vulnerable_agent.py
108
+ examples/vulnerable_agent.py:5 [HIGH] JG001 Jev guardrail gates actions with no configured confidence threshold
109
+ ↳ Set an explicit threshold and escalate below it.
110
+ examples/vulnerable_agent.py:11 [MEDIUM] JG004 Untrusted content flows into a Jev guardrail's state
111
+ ↳ Separate and sanitize untrusted state.
112
+ ...
113
+ ```
114
+
115
+ ## Configuration
116
+
117
+ Drop a `.jev-guard.toml` at your repo root (jev-guard walks up to find it):
118
+
119
+ ```toml
120
+ [jev-guard]
121
+ disable = ["JG005"] # rules to silence entirely
122
+
123
+ [jev-guard.severity]
124
+ JG004 = "HIGH" # bump a rule's severity
125
+
126
+ [jev-guard.extra]
127
+ dangerous_tools = ["wire_transfer", "post_tweet"] # your own high-impact tools
128
+ import_roots = ["my_typesafe_wrapper"] # if you wrap the SDK
129
+ guardrail_calls = ["MyGuardrail"] # your own guardrail factory
130
+ ```
131
+
132
+ Silence a single line inline:
133
+
134
+ ```python
135
+ guardrail = AutoModeMiddleware(tools=["bash"]) # jev-guard: ignore JG001
136
+ another = AutoModeMiddleware(tools=["bash"]) # jev-guard: ignore
137
+ ```
138
+
139
+ jev-guard follows assignments, so indirection is caught — `tools = ["bash"]; create_agent(tools=tools)` and `s = user_msg; classifier.invoke({"state": s})` both flag.
140
+
141
+ ## Scope and honesty
142
+
143
+ This is a **heuristic linter, not a prover.** It reads Python, matches the LangChain `langchain_typesafe` surface, and reasons about obvious patterns. It will miss guardrails assembled dynamically, wrapped in your own abstractions, or written in another language, and it can raise false positives on safe code that names a variable `user_config`. It does not test the model, measure real calibration, or prove exploitability. It tells you *where to look*, not *that you're owned*.
144
+
145
+ For what it can and can't do, and when to reach for something else, read [`GUIDE.md`](GUIDE.md).
146
+
147
+ ## License
148
+
149
+ MIT. Not affiliated with TypeSafe AI or LangChain.
jevg-0.3.0/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # jev-guard
2
+
3
+ [![ci](https://github.com/ppradyoth/jev-guard/actions/workflows/ci.yml/badge.svg)](https://github.com/ppradyoth/jev-guard/actions/workflows/ci.yml) [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) ![python](https://img.shields.io/badge/python-3.10%2B-blue)
4
+
5
+ **A static code scanner (SAST-style linter).** It reads your Python source and flags insecure usage of [Jev / TypeSafe "System One" models](https://typesafe.ai) when they're used as a security guardrail.
6
+
7
+ > jev-guard is **not** a guardrail and it does **not** run at runtime, call the model, or touch the network. It is a build-time analysis tool — think `ruff`/`bandit`, scoped to Jev guardrail patterns. It tells you where your guardrail *code* is misconfigured; it does not do any guarding itself.
8
+
9
+ **Thesis: type-safe is not the same as correct.** Jev can't emit a type error and it can't hallucinate a field — but "no hallucination" is a guarantee about *shape*, not about *truth*. A guardrail that returns a confidently wrong `noul: 0.02` for a `rm -rf /` still lets the call through. If you gate `bash` on that number, the type safety bought you nothing.
10
+
11
+ `jev-guard` scans your code for the ways a Jev-based guardrail fails open. It runs entirely offline — it reads your source, not the model — so it needs no API key and costs nothing.
12
+
13
+ ## Why this exists
14
+
15
+ Two things shipped in September 2026:
16
+
17
+ - TypeSafe released **Jev**, marketed for "score, judge, verify, guardrail, and detect jailbreaks."
18
+ - LangChain shipped **`AutoModeMiddleware`**, which uses Jev to classify tool calls as dangerous and block them before they run — the classifier pattern that used to live inside the closed-source parts of Claude Code / Codex / Cursor, now open to every agent.
19
+
20
+ The documented example is literally `AutoModeMiddleware(tools=["bash"])` — a shell gate with **no threshold set**, relying on the model's default point decision. That is the exact shape `jev-guard` was built to catch.
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ pip install jevg # PyPI package name (the CLI command is still `jev-guard`)
26
+ # or, from source:
27
+ git clone https://github.com/ppradyoth/jev-guard && cd jev-guard
28
+ pip install -e .
29
+ ```
30
+
31
+ ## Use
32
+
33
+ ```bash
34
+ jev-guard scan path/to/agent.py # scan a file
35
+ jev-guard scan src/ # scan a tree
36
+ jev-guard scan src/ --format json # machine-readable
37
+ jev-guard scan src/ --fail-on HIGH # CI gate (default): exit 1 on HIGH+
38
+ jev-guard rules # list what it checks
39
+ ```
40
+
41
+ Exit code is non-zero when any finding is at or above `--fail-on`, so it drops straight into CI:
42
+
43
+ ```yaml
44
+ - run: jev-guard scan src/ --fail-on HIGH
45
+ ```
46
+
47
+ ## In CI (GitHub Action)
48
+
49
+ ```yaml
50
+ - uses: ppradyoth/jev-guard@v0.3.0
51
+ with:
52
+ path: src/
53
+ fail-on: HIGH
54
+ ```
55
+
56
+ Surface findings in the **Security** tab by emitting SARIF and uploading it —
57
+ see [`examples/github-workflow.yml`](examples/github-workflow.yml).
58
+
59
+ ## Pre-commit
60
+
61
+ ```yaml
62
+ repos:
63
+ - repo: https://github.com/ppradyoth/jev-guard
64
+ rev: v0.3.0
65
+ hooks:
66
+ - id: jev-guard
67
+ ```
68
+
69
+ ## What it flags
70
+
71
+ | Code | Severity | What |
72
+ |-------|----------|------|
73
+ | JG001 | HIGH | Jev guardrail gates actions with no configured confidence threshold (blocks on the model default). |
74
+ | JG002 | CRITICAL | A dangerous tool (`bash`/`sql`/`http`/...) is wired into an agent with no `AutoModeMiddleware` present. |
75
+ | JG003 | MEDIUM | A `.noul`/`.choice`/`.score` decision is used to branch without ever reading a confidence field. |
76
+ | JG004 | MEDIUM | Attacker-influenceable content flows straight into a guardrail's `state` (injection surface). |
77
+ | JG005 | INFO | Jev is being used as a security control — reminder to validate thresholds against your own adversarial data. |
78
+ | JG006 | MEDIUM | Threshold set in an unsafe band (blocks only near-certain danger / almost never escalates). |
79
+ | JG007 | HIGH | Untrusted content interpolated into a question's `instructions` — the inspected text can rewrite the question. |
80
+
81
+ Full rationale and remediation for each: [`GUIDE.md`](GUIDE.md).
82
+
83
+ ## Example
84
+
85
+ ```
86
+ $ jev-guard scan examples/vulnerable_agent.py
87
+ examples/vulnerable_agent.py:5 [HIGH] JG001 Jev guardrail gates actions with no configured confidence threshold
88
+ ↳ Set an explicit threshold and escalate below it.
89
+ examples/vulnerable_agent.py:11 [MEDIUM] JG004 Untrusted content flows into a Jev guardrail's state
90
+ ↳ Separate and sanitize untrusted state.
91
+ ...
92
+ ```
93
+
94
+ ## Configuration
95
+
96
+ Drop a `.jev-guard.toml` at your repo root (jev-guard walks up to find it):
97
+
98
+ ```toml
99
+ [jev-guard]
100
+ disable = ["JG005"] # rules to silence entirely
101
+
102
+ [jev-guard.severity]
103
+ JG004 = "HIGH" # bump a rule's severity
104
+
105
+ [jev-guard.extra]
106
+ dangerous_tools = ["wire_transfer", "post_tweet"] # your own high-impact tools
107
+ import_roots = ["my_typesafe_wrapper"] # if you wrap the SDK
108
+ guardrail_calls = ["MyGuardrail"] # your own guardrail factory
109
+ ```
110
+
111
+ Silence a single line inline:
112
+
113
+ ```python
114
+ guardrail = AutoModeMiddleware(tools=["bash"]) # jev-guard: ignore JG001
115
+ another = AutoModeMiddleware(tools=["bash"]) # jev-guard: ignore
116
+ ```
117
+
118
+ jev-guard follows assignments, so indirection is caught — `tools = ["bash"]; create_agent(tools=tools)` and `s = user_msg; classifier.invoke({"state": s})` both flag.
119
+
120
+ ## Scope and honesty
121
+
122
+ This is a **heuristic linter, not a prover.** It reads Python, matches the LangChain `langchain_typesafe` surface, and reasons about obvious patterns. It will miss guardrails assembled dynamically, wrapped in your own abstractions, or written in another language, and it can raise false positives on safe code that names a variable `user_config`. It does not test the model, measure real calibration, or prove exploitability. It tells you *where to look*, not *that you're owned*.
123
+
124
+ For what it can and can't do, and when to reach for something else, read [`GUIDE.md`](GUIDE.md).
125
+
126
+ ## License
127
+
128
+ MIT. Not affiliated with TypeSafe AI or LangChain.
@@ -0,0 +1,31 @@
1
+ # Releasing jev-guard
2
+
3
+ Publishing is automated via **PyPI Trusted Publishing** — no API token is ever
4
+ stored in the repo or entered by hand. GitHub authenticates to PyPI over OIDC.
5
+
6
+ ## One-time setup (maintainer, ~2 min)
7
+
8
+ 1. Create the project on PyPI (or reserve the name) at https://pypi.org.
9
+ 2. On PyPI → the `jev-guard` project → **Publishing** → add a trusted publisher:
10
+ - Owner: `ppradyoth`
11
+ - Repository: `jev-guard`
12
+ - Workflow: `release.yml`
13
+ - Environment: `pypi`
14
+ 3. In the GitHub repo → Settings → Environments → create an environment named
15
+ `pypi` (optionally add a required reviewer for a manual gate).
16
+
17
+ ## Cutting a release
18
+
19
+ 1. Bump the version in `pyproject.toml` and `src/jev_guard/__init__.py`, and add
20
+ a `CHANGELOG.md` entry. Commit.
21
+ 2. Tag and push: `git tag -a vX.Y.Z -m vX.Y.Z && git push origin main --tags`.
22
+ 3. Create the GitHub Release for that tag (`gh release create vX.Y.Z --latest ...`).
23
+ Publishing the release triggers `release.yml`, which builds, `twine check`s,
24
+ and uploads to PyPI.
25
+ 4. Verify: `pipx run jev-guard==X.Y.Z --version`.
26
+
27
+ ## Local build (for inspection only)
28
+
29
+ ```bash
30
+ uv build && uvx twine check dist/*
31
+ ```
jevg-0.3.0/action.yml ADDED
@@ -0,0 +1,31 @@
1
+ name: jev-guard
2
+ description: Audit code that uses Jev / TypeSafe System One models as a security guardrail.
3
+ branding:
4
+ icon: shield
5
+ color: red
6
+ inputs:
7
+ path:
8
+ description: File or directory to scan.
9
+ required: false
10
+ default: "."
11
+ fail-on:
12
+ description: Exit non-zero when a finding is at or above this severity.
13
+ required: false
14
+ default: "HIGH"
15
+ format:
16
+ description: "Output format: text, json, or sarif."
17
+ required: false
18
+ default: "text"
19
+ output:
20
+ description: Optional file to write the report to (e.g. results.sarif).
21
+ required: false
22
+ default: ""
23
+ runs:
24
+ using: composite
25
+ steps:
26
+ - shell: bash
27
+ run: |
28
+ python -m pip install --quiet "jevg==0.3.0"
29
+ args=(scan "${{ inputs.path }}" --fail-on "${{ inputs.fail-on }}" --format "${{ inputs.format }}")
30
+ if [ -n "${{ inputs.output }}" ]; then args+=(--output "${{ inputs.output }}"); fi
31
+ jev-guard "${args[@]}"