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.
- jevg-0.3.0/.github/ISSUE_TEMPLATE/false-positive.md +14 -0
- jevg-0.3.0/.github/ISSUE_TEMPLATE/new-rule.md +16 -0
- jevg-0.3.0/.github/workflows/ci.yml +20 -0
- jevg-0.3.0/.github/workflows/release.yml +19 -0
- jevg-0.3.0/.gitignore +11 -0
- jevg-0.3.0/.jev-guard.toml.example +11 -0
- jevg-0.3.0/.pre-commit-hooks.yaml +8 -0
- jevg-0.3.0/CHANGELOG.md +45 -0
- jevg-0.3.0/CONTRIBUTING.md +36 -0
- jevg-0.3.0/GUIDE.md +84 -0
- jevg-0.3.0/LICENSE +21 -0
- jevg-0.3.0/PKG-INFO +149 -0
- jevg-0.3.0/README.md +128 -0
- jevg-0.3.0/RELEASING.md +31 -0
- jevg-0.3.0/action.yml +31 -0
- jevg-0.3.0/docs/adding-a-rule.md +54 -0
- jevg-0.3.0/examples/github-workflow.yml +21 -0
- jevg-0.3.0/examples/safe_agent.py +17 -0
- jevg-0.3.0/examples/vulnerable_agent.py +20 -0
- jevg-0.3.0/pyproject.toml +45 -0
- jevg-0.3.0/src/jev_guard/__init__.py +6 -0
- jevg-0.3.0/src/jev_guard/__main__.py +4 -0
- jevg-0.3.0/src/jev_guard/cli.py +88 -0
- jevg-0.3.0/src/jev_guard/config.py +56 -0
- jevg-0.3.0/src/jev_guard/findings.py +31 -0
- jevg-0.3.0/src/jev_guard/formats.py +68 -0
- jevg-0.3.0/src/jev_guard/rules.py +97 -0
- jevg-0.3.0/src/jev_guard/scanner.py +265 -0
- jevg-0.3.0/tests/fixtures/safe_agent.py +17 -0
- jevg-0.3.0/tests/fixtures/unguarded_agent.py +6 -0
- jevg-0.3.0/tests/fixtures/unrelated.py +2 -0
- jevg-0.3.0/tests/fixtures/vulnerable_agent.py +20 -0
- jevg-0.3.0/tests/test_config.py +44 -0
- jevg-0.3.0/tests/test_formats.py +34 -0
- jevg-0.3.0/tests/test_scanner.py +59 -0
- jevg-0.3.0/tests/test_taint.py +79 -0
|
@@ -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
jevg-0.3.0/CHANGELOG.md
ADDED
|
@@ -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
|
+
[](https://github.com/ppradyoth/jev-guard/actions/workflows/ci.yml) [](LICENSE) 
|
|
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
|
+
[](https://github.com/ppradyoth/jev-guard/actions/workflows/ci.yml) [](LICENSE) 
|
|
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.
|
jevg-0.3.0/RELEASING.md
ADDED
|
@@ -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[@]}"
|