From 4eedf906cad6fe8951c553285be17f3ba71fa9cb Mon Sep 17 00:00:00 2001 From: danielfdez17 Date: Thu, 5 Nov 2026 16:51:34 +0100 Subject: [PATCH] docs(defense): three-step demos for the modules of choice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue 177 asked for command-plus-expected-output demos in wiki/DEFENSE.md for the factory major (m176/m177), the SSRF minor, and m175. The SSRF minor stays its own point; the risk that a validator folds it into the major is named in the demo. README rows 8–9 and their justifications point at those demos and at the grobase live-proof branch. --- README.md | 38 ++++++++++++++++++++++++++++++++++ wiki/DEFENSE.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/README.md b/README.md index 275d17bfe..95f3cbad9 100644 --- a/README.md +++ b/README.md @@ -441,6 +441,44 @@ bonus points stop at 19. W1 replaces the two Minors W2 and W3, which cannot be counted as well. +### Modules of choice (free choice) + +| # | Module | Category | Type | Pts | Status | Evidence | Who (commits) | +|---|---|---|---|--:|---|---|---| +| 8 | **Module of choice:** contract-driven application factory | Free choice | Major | 2 | Partial | groot [`Modules of choice`](.github/workflows/modules-of-choice.yml) runs gates `m176` and `m177` statically. Live half (`BAAS_VERIFY_LIVE=1`) is wired in grobase Integration Tests on branch `ci/live-proof-gates-m175-m177` ([#177](https://github.com/Univers42/groot/issues/177)); pin that commit once CI is green. Demo steps in [`wiki/DEFENSE.md`](wiki/DEFENSE.md) | dlesieur 9 (provisioning), dlesieur 33 (self-serve) | +| 9 | **Module of choice:** SSRF guard on the HTTP engine | Free choice | Minor | 1 | CI | Kept as its own minor (not folded into row 8). 30 unit tests in grobase `src/data-plane-router/crates/data-plane-pool/src/http/mod.rs`, including `ssrf_blocks_internal_and_metadata_ips` for `169.254.169.254`; demo in [`wiki/DEFENSE.md`](wiki/DEFENSE.md) | dlesieur 8 | + +### Justification — Module of choice (Major): contract-driven application factory + +- **Why this module.** It is the central idea of the project: a backend with zero + application-specific code, where an application is a declarative contract. +- **Technical challenge.** From one JSON file, a generic provisioner must create an isolated + database, apply its schema, create roles and policies, mint scoped API keys and emit the + frontend's `PUBLIC_*` configuration — idempotently, on every boot. The self-serve endpoint + `POST /v1/tenants/me/apps` does it on demand, with a fresh `CREATE DATABASE` per application. +- **Value.** A new application costs a contract instead of a new server. The public site and + vault42 are both provisioned this way in production. +- **Why it deserves a Major.** It spans the Go control plane, the Rust data plane and the + database layer, and it comes with isolation proofs: gate `m177` proves self-serve creation, and + gate `m176` proves one application cannot read another's data (a foreign mount resolves to zero + rows — a 404, never a cross read). Both run in groot's + [`Modules of choice`](.github/workflows/modules-of-choice.yml) workflow in their static mode; + their live half is on grobase `ci/live-proof-gates-m175-m177` (Integration Tests, + `BAAS_VERIFY_LIVE=1`). Three-step demos: [`wiki/DEFENSE.md`](wiki/DEFENSE.md). + +### Justification — Module of choice (Minor): SSRF guard on the HTTP engine + +- **Why this module.** The data plane can mount a remote HTTP API as if it were a database — + which also means "let a user make our server fetch any URL", the classic SSRF hole. +- **Technical challenge.** The guard blocks loopback, private (RFC 1918), link-local — including + the cloud metadata endpoint `169.254.169.254` — CGNAT, IPv6 ULA and link-local, IPv4-mapped + addresses and internal hostnames, then **pins** the connection to the validated public IP so a + later DNS rebind cannot redirect it inward. +- **Value and weight.** Small and self-contained, and it shows security judgement on a feature we + chose to build. Kept as a separate minor from the factory major: a validator may still fold it + into the data plane — say that risk aloud. Demo: `cargo test -p data-plane-pool + ssrf_blocks_internal_and_metadata_ips` ([wiki/DEFENSE.md](wiki/DEFENSE.md)). + ### Evidence For the modules above that our earlier module table also covered: the code, diff --git a/wiki/DEFENSE.md b/wiki/DEFENSE.md index 809a1c8b5..28e333a4f 100644 --- a/wiki/DEFENSE.md +++ b/wiki/DEFENSE.md @@ -70,6 +70,60 @@ have none, so that category and the AI-opponent module are out — and **custom four-part justification in the README (why, what challenge, what value, why that weight). Our recommendation: the contract-driven factory as major, the SSRF guard as minor. +### Demo — Major: contract-driven application factory (m176 + m177) + +From a clean clone with `apps/grobase` at the pin (`git submodule update --init apps/grobase`): + +1. **Static proof (no stack).** + `bash apps/grobase/scripts/verify/m177-selfserve-apps.sh` + Expect `m177 PASS` and checks naming the flag-gated mount, `EnsureDatabase`, and the slug/DSN + Go tests. + `bash apps/grobase/scripts/verify/m176-contract-isolation-live.sh` + Expect `m176 PASS` and the resolver gated on `tenant_id`. + Same gates in CI: [`Modules of choice`](../.github/workflows/modules-of-choice.yml). + +2. **Self-serve create (live stack).** With `make all` (or grobase `make up`) and + `APPS_SELFSERVE_ENABLED=1` on tenant-control, a logged-in JWT: + `curl -sS -X POST "$KONG_URL/v1/tenants/me/apps" -H "Authorization: Bearer $DEV_JWT" -H 'Content-Type: application/json' -d '{"name":"alpha"}'` + Expect JSON with a fresh `db_id`, `api_key` and `tenant`. Create a second app (`beta`); the two + `db_id`s differ. + +3. **Cross-app isolation (live).** + `curl -sS -o /dev/null -w '%{http_code}\n' "$KONG_URL/query/v1/$BETA_DB/tables" -H "apikey: $ANON_KEY" -H "X-Baas-Api-Key: $ALPHA_KEY"` + Expect a `4xx` (foreign key → zero mount rows → 404). Own key on own `db_id` is `200`. + Live CI: grobase Integration Tests runs `m177` and `m176` with `BAAS_VERIFY_LIVE=1` + ([#177](https://github.com/Univers42/groot/issues/177)). + +### Demo — Minor: SSRF guard on the HTTP engine + +Kept as its own minor (not folded into the major): it is a security judgement on the HTTP mount +engine, not a factory feature. Risk to name aloud: a validator may call it "part of the data +plane" and refuse the separate point — have the unit-test demo ready and the README justification +open. + +1. **Unit proof (the claim they ask for).** From `apps/grobase`: + `cargo test -p data-plane-pool ssrf_blocks_internal_and_metadata_ips -- --nocapture` + Expect the test to pass while listing `169.254.169.254` among the blocked addresses + (`src/data-plane-router/crates/data-plane-pool/src/http/mod.rs`). + +2. **Same suite in CI.** grobase `CI` runs `cargo test --workspace`; the 30 HTTP SSRF tests are + in that job. Point at a green run on `main`. + +3. **What you say while it runs.** A mount whose base URL resolves to loopback, RFC 1918, + link-local (including cloud metadata) or an IPv4-mapped form is refused before the dial; the + connection is then pinned to the validated public IP so a later DNS rebind cannot walk inward. + +### Demo — Collab namespace isolation (m175, supports rows 2 and 10) + +1. `bash apps/grobase/scripts/verify/m175-collab-namespace-isolation.sh` + Expect the realtime-core authorisation tests green (wildcard cannot reach `collab:` when the + protected list is set). +2. Live (optional): `BAAS_VERIFY_LIVE=1 bash apps/grobase/scripts/verify/m175-collab-namespace-isolation.sh` + on a stack with realtime — wildcard denied on `collab:`, exact grant allowed, parity when the + flag is off. +3. Product click: two browsers on one osionos page; each sees the other's cursor. Raise + `EDITION=realtime|prod|full` first. + --- ## Code quality and teamwork