offsec-ai 2.0.1__tar.gz → 2.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 (61) hide show
  1. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/CHANGELOG.md +16 -0
  2. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/PKG-INFO +30 -6
  3. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/README.md +24 -3
  4. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/docs/DOCKER.md +109 -9
  5. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/docs/quickstart.md +6 -6
  6. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/pyproject.toml +6 -3
  7. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/__init__.py +1 -1
  8. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/core/hybrid_identity_checker.py +1 -1
  9. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/core/l7_detector.py +1 -1
  10. offsec_ai-2.0.2/tests/test_mtls.py +102 -0
  11. offsec_ai-2.0.2/tests/test_mtls_integration.py +82 -0
  12. offsec_ai-2.0.1/tests/test_dns_trace.py +0 -85
  13. offsec_ai-2.0.1/tests/test_mtls.py +0 -159
  14. offsec_ai-2.0.1/tests/test_mtls_integration.py +0 -193
  15. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/.gitignore +0 -0
  16. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/CONTRIBUTING.md +0 -0
  17. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/LICENSE +0 -0
  18. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/SECURITY.md +0 -0
  19. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/docs/api.md +0 -0
  20. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/docs/assets/logo.svg +0 -0
  21. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/docs/azure-ad-flow-explained.md +0 -0
  22. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/docs/hybrid-identity.md +0 -0
  23. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/docs/owasp-scanner.md +0 -0
  24. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/examples/comprehensive_examples.py +0 -0
  25. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/examples/mtls_examples.py +0 -0
  26. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/examples/owasp_scan_examples.py +0 -0
  27. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/examples/usage_examples.py +0 -0
  28. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/__main__.py +0 -0
  29. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/cli.py +0 -0
  30. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/core/__init__.py +0 -0
  31. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/core/ai_owasp_scanner.py +0 -0
  32. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/core/cert_analyzer.py +0 -0
  33. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/core/llm_judge.py +0 -0
  34. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/core/mcp_attacker.py +0 -0
  35. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/core/mcp_scanner.py +0 -0
  36. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/core/mtls_checker.py +0 -0
  37. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/core/owasp_scanner.py +0 -0
  38. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/core/port_scanner.py +0 -0
  39. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/core/security_headers.py +0 -0
  40. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/models/__init__.py +0 -0
  41. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/models/ai_owasp_result.py +0 -0
  42. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/models/l7_result.py +0 -0
  43. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/models/mcp_result.py +0 -0
  44. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/models/mtls_result.py +0 -0
  45. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/models/owasp_result.py +0 -0
  46. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/models/scan_result.py +0 -0
  47. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/py.typed +0 -0
  48. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/utils/__init__.py +0 -0
  49. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/utils/ai_owasp_payloads.py +0 -0
  50. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/utils/ai_owasp_remediation.py +0 -0
  51. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/utils/common_ports.py +0 -0
  52. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/utils/exporters.py +0 -0
  53. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/utils/l7_signatures.py +0 -0
  54. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/utils/mcp_cve_db.py +0 -0
  55. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/utils/mcp_payloads.py +0 -0
  56. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/src/offsec_ai/utils/owasp_remediation.py +0 -0
  57. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/tests/conftest.py +0 -0
  58. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/tests/test_ai_owasp.py +0 -0
  59. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/tests/test_mcp_attacker.py +0 -0
  60. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/tests/test_mcp_scanner.py +0 -0
  61. {offsec_ai-2.0.1 → offsec_ai-2.0.2}/tests/test_port_scanner.py +0 -0
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.0.2] - 2026-06-29
9
+
10
+ ### Changed
11
+ - README logo replaced with inline ASCII art (no external image dependency)
12
+ - User-Agent strings updated from `SimplePortChecker/1.0` to `offsec-ai/2.0` in `L7Detector` and `HybridIdentityChecker`
13
+ - `reportlab>=4.0.0` promoted to core dependency in `pyproject.toml` (was missing, caused `ModuleNotFoundError` on fresh installs)
14
+ - Docker image now installs `[ai]` extras (`openai`, `anthropic`) so LLM judge works at runtime via env vars
15
+ - Dockerfile redundant pre-installs removed; `pip install ".[ai]"` is now the single install step
16
+ - `docker-push` and `docker-release` Makefile targets added with auto-versioning from `pyproject.toml`
17
+ - CI/CD: `docker.yml` image labels updated (title, description); unused `build-args` removed
18
+ - CI/CD: `publish.yml` PyPI environment URL no longer pins a hardcoded version
19
+ - Stale files removed: `PROJECT_STRUCTURE.md`, `HYBRID_IDENTITY_QUICKREF.md`, `IMPLEMENTATION_HYBRID_IDENTITY.md`, `MANIFEST.in`, empty `scripts/` dir
20
+ - `tests/test_dns_trace.py` removed (was a live-network script, not a pytest test)
21
+ - `tests/test_mtls.py` and `tests/test_mtls_integration.py` rewritten as proper `assert`-based pytest tests
22
+ - `setup_dev.sh` updated: branding, venv path (`.venv`), and `[ai]` extras
23
+
8
24
  ## [2.0.0] - 2026-06-29
9
25
 
10
26
  ### Added — AI / LLM Security (fresh start as `offsec-ai`)
@@ -1,13 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: offsec-ai
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: Offensive-security toolkit: port scanning, L7/WAF detection, mTLS, certificate analysis, OWASP Top 10, AI/LLM OWASP Top 10 black-box probing, and MCP endpoint security scanning
5
5
  Project-URL: Homepage, https://github.com/htunn/offsec-ai
6
6
  Project-URL: Repository, https://github.com/htunn/offsec-ai
7
7
  Project-URL: Issues, https://github.com/htunn/offsec-ai/issues
8
8
  Project-URL: Documentation, https://github.com/htunn/offsec-ai#readme
