Summary
Out of the box the control plane is a fully open, tenant-spoofable API.
Evidence
crates/sandboxwich-api/src/main.rs:908-948 (auth_and_tenant). When neither SANDBOXWICH_API_TOKEN (shared_token) nor SANDBOXWICH_TENANT_TOKENS is configured, the final else branch trusts the caller-supplied x-sandboxwich-tenant header and serves the request unauthenticated:
} else {
request.headers().get("x-sandboxwich-tenant")
.and_then(|value| value.to_str().ok())
.map(str::trim).filter(|t| !t.is_empty())
.unwrap_or(&state.default_tenant_id).to_string()
};
database_url defaults to a local SQLite file and auto_migrate defaults true, so the default configuration is an open API where any client selects any tenant by header — bypassing every ensure_tenant check. Separately, in shared-token mode every authenticated caller is assigned default_tenant_id, so that mode has no tenant isolation; only tenant_tokens mode segregates tenants.
Why it matters
This is a total authn/authz bypass in the default deployment and a silent single-tenant collapse in shared-token mode. The README advertises multi-tenant boundaries.
Suggested fix
- Fail closed: refuse to start
serve (or reject all non-probe requests) when no auth is configured.
- Never derive tenant from a client header in an authenticated deployment; bind tenant to the presented credential.
- Document explicitly that shared-token mode is single-tenant only.
Summary
Out of the box the control plane is a fully open, tenant-spoofable API.
Evidence
crates/sandboxwich-api/src/main.rs:908-948(auth_and_tenant). When neitherSANDBOXWICH_API_TOKEN(shared_token) norSANDBOXWICH_TENANT_TOKENSis configured, the finalelsebranch trusts the caller-suppliedx-sandboxwich-tenantheader and serves the request unauthenticated:database_urldefaults to a local SQLite file andauto_migratedefaults true, so the default configuration is an open API where any client selects any tenant by header — bypassing everyensure_tenantcheck. Separately, in shared-token mode every authenticated caller is assigneddefault_tenant_id, so that mode has no tenant isolation; onlytenant_tokensmode segregates tenants.Why it matters
This is a total authn/authz bypass in the default deployment and a silent single-tenant collapse in shared-token mode. The README advertises multi-tenant boundaries.
Suggested fix
serve(or reject all non-probe requests) when no auth is configured.