mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-10 21:24:56 +03:00
c47eed6a64
- Fix 25 shell=True subprocess calls with list-based commands - Fix 49 verify=False in defensive skills (env-var override) - Add timeout to 231 HTTP/subprocess/socket calls - Fix 6 SQL injection patterns with whitelist validation - Replace 8 __import__() with standard imports - Remove 701 unused imports across 442 files - Add authorized-testing disclaimers to all offensive skills - Complete 11 incomplete skill directories - Expand 10 stub SKILL.md files with full content - Fix 2 YAML parse errors in frontmatter - Fix 5 pre-existing syntax errors - Convert 22 hardcoded paths/ports to environment variables - Back up 21 redundant skill pairs to .bak - Fix 2 global declaration errors - 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE) - 0 compile errors across all 724 agent.py files
232 lines
8.8 KiB
Python
232 lines
8.8 KiB
Python
#!/usr/bin/env python3
|
|
# For authorized testing only
|
|
"""Postman API security testing orchestration agent using Newman CLI."""
|
|
|
|
import json
|
|
import argparse
|
|
import subprocess
|
|
import os
|
|
from datetime import datetime
|
|
|
|
|
|
def run_newman_collection(collection_path, environment_path=None, reporters=None):
|
|
"""Execute a Postman collection using Newman CLI."""
|
|
cmd = ["newman", "run", collection_path]
|
|
if environment_path:
|
|
cmd.extend(["-e", environment_path])
|
|
if reporters:
|
|
cmd.extend(["--reporters", reporters])
|
|
else:
|
|
cmd.extend(["--reporters", "cli,json"])
|
|
cmd.extend(["--reporter-json-export", "newman-results.json"])
|
|
cmd.extend(["--timeout-request", "10000"])
|
|
cmd.extend(["--delay-request", "100"])
|
|
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
|
output = {
|
|
"exit_code": result.returncode,
|
|
"stdout_tail": result.stdout[-2000:] if result.stdout else "",
|
|
"stderr": result.stderr[:500] if result.stderr else "",
|
|
}
|
|
|
|
if os.path.exists("newman-results.json"):
|
|
with open("newman-results.json", "r") as f:
|
|
output["results"] = json.load(f)
|
|
return output
|
|
|
|
|
|
def parse_newman_results(results_path):
|
|
"""Parse Newman JSON results for security test outcomes."""
|
|
with open(results_path, "r") as f:
|
|
data = json.load(f)
|
|
|
|
run_data = data.get("run", {})
|
|
stats = run_data.get("stats", {})
|
|
executions = run_data.get("executions", [])
|
|
|
|
test_results = []
|
|
for execution in executions:
|
|
item = execution.get("item", {})
|
|
response = execution.get("response", {})
|
|
assertions = execution.get("assertions", [])
|
|
|
|
for assertion in assertions:
|
|
test_results.append({
|
|
"request_name": item.get("name", ""),
|
|
"test_name": assertion.get("assertion", ""),
|
|
"passed": not assertion.get("error"),
|
|
"status_code": response.get("code", 0),
|
|
"response_time_ms": response.get("responseTime", 0),
|
|
"error": assertion.get("error", {}).get("message", "") if assertion.get("error") else "",
|
|
})
|
|
|
|
failures = [t for t in test_results if not t["passed"]]
|
|
return {
|
|
"total_requests": stats.get("requests", {}).get("total", 0),
|
|
"total_assertions": stats.get("assertions", {}).get("total", 0),
|
|
"failed_assertions": stats.get("assertions", {}).get("failed", 0),
|
|
"test_results": test_results,
|
|
"failures": failures,
|
|
}
|
|
|
|
|
|
def generate_bola_collection(base_url, endpoints, user_a_token, user_b_token):
|
|
"""Generate Postman collection for BOLA/IDOR testing across two user contexts."""
|
|
items = []
|
|
for ep in endpoints:
|
|
method = ep.get("method", "GET")
|
|
path = ep.get("path", "")
|
|
items.append({
|
|
"name": f"BOLA: {method} {path} (User B accessing User A resource)",
|
|
"request": {
|
|
"method": method,
|
|
"header": [
|
|
{"key": "Authorization", "value": f"Bearer {user_b_token}"},
|
|
{"key": "Content-Type", "value": "application/json"},
|
|
],
|
|
"url": {"raw": f"{base_url}{path}", "host": [base_url], "path": path.strip("/").split("/")},
|
|
},
|
|
"event": [{
|
|
"listen": "test",
|
|
"script": {
|
|
"exec": [
|
|
"pm.test('BOLA Check: Should return 403 or 404', function () {",
|
|
" pm.expect(pm.response.code).to.be.oneOf([403, 404]);",
|
|
"});",
|
|
"pm.test('No data leakage in response', function () {",
|
|
f" pm.expect(pm.response.text()).to.not.include('{user_a_token[:10]}');",
|
|
"});",
|
|
],
|
|
"type": "text/javascript",
|
|
},
|
|
}],
|
|
})
|
|
|
|
collection = {
|
|
"info": {
|
|
"name": "BOLA/IDOR Security Tests",
|
|
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
|
},
|
|
"item": items,
|
|
}
|
|
return collection
|
|
|
|
|
|
def generate_injection_collection(base_url, endpoints):
|
|
"""Generate Postman collection for injection testing."""
|
|
injection_payloads = [
|
|
("SQL Injection", "' OR '1'='1"),
|
|
("XSS", "<script>alert(1)</script>"),
|
|
("Command Injection", "; cat /etc/passwd"),
|
|
("SSTI", "{{7*7}}"),
|
|
("Path Traversal", "../../../etc/passwd"),
|
|
]
|
|
|
|
items = []
|
|
for ep in endpoints:
|
|
path = ep.get("path", "")
|
|
params = ep.get("params", [])
|
|
for param in params:
|
|
for payload_name, payload in injection_payloads:
|
|
items.append({
|
|
"name": f"{payload_name}: {path}?{param}",
|
|
"request": {
|
|
"method": "GET",
|
|
"url": {
|
|
"raw": f"{base_url}{path}?{param}={payload}",
|
|
"host": [base_url],
|
|
"path": path.strip("/").split("/"),
|
|
"query": [{"key": param, "value": payload}],
|
|
},
|
|
},
|
|
"event": [{
|
|
"listen": "test",
|
|
"script": {
|
|
"exec": [
|
|
f"pm.test('{payload_name} — no 500 error', function () {{",
|
|
" pm.expect(pm.response.code).to.not.equal(500);",
|
|
"});",
|
|
f"pm.test('{payload_name} — no stack trace', function () {{",
|
|
" pm.expect(pm.response.text()).to.not.include('Traceback');",
|
|
" pm.expect(pm.response.text()).to.not.include('Exception');",
|
|
"});",
|
|
],
|
|
"type": "text/javascript",
|
|
},
|
|
}],
|
|
})
|
|
|
|
return {
|
|
"info": {
|
|
"name": "Injection Security Tests",
|
|
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
|
},
|
|
"item": items,
|
|
}
|
|
|
|
|
|
def run_audit(args):
|
|
"""Execute Postman API security testing audit."""
|
|
print(f"\n{'='*60}")
|
|
print(f" POSTMAN API SECURITY TESTING")
|
|
print(f" Generated: {datetime.utcnow().isoformat()} UTC")
|
|
print(f"{'='*60}\n")
|
|
|
|
report = {}
|
|
|
|
if args.collection:
|
|
newman = run_newman_collection(args.collection, args.environment)
|
|
report["newman_run"] = {"exit_code": newman["exit_code"]}
|
|
print(f"--- NEWMAN EXECUTION ---")
|
|
print(f" Exit code: {newman['exit_code']}")
|
|
|
|
if os.path.exists("newman-results.json"):
|
|
parsed = parse_newman_results("newman-results.json")
|
|
report["test_results"] = parsed
|
|
print(f"\n--- TEST RESULTS ---")
|
|
print(f" Total requests: {parsed['total_requests']}")
|
|
print(f" Total assertions: {parsed['total_assertions']}")
|
|
print(f" Failed: {parsed['failed_assertions']}")
|
|
for f in parsed["failures"][:15]:
|
|
print(f" FAIL: {f['request_name']} — {f['test_name']}")
|
|
if f["error"]:
|
|
print(f" {f['error'][:80]}")
|
|
|
|
if args.gen_bola:
|
|
endpoints = json.loads(args.gen_bola)
|
|
collection = generate_bola_collection(
|
|
args.base_url or "http://localhost:8080",
|
|
endpoints, args.token_a or "token_a", args.token_b or "token_b",
|
|
)
|
|
output_path = "bola-tests.postman_collection.json"
|
|
with open(output_path, "w") as f_out:
|
|
json.dump(collection, f_out, indent=2)
|
|
report["bola_collection"] = output_path
|
|
print(f"\n--- GENERATED BOLA COLLECTION ---")
|
|
print(f" Path: {output_path}")
|
|
print(f" Tests: {len(collection['item'])}")
|
|
|
|
return report
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Postman API Security Testing Agent")
|
|
parser.add_argument("--collection", help="Postman collection JSON to run with Newman")
|
|
parser.add_argument("--environment", help="Postman environment JSON")
|
|
parser.add_argument("--gen-bola", help="JSON array of endpoints for BOLA test generation")
|
|
parser.add_argument("--base-url", help="Base URL for generated collections")
|
|
parser.add_argument("--token-a", help="User A auth token for BOLA tests")
|
|
parser.add_argument("--token-b", help="User B auth token for BOLA tests")
|
|
parser.add_argument("--output", help="Save report to JSON file")
|
|
args = parser.parse_args()
|
|
|
|
report = run_audit(args)
|
|
if args.output:
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2, default=str)
|
|
print(f"\n[+] Report saved to {args.output}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|