ai-security-scanner 1.0.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.
- ai_security_scanner-1.0.0/PKG-INFO +525 -0
- ai_security_scanner-1.0.0/README.md +494 -0
- ai_security_scanner-1.0.0/ai_security_scanner.egg-info/PKG-INFO +525 -0
- ai_security_scanner-1.0.0/ai_security_scanner.egg-info/SOURCES.txt +39 -0
- ai_security_scanner-1.0.0/ai_security_scanner.egg-info/dependency_links.txt +1 -0
- ai_security_scanner-1.0.0/ai_security_scanner.egg-info/entry_points.txt +2 -0
- ai_security_scanner-1.0.0/ai_security_scanner.egg-info/requires.txt +26 -0
- ai_security_scanner-1.0.0/ai_security_scanner.egg-info/top_level.txt +1 -0
- ai_security_scanner-1.0.0/aiscan/__init__.py +0 -0
- ai_security_scanner-1.0.0/aiscan/api/__init__.py +0 -0
- ai_security_scanner-1.0.0/aiscan/api/routes.py +180 -0
- ai_security_scanner-1.0.0/aiscan/cli.py +382 -0
- ai_security_scanner-1.0.0/aiscan/config.py +23 -0
- ai_security_scanner-1.0.0/aiscan/models.py +54 -0
- ai_security_scanner-1.0.0/aiscan/reporters/__init__.py +0 -0
- ai_security_scanner-1.0.0/aiscan/reporters/console_reporter.py +49 -0
- ai_security_scanner-1.0.0/aiscan/reporters/json_reporter.py +40 -0
- ai_security_scanner-1.0.0/aiscan/rules/__init__.py +0 -0
- ai_security_scanner-1.0.0/aiscan/rules/registry.py +429 -0
- ai_security_scanner-1.0.0/aiscan/scanners/__init__.py +0 -0
- ai_security_scanner-1.0.0/aiscan/scanners/agent_scanner.py +10 -0
- ai_security_scanner-1.0.0/aiscan/scanners/api_scanner.py +12 -0
- ai_security_scanner-1.0.0/aiscan/scanners/base_scanner.py +78 -0
- ai_security_scanner-1.0.0/aiscan/scanners/integration_scanner.py +7 -0
- ai_security_scanner-1.0.0/aiscan/scanners/mcp_scanner.py +7 -0
- ai_security_scanner-1.0.0/aiscan/scanners/prompt_scanner.py +7 -0
- ai_security_scanner-1.0.0/aiscan/scanners/tm_coding_standards_scanner.py +56 -0
- ai_security_scanner-1.0.0/aiscan/sdk/__init__.py +13 -0
- ai_security_scanner-1.0.0/aiscan/sdk/decorators.py +178 -0
- ai_security_scanner-1.0.0/aiscan/sdk/http_client.py +125 -0
- ai_security_scanner-1.0.0/aiscan/sdk/middleware.py +207 -0
- ai_security_scanner-1.0.0/aiscan/sdk/scanner.py +236 -0
- ai_security_scanner-1.0.0/aiscan/tm_coding_standards/__init__.py +0 -0
- ai_security_scanner-1.0.0/aiscan/tm_coding_standards/models.py +62 -0
- ai_security_scanner-1.0.0/aiscan/tm_coding_standards/registry.py +11 -0
- ai_security_scanner-1.0.0/aiscan/utils/__init__.py +0 -0
- ai_security_scanner-1.0.0/aiscan/utils/aggregator.py +16 -0
- ai_security_scanner-1.0.0/aiscan/utils/ai_checker.py +163 -0
- ai_security_scanner-1.0.0/pyproject.toml +45 -0
- ai_security_scanner-1.0.0/setup.cfg +4 -0
- ai_security_scanner-1.0.0/setup.py +30 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ai-security-scanner
|
|
3
|
+
Version: 1.0.0
|
|
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**, and **integrations** for security vulnerabilities — prompt injection, data leakage, tool abuse, and security risks.
|
|
35
|
+
|
|
36
|
+
40 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
|
+
- [REST API](#rest-api)
|
|
49
|
+
- [Python SDK](#python-sdk)
|
|
50
|
+
- [Middleware](#middleware)
|
|
51
|
+
- [Decorators](#decorators)
|
|
52
|
+
- [HTTP client (other languages)](#http-client-other-languages)
|
|
53
|
+
- [CI/CD integration](#cicd-integration)
|
|
54
|
+
- [AI provider configuration](#ai-provider-configuration)
|
|
55
|
+
- [Suppressing false positives](#suppressing-false-positives)
|
|
56
|
+
- [Sample files](#sample-files)
|
|
57
|
+
- [Troubleshooting](#troubleshooting)
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Why aiscan
|
|
62
|
+
|
|
63
|
+
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.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
### From source
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git clone <your-repo-url>
|
|
73
|
+
cd aiscan-cli
|
|
74
|
+
pip install -r requirements-all.txt
|
|
75
|
+
pip install -e .
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Verify install
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
aiscan version
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
If `aiscan` command is not found, use `python -m aiscan.cli` instead everywhere below.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Quick start
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# 1. Generate config
|
|
92
|
+
aiscan init
|
|
93
|
+
|
|
94
|
+
# 2. Add your API key to .env
|
|
95
|
+
# ANTHROPIC_API_KEY=sk-ant-...
|
|
96
|
+
|
|
97
|
+
# 3. Scan a file
|
|
98
|
+
aiscan scan my_prompt.txt
|
|
99
|
+
aiscan scan --type api openapi.yaml
|
|
100
|
+
aiscan scan --type mcp tool_config.json
|
|
101
|
+
|
|
102
|
+
# 4. Scan without AI (static rules only, no API key needed)
|
|
103
|
+
aiscan scan --no-ai openapi.yaml
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## CLI commands
|
|
109
|
+
|
|
110
|
+
### `aiscan scan`
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
Usage: aiscan scan [OPTIONS] FILES...
|
|
114
|
+
|
|
115
|
+
Options:
|
|
116
|
+
-t, --type TEXT Scan type: prompt | agent | api | mcp | integration | all | auto [default: auto]
|
|
117
|
+
-o, --output TEXT Output format: console | json | sarif [default: console]
|
|
118
|
+
-O, --out PATH Write output to file instead of stdout
|
|
119
|
+
-f, --fail-on TEXT Exit 1 if severity reached: critical | high | medium | none [default: high]
|
|
120
|
+
--no-ai Skip AI deep scan — static rules only
|
|
121
|
+
-q, --quiet Suppress output except errors
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Examples:**
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Auto-detect scan type from extension
|
|
128
|
+
aiscan scan system_prompt.txt
|
|
129
|
+
aiscan scan agent_config.yaml
|
|
130
|
+
|
|
131
|
+
# Explicit scan type
|
|
132
|
+
aiscan scan --type prompt system_prompt.txt
|
|
133
|
+
aiscan scan --type agent agent.yaml
|
|
134
|
+
aiscan scan --type api openapi.yaml
|
|
135
|
+
aiscan scan --type mcp tool.json
|
|
136
|
+
aiscan scan --type integration webhook_config.yaml
|
|
137
|
+
|
|
138
|
+
# Scan a whole directory
|
|
139
|
+
aiscan scan --type auto ./my-project/
|
|
140
|
+
|
|
141
|
+
# Run all 5 scanners on the same file
|
|
142
|
+
aiscan scan --type all config.yaml
|
|
143
|
+
|
|
144
|
+
# JSON / SARIF output
|
|
145
|
+
aiscan scan --type api openapi.yaml --output json
|
|
146
|
+
aiscan scan --type prompt prompts/ --output sarif --out results.sarif
|
|
147
|
+
|
|
148
|
+
# CI mode
|
|
149
|
+
aiscan scan --quiet --fail-on high prompts/ && echo "passed"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### `aiscan rules`
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
aiscan rules # list all 40 rules
|
|
156
|
+
aiscan rules prompt # rules for prompt scan type
|
|
157
|
+
aiscan rules api --severity critical # filter by severity
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### `aiscan init`
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
aiscan init # creates .env for Claude
|
|
164
|
+
aiscan init --provider openai # pre-configure for OpenAI
|
|
165
|
+
aiscan init --force # overwrite existing .env
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### `aiscan serve`
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
aiscan serve # http://localhost:8000
|
|
172
|
+
aiscan serve --port 9000
|
|
173
|
+
aiscan serve --reload # dev mode, auto-restart
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### `aiscan version`
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
aiscan version
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Scan types
|
|
185
|
+
|
|
186
|
+
| Type | Detects | File extensions (auto-detect) |
|
|
187
|
+
|---|---|---|
|
|
188
|
+
| `prompt` | Prompt injection, PII leakage, jailbreaks | `.txt` `.md` `.py` `.js` `.ts` `.go` `.java` and more |
|
|
189
|
+
| `agent` | Wildcard tools, missing human approval, infinite loops | `.yaml` `.yml` |
|
|
190
|
+
| `api` | Hardcoded keys, wildcard CORS, missing auth | `.yaml` `.yml` `.json` |
|
|
191
|
+
| `mcp` | Wildcard permissions, code execution, missing rate limits | `.json` |
|
|
192
|
+
| `integration` | Default secrets, SSL disabled, data forwarding | `.env` `.toml` `.ini` `.cfg` |
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Rules reference
|
|
197
|
+
|
|
198
|
+
40 rules total — 6 categories. Run `aiscan rules` to see the live list.
|
|
199
|
+
|
|
200
|
+
| Category | Rule IDs | Example |
|
|
201
|
+
|---|---|---|
|
|
202
|
+
| Prompt | `PI-001` `PI-002` `DL-001` `DL-002` `EX-001` | `ignore previous instructions` |
|
|
203
|
+
| Agent | `AG-001`–`AG-006` | `allow_all_tools: true` |
|
|
204
|
+
| API | `AK-001` `AK-002` `CO-001` `HT-001` `JW-001` `AU-001` | hardcoded `api_key` |
|
|
205
|
+
| MCP | `MC-001`–`MC-006` | `execute_code: true` |
|
|
206
|
+
| Integration | `IN-001`–`IN-006` | `webhook_secret: changeme` |
|
|
207
|
+
| Code | `CD-001`–`CD-012` | `eval()`, SQL injection via f-string |
|
|
208
|
+
| AI deep scan | `AI-SCAN` | Subtle issues caught by Claude/GPT-4o/Gemini |
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## REST API
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
aiscan serve
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
| Method | Path | Description |
|
|
219
|
+
|---|---|---|
|
|
220
|
+
| GET | `/health` | Service status |
|
|
221
|
+
| POST | `/scan/prompt` | Scan prompt content |
|
|
222
|
+
| POST | `/scan/agent` | Scan agent config |
|
|
223
|
+
| POST | `/scan/api` | Scan API config or OpenAPI spec |
|
|
224
|
+
| POST | `/scan/mcp` | Scan MCP tool definition |
|
|
225
|
+
| POST | `/scan/integration` | Scan integration config |
|
|
226
|
+
| POST | `/scan/all` | Run all 5 scanners |
|
|
227
|
+
| GET | `/docs` | Swagger UI |
|
|
228
|
+
|
|
229
|
+
**Request:**
|
|
230
|
+
```json
|
|
231
|
+
{"content": "file content as string", "filename": "openapi.yaml"}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Response:**
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"scan_type": "api",
|
|
238
|
+
"passed": false,
|
|
239
|
+
"critical": 1, "high": 2, "medium": 0, "info": 0, "total": 3,
|
|
240
|
+
"findings": [
|
|
241
|
+
{
|
|
242
|
+
"rule_id": "AK-001",
|
|
243
|
+
"severity": "critical",
|
|
244
|
+
"threat": "security_risk",
|
|
245
|
+
"title": "Hardcoded secret or API key",
|
|
246
|
+
"detail": "Matched on line 5: ...",
|
|
247
|
+
"file": "config.yaml",
|
|
248
|
+
"line": 5,
|
|
249
|
+
"fix": "Move to environment variables or a secrets manager."
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**curl example:**
|
|
256
|
+
```bash
|
|
257
|
+
curl -X POST http://localhost:8000/scan/prompt \
|
|
258
|
+
-H "Content-Type: application/json" \
|
|
259
|
+
-d '{"content": "Ignore previous instructions", "filename": "prompt.txt"}'
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Python SDK
|
|
265
|
+
|
|
266
|
+
Use directly inside your Python project — no separate service needed.
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
from aiscan.sdk import AIScan
|
|
270
|
+
|
|
271
|
+
scanner = AIScan(no_ai=True) # static rules only
|
|
272
|
+
# or
|
|
273
|
+
scanner = AIScan(ai_provider="claude", api_key="sk-ant-...")
|
|
274
|
+
|
|
275
|
+
report = scanner.scan_prompt("Ignore previous instructions...")
|
|
276
|
+
|
|
277
|
+
print(report.passed) # False
|
|
278
|
+
print(report.critical) # 2
|
|
279
|
+
print(report.total) # 3
|
|
280
|
+
|
|
281
|
+
for f in report.findings:
|
|
282
|
+
print(f.severity, f.rule_id, f.title)
|
|
283
|
+
print(" Fix:", f.fix)
|
|
284
|
+
|
|
285
|
+
# Other methods
|
|
286
|
+
scanner.scan_file("openapi.yaml") # auto-detect type
|
|
287
|
+
scanner.scan_file("agent.yaml", scan_type="agent")
|
|
288
|
+
scanner.scan_code("app.py")
|
|
289
|
+
scanner.scan_api_spec(open("openapi.yaml").read())
|
|
290
|
+
scanner.scan_agent_config(open("agent.yaml").read())
|
|
291
|
+
scanner.scan_mcp_tool(open("tool.json").read())
|
|
292
|
+
scanner.scan_integration(open("webhook.yaml").read())
|
|
293
|
+
reports = scanner.scan_all(content, filename="config.yaml") # all 5 scanners
|
|
294
|
+
|
|
295
|
+
# Gate on severity
|
|
296
|
+
if report.has_severity("critical"):
|
|
297
|
+
block_request()
|
|
298
|
+
|
|
299
|
+
critical_only = report.findings_by_severity("critical")
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Middleware
|
|
305
|
+
|
|
306
|
+
Auto-scan every incoming request before it reaches your handlers.
|
|
307
|
+
|
|
308
|
+
### FastAPI
|
|
309
|
+
|
|
310
|
+
```python
|
|
311
|
+
from fastapi import FastAPI
|
|
312
|
+
from aiscan.sdk.middleware import AIScanFastAPIMiddleware
|
|
313
|
+
|
|
314
|
+
app = FastAPI()
|
|
315
|
+
app.add_middleware(
|
|
316
|
+
AIScanFastAPIMiddleware,
|
|
317
|
+
scan_fields=["prompt", "message", "content"],
|
|
318
|
+
fail_on="high",
|
|
319
|
+
no_ai=True,
|
|
320
|
+
)
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Flask
|
|
324
|
+
|
|
325
|
+
```python
|
|
326
|
+
from flask import Flask
|
|
327
|
+
from aiscan.sdk.middleware import AIScanFlaskMiddleware
|
|
328
|
+
|
|
329
|
+
app = Flask(__name__)
|
|
330
|
+
AIScanFlaskMiddleware(app, fields=["prompt", "message"], fail_on="high")
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Django
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
# settings.py
|
|
337
|
+
MIDDLEWARE = [
|
|
338
|
+
"aiscan.sdk.middleware.AIScanDjangoMiddleware",
|
|
339
|
+
# ...
|
|
340
|
+
]
|
|
341
|
+
AISCAN_FIELDS = ["prompt", "message"]
|
|
342
|
+
AISCAN_FAIL_ON = "high"
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Decorators
|
|
348
|
+
|
|
349
|
+
Wrap any existing function — no refactoring needed.
|
|
350
|
+
|
|
351
|
+
```python
|
|
352
|
+
from aiscan.sdk import scan_prompt_input, scan_before_llm, scan_file_input
|
|
353
|
+
|
|
354
|
+
@scan_prompt_input(field="user_message", fail_on="high")
|
|
355
|
+
def call_llm(user_message: str) -> str:
|
|
356
|
+
... # raises ValueError if unsafe
|
|
357
|
+
|
|
358
|
+
@scan_before_llm(fields=["system_prompt", "user_message"])
|
|
359
|
+
def chat(system_prompt: str, user_message: str) -> str:
|
|
360
|
+
...
|
|
361
|
+
|
|
362
|
+
@scan_file_input(field="config_path", fail_on="critical")
|
|
363
|
+
def load_agent_config(config_path: str) -> dict:
|
|
364
|
+
...
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## HTTP client (other languages)
|
|
370
|
+
|
|
371
|
+
Run `aiscan serve` as a separate service, call it from any language.
|
|
372
|
+
|
|
373
|
+
**Python:**
|
|
374
|
+
```python
|
|
375
|
+
from aiscan.sdk import AIScanClient
|
|
376
|
+
|
|
377
|
+
client = AIScanClient("http://localhost:8000")
|
|
378
|
+
result = client.scan_prompt(user_input)
|
|
379
|
+
is_safe = client.is_safe(user_input, fail_on="high")
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
**Node.js:**
|
|
383
|
+
```javascript
|
|
384
|
+
const res = await fetch("http://localhost:8000/scan/prompt", {
|
|
385
|
+
method: "POST",
|
|
386
|
+
headers: { "Content-Type": "application/json" },
|
|
387
|
+
body: JSON.stringify({ content: userInput, filename: "prompt.txt" }),
|
|
388
|
+
});
|
|
389
|
+
const { passed, findings } = await res.json();
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Go:**
|
|
393
|
+
```go
|
|
394
|
+
resp, _ := http.Post("http://localhost:8000/scan/prompt",
|
|
395
|
+
"application/json", bytes.NewBuffer(body))
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## CI/CD integration
|
|
401
|
+
|
|
402
|
+
### GitHub Actions
|
|
403
|
+
|
|
404
|
+
```yaml
|
|
405
|
+
name: AI Security Scan
|
|
406
|
+
on: [push, pull_request]
|
|
407
|
+
|
|
408
|
+
jobs:
|
|
409
|
+
scan:
|
|
410
|
+
runs-on: ubuntu-latest
|
|
411
|
+
steps:
|
|
412
|
+
- uses: actions/checkout@v4
|
|
413
|
+
- uses: actions/setup-python@v5
|
|
414
|
+
with: { python-version: "3.12" }
|
|
415
|
+
- run: pip install aiscan
|
|
416
|
+
- run: aiscan scan --type prompt prompts/ --fail-on high
|
|
417
|
+
env:
|
|
418
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
419
|
+
- run: aiscan scan --output sarif --out results.sarif .
|
|
420
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
421
|
+
if: always()
|
|
422
|
+
with: { sarif_file: results.sarif }
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Pre-commit hook
|
|
426
|
+
|
|
427
|
+
```yaml
|
|
428
|
+
# .pre-commit-config.yaml
|
|
429
|
+
repos:
|
|
430
|
+
- repo: local
|
|
431
|
+
hooks:
|
|
432
|
+
- id: aiscan
|
|
433
|
+
name: AI Security Scanner
|
|
434
|
+
entry: aiscan scan --no-ai --fail-on critical
|
|
435
|
+
language: system
|
|
436
|
+
files: \.(txt|yaml|yml|json|py|js|ts)$
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Exit codes
|
|
440
|
+
|
|
441
|
+
| Code | Meaning |
|
|
442
|
+
|---|---|
|
|
443
|
+
| `0` | Passed — no findings at or above `--fail-on` threshold |
|
|
444
|
+
| `1` | Failed — findings found at or above threshold |
|
|
445
|
+
| `2` | Tool error — bad arguments, missing file |
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## AI provider configuration
|
|
450
|
+
|
|
451
|
+
Edit `.env`:
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
# Claude (default)
|
|
455
|
+
AI_PROVIDER=claude
|
|
456
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
457
|
+
SCANNER_MODEL=claude-sonnet-4-20250514
|
|
458
|
+
|
|
459
|
+
# OpenAI
|
|
460
|
+
AI_PROVIDER=openai
|
|
461
|
+
OPENAI_API_KEY=sk-...
|
|
462
|
+
OPENAI_MODEL=gpt-4o
|
|
463
|
+
|
|
464
|
+
# Vertex AI (Gemini)
|
|
465
|
+
AI_PROVIDER=vertex
|
|
466
|
+
VERTEX_PROJECT=your-gcp-project-id
|
|
467
|
+
VERTEX_LOCATION=us-central1
|
|
468
|
+
VERTEX_MODEL=gemini-2.5-flash-001
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
If the primary provider fails, aiscan automatically retries with the next available one. Use `--no-ai` to skip AI scanning entirely.
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Suppressing false positives
|
|
476
|
+
|
|
477
|
+
Add `# noscan` (or `// noscan` for JS/TS) to the end of any line to skip it:
|
|
478
|
+
|
|
479
|
+
```python
|
|
480
|
+
result = scanner.scan_prompt("test example string") # noscan
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## Sample files
|
|
486
|
+
|
|
487
|
+
The `samples/` directory has bad and clean examples for every scan type — useful for testing and learning what each rule detects.
|
|
488
|
+
|
|
489
|
+
```bash
|
|
490
|
+
aiscan scan --no-ai samples/prompts/bad_system_prompt.txt
|
|
491
|
+
aiscan scan --no-ai samples/agents/bad_research_agent.yaml
|
|
492
|
+
aiscan scan --no-ai samples/apis/bad_config.yaml
|
|
493
|
+
aiscan scan --no-ai samples/mcp/bad_code_executor.json
|
|
494
|
+
aiscan scan --no-ai samples/integrations/bad_slack_integration.yaml
|
|
495
|
+
aiscan scan --no-ai samples/code/bad_app.py
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## Troubleshooting
|
|
501
|
+
|
|
502
|
+
**`aiscan: command not found`**
|
|
503
|
+
```bash
|
|
504
|
+
python -m aiscan.cli --help
|
|
505
|
+
pip install -e .
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
**AI scan not running**
|
|
509
|
+
```bash
|
|
510
|
+
aiscan scan --no-ai openapi.yaml # confirms static rules work
|
|
511
|
+
# then check .env has a valid API key
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
**False positives**
|
|
515
|
+
```bash
|
|
516
|
+
aiscan scan --no-ai file.txt # isolate static rule hits
|
|
517
|
+
aiscan rules # see which pattern triggered
|
|
518
|
+
# add # noscan to suppress a specific line
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
## License
|
|
524
|
+
|
|
525
|
+
MIT
|