9
- Author-email: htunn <htunnthuthu.linux@gmail.com>
10
- Maintainer-email: htunn <htunnthuthu.linux@gmail.com>
9
+ Author: Htunn
10
+ Author-email: htunnthuthu.linux@gmail.com
11
+ Maintainer: Htunn
12
+ Maintainer-email: htunnthuthu.linux@gmail.com
11
13
  License: MIT
12
14
  License-File: LICENSE
13
15
  Keywords: ai,ai-security,certificate,certificate-analysis,firewall,l7-protection,llm,mcp,model-context-protocol,network,offensive-security,owasp,pentest,pki,port-scanner,red-team,security,ssl,tls,waf
@@ -37,6 +39,7 @@ Requires-Dist: httpx>=0.25.0
37
39
  Requires-Dist: mcp>=1.0.0
38
40
  Requires-Dist: pydantic>=2.0.0
39
41
  Requires-Dist: python-nmap>=0.7.1
42
+ Requires-Dist: reportlab>=4.0.0
40
43
  Requires-Dist: requests>=2.31.0
41
44
  Requires-Dist: rich>=13.0.0
42
45
  Provides-Extra: ai
@@ -56,9 +59,15 @@ Provides-Extra: gemini
56
59
  Requires-Dist: google-generativeai>=0.7.0; extra == 'gemini'
57
60
  Description-Content-Type: text/markdown
58
61
 
59
- <p align="center">
60
- <img src="https://raw.githubusercontent.com/Htunn/offsec-ai/main/docs/assets/logo.svg" alt="offsec-ai" width="520"/>
61
- </p>
62
+ ```
63
+ ██████╗ ███████╗███████╗███████╗███████╗ ██████╗ █████╗ ██╗
64
+ ██╔═══██╗██╔════╝██╔════╝██╔════╝██╔════╝██╔════╝ ██╔══██╗██║
65
+ ██║ ██║█████╗ █████╗ ███████╗█████╗ ██║ █████╗███████║██║
66
+ ██║ ██║██╔══╝ ██╔══╝ ╚════██║██╔══╝ ██║ ╚════╝██╔══██║██║
67
+ ╚██████╔╝██║ ██║ ███████║███████╗╚██████╗ ██║ ██║██║
68
+ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝
69
+ Offensive-Security Toolkit · AI/LLM · MCP · Red-Team
70
+ ```
62
71
 
63
72
  <p align="center">
64
73
  <a href="https://pypi.org/project/offsec-ai/"><img src="https://img.shields.io/pypi/v/offsec-ai" alt="PyPI Version"/></a>
@@ -554,8 +563,23 @@ docker run --rm htunnthuthu/offsec-ai:latest owasp-scan example.com
554
563
  docker run --rm -v $(pwd):/app/output htunnthuthu/offsec-ai:latest \
555
564
  ai-owasp-scan https://api.example.com/v1/chat/completions \
556
565
  --output /app/output/llm-report.json
566
+
567
+ # LLM Judge — openai, anthropic, or gemini key auto-detected; no extra install needed
568
+ docker run --rm \
569
+ -e OPENAI_API_KEY=sk-... \
570
+ htunnthuthu/offsec-ai:latest \
571
+ ai-owasp-scan https://api.example.com/v1/chat/completions --llm-judge
572
+
573
+ # Custom OpenAI-compatible backend (Ollama, LM Studio, Azure OpenAI…)
574
+ docker run --rm \
575
+ -e OFFSEC_LLM_BASE_URL=http://host.docker.internal:11434/v1 \
576
+ -e OFFSEC_LLM_MODEL=llama3 \
577
+ htunnthuthu/offsec-ai:latest \
578
+ ai-owasp-scan https://api.example.com/v1/chat/completions --llm-judge
557
579
  ```
558
580
 
581
+ See [docs/DOCKER.md](docs/DOCKER.md) for the full Docker reference including CI/CD integration, Kubernetes jobs, Makefile publish targets, and troubleshooting.
582
+
559
583
  ---
560
584
 
561
585
  ## Configuration
@@ -1,6 +1,12 @@
1
- <p align="center">
2
- <img src="https://raw.githubusercontent.com/Htunn/offsec-ai/main/docs/assets/logo.svg" alt="offsec-ai" width="520"/>
3
- </p>
1
+ ```
2
+ ██████╗ ███████╗███████╗███████╗███████╗ ██████╗ █████╗ ██╗
3
+ ██╔═══██╗██╔════╝██╔════╝██╔════╝██╔════╝██╔════╝ ██╔══██╗██║
4
+ ██║ ██║█████╗ █████╗ ███████╗█████╗ ██║ █████╗███████║██║
5
+ ██║ ██║██╔══╝ ██╔══╝ ╚════██║██╔══╝ ██║ ╚════╝██╔══██║██║
6
+ ╚██████╔╝██║ ██║ ███████║███████╗╚██████╗ ██║ ██║██║
7
+ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝
8
+ Offensive-Security Toolkit · AI/LLM · MCP · Red-Team
9
+ ```
4
10
 
5
11
  <p align="center">
6
12
  <a href="https://pypi.org/project/offsec-ai/"><img src="https://img.shields.io/pypi/v/offsec-ai" alt="PyPI Version"/></a>
@@ -496,8 +502,23 @@ docker run --rm htunnthuthu/offsec-ai:latest owasp-scan example.com
496
502
  docker run --rm -v $(pwd):/app/output htunnthuthu/offsec-ai:latest \
497
503
  ai-owasp-scan https://api.example.com/v1/chat/completions \
498
504
  --output /app/output/llm-report.json
505
+
506
+ # LLM Judge — openai, anthropic, or gemini key auto-detected; no extra install needed
507
+ docker run --rm \
508
+ -e OPENAI_API_KEY=sk-... \
509
+ htunnthuthu/offsec-ai:latest \
510
+ ai-owasp-scan https://api.example.com/v1/chat/completions --llm-judge
511
+
512
+ # Custom OpenAI-compatible backend (Ollama, LM Studio, Azure OpenAI…)
513
+ docker run --rm \
514
+ -e OFFSEC_LLM_BASE_URL=http://host.docker.internal:11434/v1 \
515
+ -e OFFSEC_LLM_MODEL=llama3 \
516
+ htunnthuthu/offsec-ai:latest \
517
+ ai-owasp-scan https://api.example.com/v1/chat/completions --llm-judge
499
518
  ```
