Add HTTP differential testing tools

This commit is contained in:
bearsyankees
2026-08-19 12:04:05 -04:00
parent aa5867f5df
commit 7cb8da7e5a
2 changed files with 199 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
---
name: hurl
description: Reproducible, reviewable HTTP request chains and response assertions with Hurl for authorized multi-step security validation, vulnerable-versus-fixed regression cases, captured values, and low-rate semantic oracles
---
# Hurl Security Regression Playbook
Use [Hurl](https://hurl.dev/) when a security proof requires an ordered HTTP session whose requests, captured values, and assertions should be code-reviewed and replayed. It is well suited to authentication flows, redirects, cookies, CSRF tokens, upload lifecycles, patch regression, and paired semantic-differential cases.
Hurl sends exactly what the file describes. It does not make state-changing requests safe. Review scope, methods, targets, and captured secrets before every run.
## Install
Prefer an official release binary or package. On macOS:
```bash
brew install hurl
hurl --version
```
Official alternatives include release packages and `cargo install --locked hurl`; see [installation](https://hurl.dev/docs/installation.html). Record the tool version with results.
## Minimal Chain
```hurl
# lab-regression.hurl
GET {{base_url}}/session
HTTP 200
[Captures]
csrf: xpath "string(//input[@name='csrf']/@value)"
[Asserts]
header "Content-Type" startsWith "text/html"
POST {{base_url}}/action
Content-Type: application/x-www-form-urlencoded
[FormParams]
csrf: {{csrf}}
operation: noop
HTTP 204
```
Hurl keeps cookies across requests in the same file, so an explicit `Cookie` header is unnecessary here.
Run one reviewed case against one authorized target first:
```bash
hurl --test --jobs 1 --connect-timeout 5s --max-time 15s \
--variable base_url=https://lab.example lab-regression.hurl
```
When credentials are required, pass them with `--secrets-file local-secrets.env`, keep that file outside version control, and avoid verbose/debug output that could expose headers or bodies. Use `--variables-file` only for non-secret environment values.
## Designing a Security Regression
- Assert the security invariant, not only a status code: denied identity, final normalized location, absence/presence of a structural field, unchanged object state, or exact benign result.
- Capture only values needed by later requests. Do not write tokens, personal data, or response bodies into committed reports.
- Encode a malformed but non-triggering control alongside the suspected case.
- Run the same file against vulnerable and fixed builds through `base_url` or other explicit variables.
- Keep state-changing methods in a clearly labeled lab/staging file; prefer no-op actions, inert markers, and cleanup requests.
- Check every redirect step when the vulnerability crosses routing, origin, or authentication boundaries. Blindly following redirects can hide the relevant transition.
- Use unique canaries so cached or pre-existing state cannot create a false positive.
## Chain Structure
Organize longer files around capability transitions:
```text
fingerprint -> establish session -> reach boundary -> prove primitive -> verify state -> cleanup
```
At each response, assert the condition required by the next request. A final success assertion cannot explain which earlier assumption failed.
Useful Hurl features include:
- captures from headers, cookies, JSONPath, XPath, and regex queries
- assertions over status, headers, body, JSON/XML, redirects, and timing
- request-local options and variables
- `--test` plus JSON, JUnit, TAP, or HTML reports
Consult the [Hurl manual](https://hurl.dev/docs/manual.html) for version-specific syntax instead of guessing an option.
## Safety Rules
- Use an explicit `base_url`; never derive the destination from untrusted response data without validating scheme, host, and port.
- Review POST/PUT/PATCH/DELETE requests and server-side side effects before replay.
- Set bounded timeouts and retries for the target; do not use polling as an unbounded brute-force loop.
- Do not use Hurl for raw HTTP parser/smuggling cases when its HTTP stack normalizes the bytes being tested; use an appropriate raw harness in an isolated lab.
- Use `--path-as-is` when literal `/../` or `/./` path segments are the behavior under test; otherwise Hurl's underlying URL handling can normalize them.
- Redact reports. HTML/JSON/JUnit artifacts may contain request URLs, headers, captured variables, and response snippets.
- Keep authentication material in local secret storage and use dedicated test accounts with minimum privilege.
## Validation Deliverable
1. reviewed `.hurl` file with variableized target and no embedded secrets
2. vulnerable, fixed, and negative-control environment descriptions
3. assertion at every capability transition
4. deterministic results with tool version and timestamps
5. side effects, cleanup, and residual-state check
6. redacted report appropriate for sharing
+100
View File
@@ -0,0 +1,100 @@
---
name: hypothesis
description: Property-based local differential testing with Hypothesis for parsers, canonicalizers, serializers, validators, routers, and other pure functions, emphasizing explicit invariants, shrinking, reproducibility, and bounded resource use
---
# Hypothesis Differential Testing
Use [Hypothesis](https://hypothesis.readthedocs.io/) when a security property can be expressed over local code and failures are likely to hide in combinations of encoding, normalization, structure, or parser recovery. It is especially useful for comparing two implementations or checking that validation and consumption preserve the same meaning.
Do not point unrestricted generators at a live service. Hypothesis is safest and most useful against pure local adapters with no network, subprocess, filesystem, or persistent-state side effects.
## Install
Use an isolated virtual environment and install a reviewed pinned version:
```bash
python -m pip install 'hypothesis==<reviewed-version>'
```
Official project: [Hypothesis](https://github.com/HypothesisWorks/hypothesis)
## Start From an Invariant
Write the security relationship before writing strategies. Examples:
```text
allowlist(raw) implies sink(canonicalize(raw)) remains inside the allowed origin/path
validator(raw) accepts implies consumer(raw) assigns the same media type/structure
parse_A(raw) and parse_B(raw) agree on message boundaries and authoritative fields
serialize(parse(raw)) cannot introduce a delimiter, wildcard, traversal, or new field
```
A test that only checks “does not crash” can find robustness bugs but does not establish a security differential.
## Minimal Differential Harness
```python
from hypothesis import given, settings, strategies as st
def outcome(parser, raw):
try:
return ("accept", parser(raw))
except ExpectedParseError as exc:
return ("reject", type(exc).__name__)
@settings(max_examples=250, deadline=500)
@given(st.text(max_size=128))
def test_security_boundary(raw: str) -> None:
checked = outcome(security_parser, raw)
consumed = outcome(sink_parser, raw)
assert equivalent_security_meaning(checked, consumed)
```
- Bound string/list/binary sizes, recursion, examples, and deadline.
- Build structured inputs from relevant tokens rather than generating unrestricted noise.
- Normalize expected accept/reject/error outcomes explicitly so ordinary parser rejection is not mistaken for a property-test failure.
- Use `st.one_of`, `st.sampled_from`, `st.lists`, `st.binary`, `st.text`, and composite strategies to represent the actual grammar.
- Add explicit edge seeds with `@example` for known delimiters and regressions.
- Let Hypothesis shrink failures; the minimal counterexample is often the clearest explanation of the parser disagreement.
## High-Value Strategy Axes
- percent and double encoding, malformed escapes, mixed separators
- Unicode normalization, replacement characters, surrogates, case folding, IDNA
- dot segments, slash/backslash, absolute/relative paths, sibling-prefix collisions
- duplicate, empty, first/last, comma-joined, or differently cased fields
- declared length versus actual bytes, truncation, padding, and terminators
- nested objects, parser depth, ordering, unknown keys, and error recovery
- serialize/deserialize round trips and version-to-version behavior
Generate only axes supported by the target's transformation graph. Cartesian payload spraying obscures causality.
## Reproducibility
- Keep the minimized failing example as a normal regression test.
- Preserve code revision, dependency lock, locale, platform, and parser/library versions.
- Keep Hypothesis's example database in a task-specific artifact directory when replay across runs matters.
- For CI, rely on stored explicit regressions for critical cases; randomized discovery supplements them.
- Classify nondeterminism before suppressing health checks. Timing, global state, environment, and shared caches can create flaky false differentials.
## Safety and Resource Controls
- Adapt target functions so tests cannot reach the network or execute commands.
- Use temporary directories and non-secret corpora for parsers that require files.
- Put native parsers in a disposable, networkless process/container with CPU, memory, file-size, and process ceilings.
- Do not disable deadlines globally to hide hangs; isolate and bound intentionally slow examples.
- A crash, timeout, or excessive allocation is a robustness result. Prove a security boundary or exploitability separately.
- Never reuse captured credentials, customer content, or production requests as generative corpora without sanitization.
## Validation Deliverable
1. stated invariant and why it protects a security boundary
2. adapters and exact component/version pair compared
3. bounded strategies and resource settings
4. minimized counterexample and both interpretations
5. stable explicit regression test
6. impact trace from disagreement to privileged consumer
7. fixed-version or corrected-invariant result