--- name: python-pentest description: Python best practices for pentest scripts — requests sessions, error handling, output, CLI args user-invocable: false --- # Python Best Practices for Pentest Scripts Follow these conventions when generating or reviewing Python test scripts in this project. ## HTTP Requests Use `requests.Session` as a context manager for connection pooling and consistent config: ```python import requests import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) with requests.Session() as session: session.proxies = {"http": proxy, "https": proxy} session.verify = False # Required for Burp interception session.headers.update({"Authorization": f"Bearer {token}"}) resp = session.get(url) ``` ### Retry on transient failures Mount a retry adapter for robustness against flaky targets: ```python from urllib3.util import Retry from requests.adapters import HTTPAdapter retries = Retry(total=3, backoff_factor=0.3, status_forcelist=[502, 503, 504]) session.mount("https://", HTTPAdapter(max_retries=retries)) session.mount("http://", HTTPAdapter(max_retries=retries)) ``` ### Timeouts Always set timeouts. Never let a request hang indefinitely: ```python resp = session.get(url, timeout=(5, 30)) # (connect, read) in seconds ``` ### Error handling Catch specific exceptions, not bare `except`. Log and continue — pentest scripts should be resilient: ```python from requests.exceptions import ConnectionError, Timeout, HTTPError try: resp = session.get(url, timeout=(5, 30)) resp.raise_for_status() except Timeout: print(f"[TIMEOUT] {url}") except ConnectionError: print(f"[CONN_ERR] {url}") except HTTPError as e: print(f"[HTTP {e.response.status_code}] {url}") ``` ## CLI Arguments Use `argparse` (not Click) in generated standalone scripts — they must run independently without project dependencies: ```python import argparse parser = argparse.ArgumentParser(description="IDOR test for /api/users/{id}") parser.add_argument("--base-url", required=True, help="Target base URL") parser.add_argument("--auth-token", required=True, help="Bearer token") parser.add_argument("--proxy", default="http://127.0.0.1:8080", help="Proxy URL (default: Burp)") parser.add_argument("--timeout", type=int, default=30, help="Request timeout in seconds") parser.add_argument("--delay", type=float, default=0, help="Delay between requests in seconds") args = parser.parse_args() ``` Required args for every script: `--base-url`, `--auth-token`, `--proxy`. ## File Output Always write results to `scripts/output/results/` as JSON. Use `pathlib` for paths: ```python from pathlib import Path from datetime import datetime import json RESULTS_DIR = Path(__file__).resolve().parent / "results" def save_results(results: list[dict], prefix: str) -> Path: RESULTS_DIR.mkdir(parents=True, exist_ok=True) outfile = RESULTS_DIR / f"{prefix}_{datetime.now():%Y%m%d_%H%M%S}.json" outfile.write_text(json.dumps(results, indent=2)) return outfile ``` ## Script Structure Every generated script should follow this order: 1. Shebang + docstring 2. Imports (stdlib, then third-party) 3. Constants (`RESULTS_DIR`) 4. Helper functions 5. `main()` with argparse 6. `if __name__ == "__main__": main()` ## Type Hints Use type hints for function signatures. Use modern syntax (`list[dict]` not `List[Dict]`): ```python def test_endpoint(session: requests.Session, url: str, ids: range) -> list[dict]: ... ``` ## Logging vs Print Use `print()` with status prefixes for real-time progress in pentest scripts. Structured logging is overkill for standalone test scripts: ```python print(f"[+] Found: {url} returned {resp.status_code}") print(f"[-] Failed: {url} — {e}") print(f"[*] Testing ID range {start}-{end}") print(f"[!] Possible vulnerability: {url}") ``` ## Security Rules - **Never hardcode secrets** — tokens, URLs, and passwords come from CLI args only - **Never import from the project** — generated scripts must be fully standalone - **`verify=False` is intentional** — required for Burp proxy interception, suppress the warning once at the top - **Default proxy to Burp** — `http://127.0.0.1:8080` - **Respect rate limits** — support `--delay` arg for throttling