Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
54 changes: 54 additions & 0 deletions wiki/DEFENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading