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.
Files changed (42) hide show
  1. aiscan_security-0.0.2/PKG-INFO +748 -0
  2. aiscan_security-0.0.2/README.md +717 -0
  3. aiscan_security-0.0.2/aiscan/__init__.py +0 -0
  4. aiscan_security-0.0.2/aiscan/api/__init__.py +0 -0
  5. aiscan_security-0.0.2/aiscan/api/routes.py +177 -0
  6. aiscan_security-0.0.2/aiscan/cli.py +387 -0
  7. aiscan_security-0.0.2/aiscan/config.py +23 -0
  8. aiscan_security-0.0.2/aiscan/models.py +54 -0
  9. aiscan_security-0.0.2/aiscan/reporters/__init__.py +0 -0
  10. aiscan_security-0.0.2/aiscan/reporters/console_reporter.py +49 -0
  11. aiscan_security-0.0.2/aiscan/reporters/json_reporter.py +40 -0
  12. aiscan_security-0.0.2/aiscan/reporters/pdf_reporter.py +288 -0
  13. aiscan_security-0.0.2/aiscan/rules/__init__.py +0 -0
  14. aiscan_security-0.0.2/aiscan/rules/registry.py +427 -0
  15. aiscan_security-0.0.2/aiscan/scanners/__init__.py +0 -0
  16. aiscan_security-0.0.2/aiscan/scanners/agent_scanner.py +7 -0
  17. aiscan_security-0.0.2/aiscan/scanners/api_scanner.py +13 -0
  18. aiscan_security-0.0.2/aiscan/scanners/base_scanner.py +78 -0
  19. aiscan_security-0.0.2/aiscan/scanners/integration_scanner.py +7 -0
  20. aiscan_security-0.0.2/aiscan/scanners/mcp_scanner.py +7 -0
  21. aiscan_security-0.0.2/aiscan/scanners/prompt_scanner.py +7 -0
  22. aiscan_security-0.0.2/aiscan/scanners/tm_coding_standards_scanner.py +51 -0
  23. aiscan_security-0.0.2/aiscan/sdk/__init__.py +13 -0
  24. aiscan_security-0.0.2/aiscan/sdk/decorators.py +178 -0
  25. aiscan_security-0.0.2/aiscan/sdk/http_client.py +125 -0
  26. aiscan_security-0.0.2/aiscan/sdk/middleware.py +207 -0
  27. aiscan_security-0.0.2/aiscan/sdk/scanner.py +236 -0
  28. aiscan_security-0.0.2/aiscan/tm_coding_standards/__init__.py +0 -0
  29. aiscan_security-0.0.2/aiscan/tm_coding_standards/models.py +62 -0
  30. aiscan_security-0.0.2/aiscan/tm_coding_standards/registry.py +11 -0
  31. aiscan_security-0.0.2/aiscan/utils/__init__.py +0 -0
  32. aiscan_security-0.0.2/aiscan/utils/aggregator.py +16 -0
  33. aiscan_security-0.0.2/aiscan/utils/ai_checker.py +163 -0
  34. aiscan_security-0.0.2/aiscan_security.egg-info/PKG-INFO +748 -0
  35. aiscan_security-0.0.2/aiscan_security.egg-info/SOURCES.txt +40 -0
  36. aiscan_security-0.0.2/aiscan_security.egg-info/dependency_links.txt +1 -0
  37. aiscan_security-0.0.2/aiscan_security.egg-info/entry_points.txt +2 -0
  38. aiscan_security-0.0.2/aiscan_security.egg-info/requires.txt +26 -0
  39. aiscan_security-0.0.2/aiscan_security.egg-info/top_level.txt +1 -0
  40. aiscan_security-0.0.2/pyproject.toml +45 -0
  41. aiscan_security-0.0.2/setup.cfg +4 -0
  42. 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