ai-agent-security-toolkit 0.4.1__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.
Files changed (61) hide show
  1. ai_agent_security_toolkit-0.4.1/LICENSE +21 -0
  2. ai_agent_security_toolkit-0.4.1/PKG-INFO +273 -0
  3. ai_agent_security_toolkit-0.4.1/README.md +230 -0
  4. ai_agent_security_toolkit-0.4.1/pyproject.toml +93 -0
  5. ai_agent_security_toolkit-0.4.1/setup.cfg +4 -0
  6. ai_agent_security_toolkit-0.4.1/src/agentsec/__init__.py +3 -0
  7. ai_agent_security_toolkit-0.4.1/src/agentsec/cli.py +219 -0
  8. ai_agent_security_toolkit-0.4.1/src/agentsec/fuzzer/__init__.py +44 -0
  9. ai_agent_security_toolkit-0.4.1/src/agentsec/fuzzer/formats.py +222 -0
  10. ai_agent_security_toolkit-0.4.1/src/agentsec/fuzzer/harness.py +218 -0
  11. ai_agent_security_toolkit-0.4.1/src/agentsec/fuzzer/mock_agents.py +243 -0
  12. ai_agent_security_toolkit-0.4.1/src/agentsec/fuzzer/mutators.py +102 -0
  13. ai_agent_security_toolkit-0.4.1/src/agentsec/fuzzer/payloads.py +250 -0
  14. ai_agent_security_toolkit-0.4.1/src/agentsec/fuzzer/report.py +132 -0
  15. ai_agent_security_toolkit-0.4.1/src/agentsec/fuzzer/targets.py +300 -0
  16. ai_agent_security_toolkit-0.4.1/src/agentsec/integrations/__init__.py +1 -0
  17. ai_agent_security_toolkit-0.4.1/src/agentsec/integrations/langchain.py +94 -0
  18. ai_agent_security_toolkit-0.4.1/src/agentsec/integrations/mcp.py +392 -0
  19. ai_agent_security_toolkit-0.4.1/src/agentsec/middleware/__init__.py +65 -0
  20. ai_agent_security_toolkit-0.4.1/src/agentsec/middleware/audit.py +366 -0
  21. ai_agent_security_toolkit-0.4.1/src/agentsec/middleware/cef.py +183 -0
  22. ai_agent_security_toolkit-0.4.1/src/agentsec/middleware/injection.py +148 -0
  23. ai_agent_security_toolkit-0.4.1/src/agentsec/middleware/pipeline.py +111 -0
  24. ai_agent_security_toolkit-0.4.1/src/agentsec/middleware/redact.py +191 -0
  25. ai_agent_security_toolkit-0.4.1/src/agentsec/middleware/validators.py +505 -0
  26. ai_agent_security_toolkit-0.4.1/src/agentsec/py.typed +0 -0
  27. ai_agent_security_toolkit-0.4.1/src/agentsec/sandbox/__init__.py +31 -0
  28. ai_agent_security_toolkit-0.4.1/src/agentsec/sandbox/adapters.py +203 -0
  29. ai_agent_security_toolkit-0.4.1/src/agentsec/sandbox/guard.py +353 -0
  30. ai_agent_security_toolkit-0.4.1/src/agentsec/sandbox/policy.py +217 -0
  31. ai_agent_security_toolkit-0.4.1/src/agentsec/sandbox/subprocess_runner.py +226 -0
  32. ai_agent_security_toolkit-0.4.1/src/agentsec/sandbox/validators.py +209 -0
  33. ai_agent_security_toolkit-0.4.1/src/agentsec/threatmodel/__init__.py +27 -0
  34. ai_agent_security_toolkit-0.4.1/src/agentsec/threatmodel/crosswalk.py +65 -0
  35. ai_agent_security_toolkit-0.4.1/src/agentsec/threatmodel/data/frameworks.yaml +45 -0
  36. ai_agent_security_toolkit-0.4.1/src/agentsec/threatmodel/data/threats.yaml +440 -0
  37. ai_agent_security_toolkit-0.4.1/src/agentsec/threatmodel/render.py +364 -0
  38. ai_agent_security_toolkit-0.4.1/src/agentsec/types.py +64 -0
  39. ai_agent_security_toolkit-0.4.1/src/ai_agent_security_toolkit.egg-info/PKG-INFO +273 -0
  40. ai_agent_security_toolkit-0.4.1/src/ai_agent_security_toolkit.egg-info/SOURCES.txt +59 -0
  41. ai_agent_security_toolkit-0.4.1/src/ai_agent_security_toolkit.egg-info/dependency_links.txt +1 -0
  42. ai_agent_security_toolkit-0.4.1/src/ai_agent_security_toolkit.egg-info/entry_points.txt +2 -0
  43. ai_agent_security_toolkit-0.4.1/src/ai_agent_security_toolkit.egg-info/requires.txt +19 -0
  44. ai_agent_security_toolkit-0.4.1/src/ai_agent_security_toolkit.egg-info/top_level.txt +1 -0
  45. ai_agent_security_toolkit-0.4.1/tests/test_async.py +524 -0
  46. ai_agent_security_toolkit-0.4.1/tests/test_audit.py +90 -0
  47. ai_agent_security_toolkit-0.4.1/tests/test_cef.py +198 -0
  48. ai_agent_security_toolkit-0.4.1/tests/test_examples.py +173 -0
  49. ai_agent_security_toolkit-0.4.1/tests/test_formats.py +189 -0
  50. ai_agent_security_toolkit-0.4.1/tests/test_fuzz_regressions.py +137 -0
  51. ai_agent_security_toolkit-0.4.1/tests/test_fuzz_targets.py +119 -0
  52. ai_agent_security_toolkit-0.4.1/tests/test_fuzzer.py +386 -0
  53. ai_agent_security_toolkit-0.4.1/tests/test_hygiene.py +35 -0
  54. ai_agent_security_toolkit-0.4.1/tests/test_injection.py +63 -0
  55. ai_agent_security_toolkit-0.4.1/tests/test_integration_langchain.py +170 -0
  56. ai_agent_security_toolkit-0.4.1/tests/test_integration_mcp.py +122 -0
  57. ai_agent_security_toolkit-0.4.1/tests/test_mcp.py +338 -0
  58. ai_agent_security_toolkit-0.4.1/tests/test_redact_and_validators.py +165 -0
  59. ai_agent_security_toolkit-0.4.1/tests/test_sandbox.py +452 -0
  60. ai_agent_security_toolkit-0.4.1/tests/test_security_regressions.py +935 -0
  61. ai_agent_security_toolkit-0.4.1/tests/test_threatmodel_and_cli.py +331 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dustin Arrington
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,273 @@
1
+ Metadata-Version: 2.4
2
+ Name: ai-agent-security-toolkit
3
+ Version: 0.4.1
4
+ Summary: Security toolkit for LLM agents: prompt-injection fuzzer, tool-call allowlist/sandbox, output validation + tamper-evident audit logging, and a STRIDE-for-agents threat-modeling template.
5
+ Author: Dusty
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/DustyStudy/ai-agent-security-toolkit
8
+ Project-URL: Issues, https://github.com/DustyStudy/ai-agent-security-toolkit/issues
9
+ Project-URL: Changelog, https://github.com/DustyStudy/ai-agent-security-toolkit/blob/main/CHANGELOG.md
10
+ Project-URL: Security, https://github.com/DustyStudy/ai-agent-security-toolkit/security/policy
11
+ Keywords: llm,agents,prompt-injection,security,red-team,sandbox,audit,threat-modeling
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Information Technology
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Operating System :: Microsoft :: Windows
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Security
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.11
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: PyYAML>=6.0
28
+ Requires-Dist: jsonschema>=4.20
29
+ Provides-Extra: anthropic
30
+ Requires-Dist: anthropic>=0.40; extra == "anthropic"
31
+ Provides-Extra: mcp
32
+ Requires-Dist: mcp>=2; extra == "mcp"
33
+ Provides-Extra: langchain
34
+ Requires-Dist: langchain-core>=0.3; extra == "langchain"
35
+ Provides-Extra: dev
36
+ Requires-Dist: pytest>=8.0; extra == "dev"
37
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
38
+ Requires-Dist: ruff>=0.6; extra == "dev"
39
+ Requires-Dist: mypy>=1.10; extra == "dev"
40
+ Requires-Dist: types-PyYAML>=6.0; extra == "dev"
41
+ Requires-Dist: types-jsonschema>=4.20; extra == "dev"
42
+ Dynamic: license-file
43
+
44
+ # ai-agent-security-toolkit
45
+
46
+ [![CI](https://github.com/DustyStudy/ai-agent-security-toolkit/actions/workflows/ci.yml/badge.svg)](https://github.com/DustyStudy/ai-agent-security-toolkit/actions/workflows/ci.yml)
47
+ [![CodeQL](https://github.com/DustyStudy/ai-agent-security-toolkit/actions/workflows/codeql.yml/badge.svg)](https://github.com/DustyStudy/ai-agent-security-toolkit/actions/workflows/codeql.yml)
48
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
49
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/DustyStudy/ai-agent-security-toolkit/badge)](https://scorecard.dev/viewer/?uri=github.com/DustyStudy/ai-agent-security-toolkit)
50
+ [![Release](https://img.shields.io/github/v/release/DustyStudy/ai-agent-security-toolkit)](https://github.com/DustyStudy/ai-agent-security-toolkit/releases)
51
+
52
+ **Fuzz, contain, validate, and audit LLM agents.** Four pieces that fit together:
53
+
54
+ | | Module | What it does |
55
+ |---|---|---|
56
+ | 1 | [`agentsec.fuzzer`](src/agentsec/fuzzer) | Prompt-injection test harness. 25 payloads x 10 obfuscations x 2 delivery modes = 500 cases, mechanically scored with canaries (no judge model). Emits JSON/Markdown and gates CI on attack-success rate. |
57
+ | 2 | [`agentsec.sandbox`](src/agentsec/sandbox) | Deny-by-default tool allowlist for agent frameworks: per-argument validation, path confinement, SSRF checks on URL arguments, rate limits, human approval, and **taint tracking** that blocks side-effecting tools after untrusted content is read. Plus a no-shell subprocess runner. |
58
+ | 3 | [`agentsec.middleware`](src/agentsec/middleware) | Output validation (secrets/PII, markdown-image exfiltration, XSS markup, JSON-schema, protected strings) and a **hash-chained, tamper-evident audit log** with secret redaction. |
59
+ | 4 | [`agentsec.threatmodel`](src/agentsec/threatmodel) | **STRIDE-for-agents** threat catalog (25 threats mapped to the OWASP LLM Top 10 2025, the OWASP Top 10 for Agentic Applications 2026 and MITRE ATLAS) and a generator that turns a YAML system description into a threat model with data-flow diagram, threat register, and verification plan. |
60
+
61
+ ```mermaid
62
+ flowchart LR
63
+ U[User / untrusted content] --> S[screen_input<br/>scan + taint + spotlight]
64
+ S --> M[(LLM)]
65
+ M -->|tool_use| G[ToolGuard<br/>allowlist + args + taint + approval]
66
+ G --> T[Tools]
67
+ T -->|results = untrusted| S
68
+ M -->|text| O[screen_output<br/>secrets, exfil links, markup]
69
+ O --> R[Response]
70
+ S -.-> A[(Audit log<br/>hash chain)]
71
+ G -.-> A
72
+ O -.-> A
73
+ F[Fuzzer] -. measures .-> S & G & O
74
+ ```
75
+
76
+ **New here?** Take the [five-minute tour](docs/TOUR.md) (real output, no API key needed). **Deploying it?** Read the [production guide](docs/PRODUCTION.md).
77
+
78
+ ## Why this exists
79
+
80
+ A prompt-injection filter will be bypassed; that is a property of the problem, not of any one filter. What you can control is **blast radius**: what the agent is *allowed* to do once its instructions have been hijacked, what leaves the system, and whether you can reconstruct what happened. This toolkit is built around that idea, and around **measuring** it rather than asserting it.
81
+
82
+ ### Simulator results (not a model benchmark)
83
+
84
+ > **These numbers come from the built-in deterministic simulator, not from an LLM.** They show how the controls behave when the model obeys every injection. They are not a claim about the security of Claude, GPT, Gemini or any other model. See [Honest limitations](#honest-limitations).
85
+
86
+ | Target | Attack-success rate | tool_misuse | exfiltration | prompt_leak | canary echo |
87
+ |---|---:|---:|---:|---:|---:|
88
+ | `builtin:naive` (obeys any injection) | **100%** | 100% | 100% | 100% | 100% |
89
+ | `builtin:guarded-noscanner` (policy + output guard only) | **60%** | 0% | 0% | 0% | 100% |
90
+ | `builtin:guarded` (+ heuristic scanner) | **34%** | 0% | 0% | 0% | 57% |
91
+
92
+ Read it this way: tool policy and output validation reduce the *dangerous* outcomes (side effects, exfiltration, secret leaks) to zero **even when the model is fully compromised**. Merely getting the model to say something (canary echo) cannot be stopped by those layers, and the pattern-matching scanner only catches part of it. Base64, ROT13, homoglyphs, leetspeak and non-English variants slip past it, while zero-width and Unicode-tag smuggling are caught. [Full sample report](docs/sample-fuzz-report.md).
93
+
94
+ **Against a real model:** the same matrix run against Llama 3.1 8B on a local GPU took the attack-success rate from 34.9% for the model alone to 0.2% behind the full stack, with no forbidden tool call, leak or exfiltration getting through. Method, scope and every number are in [docs/PROOF.md](docs/PROOF.md).
95
+
96
+ ## Install
97
+
98
+ ```bash
99
+ pip install -e ".[dev]" # from a clone
100
+ pip install -e ".[anthropic]" # optional: fuzz Claude models directly
101
+ ```
102
+
103
+ Python 3.11+. Runtime dependencies: `PyYAML`, `jsonschema`.
104
+
105
+ ## 1. Fuzz an agent
106
+
107
+ ```bash
108
+ # self-check against the built-in simulators
109
+ agentsec fuzz --target builtin:guarded --md report.md --json report.json
110
+
111
+ # your own agent, as a Python callable: def my_agent(prompt: str) -> str
112
+ agentsec fuzz --target mypkg.agent:respond --max-asr 0.10
113
+
114
+ # an HTTP endpoint fronting your agent
115
+ agentsec fuzz --target https://staging.example.com/agent/chat
116
+
117
+ # a Claude model directly (needs ANTHROPIC_API_KEY)
118
+ agentsec fuzz --target anthropic:claude-sonnet-5-5 --max-cases 100
119
+
120
+ # a local model through Ollama (no API key), bare and behind the toolkit's guards
121
+ agentsec fuzz --target ollama:llama3.1:8b
122
+ agentsec fuzz --target guarded:ollama:llama3.1:8b
123
+ ```
124
+
125
+ Each case plants a random **canary** (split in two so a model that merely *quotes* the injection does not count), a **system-prompt secret**, and a set of **forbidden tools**. An attack succeeds if the response contains the canary or the secret, calls a forbidden tool, references the attacker domain in tool arguments, or renders a markdown/HTML image or link to the attacker domain. Results break down by goal, category, obfuscation, and delivery (payload typed by the user vs. hidden in a retrieved document).
126
+
127
+ `--max-asr 0.05` makes the command exit `1` above 5%, so it can gate a release. Add `--categories tool_misuse,exfiltration,prompt_leak --max-asr 0` to gate specifically on containment.
128
+
129
+ **CI reports.** `--sarif report.sarif` writes SARIF 2.1.0 for GitHub code scanning and `--junit report.xml` writes JUnit XML for any CI test UI. Successful attacks become findings (`error` for tool misuse, exfiltration and prompt leak; `warning` for instruction-following), each tagged with its OWASP LLM Top 10 (2025) id. Fuzz findings have no source location, so pass `--sarif-artifact path/to/agent.py` (repo-relative) to say which file the alerts attach to.
130
+
131
+ ```yaml
132
+ # .github/workflows/agent-fuzz.yml (excerpt)
133
+ permissions: {contents: read, security-events: write}
134
+ steps:
135
+ - run: agentsec fuzz --target mypkg.agent:respond --max-asr 0.10 --quiet
136
+ --sarif agentsec.sarif --sarif-artifact src/mypkg/agent.py
137
+ - uses: github/codeql-action/upload-sarif@v4
138
+ if: always()
139
+ with: {sarif_file: agentsec.sarif}
140
+ ```
141
+
142
+ Text taken from the target's output is bounded and stripped of control characters before it enters either format. The SARIF output is validated against the official 2.1.0 schema in the tests; it has not been uploaded to GitHub code scanning by this repo's CI.
143
+
144
+ Targets may be `async def`: `agentsec fuzz --target mypkg.agent:respond` detects a coroutine function and runs it on one reused event loop, and `text_target` / `sync_target` do the same when you call the harness from Python.
145
+
146
+ For richer targets, pass a callable that accepts an `AttackInput` and returns a `TargetResponse` (with `tool_calls`); see [`targets.py`](src/agentsec/fuzzer/targets.py).
147
+
148
+ ## 2. Constrain tool calls
149
+
150
+ Policies are data, so they can be reviewed and diffed. Unknown keys are load-time errors and anything unlisted is denied. See [`examples/policy.yaml`](examples/policy.yaml).
151
+
152
+ ```python
153
+ from agentsec.sandbox import Policy, ToolGuard, Session
154
+
155
+ guard = ToolGuard(Policy.from_yaml("policy.yaml"), approver=ask_a_human)
156
+ session = Session()
157
+
158
+ # Wrap a function; the model's arguments are validated before it ever runs.
159
+ search = guard.wrap("search_docs", search_docs, session)
160
+ search(query="Q3 revenue") # allowed; result marks the session tainted
161
+ guard.wrap("http_get", http_get, session)( # ToolDenied: session read untrusted content
162
+ url="https://api.corp.example/x") # and http_get has side effects
163
+ ```
164
+
165
+ What the guard enforces:
166
+
167
+ - **Default deny.** Tools and arguments not in the policy are refused; extra arguments are refused.
168
+ - **Argument constraints.** Type, enum, regex (`pattern`, `deny_patterns`), length, numeric bounds.
169
+ - **Paths** are resolved (symlinks and `..` included) and must sit under an allowed root.
170
+ - **URLs** must match scheme and host allowlists, reject embedded credentials, and reject every non-global address (private, loopback, link-local, carrier-grade NAT) including the `169.254.169.254` and `100.100.100.200` metadata services and decimal/hex/octal/IPv4-mapped IP forms, plus `localhost` and `*.localhost` names. Any other hostname is only checked if `resolve_dns: true` is set, which rejects hostnames that resolve to non-public ranges. (This narrows DNS-rebinding exposure but does not eliminate it; pin the resolved address at connect time for a full fix.)
171
+ - **Taint tracking.** Tools marked `returns_untrusted` taint the session; tools marked `side_effects` are then denied (or routed to approval) for the rest of that session. This breaks the "private data + untrusted content + outbound channel" combination without needing to detect the injection.
172
+ - **Human approval** hook, per-tool and per-session call limits, and an audit trail of every request and decision.
173
+
174
+ Drop-in helpers for the tool-calling wire formats: `run_anthropic_tool_uses` and `run_openai_tool_calls` turn model tool requests into results, with denials returned as error text the model can read. See the complete loop in [`examples/agent_loop.py`](examples/agent_loop.py).
175
+
176
+ ### Async, LangChain, LangGraph, MCP and subprocesses
177
+
178
+ Each has its own section in [`docs/SANDBOX.md`](docs/SANDBOX.md), with the limits of what was tested.
179
+
180
+ | Use | Entry point |
181
+ |---|---|
182
+ | `asyncio` applications (FastAPI, LangGraph) | `aauthorize`, `aexecute`, `awrap`, `arun_anthropic_tool_uses`, `arun_openai_tool_calls`. Same policy, taint, limit and audit behavior as the sync API. [`examples/async_agent_loop.py`](examples/async_agent_loop.py) |
183
+ | LangChain and LangGraph tools | `agentsec.integrations.langchain.guard_tools([...], guard, session)` |
184
+ | MCP servers | `agentsec.integrations.mcp.GuardedMCPClient`: allowlists and pins tool definitions, and drops poisoned or changed ones |
185
+ | External commands | `SafeCommandRunner`: no shell, executable and option allowlists, scrubbed environment, timeout. It is **not** an OS sandbox |
186
+
187
+ ## 3. Validate output and keep an audit trail
188
+
189
+ ```python
190
+ from agentsec.middleware import AgentMiddleware, AuditLogger, FileSink, InjectionScanner, OutputGuard
191
+
192
+ audit = AuditLogger(FileSink("audit.jsonl"), actor="support-copilot")
193
+ mw = AgentMiddleware(
194
+ guard=guard,
195
+ scanner=InjectionScanner(),
196
+ output_guard=OutputGuard.default(allowed_hosts=["corp.example"], protected=[SYSTEM_PROMPT_CANARY]),
197
+ audit=audit,
198
+ )
199
+
200
+ screen = mw.screen_input(ticket_text, source="ticket", untrusted=True, session=session)
201
+ ...
202
+ result = mw.screen_output(model_text) # redacts secrets/PII, strips external images, blocks XSS markup
203
+ ```
204
+
205
+ ```bash
206
+ agentsec audit verify audit.jsonl # exit 1 if any record was edited or removed
207
+ ```
208
+
209
+ The audit log chains records with SHA-256. Editing or deleting a record in the middle breaks verification from that point. It does **not** stop someone with write access from truncating the tail or replacing the whole file; ship records to append-only storage (S3 Object Lock, CloudWatch Logs with a restrictive resource policy) and anchor `AuditLogger.checkpoint()` somewhere the agent host cannot write. Secrets and PII are redacted before hashing, and `log_content=False` stores only hashes and lengths of long fields.
210
+
211
+ **SIEM feed (CEF).** Keep the hash-chained file as the evidence of record and stream a copy to your SIEM in ArcSight Common Event Format, which Splunk, Microsoft Sentinel, QRadar, Elastic and most syslog pipelines ingest:
212
+
213
+ ```python
214
+ from agentsec.middleware import AuditLogger, CefSink, FileSink, TeeSink
215
+
216
+ audit = AuditLogger(TeeSink(FileSink("audit.jsonl"), CefSink(send_to_syslog))) # or format_cef(record) yourself
217
+ ```
218
+
219
+ Every CEF value is escaped and bounded, so attacker-influenced text cannot forge a field or start a new event. A failing SIEM sink does not interrupt the agent or break the chain. Details are in the [production guide](docs/PRODUCTION.md#audit-trail). The CEF output has not been ingested by a specific SIEM product.
220
+
221
+ ## 4. Threat model an agent (STRIDE-for-agents)
222
+
223
+ ```bash
224
+ agentsec threatmodel init -o system.yaml # describe components, trust zones, data flows
225
+ agentsec threatmodel render system.yaml -o THREATMODEL.md
226
+ ```
227
+
228
+ The generator selects candidate threats by component type, draws the data-flow diagram with trust-boundary crossings, gives each row a triage hint (raised for components that ingest untrusted input, have side effects, or hold sensitive data), and links each threat to its mitigations, the toolkit control that implements it, and the fuzzer categories that test it. See the worked example: [`docs/threat-model/EXAMPLE-support-copilot.md`](docs/threat-model/EXAMPLE-support-copilot.md), the [method and blank template](docs/threat-model/README.md), and `agentsec threatmodel catalog`. Each threat is also mapped to the [OWASP Top 10 for Agentic Applications (2026)](https://genai.owasp.org/2025/12/09/owasp-top-10-for-agentic-applications-the-benchmark-for-agentic-security-in-the-age-of-autonomous-ai/) and to [MITRE ATLAS](https://github.com/mitre-atlas/atlas-data) techniques (content release 2026.09). `agentsec threatmodel crosswalk` prints the [framework crosswalk](docs/threat-model/frameworks.md), which lists the toolkit controls and fuzzer categories behind each entry.
229
+
230
+ ## Coverage against OWASP Top 10 for LLM Applications (2025)
231
+
232
+ | Risk | Where this toolkit helps |
233
+ |---|---|
234
+ | LLM01 Prompt injection | Fuzzer (measure), scanner + spotlighting (reduce), taint policy (contain) |
235
+ | LLM02 Sensitive information disclosure | Secret/PII redaction, protected-string tripwire, audit redaction |
236
+ | LLM03 Supply chain | Threat catalog entries and mitigations (no runtime control here) |
237
+ | LLM04 Data and model poisoning | Catalog + retrieval-time scanning |
238
+ | LLM05 Improper output handling | `OutputGuard` validators: markup, exfil links, JSON schema |
239
+ | LLM06 Excessive agency | Deny-by-default `Policy`, approval, taint, `SafeCommandRunner` |
240
+ | LLM07 System prompt leakage | Fuzzer `prompt_leak` category, `ProtectedStringValidator` |
241
+ | LLM08 Vector and embedding weaknesses | Catalog entries only |
242
+ | LLM09 Misinformation | Out of scope |
243
+ | LLM10 Unbounded consumption | Per-session call limits, subprocess timeouts and output caps |
244
+
245
+ The catalog also covers all ten OWASP agentic risks; the [crosswalk](docs/threat-model/frameworks.md) shows which toolkit controls back each one. A mapping means a threat is an instance of, or contributes to, that entry, not that the entry is mitigated. NIST AI RMF and ISO/IEC 42001 are not mapped.
246
+
247
+ ## Honest limitations
248
+
249
+ - **The built-in agents are simulators.** `NaiveAgent` deterministically obeys injections and understands common obfuscations; it exists so the harness, detectors and CI gate can be tested offline. The numbers above show how *these controls behave when the model is compromised*, not how any real model behaves. Run the fuzzer against your real agent for that. `AnthropicTarget` and `HttpTarget` are tested against fakes and a local server, and have not been run against a live endpoint by this repo's CI. `OllamaTarget` has been run against a real local model once, by hand ([docs/PROOF.md](docs/PROOF.md)); CI does not run a model.
250
+ - **Canary-echo success is a proxy.** It measures whether the model followed an injected instruction, which is the necessary first step of every real attack, but not the impact of a real one.
251
+ - **The corpus is a starting set** of well-known technique families, not an exhaustive or adaptive attacker. Extend it with your own payloads (`FuzzConfig(corpus=...)`).
252
+ - **The scanner is a tripwire.** Its documented bypasses are asserted in the tests on purpose.
253
+ - **Output validators are pattern-based** and will not catch data a model re-encodes.
254
+ - **No OS-level isolation.** Use containers/microVMs for code execution, and IMDSv2 with hop limit 1 for cloud-hosted agents.
255
+
256
+ ## Development
257
+
258
+ ```bash
259
+ pip install -e ".[dev]"
260
+ ruff check src tests && ruff format --check src tests
261
+ mypy src
262
+ pytest --cov=agentsec
263
+ ```
264
+
265
+ CI runs lint, type-check and tests on Linux (Python 3.11 to 3.13) and Windows and macOS (3.11, 3.12), builds and checks the package, validates the example policy, re-generates the example threat model to confirm it is current, and runs the fuzzer as a regression check that the containment layers still hold at 0% for tool misuse, exfiltration and prompt leak. See [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md).
266
+
267
+ ## Project health
268
+
269
+ The [OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/DustyStudy/ai-agent-security-toolkit) badge reflects a young, solo-maintained project. Code-Review and Contributors need other people, and Maintained scores 0 for any repository under 90 days old. The checks about the code and supply chain (pinned dependencies, fuzzing, signed releases, token permissions, SAST, vulnerabilities) are the informative ones. `codeql-action` is left on its major tag deliberately, because GitHub gates some features on the tag.
270
+
271
+ ## License
272
+
273
+ MIT
@@ -0,0 +1,230 @@
1
+ # ai-agent-security-toolkit
2
+
3
+ [![CI](https://github.com/DustyStudy/ai-agent-security-toolkit/actions/workflows/ci.yml/badge.svg)](https://github.com/DustyStudy/ai-agent-security-toolkit/actions/workflows/ci.yml)
4
+ [![CodeQL](https://github.com/DustyStudy/ai-agent-security-toolkit/actions/workflows/codeql.yml/badge.svg)](https://github.com/DustyStudy/ai-agent-security-toolkit/actions/workflows/codeql.yml)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
6
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/DustyStudy/ai-agent-security-toolkit/badge)](https://scorecard.dev/viewer/?uri=github.com/DustyStudy/ai-agent-security-toolkit)
7
+ [![Release](https://img.shields.io/github/v/release/DustyStudy/ai-agent-security-toolkit)](https://github.com/DustyStudy/ai-agent-security-toolkit/releases)
8
+
9
+ **Fuzz, contain, validate, and audit LLM agents.** Four pieces that fit together:
10
+
11
+ | | Module | What it does |
12
+ |---|---|---|
13
+ | 1 | [`agentsec.fuzzer`](src/agentsec/fuzzer) | Prompt-injection test harness. 25 payloads x 10 obfuscations x 2 delivery modes = 500 cases, mechanically scored with canaries (no judge model). Emits JSON/Markdown and gates CI on attack-success rate. |
14
+ | 2 | [`agentsec.sandbox`](src/agentsec/sandbox) | Deny-by-default tool allowlist for agent frameworks: per-argument validation, path confinement, SSRF checks on URL arguments, rate limits, human approval, and **taint tracking** that blocks side-effecting tools after untrusted content is read. Plus a no-shell subprocess runner. |
15
+ | 3 | [`agentsec.middleware`](src/agentsec/middleware) | Output validation (secrets/PII, markdown-image exfiltration, XSS markup, JSON-schema, protected strings) and a **hash-chained, tamper-evident audit log** with secret redaction. |
16
+ | 4 | [`agentsec.threatmodel`](src/agentsec/threatmodel) | **STRIDE-for-agents** threat catalog (25 threats mapped to the OWASP LLM Top 10 2025, the OWASP Top 10 for Agentic Applications 2026 and MITRE ATLAS) and a generator that turns a YAML system description into a threat model with data-flow diagram, threat register, and verification plan. |
17
+
18
+ ```mermaid
19
+ flowchart LR
20
+ U[User / untrusted content] --> S[screen_input<br/>scan + taint + spotlight]
21
+ S --> M[(LLM)]
22
+ M -->|tool_use| G[ToolGuard<br/>allowlist + args + taint + approval]
23
+ G --> T[Tools]
24
+ T -->|results = untrusted| S
25
+ M -->|text| O[screen_output<br/>secrets, exfil links, markup]
26
+ O --> R[Response]
27
+ S -.-> A[(Audit log<br/>hash chain)]
28
+ G -.-> A
29
+ O -.-> A
30
+ F[Fuzzer] -. measures .-> S & G & O
31
+ ```
32
+
33
+ **New here?** Take the [five-minute tour](docs/TOUR.md) (real output, no API key needed). **Deploying it?** Read the [production guide](docs/PRODUCTION.md).
34
+
35
+ ## Why this exists
36
+
37
+ A prompt-injection filter will be bypassed; that is a property of the problem, not of any one filter. What you can control is **blast radius**: what the agent is *allowed* to do once its instructions have been hijacked, what leaves the system, and whether you can reconstruct what happened. This toolkit is built around that idea, and around **measuring** it rather than asserting it.
38
+
39
+ ### Simulator results (not a model benchmark)
40
+
41
+ > **These numbers come from the built-in deterministic simulator, not from an LLM.** They show how the controls behave when the model obeys every injection. They are not a claim about the security of Claude, GPT, Gemini or any other model. See [Honest limitations](#honest-limitations).
42
+
43
+ | Target | Attack-success rate | tool_misuse | exfiltration | prompt_leak | canary echo |
44
+ |---|---:|---:|---:|---:|---:|
45
+ | `builtin:naive` (obeys any injection) | **100%** | 100% | 100% | 100% | 100% |
46
+ | `builtin:guarded-noscanner` (policy + output guard only) | **60%** | 0% | 0% | 0% | 100% |
47
+ | `builtin:guarded` (+ heuristic scanner) | **34%** | 0% | 0% | 0% | 57% |
48
+
49
+ Read it this way: tool policy and output validation reduce the *dangerous* outcomes (side effects, exfiltration, secret leaks) to zero **even when the model is fully compromised**. Merely getting the model to say something (canary echo) cannot be stopped by those layers, and the pattern-matching scanner only catches part of it. Base64, ROT13, homoglyphs, leetspeak and non-English variants slip past it, while zero-width and Unicode-tag smuggling are caught. [Full sample report](docs/sample-fuzz-report.md).
50
+
51
+ **Against a real model:** the same matrix run against Llama 3.1 8B on a local GPU took the attack-success rate from 34.9% for the model alone to 0.2% behind the full stack, with no forbidden tool call, leak or exfiltration getting through. Method, scope and every number are in [docs/PROOF.md](docs/PROOF.md).
52
+
53
+ ## Install
54
+
55
+ ```bash
56
+ pip install -e ".[dev]" # from a clone
57
+ pip install -e ".[anthropic]" # optional: fuzz Claude models directly
58
+ ```
59
+
60
+ Python 3.11+. Runtime dependencies: `PyYAML`, `jsonschema`.
61
+
62
+ ## 1. Fuzz an agent
63
+
64
+ ```bash
65
+ # self-check against the built-in simulators
66
+ agentsec fuzz --target builtin:guarded --md report.md --json report.json
67
+
68
+ # your own agent, as a Python callable: def my_agent(prompt: str) -> str
69
+ agentsec fuzz --target mypkg.agent:respond --max-asr 0.10
70
+
71
+ # an HTTP endpoint fronting your agent
72
+ agentsec fuzz --target https://staging.example.com/agent/chat
73
+
74
+ # a Claude model directly (needs ANTHROPIC_API_KEY)
75
+ agentsec fuzz --target anthropic:claude-sonnet-5-5 --max-cases 100
76
+
77
+ # a local model through Ollama (no API key), bare and behind the toolkit's guards
78
+ agentsec fuzz --target ollama:llama3.1:8b
79
+ agentsec fuzz --target guarded:ollama:llama3.1:8b
80
+ ```
81
+
82
+ Each case plants a random **canary** (split in two so a model that merely *quotes* the injection does not count), a **system-prompt secret**, and a set of **forbidden tools**. An attack succeeds if the response contains the canary or the secret, calls a forbidden tool, references the attacker domain in tool arguments, or renders a markdown/HTML image or link to the attacker domain. Results break down by goal, category, obfuscation, and delivery (payload typed by the user vs. hidden in a retrieved document).
83
+
84
+ `--max-asr 0.05` makes the command exit `1` above 5%, so it can gate a release. Add `--categories tool_misuse,exfiltration,prompt_leak --max-asr 0` to gate specifically on containment.
85
+
86
+ **CI reports.** `--sarif report.sarif` writes SARIF 2.1.0 for GitHub code scanning and `--junit report.xml` writes JUnit XML for any CI test UI. Successful attacks become findings (`error` for tool misuse, exfiltration and prompt leak; `warning` for instruction-following), each tagged with its OWASP LLM Top 10 (2025) id. Fuzz findings have no source location, so pass `--sarif-artifact path/to/agent.py` (repo-relative) to say which file the alerts attach to.
87
+
88
+ ```yaml
89
+ # .github/workflows/agent-fuzz.yml (excerpt)
90
+ permissions: {contents: read, security-events: write}
91
+ steps:
92
+ - run: agentsec fuzz --target mypkg.agent:respond --max-asr 0.10 --quiet
93
+ --sarif agentsec.sarif --sarif-artifact src/mypkg/agent.py
94
+ - uses: github/codeql-action/upload-sarif@v4
95
+ if: always()
96
+ with: {sarif_file: agentsec.sarif}
97
+ ```
98
+
99
+ Text taken from the target's output is bounded and stripped of control characters before it enters either format. The SARIF output is validated against the official 2.1.0 schema in the tests; it has not been uploaded to GitHub code scanning by this repo's CI.
100
+
101
+ Targets may be `async def`: `agentsec fuzz --target mypkg.agent:respond` detects a coroutine function and runs it on one reused event loop, and `text_target` / `sync_target` do the same when you call the harness from Python.
102
+
103
+ For richer targets, pass a callable that accepts an `AttackInput` and returns a `TargetResponse` (with `tool_calls`); see [`targets.py`](src/agentsec/fuzzer/targets.py).
104
+
105
+ ## 2. Constrain tool calls
106
+
107
+ Policies are data, so they can be reviewed and diffed. Unknown keys are load-time errors and anything unlisted is denied. See [`examples/policy.yaml`](examples/policy.yaml).
108
+
109
+ ```python
110
+ from agentsec.sandbox import Policy, ToolGuard, Session
111
+
112
+ guard = ToolGuard(Policy.from_yaml("policy.yaml"), approver=ask_a_human)
113
+ session = Session()
114
+
115
+ # Wrap a function; the model's arguments are validated before it ever runs.
116
+ search = guard.wrap("search_docs", search_docs, session)
117
+ search(query="Q3 revenue") # allowed; result marks the session tainted
118
+ guard.wrap("http_get", http_get, session)( # ToolDenied: session read untrusted content
119
+ url="https://api.corp.example/x") # and http_get has side effects
120
+ ```
121
+
122
+ What the guard enforces:
123
+
124
+ - **Default deny.** Tools and arguments not in the policy are refused; extra arguments are refused.
125
+ - **Argument constraints.** Type, enum, regex (`pattern`, `deny_patterns`), length, numeric bounds.
126
+ - **Paths** are resolved (symlinks and `..` included) and must sit under an allowed root.
127
+ - **URLs** must match scheme and host allowlists, reject embedded credentials, and reject every non-global address (private, loopback, link-local, carrier-grade NAT) including the `169.254.169.254` and `100.100.100.200` metadata services and decimal/hex/octal/IPv4-mapped IP forms, plus `localhost` and `*.localhost` names. Any other hostname is only checked if `resolve_dns: true` is set, which rejects hostnames that resolve to non-public ranges. (This narrows DNS-rebinding exposure but does not eliminate it; pin the resolved address at connect time for a full fix.)
128
+ - **Taint tracking.** Tools marked `returns_untrusted` taint the session; tools marked `side_effects` are then denied (or routed to approval) for the rest of that session. This breaks the "private data + untrusted content + outbound channel" combination without needing to detect the injection.
129
+ - **Human approval** hook, per-tool and per-session call limits, and an audit trail of every request and decision.
130
+
131
+ Drop-in helpers for the tool-calling wire formats: `run_anthropic_tool_uses` and `run_openai_tool_calls` turn model tool requests into results, with denials returned as error text the model can read. See the complete loop in [`examples/agent_loop.py`](examples/agent_loop.py).
132
+
133
+ ### Async, LangChain, LangGraph, MCP and subprocesses
134
+
135
+ Each has its own section in [`docs/SANDBOX.md`](docs/SANDBOX.md), with the limits of what was tested.
136
+
137
+ | Use | Entry point |
138
+ |---|---|
139
+ | `asyncio` applications (FastAPI, LangGraph) | `aauthorize`, `aexecute`, `awrap`, `arun_anthropic_tool_uses`, `arun_openai_tool_calls`. Same policy, taint, limit and audit behavior as the sync API. [`examples/async_agent_loop.py`](examples/async_agent_loop.py) |
140
+ | LangChain and LangGraph tools | `agentsec.integrations.langchain.guard_tools([...], guard, session)` |
141
+ | MCP servers | `agentsec.integrations.mcp.GuardedMCPClient`: allowlists and pins tool definitions, and drops poisoned or changed ones |
142
+ | External commands | `SafeCommandRunner`: no shell, executable and option allowlists, scrubbed environment, timeout. It is **not** an OS sandbox |
143
+
144
+ ## 3. Validate output and keep an audit trail
145
+
146
+ ```python
147
+ from agentsec.middleware import AgentMiddleware, AuditLogger, FileSink, InjectionScanner, OutputGuard
148
+
149
+ audit = AuditLogger(FileSink("audit.jsonl"), actor="support-copilot")
150
+ mw = AgentMiddleware(
151
+ guard=guard,
152
+ scanner=InjectionScanner(),
153
+ output_guard=OutputGuard.default(allowed_hosts=["corp.example"], protected=[SYSTEM_PROMPT_CANARY]),
154
+ audit=audit,
155
+ )
156
+
157
+ screen = mw.screen_input(ticket_text, source="ticket", untrusted=True, session=session)
158
+ ...
159
+ result = mw.screen_output(model_text) # redacts secrets/PII, strips external images, blocks XSS markup
160
+ ```
161
+
162
+ ```bash
163
+ agentsec audit verify audit.jsonl # exit 1 if any record was edited or removed
164
+ ```
165
+
166
+ The audit log chains records with SHA-256. Editing or deleting a record in the middle breaks verification from that point. It does **not** stop someone with write access from truncating the tail or replacing the whole file; ship records to append-only storage (S3 Object Lock, CloudWatch Logs with a restrictive resource policy) and anchor `AuditLogger.checkpoint()` somewhere the agent host cannot write. Secrets and PII are redacted before hashing, and `log_content=False` stores only hashes and lengths of long fields.
167
+
168
+ **SIEM feed (CEF).** Keep the hash-chained file as the evidence of record and stream a copy to your SIEM in ArcSight Common Event Format, which Splunk, Microsoft Sentinel, QRadar, Elastic and most syslog pipelines ingest:
169
+
170
+ ```python
171
+ from agentsec.middleware import AuditLogger, CefSink, FileSink, TeeSink
172
+
173
+ audit = AuditLogger(TeeSink(FileSink("audit.jsonl"), CefSink(send_to_syslog))) # or format_cef(record) yourself
174
+ ```
175
+
176
+ Every CEF value is escaped and bounded, so attacker-influenced text cannot forge a field or start a new event. A failing SIEM sink does not interrupt the agent or break the chain. Details are in the [production guide](docs/PRODUCTION.md#audit-trail). The CEF output has not been ingested by a specific SIEM product.
177
+
178
+ ## 4. Threat model an agent (STRIDE-for-agents)
179
+
180
+ ```bash
181
+ agentsec threatmodel init -o system.yaml # describe components, trust zones, data flows
182
+ agentsec threatmodel render system.yaml -o THREATMODEL.md
183
+ ```
184
+
185
+ The generator selects candidate threats by component type, draws the data-flow diagram with trust-boundary crossings, gives each row a triage hint (raised for components that ingest untrusted input, have side effects, or hold sensitive data), and links each threat to its mitigations, the toolkit control that implements it, and the fuzzer categories that test it. See the worked example: [`docs/threat-model/EXAMPLE-support-copilot.md`](docs/threat-model/EXAMPLE-support-copilot.md), the [method and blank template](docs/threat-model/README.md), and `agentsec threatmodel catalog`. Each threat is also mapped to the [OWASP Top 10 for Agentic Applications (2026)](https://genai.owasp.org/2025/12/09/owasp-top-10-for-agentic-applications-the-benchmark-for-agentic-security-in-the-age-of-autonomous-ai/) and to [MITRE ATLAS](https://github.com/mitre-atlas/atlas-data) techniques (content release 2026.09). `agentsec threatmodel crosswalk` prints the [framework crosswalk](docs/threat-model/frameworks.md), which lists the toolkit controls and fuzzer categories behind each entry.
186
+
187
+ ## Coverage against OWASP Top 10 for LLM Applications (2025)
188
+
189
+ | Risk | Where this toolkit helps |
190
+ |---|---|
191
+ | LLM01 Prompt injection | Fuzzer (measure), scanner + spotlighting (reduce), taint policy (contain) |
192
+ | LLM02 Sensitive information disclosure | Secret/PII redaction, protected-string tripwire, audit redaction |
193
+ | LLM03 Supply chain | Threat catalog entries and mitigations (no runtime control here) |
194
+ | LLM04 Data and model poisoning | Catalog + retrieval-time scanning |
195
+ | LLM05 Improper output handling | `OutputGuard` validators: markup, exfil links, JSON schema |
196
+ | LLM06 Excessive agency | Deny-by-default `Policy`, approval, taint, `SafeCommandRunner` |
197
+ | LLM07 System prompt leakage | Fuzzer `prompt_leak` category, `ProtectedStringValidator` |
198
+ | LLM08 Vector and embedding weaknesses | Catalog entries only |
199
+ | LLM09 Misinformation | Out of scope |
200
+ | LLM10 Unbounded consumption | Per-session call limits, subprocess timeouts and output caps |
201
+
202
+ The catalog also covers all ten OWASP agentic risks; the [crosswalk](docs/threat-model/frameworks.md) shows which toolkit controls back each one. A mapping means a threat is an instance of, or contributes to, that entry, not that the entry is mitigated. NIST AI RMF and ISO/IEC 42001 are not mapped.
203
+
204
+ ## Honest limitations
205
+
206
+ - **The built-in agents are simulators.** `NaiveAgent` deterministically obeys injections and understands common obfuscations; it exists so the harness, detectors and CI gate can be tested offline. The numbers above show how *these controls behave when the model is compromised*, not how any real model behaves. Run the fuzzer against your real agent for that. `AnthropicTarget` and `HttpTarget` are tested against fakes and a local server, and have not been run against a live endpoint by this repo's CI. `OllamaTarget` has been run against a real local model once, by hand ([docs/PROOF.md](docs/PROOF.md)); CI does not run a model.
207
+ - **Canary-echo success is a proxy.** It measures whether the model followed an injected instruction, which is the necessary first step of every real attack, but not the impact of a real one.
208
+ - **The corpus is a starting set** of well-known technique families, not an exhaustive or adaptive attacker. Extend it with your own payloads (`FuzzConfig(corpus=...)`).
209
+ - **The scanner is a tripwire.** Its documented bypasses are asserted in the tests on purpose.
210
+ - **Output validators are pattern-based** and will not catch data a model re-encodes.
211
+ - **No OS-level isolation.** Use containers/microVMs for code execution, and IMDSv2 with hop limit 1 for cloud-hosted agents.
212
+
213
+ ## Development
214
+
215
+ ```bash
216
+ pip install -e ".[dev]"
217
+ ruff check src tests && ruff format --check src tests
218
+ mypy src
219
+ pytest --cov=agentsec
220
+ ```
221
+
222
+ CI runs lint, type-check and tests on Linux (Python 3.11 to 3.13) and Windows and macOS (3.11, 3.12), builds and checks the package, validates the example policy, re-generates the example threat model to confirm it is current, and runs the fuzzer as a regression check that the containment layers still hold at 0% for tool misuse, exfiltration and prompt leak. See [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md).
223
+
224
+ ## Project health
225
+
226
+ The [OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/DustyStudy/ai-agent-security-toolkit) badge reflects a young, solo-maintained project. Code-Review and Contributors need other people, and Maintained scores 0 for any repository under 90 days old. The checks about the code and supply chain (pinned dependencies, fuzzing, signed releases, token permissions, SAST, vulnerabilities) are the informative ones. `codeql-action` is left on its major tag deliberately, because GitHub gates some features on the tag.
227
+
228
+ ## License
229
+
230
+ MIT
@@ -0,0 +1,93 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ai-agent-security-toolkit"
7
+ version = "0.4.1"
8
+ description = "Security toolkit for LLM agents: prompt-injection fuzzer, tool-call allowlist/sandbox, output validation + tamper-evident audit logging, and a STRIDE-for-agents threat-modeling template."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Dusty" }]
13
+ keywords = ["llm", "agents", "prompt-injection", "security", "red-team", "sandbox", "audit", "threat-modeling"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "Intended Audience :: Information Technology",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: MacOS",
20
+ "Operating System :: POSIX :: Linux",
21
+ "Operating System :: Microsoft :: Windows",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Topic :: Security",
26
+ "Typing :: Typed",
27
+ ]
28
+ dependencies = [
29
+ "PyYAML>=6.0",
30
+ "jsonschema>=4.20",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ anthropic = ["anthropic>=0.40"]
35
+ mcp = ["mcp>=2"]
36
+ langchain = ["langchain-core>=0.3"]
37
+ dev = [
38
+ "pytest>=8.0",
39
+ "pytest-cov>=5.0",
40
+ "ruff>=0.6",
41
+ "mypy>=1.10",
42
+ "types-PyYAML>=6.0",
43
+ "types-jsonschema>=4.20",
44
+ ]
45
+
46
+ [project.urls]
47
+ Homepage = "https://github.com/DustyStudy/ai-agent-security-toolkit"
48
+ Issues = "https://github.com/DustyStudy/ai-agent-security-toolkit/issues"
49
+ Changelog = "https://github.com/DustyStudy/ai-agent-security-toolkit/blob/main/CHANGELOG.md"
50
+ Security = "https://github.com/DustyStudy/ai-agent-security-toolkit/security/policy"
51
+
52
+ [project.scripts]
53
+ agentsec = "agentsec.cli:main"
54
+
55
+ [tool.setuptools.packages.find]
56
+ where = ["src"]
57
+
58
+ [tool.setuptools.package-data]
59
+ agentsec = ["py.typed", "threatmodel/data/*.yaml"]
60
+
61
+ [tool.pytest.ini_options]
62
+ testpaths = ["tests"]
63
+ pythonpath = ["."]
64
+
65
+ [tool.coverage.run]
66
+ source = ["agentsec"]
67
+
68
+ [tool.ruff]
69
+ line-length = 100
70
+ target-version = "py311"
71
+
72
+ [tool.ruff.lint]
73
+ select = ["E", "F", "I", "UP", "B", "S"]
74
+ ignore = ["E501"]
75
+
76
+ [tool.ruff.lint.per-file-ignores]
77
+ "tests/*" = ["S"]
78
+ # Fuzz targets signal a broken invariant with `assert`.
79
+ "fuzz/*" = ["S101"]
80
+ # Deliberately-vulnerable simulator + attack corpus: fake credentials and
81
+ # canary strings are the point of these files.
82
+ "src/agentsec/fuzzer/mock_agents.py" = ["S105", "S106"]
83
+
84
+ [tool.mypy]
85
+ python_version = "3.11"
86
+ packages = ["agentsec"]
87
+ mypy_path = "src"
88
+ explicit_package_bases = true
89
+ ignore_missing_imports = true
90
+ warn_unused_ignores = true
91
+ warn_redundant_casts = true
92
+ check_untyped_defs = true
93
+ disallow_untyped_defs = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """ai-agent-security-toolkit: fuzz, contain, validate and audit LLM agents."""
2
+
3
+ __version__ = "0.4.1"