Security Assurance Case¶
This document is httptap's security assurance case. It explains why the project believes its security properties hold, not just what those properties are. It is structured according to the OpenSSF Best Practices silver-level assurance_case criterion.
Last reviewed: 2026-10-05.
The assurance case is a living document; it is reviewed at every major release and whenever the threat landscape or feature set changes materially. Proposals for amendments are accepted as pull requests against this file.
What httptap Is¶
httptap is a command-line diagnostic tool. A developer supplies a single URL (and optionally headers, a body, a proxy, a CA bundle, etc.) and httptap performs one HTTP request (or a short redirect chain) and renders per-phase timing and TLS information. It does not:
- accept network input from untrusted peers (it is not a server);
- manage user accounts, sessions, or long-lived credentials;
- execute remote code or evaluate server-supplied scripts;
- persist secrets or user data beyond the optional
--jsonreport,--hararchive and--prometheustextfile; - send measurements anywhere other than the OTLP collector the user names with the optional
--otlp.
Security Requirements¶
The project commits to the following observable security properties. Each is mapped to supporting arguments in the sections below.
| # | Requirement | Rationale |
|---|---|---|
| SR-1 | TLS certificate verification is enabled by default for every HTTPS target. | Prevents passive and active MITM by default. |
| SR-2 | Plaintext HTTP, weakened TLS, or custom CA bundles require an explicit user opt-in. | Ensures insecure configurations are always deliberate. |
| SR-3 | Credentials supplied by the user (Authorization, Cookie, Proxy-Authorization headers) are not sent to redirect targets on a different origin (scheme, host or port), and request bodies are not re-sent after a redirect that switches the method to GET. | Prevents credential theft via open redirects. |
| SR-4 | The tool does not execute content served by the remote host. | No code-execution primitive from the server. |
| SR-5 | Release artifacts (PyPI wheels/sdist, container images, git tags and release commits) are signed and their build provenance is verifiable. | Protects users from tampered distributions. |
| SR-6 | All CI workflow tokens follow least privilege and are pinned by SHA. | Reduces the attack surface of the build pipeline. |
| SR-7 | Supply chain (dependencies, GitHub Actions, Docker images) is monitored for known vulnerabilities. | Timely patching of upstream weaknesses. |
Trust Boundaries¶
┌─────────────────────┐
│ CLI user │ trusted
│ (argv, stdin, env) │
└──────────┬──────────┘
│
▼
┌─────────────────────┐ --json, --har, --prometheus ┌─────────────────────┐
│ httptap process │ ────────────────────────────► │ Local files, stdout │ trusted
│ (Python 3.11+) │ └─────────────────────┘
│ │ --otlp (OTLP/HTTP) ┌─────────────────────┐
│ │ ────────────────────────────► │ OTLP collector │ user-chosen
└──────────┬──────────┘ └─────────────────────┘
│ TLS/HTTP ◄─── untrusted: network, proxy, remote host
▼
┌─────────────────────┐
│ Remote HTTP server │ untrusted
└─────────────────────┘
- User → httptap is trusted: the user is assumed to have legitimate reasons to issue any given request. Input validation still rejects malformed URLs, methods, timeouts, etc. to prevent operator mistakes.
- httptap → network → remote server is untrusted. All data crossing this boundary is treated as attacker-controlled: response headers, status codes,
Locationvalues, TLS certificates, content bodies. - httptap → local outputs is trusted:
--jsonwrites the report to a file or stdout,--harwrites a HAR 1.2 archive to a file or stdout, and--harand--prometheuswrite their files atomically (temporary file in the same directory, then rename). Prometheus labels carry only the hostname and redirect step number, never paths or query strings. Files land where the user points them and are readable by whoever can read that location. - httptap → OTLP collector crosses the network to an endpoint the user supplies with
--otlp(optionalhttptap[otel]extra), overhttp://orhttps://as given. Each request step becomes one span with child spans per phase, carrying the method, status code, body size, hostname, peer IP, HTTP and TLS versions, and the error message of a failed step. Spans never include the full URL (path, query string, credentials) or any headers. Delivery failures are reported as warnings and do not change the exit code. - Build pipeline → PyPI / GitHub Releases is a separate trust boundary secured by GitHub OIDC (no long-lived keys), Sigstore signing, and SHA- pinned actions.
Threat Model¶
Threats are listed using the STRIDE categories that apply to a diagnostic HTTP client. Threats outside the scope of a client (e.g., server-side DoS) are explicitly excluded as non-goals.
| STRIDE | Threat | Mitigation |
|---|---|---|
| Spoofing | Attacker impersonates the intended HTTPS server. | TLS certificate verification on by default (SR-1); --ignore-ssl is opt-in and documented as unsafe (SR-2). |
| Spoofing | Malicious PyPI mirror serves tampered wheel. | PyPI uses TLS; releases are Sigstore-signed with SLSA v1.0 provenance (SR-5); users can verify with gh attestation verify. |
| Tampering | Modified artifact on GitHub Releases. | Same as above — build provenance attestations allow independent verification. |
| Tampering | CI pipeline poisoned via compromised third-party action. | Every action is SHA-pinned (enforced by Scorecard Pinned-Dependencies 10/10 and zizmor pedantic); Dependabot raises PRs to update pins (SR-6, SR-7). |
| Repudiation | — | Out of scope; httptap is not a multi-user system. |
| Information disclosure | Credentials in -H Authorization leak to redirect target on a different host. | httptap follows redirects itself (follow_redirects=False in httpx) and drops Authorization, Cookie and Proxy-Authorization when a redirect changes scheme, host or port; 303, and 301/302 after POST, switch to GET without a body (SR-3). |
| Information disclosure | --json or --har export includes auth headers or proxy credentials on disk. | Authorization, Proxy-Authorization, Cookie, Set-Cookie and API-key headers are masked in output and export, and URL credentials are redacted in the target and proxy URLs, in Location/Content-Location headers and the redirect target, and in the --otlp endpoint shown in export warnings; users are still advised in SECURITY.md and docs/troubleshooting.md to review exports before sharing. |
| Information disclosure | Telemetry exports reveal request details to whoever reads the textfile or runs the collector. | Prometheus labels are limited to hostname and step; OTLP spans omit the full URL and headers. OTLP export is opt-in and goes only to the endpoint named with --otlp; https:// is recommended for remote collectors. |
| Information disclosure | MITM on insecure proxy. | Proxy URLs are validated (scheme, host, port); socks5h:// / https:// recommended for sensitive targets; proxy source is reported in output and JSON for audit. |
| Denial of service | Malicious server streams unbounded body. | -m/--timeout (default 20s) is a hard deadline for the whole chain: a watchdog shuts the connection down when it passes, so a server that stalls or trickles bytes cannot extend the run. |
| Denial of service | Malicious server streams zip bomb or gigantic body. | httptap does not decode or persist bodies beyond counting bytes for the timing metric, so memory cost is linear and bounded by the timeout. |
| Elevation of privilege | Malicious response body triggers parser RCE. | Bodies are never parsed for content — only length is read. No HTML, JS, or embedded-script interpretation (SR-4). |
| Elevation of privilege | Malicious CLI argument triggers shell injection in downstream invocation. | Arguments are parsed by argparse (no shell), forwarded as list[str] to httpx (no shell); there is no shell invocation in the request path. |
Out-of-scope threats¶
- Adversary with local code execution on the developer's machine. Out of scope — that adversary already owns the process.
- Adversary controlling the user's terminal / TTY. Out of scope.
- Cryptanalytic attacks on TLS itself. Delegated to OpenSSL; mitigations are inherited from the system Python build.
- Post-quantum threats. Tracked upstream (OpenSSL / Python); out of scope for httptap itself.
Applied Secure-Design Principles¶
Mapped to Saltzer & Schroeder (1975) plus modern additions.
| Principle | Application in httptap |
|---|---|
| Economy of mechanism | Small codebase (~6 kLoC), one purpose, no plugin loader, no runtime config files. |
| Fail-safe defaults | TLS verification on, sane default timeout, HTTP/2 preferred, no redirect following by default. |
| Complete mediation | Every outbound HTTP request is routed through HTTPClientRequestExecutor; there is no legacy code path. The only secondary path is a TLS-only probe to the same host and port, without an HTTP request: a fallback probe when the live connection exposes no TLS data, and an unverified diagnostic probe that reports the certificate after a verification failure (the request still fails). Both are skipped when a proxy is in use and bounded by the request deadline. |
| Open design | Entire codebase is Apache-2.0 on GitHub; no security-through-obscurity. |
| Separation of privilege | Release pipeline is separate from development environment; PyPI publishing uses a GitHub Environment gated by OIDC. |
| Least privilege | Every CI job declares explicit minimum permissions:; no workflow has write-all. Token-Permissions Scorecard check scores 10/10. |
| Least common mechanism | No shared state across runs (single-request tool); no caches or background daemons. |
| Psychological acceptability | Curl-compatible flag aliases (-X, -L, -k, -x, -H) keep the mental model familiar. |
| Work factor | Attacker gains over a developer's local curl invocation are essentially zero — httptap exposes no more than curl does. |
| Compromise recording | JSON export captures the full request/response metadata and the proxy source, so post-hoc forensics is straightforward. |
| Defense in depth | Input validation + TLS verification + pinned build dependencies + SAST + secret scanning + Dependabot + signed releases. |
Countered Common Implementation Weaknesses¶
Derived from the CWE Top 25 (2023) and OWASP ASVS 4.0. Items not listed are either not applicable to an HTTP client or handled upstream.
| CWE | Weakness | Countermeasure |
|---|---|---|
| CWE-20 | Improper input validation | argparse enum/type coercion; URL/method/timeout/proxy explicitly checked (proxy scheme, host and port, with exit code 64); -H names must be RFC 9110 tokens and values printable ASCII; redirect targets are validated before they are followed. |
| CWE-22 | Path traversal (in @file data loader) | Path is taken verbatim from the user; no server-supplied path is ever used to open a file. |
| CWE-78 | OS command injection | No subprocess/os.system call on user-controlled data in the request path. |
| CWE-79 | XSS | No HTML rendering; server-controlled values (URL, Server, Location, certificate fields, error messages) are escaped with rich.markup.escape before Rich rendering, and single-line modes print without markup. |
| CWE-89 | SQL injection | No database. |
| CWE-94 | Code injection | eval/exec are not used; response bodies are never parsed. |
| CWE-113 | HTTP request splitting (CRLF in headers) | -H values containing CR, LF or other control characters are rejected before any request is made. |
| CWE-116 | Improper output encoding | Server-controlled strings are escaped before Rich markup rendering; JSON export uses json.dumps with strict escaping. |
| CWE-200 | Sensitive information disclosure | Sensitive headers are masked and URL credentials (target, proxy, Location/Content-Location, --otlp endpoint) are redacted in output, warnings and JSON export; Prometheus and OTLP exports carry no URL paths, query strings, or headers; credential headers are not forwarded to other origins on redirects (SR-3); SECURITY.md and docs advise reviewing exports before sharing. |
| CWE-295 | Improper certificate validation | TLS verification on by default; --ignore-ssl opt-in only, explicitly documented. |
| CWE-319 | Cleartext transmission | HTTPS preferred; plain HTTP requires explicit http:// URL; proxy source reported. |
| CWE-327 | Broken crypto | Delegated to stdlib ssl; weak algorithms surface only when diagnosing remote servers. |
| CWE-330 | Insufficient randomness | No RNG use beyond OpenSSL-provided CSPRNG for TLS. |
| CWE-352 | CSRF | Not applicable — httptap is a client, not a server. |
| CWE-400 | Uncontrolled resource consumption | Total deadline for the whole chain, enforced even on stalled reads; bounded redirect chain (max 10). |
| CWE-502 | Unsafe deserialization | json.loads only; no pickle, yaml.load, or marshal. |
| CWE-601 | Open redirect (credential leak) | Redirects are followed by httptap with an explicit origin check: Authorization, Cookie and Proxy-Authorization are dropped on cross-origin hops. |
| CWE-918 | SSRF | httptap is the client; it does not proxy requests on behalf of other systems. |
Supply-Chain Assurance¶
Supporting the release-integrity property (SR-5):
- Publishing: PyPI (and TestPyPI as a pre-production smoke test) via GitHub OIDC Trusted Publishing — no long-lived PyPI tokens anywhere. PEP 740 attestations are surfaced on PyPI as "Verified publisher".
- Container images: multi-arch (linux/amd64, linux/arm64) images are built with Buildx and pushed to GHCR, signed keylessly with cosign, and accompanied by SLSA build provenance attached to the registry.
- Git signing: release commits and annotated tags are signed keylessly with gitsign (x.509 via Fulcio
- Rekor transparency log), using the release workflow's OIDC identity.
- Signing: Sigstore keyless signing through
actions/attest-build-provenanceand cosign. Signing keys are short-lived, issued per-run by Fulcio, and verifiable via the Rekor transparency log. - Provenance: SLSA v1.0 attestation accompanies every wheel, sdist, and container image digest.
- Dockerfile linting:
hadolintruns on every PR with a warning-level failure threshold. - Pinning: every GitHub Action in every workflow is pinned by SHA; enforced by Scorecard Pinned-Dependencies and zizmor pedantic on every PR.
- Dependency tracking: SBOM in CycloneDX and SPDX formats is generated during release and attached as a GitHub Release asset.
- Exploitability disclosure: an OpenVEX document (
httptap-X.Y.Z.openvex.json) ships alongside the SBOM, declaring for each dependency CVE whetherhttptapis actually affected. The source of truth is versioned in.vex/httptap.openvex.json; scanners that consume VEX (Grype, Trivy, Snyk) use it to suppress false-positive alerts on unreachable vulnerable code paths.
Users can verify a downloaded artifact independently:
Known Residual Risks¶
These are documented rather than mitigated. They represent trade-offs that are explicit rather than oversights.
- Solo maintainer. Bus factor is 1 (tracked in GOVERNANCE.md). The continuity plan mitigates single-point-of-failure for operations but not for code review: a single reviewer can merge changes without a second set of eyes. Pre-commit, CI gates, and public audit trail partly compensate.
- No runtime sandboxing. httptap executes with the user's full privileges. This is appropriate for a developer diagnostic tool but means a bug in
httptapitself runs with the user's privileges. - TLS trust anchors inherited from the OS. If the OS trust store is compromised (e.g., a corporate MITM proxy installs a private CA), httptap cannot detect this. The
network.tls_custom_caandproxy_sourcefields in the JSON export document whether a custom CA bundle or proxy was in use.
Change History¶
| Date | Notes |
|---|---|
| 2026-04-12 | Initial assurance case for httptap 0.4.7 (silver submission). |
| 2026-04-13 | OSS hardening for 0.5.0: gitsign-signed release commits/tags, TestPyPI pre-flight, signed GHCR container images with SLSA provenance, hadolint in CI, man-page artifact. |
| 2026-09-17 | Security fixes in 0.6.2 (GHSA-pgxm-hj3g-p7wv): SR-3 is enforced by an explicit origin check on redirects, server-controlled values are escaped before Rich rendering (CWE-79/116), proxy credentials are redacted (CWE-200); OpenVEX now records the advisory status. |
| 2026-10-05 | Added the --prometheus textfile and --otlp trace outputs to the trust boundaries, threat model, and CWE-200 countermeasures; documented the TLS fallback and diagnostic probes under complete mediation; documented the input validation of -x/--proxy and -H (CWE-20, CWE-113), URL credential redaction in Location headers and the --otlp endpoint (CWE-200), and the hard total deadline (CWE-400). |
References¶
- SECURITY.md — vulnerability reporting process and supported versions.
- GOVERNANCE.md — project roles, decisions, and continuity plan.
- ROADMAP.md — scope, non-goals, and deprecation policy.
- Troubleshooting & FAQ — operational guidance.
- CWE Top 25 and OWASP ASVS 4.0 — reference catalogs of implementation weaknesses.