Kata: Préparation de commandes
OrdersManager consumes the fictional products API (https://products.com) to
prepare sports-product orders.
After pulumi up completes, run the Hurl smoke scenarios in smoke-tests/prod/
against the deployed Lambda Function URL:
export LAMBDA_API_KEY="your-pulumi-secret"
task smoke:prodTo see per-scenario results:
task smoke:prod:listThe URL is baked into Taskfile.yml as PROD_URL; the API key must be supplied
via the LAMBDA_API_KEY environment variable (the same Pulumi secret set with
pulumi config set --secret lambdaApiKey). These scenarios validate the
production authentication gate (A01) and the availability response contract
without relying on the local fixture.
lib/orders_manager.ml— theOrders_managerclient (list, availability, search, cart validation)lib/cohttp_backend.ml— the production SSRF-safe HTTP backend (dial-time IP check)lib/product.ml/lib/wire.ml— wire types (Product,Cart_item,Cart_validation, …)lib/test/orders_manager_test.ml— unit tests (mocked HTTP backend)server/main.exe— a real HTTP server emulating the products API, used as the smoke-test targetinfra/catalog/products.json— the single shared product catalog (JSON fixture) served by both the local fixture and the production Lambda layersmoke-tests/*.hurl— QA smoke scenarios, run with thehurltool against a real serverintegration/client_smoke.ml— standalone client integration test driving the real client against the fixturedocs/dantotsu/— Dantotsu defect-analysis reports (added per fix, removed once verified)
The products API fixture and the OrdersManager client include OWASP-aligned
controls. Each control had a Dantotsu root-cause analysis (reports are removed
once their fixes are verified; see the Dantotsu defect analysis section).
-
A01 – Broken Access Control
- The fixture authenticates every request with an
x-api-keyheader (-api-keyflag /PRODUCTS_API_KEYenv); anonymous calls get401. - The listen address defaults to loopback and refuses non-loopback binds
unless
-allow-externalis passed, preventing accidental exposure. - The
Orders_managerclient sendsx-api-keyvia the~api_keyoption when the upstream API requires authentication.
- The fixture authenticates every request with an
-
A02 – Cryptographic Failures
Orders_manager.createrejects plainhttp://base URLs by default; use~allow_insecure_http:trueonly for the local dev fixture.- The Cohttp backend verifies TLS against the default CA trust store
(
ca-certs); it does not pin custom RootCAs or a minimum TLS version. - The fixture can serve HTTPS via
-tls-cert/-tls-key, and setsStrict-Transport-Securityover TLS plus baseline defensive headers.
-
A04 – Insecure Design
- SSRF protection: the base URL host is validated at construction time
(IP-literal check) and at dial time (DNS resolution + IP range check) to
prevent requests to cloud metadata endpoints (
169.254.169.254), loopback, private subnets, and link-local addresses. - Negative quantity validation:
validate_cartrejects zero or negativeCart_item.quantityvalues (Cart_item_invalid_quantityreason), preventing cart-total manipulation.
- SSRF protection: the base URL host is validated at construction time
(IP-literal check) and at dial time (DNS resolution + IP range check) to
prevent requests to cloud metadata endpoints (
-
A05 – Security Misconfiguration
- The server sets
ReadHeaderTimeout,ReadTimeout,WriteTimeout,IdleTimeout, andMaxHeaderBytes(1 MiB). - The
recovermiddleware catches exceptions in any handler or middleware, logs them, and returns a clean500instead of crashing the process. - Security headers (
X-Content-Type-Options,X-Frame-Options,Cache-Control,Content-Security-Policy, HSTS over TLS) are set on every response. - The deployed Lambda Function URL (infra) caps reserved concurrency and enforces a ≥32-byte API key to resist brute-force and billing-DoS.
- The server sets
-
A09 – Security Logging and Monitoring Failures
- The server logs every request (method, path, status, remote, elapsed) via
the
Logmodule through therequest_loggermiddleware. - The
Orders_managerclient logs outbound requests (URL, status, count, elapsed), upstream errors, and cart validation failures at appropriate levels.
- The server logs every request (method, path, status, remote, elapsed) via
the
-
A10 – Server-Side Request Forgery (SSRF)
- See A04 SSRF protection above — the same countermeasure addresses A10.
Orders_manager.create option |
Purpose |
|---|---|
~api_key:"…" |
Sets the x-api-key header on outbound requests (A01). |
~http_client:(module …) |
Injects the HTTP backend (production: Cohttp_backend; tests: Fake_transport; test-only SSRF bypass). |
~allow_insecure_http:true |
Allows http:// base URLs for local dev only (A02). |
~logger |
Injects a Log.t for structured logging (A09). |
lib/log.ml (the Log module) is the mandatory structured-logging interface
for all code in this repository. Raw print_endline/Printf.printf MUST NOT
be used for security-relevant events.
| Level | When to use |
|---|---|
Info |
Successful request/call completion (access log). |
Warn |
Recoverable failure: non-OK upstream status, cart validation failure, unauthenticated request rejected. |
Error |
Unexpected failure: request build error, transport error, JSON decode error, recovered panic. |
Every request is logged once by the request_logger middleware with:
| Field | Example | Notes |
|---|---|---|
method |
GET |
HTTP method |
path |
/products |
URL path |
status |
200 |
Response status code |
remote |
127.0.0.1:54321 |
RemoteAddr |
elapsed_ms |
3 |
Wall-clock latency in ms |
Recovered panics are logged at Error with method, path, remote, and
panic by the recover middleware.
| Event | Level | Fields |
|---|---|---|
| Request completed | Info |
url, status, product_count, elapsed |
| Non-OK upstream status | Warn |
url, status, elapsed |
| Request build error | Error |
base_url, error |
| Transport error | Error |
url, elapsed, error |
| JSON decode error | Error |
url, status, error |
| Cart validation failure | Warn |
item_count, invalid_count, details |
- All field names are
snake_casestring keys (theLogmodule's key-value pairs). elapsed/elapsed_msare wall-clock durations from request start.- Never log secrets (API keys, credentials). The
x-api-keyvalue is never emitted; only its presence/absence is implied by the auth outcome. - New HTTP handlers or outbound HTTP calls MUST follow this contract — see docs/checklists/code-review.md.
The smoke suite is black-box HTTP: it runs hurl --test against a real server
process, plus a standalone OCaml client integration test that drives the real
Orders_manager client (via the SSRF-safe Cohttp_backend) against the same
fixture. By default the Taskfile boots the local server/main.exe fixture on
127.0.0.1:18080; set BASE_URL to point at a deployed instance instead.
task smoke # run every Hurl scenario + the client integration test
task smoke:list # per-scenario PASS/FAIL summary
task smoke:count # how many scenarios
task integration:client # run only the OCaml client integration test (fixture must be running)The client integration test (integration/client_smoke.exe) exercises the
client↔server auth contract end-to-end (A01): a manager with the wrong
x-api-key must fail and one with the right key must succeed, closing the gap
the Hurl (server-only) and Alcotest (mock-only) suites left open.
Prerequisites: go, hurl, task.
Every OWASP issue fixed in this kata had a Dantotsu root-cause analysis report
under docs/dantotsu/ (following the _template.md formalism). Each report was
removed once its countermeasure was verified in code and tests; the security
controls themselves remain in place (documented in the OWASP controls above).
The _template.md formalism is retained for any future report.