aiscan-security 0.0.2__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.
- aiscan_security-0.0.2/PKG-INFO +748 -0
- aiscan_security-0.0.2/README.md +717 -0
- aiscan_security-0.0.2/aiscan/__init__.py +0 -0
- aiscan_security-0.0.2/aiscan/api/__init__.py +0 -0
- aiscan_security-0.0.2/aiscan/api/routes.py +177 -0
- aiscan_security-0.0.2/aiscan/cli.py +387 -0
- aiscan_security-0.0.2/aiscan/config.py +23 -0
- aiscan_security-0.0.2/aiscan/models.py +54 -0
- aiscan_security-0.0.2/aiscan/reporters/__init__.py +0 -0
- aiscan_security-0.0.2/aiscan/reporters/console_reporter.py +49 -0
- aiscan_security-0.0.2/aiscan/reporters/json_reporter.py +40 -0
- aiscan_security-0.0.2/aiscan/reporters/pdf_reporter.py +288 -0
- aiscan_security-0.0.2/aiscan/rules/__init__.py +0 -0
- aiscan_security-0.0.2/aiscan/rules/registry.py +427 -0
- aiscan_security-0.0.2/aiscan/scanners/__init__.py +0 -0
- aiscan_security-0.0.2/aiscan/scanners/agent_scanner.py +7 -0
- aiscan_security-0.0.2/aiscan/scanners/api_scanner.py +13 -0
- aiscan_security-0.0.2/aiscan/scanners/base_scanner.py +78 -0
- aiscan_security-0.0.2/aiscan/scanners/integration_scanner.py +7 -0
- aiscan_security-0.0.2/aiscan/scanners/mcp_scanner.py +7 -0
- aiscan_security-0.0.2/aiscan/scanners/prompt_scanner.py +7 -0
- aiscan_security-0.0.2/aiscan/scanners/tm_coding_standards_scanner.py +51 -0
- aiscan_security-0.0.2/aiscan/sdk/__init__.py +13 -0
- aiscan_security-0.0.2/aiscan/sdk/decorators.py +178 -0
- aiscan_security-0.0.2/aiscan/sdk/http_client.py +125 -0
- aiscan_security-0.0.2/aiscan/sdk/middleware.py +207 -0
- aiscan_security-0.0.2/aiscan/sdk/scanner.py +236 -0
- aiscan_security-0.0.2/aiscan/tm_coding_standards/__init__.py +0 -0
- aiscan_security-0.0.2/aiscan/tm_coding_standards/models.py +62 -0
- aiscan_security-0.0.2/aiscan/tm_coding_standards/registry.py +11 -0
- aiscan_security-0.0.2/aiscan/utils/__init__.py +0 -0
- aiscan_security-0.0.2/aiscan/utils/aggregator.py +16 -0
- aiscan_security-0.0.2/aiscan/utils/ai_checker.py +163 -0
- aiscan_security-0.0.2/aiscan_security.egg-info/PKG-INFO +748 -0
- aiscan_security-0.0.2/aiscan_security.egg-info/SOURCES.txt +40 -0
- aiscan_security-0.0.2/aiscan_security.egg-info/dependency_links.txt +1 -0
- aiscan_security-0.0.2/aiscan_security.egg-info/entry_points.txt +2 -0
- aiscan_security-0.0.2/aiscan_security.egg-info/requires.txt +26 -0
- aiscan_security-0.0.2/aiscan_security.egg-info/top_level.txt +1 -0
- aiscan_security-0.0.2/pyproject.toml +45 -0
- aiscan_security-0.0.2/setup.cfg +4 -0
- aiscan_security-0.0.2/setup.py +30 -0
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aiscan-security
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: AI Security Scanner — scan prompts, agents, APIs, MCP tools and integrations
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: typer>=0.12.0
|
|
8
|
+
Requires-Dist: rich>=13.7.0
|
|
9
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
10
|
+
Requires-Dist: pydantic>=2.7.0
|
|
11
|
+
Requires-Dist: pydantic-settings>=2.2.0
|
|
12
|
+
Requires-Dist: anthropic>=0.28.0
|
|
13
|
+
Requires-Dist: openai>=1.30.0
|
|
14
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
15
|
+
Requires-Dist: structlog>=24.1.0
|
|
16
|
+
Requires-Dist: twine>=6.2.0
|
|
17
|
+
Requires-Dist: uvicorn>=0.49.0
|
|
18
|
+
Requires-Dist: fastapi>=0.136.3
|
|
19
|
+
Requires-Dist: python-multipart>=0.0.32
|
|
20
|
+
Provides-Extra: api
|
|
21
|
+
Requires-Dist: fastapi>=0.111.0; extra == "api"
|
|
22
|
+
Requires-Dist: uvicorn[standard]>=0.29.0; extra == "api"
|
|
23
|
+
Provides-Extra: vertex
|
|
24
|
+
Requires-Dist: google-cloud-aiplatform>=1.60.0; extra == "vertex"
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
28
|
+
Requires-Dist: httpx>=0.27.0; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
30
|
+
Dynamic: requires-python
|
|
31
|
+
|
|
32
|
+
# aiscan — AI Security Scanner
|
|
33
|
+
|
|
34
|
+
Scans **prompts**, **agents**, **APIs**, **MCP tools**, **integrations**, and **Python code** for security vulnerabilities — prompt injection, data leakage, tool abuse, security risks, and TMotions Python coding standards violations.
|
|
35
|
+
|
|
36
|
+
Built-in rules + AI deep scan (Claude / GPT-4o / Gemini). Runs as a CLI, REST API, Python SDK, or middleware.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Table of contents
|
|
41
|
+
|
|
42
|
+
- [Why aiscan](#why-aiscan)
|
|
43
|
+
- [Installation](#installation)
|
|
44
|
+
- [Quick start](#quick-start)
|
|
45
|
+
- [CLI commands](#cli-commands)
|
|
46
|
+
- [Scan types](#scan-types)
|
|
47
|
+
- [Rules reference](#rules-reference)
|
|
48
|
+
- [TMotions Python Coding Standards](#tmotions-python-coding-standards)
|
|
49
|
+
- [PDF export](#pdf-export)
|
|
50
|
+
- [REST API](#rest-api)
|
|
51
|
+
- [Python SDK](#python-sdk)
|
|
52
|
+
- [Middleware](#middleware)
|
|
53
|
+
- [Decorators](#decorators)
|
|
54
|
+
- [HTTP client (other languages)](#http-client-other-languages)
|
|
55
|
+
- [CI/CD integration](#cicd-integration)
|
|
56
|
+
- [AI provider configuration](#ai-provider-configuration)
|
|
57
|
+
- [Suppressing false positives](#suppressing-false-positives)
|
|
58
|
+
- [Sample files](#sample-files)
|
|
59
|
+
- [Troubleshooting](#troubleshooting)
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Why aiscan
|
|
64
|
+
|
|
65
|
+
Traditional security tools (SonarQube, Snyk, Semgrep) were built before AI systems existed. They have no rules for prompt injection, agent tool permissions, or MCP tool definitions. aiscan fills that gap — it treats prompts, agent configs, and AI integrations as first-class security artifacts. It also enforces TMotions internal Python coding standards on top of security scanning.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Installation
|
|
70
|
+
|
|
71
|
+
### From source
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
git clone <your-repo-url>
|
|
75
|
+
cd ai-security-scanner
|
|
76
|
+
pip install -r requirements.txt
|
|
77
|
+
pip install -e .
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Verify install
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
aiscan version
|
|
84
|
+
# aiscan 0.0.2
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
If `aiscan` command is not found, use `python -m aiscan.cli` instead everywhere below.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Quick start
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# 1. Generate config
|
|
95
|
+
aiscan init
|
|
96
|
+
|
|
97
|
+
# 2. Add your API key to .env
|
|
98
|
+
# ANTHROPIC_API_KEY=sk-ant-...
|
|
99
|
+
|
|
100
|
+
# 3. Scan files
|
|
101
|
+
aiscan scan my_prompt.txt
|
|
102
|
+
aiscan scan --type api openapi.yaml
|
|
103
|
+
aiscan scan --type mcp tool_config.json
|
|
104
|
+
aiscan scan --type code my_script.py # TMotions coding standards check
|
|
105
|
+
|
|
106
|
+
# 4. Scan without AI (static rules only, no API key needed)
|
|
107
|
+
aiscan scan --no-ai openapi.yaml
|
|
108
|
+
|
|
109
|
+
# 5. Export results to PDF
|
|
110
|
+
aiscan scan --type api openapi.yaml --output pdf --out report.pdf
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## CLI commands
|
|
116
|
+
|
|
117
|
+
### `aiscan scan`
|
|
118
|
+
|
|
119
|
+
Analyze one or more files for AI security threats, code quality issues, and compliance with TMotions Python coding standards.
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
Usage: aiscan scan [OPTIONS] FILES...
|
|
123
|
+
|
|
124
|
+
Options:
|
|
125
|
+
-t, --type TEXT Scan type: prompt | agent | api | mcp | integration | code | all | auto [default: auto]
|
|
126
|
+
-o, --output TEXT Output format: console | json | sarif | pdf [default: console]
|
|
127
|
+
-O, --out PATH Write output to file instead of stdout
|
|
128
|
+
-f, --fail-on TEXT Exit 1 if severity reached: critical | high | medium | none [default: high]
|
|
129
|
+
--no-ai Skip AI deep scan — static rules only
|
|
130
|
+
-q, --quiet Suppress output except errors
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Examples:**
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Auto-detect scan type from extension
|
|
137
|
+
aiscan scan system_prompt.txt
|
|
138
|
+
aiscan scan agent_config.yaml
|
|
139
|
+
|
|
140
|
+
# Explicit scan type
|
|
141
|
+
aiscan scan --type prompt system_prompt.txt
|
|
142
|
+
aiscan scan --type agent agent.yaml
|
|
143
|
+
aiscan scan --type api openapi.yaml
|
|
144
|
+
aiscan scan --type mcp tool.json
|
|
145
|
+
aiscan scan --type integration webhook_config.yaml
|
|
146
|
+
aiscan scan --type code my_script.py
|
|
147
|
+
|
|
148
|
+
# Python files auto-run BOTH prompt scanner AND coding standards scanner
|
|
149
|
+
aiscan scan app.py # runs prompt + code automatically
|
|
150
|
+
|
|
151
|
+
# Scan a whole directory
|
|
152
|
+
aiscan scan --type auto ./my-project/
|
|
153
|
+
|
|
154
|
+
# Run all scanners on the same file
|
|
155
|
+
aiscan scan --type all config.yaml
|
|
156
|
+
|
|
157
|
+
# Output formats
|
|
158
|
+
aiscan scan --type api openapi.yaml --output json
|
|
159
|
+
aiscan scan --type api openapi.yaml --output pdf --out report.pdf
|
|
160
|
+
aiscan scan --type prompt prompts/ --output sarif --out results.sarif
|
|
161
|
+
|
|
162
|
+
# CI mode — exit 1 if any high+ finding
|
|
163
|
+
aiscan scan --quiet --fail-on high prompts/ && echo "passed"
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
> **Note:** `.py` files automatically run both the prompt scanner (for injection/leakage patterns) and the TMotions coding standards scanner.
|
|
167
|
+
|
|
168
|
+
### `aiscan rules`
|
|
169
|
+
|
|
170
|
+
List all security and coding standards rules.
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
aiscan rules # list all rules
|
|
174
|
+
aiscan rules prompt # filter by scan type
|
|
175
|
+
aiscan rules api --severity critical # filter by severity
|
|
176
|
+
aiscan rules code # show TMotions Python coding standards rules (PY-*)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### `aiscan init`
|
|
180
|
+
|
|
181
|
+
Create a `.env` config file in the current directory.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
aiscan init # creates .env for Claude (default)
|
|
185
|
+
aiscan init --provider openai # pre-configure for OpenAI
|
|
186
|
+
aiscan init --provider vertex # pre-configure for Vertex AI
|
|
187
|
+
aiscan init --force # overwrite existing .env
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### `aiscan serve`
|
|
191
|
+
|
|
192
|
+
Start the REST API server.
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
aiscan serve # http://localhost:8000
|
|
196
|
+
aiscan serve --port 9000 # custom port
|
|
197
|
+
aiscan serve --reload # dev mode, auto-restart on code changes
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### `aiscan version`
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
aiscan version
|
|
204
|
+
# aiscan 0.0.2
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Scan types
|
|
210
|
+
|
|
211
|
+
| Type | Detects | File extensions (auto-detect) |
|
|
212
|
+
|---|---|---|
|
|
213
|
+
| `prompt` | Prompt injection, PII leakage, jailbreaks, code security issues | `.txt` `.md` `.py` `.js` `.ts` `.jsx` `.tsx` `.go` `.java` `.rb` `.php` `.cs` `.cpp` `.c` `.rs` `.swift` `.kt` |
|
|
214
|
+
| `agent` | Wildcard tools, missing human approval, infinite loops, data persistence | `.yaml` `.yml` |
|
|
215
|
+
| `api` | Hardcoded keys, wildcard CORS, insecure transport, missing auth | `.yaml` `.yml` `.json` |
|
|
216
|
+
| `mcp` | Wildcard permissions, code execution, missing rate limits, no auth | `.json` |
|
|
217
|
+
| `integration` | Default webhook secrets, SSL disabled, raw input forwarding, silent errors | `.env` `.toml` `.ini` `.cfg` `.conf` `.properties` |
|
|
218
|
+
| `code` | TMotions Python coding standards (PY-* rules) | `.py` only |
|
|
219
|
+
|
|
220
|
+
> **Auto-detect behaviour for `.py` files:** aiscan runs both `prompt` and `code` scanners simultaneously.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Rules reference
|
|
225
|
+
|
|
226
|
+
Run `aiscan rules` to see the full live list.
|
|
227
|
+
|
|
228
|
+
### AI security rules
|
|
229
|
+
|
|
230
|
+
| Category | Rule IDs | Example |
|
|
231
|
+
|---|---|---|
|
|
232
|
+
| Prompt | `PI-001` `PI-002` `DL-001` `DL-002` `EX-001` | `ignore previous instructions` |
|
|
233
|
+
| Agent | `AG-001`–`AG-006` | `allow_all_tools: true` |
|
|
234
|
+
| API | `AK-001` `AK-002` `CO-001` `HT-001` `JW-001` `AU-001` | hardcoded `api_key` |
|
|
235
|
+
| MCP | `MC-001`–`MC-006` | `execute_code: true` |
|
|
236
|
+
| Integration | `IN-001`–`IN-006` | `webhook_secret: changeme` |
|
|
237
|
+
| Code (security) | `CD-001`–`CD-012` | `eval()`, f-string SQL |
|
|
238
|
+
| AI deep scan | `AI-SCAN` | Subtle issues from Claude/GPT-4o/Gemini |
|
|
239
|
+
|
|
240
|
+
### TMotions Python coding standards rules (PY-*)
|
|
241
|
+
|
|
242
|
+
| Rule ID | Severity | What it checks |
|
|
243
|
+
|---|---|---|
|
|
244
|
+
| `PY-010` | Critical | Hardcoded password in source |
|
|
245
|
+
| `PY-011` | Critical | SQL injection via string concatenation |
|
|
246
|
+
| `PY-012` | Critical | Use of `eval()` |
|
|
247
|
+
| `PY-013` | Critical | Use of `exec()` |
|
|
248
|
+
| `PY-021` | Critical | `subprocess` with `shell=True` |
|
|
249
|
+
| `PY-022` | Critical | Weak hashing — `hashlib.md5` |
|
|
250
|
+
| `PY-014` | High | Bare `except:` clause |
|
|
251
|
+
| `PY-015` | High | Generic `raise Exception()` |
|
|
252
|
+
| `PY-023` | High | Insecure random (`random.randint` etc.) |
|
|
253
|
+
|
|
254
|
+
Run `aiscan rules code` to see all PY-* rules with full details.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## TMotions Python Coding Standards
|
|
259
|
+
|
|
260
|
+
The `code` scan type enforces TMotions internal Python coding standards on `.py` files.
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
# CLI
|
|
264
|
+
aiscan scan --type code my_script.py
|
|
265
|
+
|
|
266
|
+
# Python files auto-run this alongside the security scan
|
|
267
|
+
aiscan scan app.py # runs both prompt + code
|
|
268
|
+
|
|
269
|
+
# REST API
|
|
270
|
+
curl -X POST http://localhost:8000/tm-coding-standards \
|
|
271
|
+
-F "file=@my_script.py"
|
|
272
|
+
|
|
273
|
+
# Via directory scan
|
|
274
|
+
aiscan scan --type auto ./src/
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**REST endpoint** (`POST /tm-coding-standards`) accepts a file upload (not JSON body) and returns:
|
|
278
|
+
|
|
279
|
+
```json
|
|
280
|
+
{
|
|
281
|
+
"target": "my_script.py",
|
|
282
|
+
"total_findings": 2,
|
|
283
|
+
"findings": [
|
|
284
|
+
{
|
|
285
|
+
"rule_id": "PY-012",
|
|
286
|
+
"severity": "critical",
|
|
287
|
+
"title": "Use of eval() detected",
|
|
288
|
+
"detail": "eval(user_input)",
|
|
289
|
+
"file": "my_script.py",
|
|
290
|
+
"line": 15,
|
|
291
|
+
"fix": "Avoid eval(); use safer alternatives."
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Sample file** for testing: `samples/code/tmcs_bad_code.py`
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
aiscan scan --type code samples/code/tmcs_bad_code.py
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## PDF export
|
|
306
|
+
|
|
307
|
+
Export any scan result directly to a PDF report from the CLI.
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# Save to default name (aiscan-report.pdf)
|
|
311
|
+
aiscan scan --type prompt prompts/ --output pdf
|
|
312
|
+
|
|
313
|
+
# Save to custom path
|
|
314
|
+
aiscan scan --type api openapi.yaml --output pdf --out reports/scan.pdf
|
|
315
|
+
|
|
316
|
+
# Scan everything, export PDF
|
|
317
|
+
aiscan scan --no-ai --type auto . --output pdf --out report.pdf
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
The PDF includes:
|
|
321
|
+
- Summary header — overall PASSED/FAILED, file count, critical/high/total counts
|
|
322
|
+
- Per-file findings table — severity icons, rule ID, threat type, title, line number
|
|
323
|
+
- Fix suggestions — detailed remediation for every critical and high finding
|
|
324
|
+
- Page footer with timestamp and page number
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## REST API
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
aiscan serve
|
|
332
|
+
# Swagger UI: http://localhost:8000/docs
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
| Method | Path | Body | Description |
|
|
336
|
+
|---|---|---|---|
|
|
337
|
+
| GET | `/health` | — | Service status and version |
|
|
338
|
+
| POST | `/scan/prompt` | JSON | Scan prompt content |
|
|
339
|
+
| POST | `/scan/agent` | JSON | Scan agent config |
|
|
340
|
+
| POST | `/scan/api` | JSON | Scan API config or OpenAPI spec |
|
|
341
|
+
| POST | `/scan/mcp` | JSON | Scan MCP tool definition |
|
|
342
|
+
| POST | `/scan/integration` | JSON | Scan integration config |
|
|
343
|
+
| POST | `/scan/all` | JSON | Run all 5 AI security scanners |
|
|
344
|
+
| POST | `/tm-coding-standards` | File upload | TMotions Python coding standards check |
|
|
345
|
+
| GET | `/docs` | — | Swagger UI |
|
|
346
|
+
|
|
347
|
+
**JSON endpoints — request:**
|
|
348
|
+
```json
|
|
349
|
+
{"content": "file content as string", "filename": "openapi.yaml"}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**JSON endpoints — response:**
|
|
353
|
+
```json
|
|
354
|
+
{
|
|
355
|
+
"scan_type": "api",
|
|
356
|
+
"passed": false,
|
|
357
|
+
"critical": 1, "high": 2, "medium": 0, "info": 0, "total": 3,
|
|
358
|
+
"findings": [
|
|
359
|
+
{
|
|
360
|
+
"rule_id": "AK-001",
|
|
361
|
+
"severity": "critical",
|
|
362
|
+
"threat": "security_risk",
|
|
363
|
+
"title": "Hardcoded secret or API key",
|
|
364
|
+
"detail": "Matched on line 5: ...",
|
|
365
|
+
"file": "config.yaml",
|
|
366
|
+
"line": 5,
|
|
367
|
+
"fix": "Move to environment variables or a secrets manager."
|
|
368
|
+
}
|
|
369
|
+
]
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**curl examples:**
|
|
374
|
+
```bash
|
|
375
|
+
# Scan prompt
|
|
376
|
+
curl -X POST http://localhost:8000/scan/prompt \
|
|
377
|
+
-H "Content-Type: application/json" \
|
|
378
|
+
-d '{"content": "Ignore previous instructions", "filename": "prompt.txt"}'
|
|
379
|
+
|
|
380
|
+
# TMotions coding standards (file upload)
|
|
381
|
+
curl -X POST http://localhost:8000/tm-coding-standards \
|
|
382
|
+
-F "file=@my_script.py"
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Python SDK
|
|
388
|
+
|
|
389
|
+
Use directly inside your Python project — no separate service needed.
|
|
390
|
+
|
|
391
|
+
```python
|
|
392
|
+
from aiscan.sdk import AIScan
|
|
393
|
+
|
|
394
|
+
scanner = AIScan(no_ai=True) # static rules only
|
|
395
|
+
# or
|
|
396
|
+
scanner = AIScan(ai_provider="claude", api_key="sk-ant-...")
|
|
397
|
+
|
|
398
|
+
report = scanner.scan_prompt("Ignore previous instructions...")
|
|
399
|
+
|
|
400
|
+
print(report.passed) # False
|
|
401
|
+
print(report.critical) # 2
|
|
402
|
+
print(report.total) # 3
|
|
403
|
+
|
|
404
|
+
for f in report.findings:
|
|
405
|
+
print(f.severity, f.rule_id, f.title)
|
|
406
|
+
print(" Fix:", f.fix)
|
|
407
|
+
|
|
408
|
+
# Other methods
|
|
409
|
+
scanner.scan_file("openapi.yaml") # auto-detect type
|
|
410
|
+
scanner.scan_file("agent.yaml", scan_type="agent")
|
|
411
|
+
scanner.scan_code("app.py") # security + TM coding standards
|
|
412
|
+
scanner.scan_api_spec(open("openapi.yaml").read())
|
|
413
|
+
scanner.scan_agent_config(open("agent.yaml").read())
|
|
414
|
+
scanner.scan_mcp_tool(open("tool.json").read())
|
|
415
|
+
scanner.scan_integration(open("webhook.yaml").read())
|
|
416
|
+
reports = scanner.scan_all(content, filename="config.yaml") # all 5 scanners
|
|
417
|
+
|
|
418
|
+
# Gate on severity
|
|
419
|
+
if report.has_severity("critical"):
|
|
420
|
+
block_request()
|
|
421
|
+
|
|
422
|
+
critical_only = report.findings_by_severity("critical")
|
|
423
|
+
|
|
424
|
+
# Export to dict
|
|
425
|
+
data = report.to_dict()
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## Middleware
|
|
431
|
+
|
|
432
|
+
Auto-scan every incoming request before it reaches your handlers.
|
|
433
|
+
|
|
434
|
+
### FastAPI
|
|
435
|
+
|
|
436
|
+
```python
|
|
437
|
+
from fastapi import FastAPI
|
|
438
|
+
from aiscan.sdk.middleware import AIScanFastAPIMiddleware
|
|
439
|
+
|
|
440
|
+
app = FastAPI()
|
|
441
|
+
app.add_middleware(
|
|
442
|
+
AIScanFastAPIMiddleware,
|
|
443
|
+
scan_fields=["prompt", "message", "content"],
|
|
444
|
+
fail_on="high",
|
|
445
|
+
no_ai=True,
|
|
446
|
+
)
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Blocked requests return:
|
|
450
|
+
```json
|
|
451
|
+
{
|
|
452
|
+
"error": "Request blocked by AI Security Scanner",
|
|
453
|
+
"field": "prompt",
|
|
454
|
+
"issues": [{"rule": "PI-001", "severity": "critical", "issue": "...", "fix": "..."}]
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Flask
|
|
459
|
+
|
|
460
|
+
```python
|
|
461
|
+
from flask import Flask
|
|
462
|
+
from aiscan.sdk.middleware import AIScanFlaskMiddleware
|
|
463
|
+
|
|
464
|
+
app = Flask(__name__)
|
|
465
|
+
AIScanFlaskMiddleware(app, fields=["prompt", "message"], fail_on="high")
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Django
|
|
469
|
+
|
|
470
|
+
```python
|
|
471
|
+
# settings.py
|
|
472
|
+
MIDDLEWARE = [
|
|
473
|
+
"aiscan.sdk.middleware.AIScanDjangoMiddleware",
|
|
474
|
+
# ...
|
|
475
|
+
]
|
|
476
|
+
AISCAN_FIELDS = ["prompt", "message"]
|
|
477
|
+
AISCAN_FAIL_ON = "high"
|
|
478
|
+
AISCAN_NO_AI = False
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## Decorators
|
|
484
|
+
|
|
485
|
+
Wrap any existing function — no refactoring needed.
|
|
486
|
+
|
|
487
|
+
```python
|
|
488
|
+
from aiscan.sdk import scan_prompt_input, scan_before_llm, scan_file_input
|
|
489
|
+
|
|
490
|
+
# Scan a specific argument before the function runs
|
|
491
|
+
@scan_prompt_input(field="user_message", fail_on="high")
|
|
492
|
+
def call_llm(user_message: str) -> str:
|
|
493
|
+
... # raises ValueError if unsafe
|
|
494
|
+
|
|
495
|
+
# Scan all prompt-related arguments
|
|
496
|
+
@scan_before_llm(fields=["system_prompt", "user_message"])
|
|
497
|
+
def chat(system_prompt: str, user_message: str) -> str:
|
|
498
|
+
...
|
|
499
|
+
|
|
500
|
+
# Scan a file path before loading it
|
|
501
|
+
@scan_file_input(field="config_path", fail_on="critical")
|
|
502
|
+
def load_agent_config(config_path: str) -> dict:
|
|
503
|
+
...
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
## HTTP client (other languages)
|
|
509
|
+
|
|
510
|
+
Run `aiscan serve` as a separate service, call it from any language.
|
|
511
|
+
|
|
512
|
+
**Python:**
|
|
513
|
+
```python
|
|
514
|
+
from aiscan.sdk import AIScanClient
|
|
515
|
+
|
|
516
|
+
client = AIScanClient("http://localhost:8000")
|
|
517
|
+
result = client.scan_prompt(user_input)
|
|
518
|
+
is_safe = client.is_safe(user_input, fail_on="high")
|
|
519
|
+
result = client.scan_file("openapi.yaml")
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
**Node.js:**
|
|
523
|
+
```javascript
|
|
524
|
+
const res = await fetch("http://localhost:8000/scan/prompt", {
|
|
525
|
+
method: "POST",
|
|
526
|
+
headers: { "Content-Type": "application/json" },
|
|
527
|
+
body: JSON.stringify({ content: userInput, filename: "prompt.txt" }),
|
|
528
|
+
});
|
|
529
|
+
const { passed, findings } = await res.json();
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
**Go:**
|
|
533
|
+
```go
|
|
534
|
+
resp, _ := http.Post("http://localhost:8000/scan/prompt",
|
|
535
|
+
"application/json", bytes.NewBuffer(body))
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
**TMotions coding standards from any language:**
|
|
539
|
+
```bash
|
|
540
|
+
# multipart file upload — works from any HTTP client
|
|
541
|
+
curl -X POST http://localhost:8000/tm-coding-standards -F "file=@script.py"
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
## CI/CD integration
|
|
547
|
+
|
|
548
|
+
### GitHub Actions
|
|
549
|
+
|
|
550
|
+
```yaml
|
|
551
|
+
name: AI Security Scan
|
|
552
|
+
on: [push, pull_request]
|
|
553
|
+
|
|
554
|
+
jobs:
|
|
555
|
+
scan:
|
|
556
|
+
runs-on: ubuntu-latest
|
|
557
|
+
steps:
|
|
558
|
+
- uses: actions/checkout@v4
|
|
559
|
+
- uses: actions/setup-python@v5
|
|
560
|
+
with: { python-version: "3.12" }
|
|
561
|
+
- run: pip install -e .
|
|
562
|
+
- name: Scan prompts and configs
|
|
563
|
+
run: aiscan scan --type prompt prompts/ --fail-on high
|
|
564
|
+
env:
|
|
565
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
566
|
+
- name: Scan Python code (TMotions standards)
|
|
567
|
+
run: aiscan scan --type code src/ --fail-on high
|
|
568
|
+
- name: Export SARIF
|
|
569
|
+
run: aiscan scan --output sarif --out results.sarif .
|
|
570
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
571
|
+
if: always()
|
|
572
|
+
with: { sarif_file: results.sarif }
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Pre-commit hook
|
|
576
|
+
|
|
577
|
+
```yaml
|
|
578
|
+
# .pre-commit-config.yaml
|
|
579
|
+
repos:
|
|
580
|
+
- repo: local
|
|
581
|
+
hooks:
|
|
582
|
+
- id: aiscan
|
|
583
|
+
name: AI Security Scanner
|
|
584
|
+
entry: aiscan scan --no-ai --fail-on critical
|
|
585
|
+
language: system
|
|
586
|
+
files: \.(txt|yaml|yml|json|py|js|ts)$
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### Makefile
|
|
590
|
+
|
|
591
|
+
```bash
|
|
592
|
+
make install # pip install -e ".[dev]"
|
|
593
|
+
make test # run pytest
|
|
594
|
+
make lint # ruff check
|
|
595
|
+
make scan-samples # scan all bad sample files
|
|
596
|
+
make rules # list all rules
|
|
597
|
+
make version # print version
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### Exit codes
|
|
601
|
+
|
|
602
|
+
| Code | Meaning |
|
|
603
|
+
|---|---|
|
|
604
|
+
| `0` | Passed — no findings at or above `--fail-on` threshold |
|
|
605
|
+
| `1` | Failed — findings found at or above threshold |
|
|
606
|
+
| `2` | Tool error — bad arguments, missing file |
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## AI provider configuration
|
|
611
|
+
|
|
612
|
+
Run `aiscan init` to create `.env`, then edit it:
|
|
613
|
+
|
|
614
|
+
```bash
|
|
615
|
+
# Claude (default)
|
|
616
|
+
AI_PROVIDER=claude
|
|
617
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
618
|
+
SCANNER_MODEL=claude-sonnet-4-20250514
|
|
619
|
+
|
|
620
|
+
# OpenAI
|
|
621
|
+
AI_PROVIDER=openai
|
|
622
|
+
OPENAI_API_KEY=sk-...
|
|
623
|
+
OPENAI_MODEL=gpt-4o
|
|
624
|
+
|
|
625
|
+
# Vertex AI (Gemini)
|
|
626
|
+
AI_PROVIDER=vertex
|
|
627
|
+
VERTEX_PROJECT=your-gcp-project-id
|
|
628
|
+
VERTEX_LOCATION=us-central1
|
|
629
|
+
VERTEX_MODEL=gemini-2.5-flash-001
|
|
630
|
+
|
|
631
|
+
# Shared
|
|
632
|
+
MAX_TOKENS=2000
|
|
633
|
+
LOG_LEVEL=INFO
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
If the primary provider fails (rate limit, auth error), aiscan automatically retries with the next available one. Use `--no-ai` to skip AI scanning entirely — static rules and TMotions coding standards still run.
|
|
637
|
+
|
|
638
|
+
---
|
|
639
|
+
|
|
640
|
+
## Suppressing false positives
|
|
641
|
+
|
|
642
|
+
Add `# noscan` (Python/YAML) or `// noscan` (JS/TS) to the end of any line to skip it:
|
|
643
|
+
|
|
644
|
+
```python
|
|
645
|
+
result = scanner.scan_prompt("test example string") # noscan
|
|
646
|
+
except Exception: # noscan — intentional catch-all in middleware
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
---
|
|
650
|
+
|
|
651
|
+
## Sample files
|
|
652
|
+
|
|
653
|
+
```
|
|
654
|
+
samples/
|
|
655
|
+
├── prompts/
|
|
656
|
+
│ ├── bad_system_prompt.txt — jailbreak + PII (9 findings)
|
|
657
|
+
│ ├── bad_user_template.txt — chat tokens + exfil URL (6 findings)
|
|
658
|
+
│ ├── bad_agent_instructions.txt — role override (4 findings)
|
|
659
|
+
│ ├── clean_customer_support.txt — safe (0 findings)
|
|
660
|
+
│ └── clean_coding_assistant.txt — safe (0 findings)
|
|
661
|
+
├── agents/
|
|
662
|
+
│ ├── bad_research_agent.yaml — wildcard tools + no HITL (8 findings)
|
|
663
|
+
│ ├── bad_finance_agent.yaml — auto-approves transactions (6 findings)
|
|
664
|
+
│ ├── clean_support_agent.yaml — safe (0 findings)
|
|
665
|
+
│ └── clean_data_agent.yaml — safe (0 findings)
|
|
666
|
+
├── apis/
|
|
667
|
+
│ ├── bad_config.yaml — 5 hardcoded keys + wildcard CORS (10 findings)
|
|
668
|
+
│ ├── bad_openapi.yaml — unauthenticated endpoints (3 findings)
|
|
669
|
+
│ ├── clean_config.yaml — safe (0 findings)
|
|
670
|
+
│ └── clean_openapi.yaml — safe (0 findings)
|
|
671
|
+
├── mcp/
|
|
672
|
+
│ ├── bad_code_executor.json — shell access + wildcard perms (10+ findings)
|
|
673
|
+
│ ├── bad_database_tool.json — no auth + raw SQL (5 findings)
|
|
674
|
+
│ ├── bad_web_search.json — injection in description (4 findings)
|
|
675
|
+
│ ├── clean_product_search.json — safe (0 findings)
|
|
676
|
+
│ └── clean_calendar_tool.json — safe (0 findings)
|
|
677
|
+
├── integrations/
|
|
678
|
+
│ ├── bad_slack_integration.yaml — default secret + SSL off (4 findings)
|
|
679
|
+
│ ├── bad_email_integration.yaml — multiple issues (5 findings)
|
|
680
|
+
│ └── clean_slack_integration.yaml — safe (0 findings)
|
|
681
|
+
└── code/
|
|
682
|
+
├── bad_app.py — hardcoded keys + SQL injection + eval (17 findings)
|
|
683
|
+
├── bad_app.js — hardcoded keys + console.log (4 findings)
|
|
684
|
+
├── bad_api.ts — SSL disabled + credentials (3 findings)
|
|
685
|
+
├── bad_settings.py — multiple hardcoded secrets (6 findings)
|
|
686
|
+
├── tmcs_bad_code.py — TMotions coding standards violations
|
|
687
|
+
└── clean_app.py — safe (0 findings)
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
```bash
|
|
691
|
+
# Test all bad samples
|
|
692
|
+
aiscan scan --no-ai samples/prompts/bad_system_prompt.txt
|
|
693
|
+
aiscan scan --no-ai samples/agents/bad_research_agent.yaml
|
|
694
|
+
aiscan scan --no-ai samples/apis/bad_config.yaml
|
|
695
|
+
aiscan scan --no-ai samples/mcp/bad_code_executor.json
|
|
696
|
+
aiscan scan --no-ai samples/integrations/bad_slack_integration.yaml
|
|
697
|
+
aiscan scan --no-ai samples/code/bad_app.py
|
|
698
|
+
aiscan scan --type code samples/code/tmcs_bad_code.py
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
---
|
|
702
|
+
|
|
703
|
+
## Troubleshooting
|
|
704
|
+
|
|
705
|
+
**`aiscan: command not found`**
|
|
706
|
+
```bash
|
|
707
|
+
python -m aiscan.cli --help
|
|
708
|
+
pip install -e .
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
**`ImportError: cannot import name 'scan_tm_code'`**
|
|
712
|
+
|
|
713
|
+
The function in `tm_coding_standards_scanner.py` was named differently. Fix by adding an alias at the bottom of the file:
|
|
714
|
+
```python
|
|
715
|
+
scan_tm_code = tm_coding_standards_scanner
|
|
716
|
+
__all__ = ["scan_tm_code", "tm_coding_standards_scanner"]
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
**AI scan not running**
|
|
720
|
+
```bash
|
|
721
|
+
aiscan scan --no-ai openapi.yaml # confirms static rules work
|
|
722
|
+
# check .env has a valid API key and AI_PROVIDER is set
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
**False positives**
|
|
726
|
+
```bash
|
|
727
|
+
aiscan scan --no-ai file.txt # isolate static rule hits
|
|
728
|
+
aiscan rules # see which pattern triggered
|
|
729
|
+
# add # noscan to suppress a specific line
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
**`BackendUnavailable: Cannot import setuptools.backends.legacy`**
|
|
733
|
+
```bash
|
|
734
|
+
pip install --upgrade setuptools wheel
|
|
735
|
+
pip install -e .
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
---
|
|
739
|
+
|
|
740
|
+
## Version
|
|
741
|
+
|
|
742
|
+
`0.0.2`
|
|
743
|
+
|
|
744
|
+
---
|
|
745
|
+
|
|
746
|
+
## License
|
|
747
|
+
|
|
748
|
+
MIT
|