500
519
 
520
+ See [docs/DOCKER.md](docs/DOCKER.md) for the full Docker reference including CI/CD integration, Kubernetes jobs, Makefile publish targets, and troubleshooting.
521
+
501
522
  ---
502
523
 
503
524
  ## Configuration
@@ -260,9 +260,73 @@ spec:
260
260
  - ✅ OCSP and CRL URL extraction for revocation checking
261
261
  - ✅ Certificate fingerprint generation (SHA-1, SHA-256)
262
262
 
263
- ## �🔧 Configuration & Environment
263
+ ## 🤖 AI / LLM Security Usage
264
+
265
+ The image ships with `openai` and `anthropic` pre-installed (`[ai]` extra). Pass an API key at runtime to enable the **LLM Judge** for smarter semantic vulnerability detection.
266
+
267
+ ### AI OWASP Top 10 Scan (rule-based, no key required)
268
+ ```bash
269
+ docker run --rm htunnthuthu/offsec-ai:latest \
270
+ ai-owasp-scan https://api.example.com/v1/chat/completions
271
+ ```
272
+
273
+ ### AI OWASP Top 10 Scan with LLM Judge
264
274
 
265
- ### Environment Variables
275
+ ```bash
276
+ # OpenAI judge
277
+ docker run --rm \
278
+ -e OPENAI_API_KEY=sk-... \
279
+ htunnthuthu/offsec-ai:latest \
280
+ ai-owasp-scan https://api.example.com/v1/chat/completions --llm-judge
281
+
282
+ # Anthropic judge
283
+ docker run --rm \
284
+ -e ANTHROPIC_API_KEY=sk-ant-... \
285
+ htunnthuthu/offsec-ai:latest \
286
+ ai-owasp-scan https://api.example.com/v1/chat/completions --llm-judge
287
+
288
+ # Google Gemini judge (no extra package needed)
289
+ docker run --rm \
290
+ -e GEMINI_API_KEY=AIzaSy... \
291
+ htunnthuthu/offsec-ai:latest \
292
+ ai-owasp-scan https://api.example.com/v1/chat/completions --llm-judge
293
+
294
+ # Custom OpenAI-compatible endpoint (Ollama, LM Studio, Azure OpenAI, etc.)
295
+ docker run --rm \
296
+ -e OFFSEC_LLM_BASE_URL=http://host.docker.internal:11434/v1 \
297
+ -e OFFSEC_LLM_MODEL=llama3 \
298
+ htunnthuthu/offsec-ai:latest \
299
+ ai-owasp-scan https://api.example.com/v1/chat/completions --llm-judge
300
+ ```
301
+
302
+ ### MCP Security Scan
303
+ ```bash
304
+ # Scan an MCP endpoint
305
+ docker run --rm htunnthuthu/offsec-ai:latest \
306
+ mcp-scan https://mcp.example.com/mcp
307
+
308
+ # Active MCP attack (requires authorization flag)
309
+ docker run --rm htunnthuthu/offsec-ai:latest \
310
+ mcp-attack https://mcp.example.com/mcp --i-have-authorization --mode deep
311
+ ```
312
+
313
+ ---
314
+
315
+ ## 🔧 Configuration & Environment
316
+
317
+ ### LLM / AI Judge Environment Variables
318
+
319
+ | Variable | Provider | Description |
320
+ |----------|----------|-------------|
321
+ | `OPENAI_API_KEY` | OpenAI | Enables OpenAI judge (e.g. `gpt-4o-mini`) |
322
+ | `ANTHROPIC_API_KEY` | Anthropic | Enables Anthropic judge (e.g. `claude-3-haiku`) |
323
+ | `GEMINI_API_KEY` | Google | Enables Gemini judge (e.g. `gemini-1.5-flash`) |
324
+ | `OFFSEC_LLM_BASE_URL` | Any OpenAI-compatible | Use a custom/local endpoint as the judge backend |
325
+ | `OFFSEC_LLM_MODEL` | All | Override the default model name |
326
+
327
+ Provider is **auto-detected** from whichever key is set; `OFFSEC_LLM_BASE_URL` takes precedence over `OPENAI_API_KEY`.
328
+
329
+ ### General Environment Variables
266
330
  ```bash
267
331
  # Set timeout for operations
268
332
  docker run --rm -e TIMEOUT=30 htunnthuthu/offsec-ai:latest scan example.com
@@ -294,12 +358,12 @@ docker run --rm -v /host/certs:/app/certs \
294
358
  ## 🔒 Security Features
295
359
 
296
360
  ### Non-Root User
297
- - ✅ Container runs as non-root user `scanner` (UID: 1000)
361
+ - ✅ Container runs as non-root user `appuser` (UID: 1000)
298
362
  - ✅ No privileged access required
299
363
  - ✅ Minimal attack surface
300
364
 
301
365
  ### Minimal Dependencies
302
- - ✅ Based on Alpine Linux for small footprint
366
+ - ✅ Based on Debian slim for minimal footprint
303
367
  - ✅ Only essential packages included
304
368
  - ✅ Regular security updates via automated builds
305
369
 
@@ -311,17 +375,18 @@ docker run --rm -v /host/certs:/app/certs \
311
375
  ## 🏷️ Image Specifications
312
376
 
313
377
  ### Base Image
314
- - **OS**: Alpine Linux (latest stable)
378
+ - **OS**: Debian GNU/Linux 12 Bookworm slim (`python:3.12-slim-bookworm`)
315
379
  - **Python**: 3.12+
316
380
  - **Architecture**: Multi-arch (AMD64, ARM64)
317
- - **User**: Non-root (`scanner:scanner`)
381
+ - **User**: Non-root (`appuser:appuser`, UID/GID 1000)
318
382
 
319
383
  ### Installed Tools
320
- - ✅ Simple Port Checker (latest version)
384
+ - ✅ `offsec-ai` (latest version)
321
385
  - ✅ Python runtime and required dependencies
386
+ - ✅ `openai` & `anthropic` packages — bundled via `[ai]` extra; LLM judge activates automatically when you pass an API key env var
322
387
  - ✅ SSL/TLS libraries for certificate handling
323
388
  - ✅ DNS resolution utilities
324
- - ✅ OpenSSL for certificate chain extraction
389
+ - ✅ `nmap` for port scanning
325
390
  - ✅ Cryptography libraries for certificate analysis
326
391
 
327
392
  ### Performance
@@ -430,7 +495,42 @@ docker run --rm --memory=512m htunnthuthu/offsec-ai:latest scan example.com
430
495
  docker run --rm --cpus=2 htunnthuthu/offsec-ai:latest full-scan example.com
431
496
  ```
