aigis-lint 0.2.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.
- aigis_lint-0.2.0/.aigis.yaml +8 -0
- aigis_lint-0.2.0/.github/workflows/aigis.yml +30 -0
- aigis_lint-0.2.0/.gitignore +23 -0
- aigis_lint-0.2.0/26.0.1 +0 -0
- aigis_lint-0.2.0/LICENSE +21 -0
- aigis_lint-0.2.0/PKG-INFO +350 -0
- aigis_lint-0.2.0/README.md +329 -0
- aigis_lint-0.2.0/docs/RELEASE_NOTES_v0.2.0.md +88 -0
- aigis_lint-0.2.0/docs/aeg003_post_ag2_validation.md +41 -0
- aigis_lint-0.2.0/docs/favicon.png +0 -0
- aigis_lint-0.2.0/docs/framework_scorecard.md +75 -0
- aigis_lint-0.2.0/docs/issues_seed.md +87 -0
- aigis_lint-0.2.0/docs/launch_checklist.md +48 -0
- aigis_lint-0.2.0/docs/logo-200.png +0 -0
- aigis_lint-0.2.0/docs/logo-64.png +0 -0
- aigis_lint-0.2.0/docs/logo.png +0 -0
- aigis_lint-0.2.0/docs/media_plan.md +97 -0
- aigis_lint-0.2.0/docs/release_readiness.md +73 -0
- aigis_lint-0.2.0/docs/repo_positioning.md +44 -0
- aigis_lint-0.2.0/docs/roadmap.md +41 -0
- aigis_lint-0.2.0/docs/roadmap_issues.md +139 -0
- aigis_lint-0.2.0/examples/.aigis.yaml +23 -0
- aigis_lint-0.2.0/examples/safe_agent.py +32 -0
- aigis_lint-0.2.0/examples/unbounded_agent.py +27 -0
- aigis_lint-0.2.0/examples/unbounded_retry.py +17 -0
- aigis_lint-0.2.0/examples/unsafe_tool.py +20 -0
- aigis_lint-0.2.0/pyproject.toml +39 -0
- aigis_lint-0.2.0/python +0 -0
- aigis_lint-0.2.0/src/aigis/__init__.py +3 -0
- aigis_lint-0.2.0/src/aigis/analyzer.py +982 -0
- aigis_lint-0.2.0/src/aigis/baseline.py +67 -0
- aigis_lint-0.2.0/src/aigis/cli.py +164 -0
- aigis_lint-0.2.0/src/aigis/config.py +45 -0
- aigis_lint-0.2.0/src/aigis/frameworks/__init__.py +63 -0
- aigis_lint-0.2.0/src/aigis/frameworks/autogen.py +45 -0
- aigis_lint-0.2.0/src/aigis/frameworks/crewai.py +21 -0
- aigis_lint-0.2.0/src/aigis/frameworks/custom.py +17 -0
- aigis_lint-0.2.0/src/aigis/frameworks/langchain.py +7 -0
- aigis_lint-0.2.0/src/aigis/frameworks/langgraph.py +26 -0
- aigis_lint-0.2.0/src/aigis/frameworks/openai_agents.py +35 -0
- aigis_lint-0.2.0/src/aigis/graph.py +36 -0
- aigis_lint-0.2.0/src/aigis/models.py +110 -0
- aigis_lint-0.2.0/src/aigis/output.py +238 -0
- aigis_lint-0.2.0/src/aigis/output_html.py +495 -0
- aigis_lint-0.2.0/src/aigis/rules/__init__.py +25 -0
- aigis_lint-0.2.0/src/aigis/rules/aigis001_unguarded_mutating.py +68 -0
- aigis_lint-0.2.0/src/aigis/rules/aigis002_privileged_no_consent.py +81 -0
- aigis_lint-0.2.0/src/aigis/rules/aigis003_missing_budget.py +76 -0
- aigis_lint-0.2.0/src/aigis/rules/aigis004_unbounded_retry.py +149 -0
- aigis_lint-0.2.0/src/aigis/rules/aigis005_user_controlled_budget.py +124 -0
- aigis_lint-0.2.0/src/aigis/rules/aigis006_raw_history_retrieval.py +130 -0
- aigis_lint-0.2.0/src/aigis/suppression.py +87 -0
- aigis_lint-0.2.0/tests/__init__.py +0 -0
- aigis_lint-0.2.0/tests/conftest.py +11 -0
- aigis_lint-0.2.0/tests/fixtures/autogen_exec_budget.py +24 -0
- aigis_lint-0.2.0/tests/fixtures/autogen_groupchat_manager_propagation.py +17 -0
- aigis_lint-0.2.0/tests/fixtures/autogen_initiate_group_chat_safe.py +16 -0
- aigis_lint-0.2.0/tests/fixtures/autogen_initiate_group_chat_unsafe.py +14 -0
- aigis_lint-0.2.0/tests/fixtures/autogen_no_exec_budget.py +16 -0
- aigis_lint-0.2.0/tests/fixtures/autogen_safe.py +18 -0
- aigis_lint-0.2.0/tests/fixtures/autogen_unsafe.py +22 -0
- aigis_lint-0.2.0/tests/fixtures/crewai_safe.py +28 -0
- aigis_lint-0.2.0/tests/fixtures/crewai_unsafe.py +31 -0
- aigis_lint-0.2.0/tests/fixtures/custom_approval.py +54 -0
- aigis_lint-0.2.0/tests/fixtures/exec_budget_wrong_var.py +12 -0
- aigis_lint-0.2.0/tests/fixtures/false_positive_edge.py +30 -0
- aigis_lint-0.2.0/tests/fixtures/inline_suppression.py +23 -0
- aigis_lint-0.2.0/tests/fixtures/langgraph_config_var_budget.py +19 -0
- aigis_lint-0.2.0/tests/fixtures/langgraph_exec_budget.py +18 -0
- aigis_lint-0.2.0/tests/fixtures/langgraph_interrupt_false_positive.py +25 -0
- aigis_lint-0.2.0/tests/fixtures/langgraph_interrupt_safe.py +26 -0
- aigis_lint-0.2.0/tests/fixtures/langgraph_safe_patterns.py +34 -0
- aigis_lint-0.2.0/tests/fixtures/misleading_names.py +21 -0
- aigis_lint-0.2.0/tests/fixtures/nested_wrappers.py +47 -0
- aigis_lint-0.2.0/tests/fixtures/openai_agents_alias_budget.py +14 -0
- aigis_lint-0.2.0/tests/fixtures/openai_agents_exec_budget.py +22 -0
- aigis_lint-0.2.0/tests/fixtures/openai_agents_no_exec_budget.py +22 -0
- aigis_lint-0.2.0/tests/fixtures/openai_agents_safe.py +34 -0
- aigis_lint-0.2.0/tests/fixtures/openai_agents_unsafe.py +37 -0
- aigis_lint-0.2.0/tests/fixtures/raw_history_retrieval.py +21 -0
- aigis_lint-0.2.0/tests/fixtures/raw_history_retrieval_safe.py +10 -0
- aigis_lint-0.2.0/tests/fixtures/readonly_tool.py +31 -0
- aigis_lint-0.2.0/tests/fixtures/retry_bounded.py +22 -0
- aigis_lint-0.2.0/tests/fixtures/retry_unbounded.py +20 -0
- aigis_lint-0.2.0/tests/fixtures/safe_guarded.py +32 -0
- aigis_lint-0.2.0/tests/fixtures/safe_langgraph.py +14 -0
- aigis_lint-0.2.0/tests/fixtures/test_exclusion/src/agent.py +7 -0
- aigis_lint-0.2.0/tests/fixtures/test_exclusion/tests/test_agent.py +7 -0
- aigis_lint-0.2.0/tests/fixtures/unsafe_no_approval.py +16 -0
- aigis_lint-0.2.0/tests/fixtures/unsafe_no_budget.py +13 -0
- aigis_lint-0.2.0/tests/fixtures/unsafe_privileged.py +19 -0
- aigis_lint-0.2.0/tests/fixtures/user_controlled_budget.py +8 -0
- aigis_lint-0.2.0/tests/fixtures/user_controlled_budget_safe.py +8 -0
- aigis_lint-0.2.0/tests/test_analyzer.py +193 -0
- aigis_lint-0.2.0/tests/test_baseline.py +76 -0
- aigis_lint-0.2.0/tests/test_cli.py +119 -0
- aigis_lint-0.2.0/tests/test_output.py +153 -0
- aigis_lint-0.2.0/tests/test_rules.py +471 -0
- aigis_lint-0.2.0/tests/test_suppression.py +84 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Aigis project-level configuration
|
|
2
|
+
#
|
|
3
|
+
# The examples/ directory contains intentionally unsafe code
|
|
4
|
+
# used for demos and documentation. Suppress findings there.
|
|
5
|
+
|
|
6
|
+
suppressions:
|
|
7
|
+
- path: "**/examples/**"
|
|
8
|
+
reason: "Demo fixtures — intentionally unsafe for documentation"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Aigis Governance Scan
|
|
2
|
+
#
|
|
3
|
+
# Scans Python code for unsafe AI agent autonomy patterns on every push and PR.
|
|
4
|
+
|
|
5
|
+
name: Aigis Governance Scan
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
pull_request:
|
|
11
|
+
branches: [main]
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
aigis:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
|
|
23
|
+
- name: Install Aigis
|
|
24
|
+
run: pip install -e . && pip install pytest
|
|
25
|
+
|
|
26
|
+
- name: Run Aigis scan
|
|
27
|
+
run: aigis scan . -f console
|
|
28
|
+
|
|
29
|
+
- name: Run tests
|
|
30
|
+
run: python -m pytest tests/ -q
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*.egg-info/
|
|
4
|
+
dist/
|
|
5
|
+
build/
|
|
6
|
+
.eggs/
|
|
7
|
+
*.egg
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.mypy_cache/
|
|
10
|
+
*.so
|
|
11
|
+
.env
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
test-repos/
|
|
15
|
+
aigis-baseline.json
|
|
16
|
+
.claude/
|
|
17
|
+
.cursor/
|
|
18
|
+
.windsurfrules
|
|
19
|
+
northStar.txt
|
|
20
|
+
AGENTS.md
|
|
21
|
+
CLAUDE.md
|
|
22
|
+
project-state/
|
|
23
|
+
.github/copilot-instructions.md
|
aigis_lint-0.2.0/26.0.1
ADDED
|
File without changes
|
aigis_lint-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 aigis contributors
|
|
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.
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aigis-lint
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: AI Execution Governance Linter — static analysis for unsafe AI autonomy
|
|
5
|
+
Project-URL: Homepage, https://github.com/tyreamer/aigis
|
|
6
|
+
Project-URL: Repository, https://github.com/tyreamer/aigis
|
|
7
|
+
Project-URL: Issues, https://github.com/tyreamer/aigis/issues
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: agents,ai,governance,linter,security,static-analysis
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Security
|
|
15
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: pyyaml>=6.0
|
|
18
|
+
Requires-Dist: rich>=13.0
|
|
19
|
+
Requires-Dist: typer>=0.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# aigis
|
|
23
|
+
|
|
24
|
+
**Governance linting for AI agents.** Verify that your agents can't delete, execute, or exfiltrate without approval — before they ever run.
|
|
25
|
+
|
|
26
|
+
Aigis statically analyzes Python AI agent code and reports missing governance controls: approval gates on dangerous tools, consent wrappers on privileged operations, and execution budgets on agent loops. It works across LangChain, LangGraph, OpenAI Agents SDK, CrewAI, and AutoGen — with zero runtime dependencies.
|
|
27
|
+
|
|
28
|
+
> **Public Alpha (v0.2.0)** — core rules are stable and validated against real-world repos. API surface and framework coverage may evolve. [Feedback welcome.](https://github.com/tyreamer/aigis/issues)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Why This Exists
|
|
33
|
+
|
|
34
|
+
AI agents call tools. Tools can delete files, run shell commands, and send HTTP requests. Most agent frameworks make it easy to expose these capabilities — and easy to forget the controls.
|
|
35
|
+
|
|
36
|
+
Traditional SAST finds software vulnerabilities (SQL injection, XSS). Runtime AI safety tools catch bad behavior after deployment. Neither answers the question that matters before you ship:
|
|
37
|
+
|
|
38
|
+
**Can this agent take high-impact actions without human approval, and can it run forever?**
|
|
39
|
+
|
|
40
|
+
Aigis answers that question at build time, from code alone, with no LLM required.
|
|
41
|
+
|
|
42
|
+
## What One Scan Gives You
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
$ aigis scan examples/unsafe_tool.py
|
|
46
|
+
```
|
|
47
|
+
```
|
|
48
|
+
aigis v0.2.0 - AI Execution Governance Linter
|
|
49
|
+
Scanning: examples/unsafe_tool.py
|
|
50
|
+
|
|
51
|
+
AIGIS001 ERROR examples/unsafe_tool.py:13:0
|
|
52
|
+
Tool 'run_cmd' reaches side-effecting sink(s) [subprocess.run] without an approval gate
|
|
53
|
+
Evidence: sink=subprocess execution | approval=no | confidence=high
|
|
54
|
+
Fix: Add an approval decorator or wrap side-effecting calls with a confirmation check.
|
|
55
|
+
|
|
56
|
+
AIGIS002 ERROR examples/unsafe_tool.py:13:0
|
|
57
|
+
Tool 'run_cmd' performs privileged operation(s) [subprocess.run] without a consent/policy wrapper
|
|
58
|
+
Evidence: sink=subprocess execution | approval=no | confidence=high
|
|
59
|
+
Fix: Add a consent/policy decorator (e.g. @requires_consent, @policy_check).
|
|
60
|
+
|
|
61
|
+
Found 2 finding(s) (2 error, 0 warning)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
From one scan, you get:
|
|
65
|
+
- Every tool that can mutate, execute, or send data — and whether it has an approval gate
|
|
66
|
+
- Every privileged operation (subprocess, system commands) — and whether it has a consent wrapper
|
|
67
|
+
- Every agent entry point — and whether it has an iteration/budget limit
|
|
68
|
+
- Structured evidence explaining *what* was found, *why* it matters, and *how* to fix it
|
|
69
|
+
- Output in console, JSON, SARIF, or a visual HTML report
|
|
70
|
+
|
|
71
|
+
## The Code That Defines the Category
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
@tool
|
|
75
|
+
def run_cmd(cmd: str, timeout: int = 30) -> str:
|
|
76
|
+
"""Execute a shell command."""
|
|
77
|
+
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
|
78
|
+
return result.stdout
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
An AI agent tool that runs arbitrary shell commands with `shell=True` and agent-controlled input. No human approval. No consent policy. No iteration limit on the agent calling it.
|
|
82
|
+
|
|
83
|
+
Aigis fires both **AIGIS001** (no approval gate) and **AIGIS002** (no consent wrapper). This pattern was found in a real course repo with hundreds of forks.
|
|
84
|
+
|
|
85
|
+
## Who This Is For
|
|
86
|
+
|
|
87
|
+
- **AI platform teams** building tool-using agents for production
|
|
88
|
+
- **AppSec engineers** adding agent code to their security review process
|
|
89
|
+
- **Architecture leads** establishing governance standards for agentic systems
|
|
90
|
+
- **Platform engineering** teams shipping LangGraph/CrewAI/OpenAI Agents infrastructure
|
|
91
|
+
|
|
92
|
+
Aigis is built for teams where agents interact with real systems — not toy chatbots.
|
|
93
|
+
|
|
94
|
+
## Install
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pip install git+https://github.com/tyreamer/aigis.git
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Or clone and install locally:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
git clone https://github.com/tyreamer/aigis.git
|
|
104
|
+
cd aigis
|
|
105
|
+
pip install -e .
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 5-Minute Quick Start
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Scan your agent code
|
|
112
|
+
aigis scan /path/to/your/project
|
|
113
|
+
|
|
114
|
+
# Scan the included examples
|
|
115
|
+
aigis scan examples/unsafe_tool.py # fires AIGIS001 + AIGIS002
|
|
116
|
+
aigis scan examples/unbounded_agent.py # fires AIGIS001 + AIGIS003
|
|
117
|
+
aigis scan examples/safe_agent.py # clean — no findings
|
|
118
|
+
|
|
119
|
+
# Generate a visual HTML report
|
|
120
|
+
aigis scan /path/to/your/project -f html -o report.html
|
|
121
|
+
|
|
122
|
+
# CI-ready outputs
|
|
123
|
+
aigis scan . -f json # structured JSON
|
|
124
|
+
aigis scan . -f sarif -o results.sarif # GitHub Code Scanning
|
|
125
|
+
|
|
126
|
+
# Baseline workflow: accept current findings, fail only on new ones
|
|
127
|
+
aigis baseline . -o .aigis-baseline.json
|
|
128
|
+
aigis scan . --baseline .aigis-baseline.json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Rules
|
|
132
|
+
|
|
133
|
+
### AIGIS001 — Mutating Tool Without Approval Gate
|
|
134
|
+
|
|
135
|
+
Fires when a tool performs side effects (file I/O, subprocess, HTTP mutations) with no approval mechanism.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
# Flagged:
|
|
139
|
+
@tool
|
|
140
|
+
def delete_user(user_id: str):
|
|
141
|
+
os.remove(f"/data/{user_id}.json")
|
|
142
|
+
|
|
143
|
+
# Clean:
|
|
144
|
+
@tool
|
|
145
|
+
@requires_approval
|
|
146
|
+
def delete_user(user_id: str):
|
|
147
|
+
os.remove(f"/data/{user_id}.json")
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Detected sinks:** `os.remove`, `shutil.rmtree`, `subprocess.run`, `os.system`, `requests.post`, `httpx.put`, `open()` with write mode, and more.
|
|
151
|
+
|
|
152
|
+
### AIGIS002 — Privileged Operation Without Consent Wrapper
|
|
153
|
+
|
|
154
|
+
Fires when a tool calls subprocess or system commands without an explicit consent or policy wrapper. Generic `@requires_approval` is not sufficient — requires `@requires_consent`, `@policy_check`, or similar.
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
# Flagged (generic approval is not consent-level):
|
|
158
|
+
@tool
|
|
159
|
+
@requires_approval
|
|
160
|
+
def run_command(cmd: str):
|
|
161
|
+
subprocess.run(cmd, shell=True)
|
|
162
|
+
|
|
163
|
+
# Clean:
|
|
164
|
+
@tool
|
|
165
|
+
@requires_consent
|
|
166
|
+
def run_command(cmd: str):
|
|
167
|
+
subprocess.run(cmd, shell=True)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### AIGIS003 — Missing Execution Budget
|
|
171
|
+
|
|
172
|
+
Fires when an agent entry point has no iteration or budget limit — neither on the constructor nor on any execution call in the same file.
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
# Flagged:
|
|
176
|
+
agent = Agent(name="x", tools=[my_tool])
|
|
177
|
+
Runner.run(agent, input="go") # no max_turns anywhere
|
|
178
|
+
|
|
179
|
+
# Clean (constructor budget):
|
|
180
|
+
agent = AgentExecutor(agent=llm, tools=[t], max_iterations=10)
|
|
181
|
+
|
|
182
|
+
# Clean (execution-time budget):
|
|
183
|
+
agent = Agent(name="x", tools=[my_tool])
|
|
184
|
+
Runner.run(agent, input="go", max_turns=10)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Aigis checks budget controls at both construction time and execution time, including `Runner.run(max_turns=N)`, `app.invoke(config={"recursion_limit": N})`, `initiate_group_chat(max_rounds=N)`, and config variable resolution.
|
|
188
|
+
|
|
189
|
+
### AIGIS004 — Unbounded Retry / Loop
|
|
190
|
+
|
|
191
|
+
Fires when a tool contains a retry decorator without max attempts, or a `while True` loop without a break condition.
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
# Flagged:
|
|
195
|
+
@tool
|
|
196
|
+
@retry # no stop=, no max_retries=
|
|
197
|
+
def fetch_data(url: str) -> str:
|
|
198
|
+
return requests.post(url).text
|
|
199
|
+
|
|
200
|
+
# Clean:
|
|
201
|
+
@tool
|
|
202
|
+
@retry(stop=stop_after_attempt(3))
|
|
203
|
+
def fetch_data(url: str) -> str:
|
|
204
|
+
return requests.post(url).text
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### AIGIS005 — User-Controlled Budget Without Cap
|
|
208
|
+
|
|
209
|
+
Fires when an execution budget parameter (`max_turns`, `recursion_limit`, etc.) receives its value from a variable rather than a constant, with no visible server-side cap.
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
# Flagged:
|
|
213
|
+
def run_agent(user_max_turns: int):
|
|
214
|
+
Runner.run(agent, input="go", max_turns=user_max_turns) # no cap
|
|
215
|
+
|
|
216
|
+
# Clean:
|
|
217
|
+
def run_agent(user_max_turns: int):
|
|
218
|
+
Runner.run(agent, input="go", max_turns=min(user_max_turns, 50))
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### AIGIS006 — Raw Chat History as Retrieval Query
|
|
222
|
+
|
|
223
|
+
Fires when a raw chat history variable (`messages`, `chat_history`, `conversation`) is passed directly to a retrieval function without a query rewriting step.
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
# Flagged:
|
|
227
|
+
results = store.similarity_search(chat_history) # raw transcript as query
|
|
228
|
+
|
|
229
|
+
# Clean:
|
|
230
|
+
query = condense_question(chat_history)
|
|
231
|
+
results = store.similarity_search(query)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Supported Frameworks
|
|
235
|
+
|
|
236
|
+
| Framework | Tool Detection | Entry Points | Budget Controls |
|
|
237
|
+
|-----------|---------------|-------------|-----------------|
|
|
238
|
+
| **LangChain** | `@tool` | `AgentExecutor` | `max_iterations`, `timeout` |
|
|
239
|
+
| **LangGraph** | `add_node` | `compile()` | `recursion_limit` |
|
|
240
|
+
| **OpenAI Agents** | `@function_tool` | `Agent()` | `max_turns` (constructor or `Runner.run`) |
|
|
241
|
+
| **CrewAI** | `@tool` | `Crew()` | `max_iter`, `max_rpm` |
|
|
242
|
+
| **AutoGen / AG2** | `register_for_llm` | `AssistantAgent`, `GroupChat` | `max_turns`, `max_round` |
|
|
243
|
+
|
|
244
|
+
## Output Formats
|
|
245
|
+
|
|
246
|
+
| Format | Use Case | Command |
|
|
247
|
+
|--------|----------|---------|
|
|
248
|
+
| **Console** | Local development | `aigis scan .` |
|
|
249
|
+
| **JSON** | CI pipelines, scripting | `aigis scan . -f json` |
|
|
250
|
+
| **SARIF** | GitHub Code Scanning | `aigis scan . -f sarif -o results.sarif` |
|
|
251
|
+
| **HTML** | Reports, reviews, demos | `aigis scan . -f html -o report.html` |
|
|
252
|
+
|
|
253
|
+
The HTML report is a self-contained dark-mode file with filters, expandable evidence cards, remediation guidance, and framework-specific context. No backend required — open it in any browser.
|
|
254
|
+
|
|
255
|
+
## Suppression
|
|
256
|
+
|
|
257
|
+
### Inline
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
@tool # aigis: disable=AIGIS001 -- reviewed and accepted risk
|
|
261
|
+
def my_tool():
|
|
262
|
+
os.remove(path)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Config File
|
|
266
|
+
|
|
267
|
+
Create `.aigis.yaml` in your project root:
|
|
268
|
+
|
|
269
|
+
```yaml
|
|
270
|
+
suppressions:
|
|
271
|
+
- rule: AIGIS001
|
|
272
|
+
path: "scripts/**"
|
|
273
|
+
reason: "Internal tooling with runtime approval"
|
|
274
|
+
|
|
275
|
+
- rule: AIGIS003
|
|
276
|
+
symbol: my_agent
|
|
277
|
+
reason: "Budget enforced by external orchestrator"
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Baseline
|
|
281
|
+
|
|
282
|
+
Accept current findings, fail only on new ones:
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
aigis baseline . -o .aigis-baseline.json
|
|
286
|
+
aigis scan . --baseline .aigis-baseline.json
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Fingerprints use rule ID + file path + tool name (not line numbers), so they survive code edits.
|
|
290
|
+
|
|
291
|
+
## How Aigis Is Different
|
|
292
|
+
|
|
293
|
+
| | Traditional SAST | Runtime AI Safety | **Aigis** |
|
|
294
|
+
|---|---|---|---|
|
|
295
|
+
| **When** | Build time | Runtime | **Build time** |
|
|
296
|
+
| **What** | Software vulns (SQLi, XSS) | Model behavior, guardrails | **Agent governance controls** |
|
|
297
|
+
| **Checks for** | Code flaws | Harmful outputs | **Missing approval, consent, bounds** |
|
|
298
|
+
| **Requires runtime** | No | Yes | **No** |
|
|
299
|
+
| **AI/LLM needed** | No | Often | **No** |
|
|
300
|
+
|
|
301
|
+
Aigis is not a prompt scanner, a model guardrail, or a vulnerability finder. It checks whether the structural controls that should exist in agent code — approval gates, consent wrappers, execution budgets — are actually present.
|
|
302
|
+
|
|
303
|
+
## What It Does NOT Detect
|
|
304
|
+
|
|
305
|
+
- **Cross-file call graphs** — sinks must be in the same function body as the tool
|
|
306
|
+
- **Data-flow analysis** — cannot track tainted inputs through variables
|
|
307
|
+
- **Runtime behavior** — all analysis is static and deterministic
|
|
308
|
+
- **SQL mutations** — `cursor.execute()` is too ambiguous without query analysis
|
|
309
|
+
- **Dynamic tool registration** — runtime reflection / metaprogramming
|
|
310
|
+
- **Non-Python code** — Python only
|
|
311
|
+
- **LLM-based judgment** — purely pattern-based, no semantic understanding
|
|
312
|
+
|
|
313
|
+
## Roadmap
|
|
314
|
+
|
|
315
|
+
**What works well today:**
|
|
316
|
+
- Tool detection and sink analysis across 6 frameworks
|
|
317
|
+
- Approval/consent/budget governance checks
|
|
318
|
+
- Constructor-time and execution-time budget detection
|
|
319
|
+
- Suppression, baselines, and 4 output formats
|
|
320
|
+
|
|
321
|
+
**What's next:**
|
|
322
|
+
- Cross-file call graph support
|
|
323
|
+
- Data-flow tracking for indirect sinks
|
|
324
|
+
- Posture summary (aggregate governance metrics per scan)
|
|
325
|
+
- PyPI package publishing (`pip install aigis`)
|
|
326
|
+
- Published GitHub Action
|
|
327
|
+
- Additional framework depth (LlamaIndex, Google ADK)
|
|
328
|
+
|
|
329
|
+
**Explicitly out of scope for now:**
|
|
330
|
+
- TypeScript/JavaScript support
|
|
331
|
+
- Runtime monitoring
|
|
332
|
+
- LLM-based detection
|
|
333
|
+
- Cloud dashboard or hosted service
|
|
334
|
+
|
|
335
|
+
## Design Principles
|
|
336
|
+
|
|
337
|
+
- **Deterministic** — code patterns only, never LLM judgment
|
|
338
|
+
- **Tri-state** — yes / no / unknown; unknown does not fail
|
|
339
|
+
- **Low noise** — false negatives over false positives
|
|
340
|
+
- **Missing guard is first-class** — the absence of a control is the finding
|
|
341
|
+
- **Evidence-first** — every finding explains what, why, and how to fix
|
|
342
|
+
|
|
343
|
+
## Feedback
|
|
344
|
+
|
|
345
|
+
Aigis is in public alpha. If you're building tool-using agents and want governance visibility before production, we want to hear from you.
|
|
346
|
+
|
|
347
|
+
- [Open an issue](https://github.com/tyreamer/aigis/issues) with findings, false positives, or framework gaps
|
|
348
|
+
- [Start a discussion](https://github.com/tyreamer/aigis/discussions) about your governance workflow
|
|
349
|
+
|
|
350
|
+
We're especially interested in feedback from teams shipping agents with file access, network access, or subprocess execution.
|