432
497
 
433
- ## 🔗 Related Links
498
+ ## �️ Building & Publishing Locally
499
+
500
+ The `Makefile` provides convenience targets for building and pushing to Docker Hub.
501
+
502
+ ```bash
503
+ # Build the image locally
504
+ make docker-build
505
+
506
+ # Build without cache
507
+ make docker-build-no-cache
508
+
509
+ # Build multi-arch (linux/amd64 + linux/arm64) — requires docker buildx
510
+ make docker-build-multi
511
+
512
+ # Push to Docker Hub (builds first, then tags + pushes :version and :latest)
513
+ make docker-push # uses DOCKER_USERNAME=htunn by default
514
+ make docker-push DOCKER_USERNAME=youruser # override username
515
+
516
+ # Full release in one command
517
+ make docker-release # equivalent to docker-build + docker-push
518
+
519
+ # Test the local image
520
+ make docker-test
521
+
522
+ # Scan image for vulnerabilities (requires trivy)
523
+ make docker-scan
524
+
525
+ # Clean up local Docker artifacts
526
+ make docker-clean
527
+ ```
528
+
529
+ The `DOCKER_VERSION` is read automatically from `pyproject.toml`, so the published tags match the package version exactly.
530
+
531
+ ---
532
+
533
+ ## �🔗 Related Links
434
534
 
435
535
  - **GitHub Repository**: [Htunn/offsec-ai](https://github.com/Htunn/offsec-ai)
436
536
  - **PyPI Package**: [offsec-ai](https://pypi.org/project/offsec-ai/)
@@ -28,9 +28,8 @@ offsec-ai ai-owasp-scan https://api.example.com/v1/chat/completions \
28
28
  offsec-ai mcp-scan https://mcp.example.com/mcp
29
29
  offsec-ai mcp-scan https://mcp.example.com/mcp --output report.json
30
30
 
31
- # MCP attacker (requires --authorized flag)
32
- offsec-ai mcp-attack https://mcp.example.com/mcp --authorized \
33
- --auth-token "$TOKEN"
31
+ # MCP attacker (requires --i-have-authorization flag)
32
+ offsec-ai mcp-attack https://mcp.example.com/mcp --i-have-authorization
34
33
  ```
35
34
 
36
35
  ### Python API
@@ -167,6 +166,7 @@ offsec-ai scan example.com --verbose
167
166
  ## Examples
168
167
 
169
168
  See the `examples/` directory for complete usage examples:
170
- - `usage_examples.py` - Comprehensive examples
171
- - `batch_scanning.py` - Batch scanning multiple targets
172
- - `custom_config.py` - Custom configuration examples
169
+ - `usage_examples.py` Core infrastructure scanning examples
170
+ - `comprehensive_examples.py` All features with detailed output
171
+ - `mtls_examples.py` mTLS testing and certificate generation
172
+ - `owasp_scan_examples.py` — OWASP Top 10 scanning with PDF/CSV export
@@ -4,16 +4,18 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "offsec-ai"
7
- version = "2.0.1"
7
+ version = "2.0.2"
8
8
  description = "Offensive-security toolkit: port scanning, L7/WAF detection, mTLS, certificate analysis, OWASP Top 10, AI/LLM OWASP Top 10 black-box probing, and MCP endpoint security scanning"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
11
11
  license = {text = "MIT"}
12
12
  authors = [
13
- {name = "htunn", email = "htunnthuthu.linux@gmail.com"}
13
+ {name = "Htunn"},
14
+ {email = "htunnthuthu.linux@gmail.com"}
14
15
  ]
15
16
  maintainers = [
16
- {name = "htunn", email = "htunnthuthu.linux@gmail.com"}
17
+ {name = "Htunn"},
18
+ {email = "htunnthuthu.linux@gmail.com"}
17
19
  ]
18
20
  keywords = ["firewall", "port-scanner", "l7-protection", "waf", "network", "security", "ssl", "tls", "certificate", "certificate-analysis", "pki", "offensive-security", "red-team", "pentest", "ai", "llm", "mcp", "owasp", "ai-security", "model-context-protocol"]
19
21
  classifiers = [
@@ -46,6 +48,7 @@ dependencies = [
46
48
  "cryptography>=41.0.0",
47
49
  "mcp>=1.0.0",
48
50
  "httpx>=0.25.0",
51
+ "reportlab>=4.0.0",
49
52
  ]
50
53
 
51
54
  [project.optional-dependencies]
@@ -16,7 +16,7 @@ Capabilities:
16
16
  - Rich CLI interface with progress bars
17
17
  """
18
18
 
19
- __version__ = "2.0.1"
19
+ __version__ = "2.0.2"
20
20
  __author__ = "htunn"
21
21
  __email__ = "htunnthuthu.linux@gmail.com"
22
22
  __license__ = "MIT"
@@ -91,7 +91,7 @@ class HybridIdentityChecker:
91
91
  timeout: Request timeout in seconds
92
92
  """
93
93
  self.timeout = timeout
94
- self.user_agent = "SimplePortChecker/1.0 (Hybrid Identity Scanner)"
94
+ self.user_agent = "offsec-ai/2.0 (Hybrid Identity Scanner)"
95
95
 
96
96
  async def check(self, fqdn: str) -> HybridIdentityResult:
97
97
  """
@@ -55,7 +55,7 @@ class L7Detector:
55
55
  user_agent: Custom User-Agent string
56
56
  """
57
57
  self.timeout = timeout
58
- self.user_agent = user_agent or "SimplePortChecker/1.0 (Security Scanner)"
58
+ self.user_agent = user_agent or "offsec-ai/2.0 (Security Scanner)"
59
59
  self.signatures = L7_SIGNATURES
60
60
 
61
61
  async def detect(self, host: str, port: int = None, path: str = "/", trace_dns: bool = False) -> L7Result:
@@ -0,0 +1,102 @@
1
+ """Tests for mTLS models and CLI commands."""
2
+
3
+ from datetime import datetime
4
+
5
+
6
+ def test_imports():
7
+ """All mTLS modules import without error."""
8
+ from offsec_ai.models.mtls_result import BatchMTLSResult, CertificateInfo, MTLSResult # noqa: F401
9
+ from offsec_ai.core.mtls_checker import MTLSChecker # noqa: F401
10
+
11
+
12
+ def test_certificate_info_model():
13
+ """CertificateInfo model fields are created correctly."""
14
+ from offsec_ai.models.mtls_result import CertificateInfo
15
+
16
+ cert = CertificateInfo(
17
+ subject="CN=test.example.com",
18
+ issuer="CN=Test CA",
19
+ version=3,
20
+ serial_number="12345",
21
+ not_valid_before="2024-01-01T00:00:00",
22
+ not_valid_after="2025-01-01T00:00:00",
23
+ signature_algorithm="sha256WithRSAEncryption",
24
+ key_algorithm="RSAPublicKey",
25
+ key_size=2048,
26
+ san_dns_names=["test.example.com", "*.example.com"],
27
+ san_ip_addresses=["192.168.1.1"],
28
+ is_ca=False,
29
+ is_self_signed=False,
30
+ fingerprint_sha256="abcd1234...",
31
+ )
32
+ assert cert.subject == "CN=test.example.com"
33
+ assert cert.key_size == 2048
34
+ assert "test.example.com" in cert.san_dns_names
35
+ assert cert.is_ca is False
36
+
37
+
38
+ def test_mtls_result_model():
39
+ """MTLSResult is created, fields are accessible, and serializes to JSON."""
40
+ from offsec_ai.models.mtls_result import CertificateInfo, MTLSResult
41
+
42
+ cert = CertificateInfo(
43
+ subject="CN=test.example.com",
44
+ issuer="CN=Test CA",
45
+ version=3,
46
+ serial_number="12345",
47
+ not_valid_before="2024-01-01T00:00:00",
48
+ not_valid_after="2025-01-01T00:00:00",
49
+ signature_algorithm="sha256WithRSAEncryption",
50
+ key_algorithm="RSAPublicKey",
51
+ key_size=2048,
52
+ san_dns_names=["test.example.com"],
53
+ san_ip_addresses=[],
54
+ is_ca=False,
55
+ is_self_signed=False,
56
+ fingerprint_sha256="abcd1234...",
57
+ )
58
+ result = MTLSResult(
59
+ target="test.example.com",
60
+ port=443,
61
+ supports_mtls=True,
62
+ requires_client_cert=False,
63
+ server_cert_info=cert,
64
+ client_cert_requested=True,
65
+ handshake_successful=False,
66
+ error_message=None,
67
+ cipher_suite="TLS_AES_256_GCM_SHA384",
68
+ tls_version="TLSv1.3",
69
+ verification_mode="default",
70
+ ca_bundle_path="/etc/ssl/certs/ca-certificates.crt",
71
+ timestamp=datetime.now().isoformat(),
72
+ )
73
+ assert result.target == "test.example.com"
74
+ assert result.port == 443
75
+ assert result.supports_mtls is True
76
+ assert result.server_cert_info.key_size == 2048
77
+
78
+ json_str = result.model_dump_json()
79
+ assert "test.example.com" in json_str
80
+ assert "TLSv1.3" in json_str
81
+
82
+
83
+ def test_cli_mtls_commands_exist():
84
+ """mTLS CLI commands are registered and return help text."""
85
+ from click.testing import CliRunner
86
+
87
+ from offsec_ai.cli import main
88
+
89
+ runner = CliRunner()
90
+
91
+ result = runner.invoke(main, ["--help"])
92
+ assert result.exit_code == 0
93
+ assert "mtls-check" in result.output
94
+
95
+ result = runner.invoke(main, ["mtls-check", "--help"])
96
+ assert result.exit_code == 0
97
+
98
+ result = runner.invoke(main, ["mtls-gen-cert", "--help"])
99
+ assert result.exit_code == 0
100
+
101
+ result = runner.invoke(main, ["mtls-validate-cert", "--help"])
102
+ assert result.exit_code == 0
@@ -0,0 +1,82 @@
1
+ """Integration-level tests for mTLS: models, CLI structure, and documentation presence."""
2
+
3
+ import os
4
+ from datetime import datetime
5
+
6
+
7
+ def test_mtls_model_full_lifecycle():
8
+ """MTLSResult supports full create → serialize → field-access lifecycle."""
9
+ from offsec_ai.models.mtls_result import CertificateInfo, MTLSResult
10
+
11
+ cert = CertificateInfo(
12
+ subject="CN=test.example.com,O=Test Org,C=US",
13
+ issuer="CN=Test CA,O=Test CA Org,C=US",
14
+ version=3,
15
+ serial_number="123456789",
16
+ not_valid_before="2024-01-01T00:00:00Z",
17
+ not_valid_after="2025-01-01T00:00:00Z",
18
+ signature_algorithm="sha256WithRSAEncryption",
19
+ key_algorithm="RSAPublicKey",
20
+ key_size=2048,
21
+ san_dns_names=["test.example.com", "www.test.example.com"],
22
+ san_ip_addresses=["192.168.1.100"],
23
+ is_ca=False,
24
+ is_self_signed=False,
25
+ fingerprint_sha256="a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
26
+ )
27
+ mtls_result = MTLSResult(
28
+ target="test.example.com",
29
+ port=443,
30
+ supports_mtls=True,
31
+ requires_client_cert=True,
32
+ server_cert_info=cert,
33
+ client_cert_requested=True,
34
+ handshake_successful=True,
35
+ error_message=None,
36
+ cipher_suite="TLS_AES_256_GCM_SHA384",
37
+ tls_version="TLSv1.3",
38
+ verification_mode="strict",
39
+ ca_bundle_path="/etc/ssl/certs/ca-certificates.crt",
40
+ timestamp=datetime.now().isoformat(),
41
+ )
42
+
43
+ assert mtls_result.target == "test.example.com"
44
+ assert mtls_result.supports_mtls is True
45
+ assert mtls_result.server_cert_info.subject == "CN=test.example.com,O=Test Org,C=US"
46
+
47
+ json_str = mtls_result.model_dump_json(indent=2)
48
+ assert len(json_str) > 100
49
+ assert "TLSv1.3" in json_str
50
+
51
+
52
+ def test_cli_structure():
53
+ """All expected mTLS commands are present in the CLI."""
54
+ from offsec_ai.cli import main
55
+
56
+ cli_source = main.__module__
57
+ import inspect
58
+ import offsec_ai.cli as cli_module
59
+
60
+ src = inspect.getsource(cli_module)
61
+ for symbol in ("mtls-check", "mtls-gen-cert", "mtls-validate-cert", "MTLSChecker"):
62
+ assert symbol in src, f"Expected '{symbol}' in cli.py"
63
+
64
+
65
+ def test_documentation_presence():
66
+ """Key documentation files for mTLS exist and contain expected content."""
67
+ repo_root = os.path.join(os.path.dirname(__file__), "..")
68
+
69
+ readme = os.path.join(repo_root, "README.md")
70
+ assert os.path.isfile(readme)
71
+ content = open(readme).read()
72
+ assert "mTLS" in content
73
+ assert "mutual TLS" in content
74
+
75
+ api_doc = os.path.join(repo_root, "docs", "api.md")
76
+ assert os.path.isfile(api_doc)
77
+ content = open(api_doc).read()
78
+ assert "MTLSChecker" in content
79
+ assert "MTLSResult" in content
80
+
81
+ examples = os.path.join(repo_root, "examples", "mtls_examples.py")
82
+ assert os.path.isfile(examples)
@@ -1,85 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Test script for DNS trace functionality.
4
- """
5
-
6
- import asyncio
7
- import dns.resolver
8
- import json
9
-
10
- from offsec_ai.core.l7_detector import L7Detector
11
-
12
- async def main():
13
- """Run DNS trace tests."""
14
- print("Testing DNS trace...")
15
-
16
- # Domains to test
17
- domains = ["sts-qa.gcc.gov.sg", "extadfs.gic.com.sg", "www.google.com"]
18
-
19
- for domain in domains:
20
- print(f"\n=== Testing {domain} ===")
21
-
22
- # Simple DNS lookup using dns.resolver directly
23
- try:
24
- print("\nDirect DNS resolution:")
25
- resolver = dns.resolver.Resolver()
26
-
27
- # CNAME lookup
28
- try:
29
- cname_answers = resolver.resolve(domain, "CNAME")
30
- for cname in cname_answers:
31
- cname_str = str(cname.target).lower().rstrip('.')
32
- print(f"CNAME: {domain} → {cname_str}")
33
-
34
- # Try to resolve the CNAME target
35
- try:
36
- a_answers = resolver.resolve(cname_str, "A")
37
- for a_record in a_answers:
38
- ip_str = str(a_record)
39
- print(f"IP: {cname_str} → {ip_str}")
40
- except Exception as e:
41
- print(f"Failed to resolve CNAME to IP: {e}")
42
- except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
43
- print("No CNAME records found")
44
-
45
- # Try direct A record lookup
46
- try:
47
- a_answers = resolver.resolve(domain, "A")
48
- for a_record in a_answers:
49
- ip_str = str(a_record)
50
- print(f"Direct A record: {domain} → {ip_str}")
51
- except Exception as e:
52
- print(f"Failed to resolve A records: {e}")
53
- except Exception as e:
54
- print(f"DNS resolution error: {e}")
55
-
56
- # Test with our L7Detector
57
- print("\nUsing L7Detector.detect():")
58
- detector = L7Detector(timeout=5.0)
59
-
60
- try:
61
- # Execute the trace domain function directly
62
- detections, dns_trace = await detector._trace_domain_protection(domain)
63
- print(f"Trace results:\nDetections: {len(detections)}")
64
- print(f"DNS trace data: {json.dumps(dns_trace, indent=2)}")
65
-
66
- # Test the full detect method with our own trace function
67
- dns_detections, dns_trace = await detector._trace_domain_protection(domain)
68
- result = await detector.detect(domain)
69
-
70
- # Manually check and update the dns_trace field
71
- print(f"\nFull detection results:")
72
- print(f"Is protected: {result.is_protected}")
73
- if result.primary_protection:
74
- print(f"Primary protection: {result.primary_protection.service.value} ({result.primary_protection.confidence:.1%})")
75
-
76
- print(f"DNS trace in result before: {json.dumps(result.dns_trace, indent=2)}")
77
-
78
- # Manually update the result for debugging
79
- result.dns_trace = dns_trace
80
- print(f"DNS trace in result after manual update: {json.dumps(result.dns_trace, indent=2)}")
81
- except Exception as e:
82
- print(f"L7Detector error: {e}")
83
-
84
- if __name__ == "__main__":
85
- asyncio.run(main())
@@ -1,159 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Test script for mTLS functionality.
4
-
5
- This script tests the mTLS checker without requiring all dependencies to be installed.
6
- """
7
-
8
- import sys
9
- import os
10
-
11
- # Add the src directory to Python path
12
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))
13
-
14
- def test_imports():
15
- """Test that all modules can be imported."""
16
- print("Testing imports...")
17
-
18
- try:
19
- from offsec_ai.models.mtls_result import MTLSResult, CertificateInfo, BatchMTLSResult
20
- print("✓ mTLS models imported successfully")
21
- except ImportError as e:
22
- print(f"✗ Failed to import mTLS models: {e}")
23
- return False
24
-
25
- try:
26
- from offsec_ai.core.mtls_checker import MTLSChecker
27
- print("✓ MTLSChecker imported successfully")
28
- except ImportError as e:
29
- print(f"✗ Failed to import MTLSChecker: {e}")
30
- return False
31
-
32
- return True
33
-
34
- def test_mtls_result_model():
35
- """Test the MTLSResult model."""
36
- print("\nTesting MTLSResult model...")
37
-
38
- from offsec_ai.models.mtls_result import MTLSResult, CertificateInfo
39
- from datetime import datetime
40
-
41
- # Test CertificateInfo
42
- cert_info = CertificateInfo(
43
- subject="CN=test.example.com",
44
- issuer="CN=Test CA",
45
- version=3,
46
- serial_number="12345",
47
- not_valid_before="2024-01-01T00:00:00",
48
- not_valid_after="2025-01-01T00:00:00",
49
- signature_algorithm="sha256WithRSAEncryption",
50
- key_algorithm="RSAPublicKey",
51
- key_size=2048,
52
- san_dns_names=["test.example.com", "*.example.com"],
53
- san_ip_addresses=["192.168.1.1"],
54
- is_ca=False,
55
- is_self_signed=False,
56
- fingerprint_sha256="abcd1234..."
57
- )
58
- print("✓ CertificateInfo model created successfully")
59
-
60
- # Test MTLSResult
61
- result = MTLSResult(
62
- target="test.example.com",
63
- port=443,
64
- supports_mtls=True,
65
- requires_client_cert=False,
66
- server_cert_info=cert_info,
67
- client_cert_requested=True,
68
- handshake_successful=False,
69
- error_message=None,
70
- cipher_suite="TLS_AES_256_GCM_SHA384",
71
- tls_version="TLSv1.3",
72
- verification_mode="default",
73
- ca_bundle_path="/etc/ssl/certs/ca-certificates.crt",
74
- timestamp=datetime.now().isoformat()
75
- )
76
- print("✓ MTLSResult model created successfully")
77
-
78
- # Test JSON serialization
79
- json_data = result.model_dump_json()
80
- print("✓ MTLSResult JSON serialization works")
81
-
82
- return True
83
-
84
- def test_cli_help():
85
- """Test the CLI help for mTLS commands."""
86
- print("\nTesting CLI help...")
87
-
88
- try:
89
- from offsec_ai.cli import main
90
- import click.testing
91
-
92
- runner = click.testing.CliRunner()
93
-
94
- # Test main help
95
- result = runner.invoke(main, ['--help'])
96
- if 'mtls-check' in result.output:
97
- print("✓ mtls-check command found in CLI help")
98
- else:
99
- print("✗ mtls-check command not found in CLI help")
100
- return False
101
-
102
- # Test mtls-check help
103
- result = runner.invoke(main, ['mtls-check', '--help'])
104
- if result.exit_code == 0:
105
- print("✓ mtls-check help works")
106
- else:
107
- print(f"✗ mtls-check help failed: {result.output}")
108
- return False
109
-
110
- # Test mtls-gen-cert help
111
- result = runner.invoke(main, ['mtls-gen-cert', '--help'])
112
- if result.exit_code == 0:
113
- print("✓ mtls-gen-cert help works")
114
- else:
115
- print(f"✗ mtls-gen-cert help failed: {result.output}")
116
- return False
117
-
118
- return True
119
-
120
- except ImportError as e:
121
- print(f"✗ Failed to import CLI: {e}")
122
- return False
123
-
124
- def main():
125
- """Run all tests."""
126
- print("Simple Port Checker - mTLS Functionality Test")
127
- print("=" * 50)
128
-
129
- tests = [
130
- test_imports,
131
- test_mtls_result_model,
132
- test_cli_help,
133
- ]
134
-
135
- passed = 0
136
- failed = 0
137
-
138
- for test in tests:
139
- try:
140
- if test():
141
- passed += 1
142
- else:
143
- failed += 1
144
- except Exception as e:
145
- print(f"✗ Test {test.__name__} failed with exception: {e}")
146
- failed += 1
147
-
148
- print("\n" + "=" * 50)
149
- print(f"Tests completed: {passed} passed, {failed} failed")
150
-
151
- if failed == 0:
152
- print("🎉 All tests passed! mTLS functionality is working correctly.")
153
- return 0
154
- else:
155
- print("❌ Some tests failed. Please check the output above.")
156
- return 1
157
-
158
- if __name__ == "__main__":
159
- sys.exit(main())
@@ -1,193 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Simple test for mTLS functionality without full installation.
4
- """
5
-
6
- import sys
7
- import os
8
-
9
- # Add the src directory to Python path
10
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
11
-
12
- def test_mtls_model():
13
- """Test the MTLSResult model directly."""
14
- print("Testing mTLS models...")
15
-
16
- try:
17
- from offsec_ai.models.mtls_result import MTLSResult, CertificateInfo
18
- from datetime import datetime
19
-
20
- # Create a test certificate info
21
- cert_info = CertificateInfo(
22
- subject="CN=test.example.com,O=Test Org,C=US",
23
- issuer="CN=Test CA,O=Test CA Org,C=US",
24
- version=3,
25
- serial_number="123456789",
26
- not_valid_before="2024-01-01T00:00:00Z",
27
- not_valid_after="2025-01-01T00:00:00Z",
28
- signature_algorithm="sha256WithRSAEncryption",
29
- key_algorithm="RSAPublicKey",
30
- key_size=2048,
31
- san_dns_names=["test.example.com", "www.test.example.com"],
32
- san_ip_addresses=["192.168.1.100"],
33
- is_ca=False,
34
- is_self_signed=False,
35
- fingerprint_sha256="a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456"
36
- )
37
- print("✓ CertificateInfo created successfully")
38
-
39
- # Create a test mTLS result
40
- mtls_result = MTLSResult(
41
- target="test.example.com",
42
- port=443,
43
- supports_mtls=True,
44
- requires_client_cert=True,
45
- server_cert_info=cert_info,
46
- client_cert_requested=True,
47
- handshake_successful=True,
48
- error_message=None,
49
- cipher_suite="TLS_AES_256_GCM_SHA384",
50
- tls_version="TLSv1.3",
51
- verification_mode="strict",
52
- ca_bundle_path="/etc/ssl/certs/ca-certificates.crt",
53
- timestamp=datetime.now().isoformat()
54
- )
55
- print("✓ MTLSResult created successfully")
56
-
57
- # Test JSON serialization
58
- json_str = mtls_result.model_dump_json(indent=2)
59
- print("✓ JSON serialization works")
60
- print(f"JSON length: {len(json_str)} characters")
61
-
62
- # Test field access
63
- print(f"✓ Target: {mtls_result.target}")
64
- print(f"✓ Supports mTLS: {mtls_result.supports_mtls}")
65
- print(f"✓ Server cert subject: {mtls_result.server_cert_info.subject}")
66
-
67
- return True
68
-
69
- except Exception as e:
70
- print(f"✗ Error: {e}")
71
- import traceback
72
- traceback.print_exc()
73
- return False
74
-
75
- def test_cli_structure():
76
- """Test if CLI structure includes mTLS commands."""
77
- print("\nTesting CLI structure...")
78
-
79
- try:
80
- # Read the CLI file to check for mTLS commands
81
- cli_file = os.path.join(os.path.dirname(__file__), 'src', 'offsec_ai', 'cli.py')
82
-
83
- with open(cli_file, 'r') as f:
84
- cli_content = f.read()
85
-
86
- # Check for mTLS command definitions
87
- mtls_commands = [
88
- 'mtls-check',
89
- 'mtls-gen-cert',
90
- 'mtls-validate-cert',
91
- '_run_mtls_check',
92
- 'MTLSChecker'
93
- ]
94
-
95
- found_commands = []
96
- for cmd in mtls_commands:
97
- if cmd in cli_content:
98
- found_commands.append(cmd)
99
- print(f"✓ Found {cmd} in CLI")
100
- else:
101
- print(f"✗ Missing {cmd} in CLI")
102
-
103
- if len(found_commands) == len(mtls_commands):
104
- print("✓ All mTLS commands found in CLI")
105
- return True
106
- else:
107
- print(f"✗ Only {len(found_commands)}/{len(mtls_commands)} commands found")
108
- return False
109
-
110
- except Exception as e:
111
- print(f"✗ Error checking CLI: {e}")
112
- return False
113
-
114
- def test_documentation():
115
- """Test if documentation includes mTLS information."""
116
- print("\nTesting documentation...")
117
-
118
- try:
119
- # Check README
120
- readme_file = os.path.join(os.path.dirname(__file__), 'README.md')
121
- with open(readme_file, 'r') as f:
122
- readme_content = f.read()
123
-
124
- if 'mTLS' in readme_content and 'mutual TLS' in readme_content:
125
- print("✓ README includes mTLS documentation")
126
- else:
127
- print("✗ README missing mTLS documentation")
128
- return False
129
-
130
- # Check API docs
131
- api_docs_file = os.path.join(os.path.dirname(__file__), 'docs', 'api.md')
132
- with open(api_docs_file, 'r') as f:
133
- api_content = f.read()
134
-
135
- if 'MTLSChecker' in api_content and 'MTLSResult' in api_content:
136
- print("✓ API docs include mTLS documentation")
137
- else:
138
- print("✗ API docs missing mTLS documentation")
139
- return False
140
-
141
- # Check examples
142
- examples_file = os.path.join(os.path.dirname(__file__), 'examples', 'mtls_examples.py')
143
- if os.path.exists(examples_file):
144
- print("✓ mTLS examples file exists")
145
- else:
146
- print("✗ mTLS examples file missing")
147
- return False
148
-
149
- return True
150
-
151
- except Exception as e:
152
- print(f"✗ Error checking documentation: {e}")
153
- return False
154
-
155
- def main():
156
- """Run all tests."""
157
- print("Simple Port Checker - mTLS Integration Test")
158
- print("=" * 50)
159
-
160
- tests = [
161
- test_mtls_model,
162
- test_cli_structure,
163
- test_documentation,
164
- ]
165
-
166
- results = []
167
- for test in tests:
168
- try:
169
- result = test()
170
- results.append(result)
171
- except Exception as e:
172
- print(f"✗ Test {test.__name__} failed with exception: {e}")
173
- results.append(False)
174
-
175
- passed = sum(results)
176
- total = len(results)
177
-
178
- print("\n" + "=" * 50)
179
- print(f"Tests completed: {passed}/{total} passed")
180
-
181
- if passed == total:
182
- print("🎉 All tests passed! mTLS functionality has been successfully integrated.")
183
- print("\nNext steps:")
184
- print("1. Install dependencies: pip install cryptography certifi")
185
- print("2. Test CLI: offsec-ai mtls-check --help")
186
- print("3. Try example: python examples/mtls_examples.py")
187
- return 0
188
- else:
189
- print("❌ Some tests failed. Please check the output above.")
190
- return 1
191
-
192
- if __name__ == "__main__":
193
- sys.exit(main())
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes