Reconcile docs with reality; fix module graph, token compare, health #1
@@ -142,7 +142,11 @@ func main() {
|
||||
providerHealth := map[string]*provider.Supervisor{}
|
||||
surface := func(r *http.Request) authz.Surface {
|
||||
v := authz.ParseSurface(r.Header.Get("X-Orchestra-Surface"))
|
||||
if v == "" {
|
||||
if v == "" || v == authz.System {
|
||||
// System means "the plane itself, in-process" and is always
|
||||
// FullControl with no token gate (AUDIT.md B8) — it must never
|
||||
// be constructible from an HTTP request, or any LAN caller
|
||||
// declaring this header gets unauthenticated full control.
|
||||
return authz.Web
|
||||
}
|
||||
return v
|
||||
|
||||
@@ -75,6 +75,16 @@ func HTTP(tokens map[Surface]string, next http.Handler) http.Handler {
|
||||
if s == "" {
|
||||
s = Web
|
||||
}
|
||||
// System means "the plane itself, in-process" (router, coordinator,
|
||||
// adapters, lease-expiry reclaim) and is always FullControl with no
|
||||
// token gate — it must never be reachable by declaring it over HTTP.
|
||||
// Without this, tokens[System] being unset (as it is by default: no
|
||||
// caller ever needs a System token) makes the check at line ~78 a
|
||||
// no-op, and any LAN request with this header gets unauthenticated
|
||||
// full control over every task.
|
||||
if s == System {
|
||||
s = Web
|
||||
}
|
||||
if expected := tokens[s]; expected != "" && r.Header.Get("Authorization") != "Bearer "+expected {
|
||||
http.Error(w, "unauthorized surface", http.StatusUnauthorized)
|
||||
return
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package authz
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSurfaceCapabilities(t *testing.T) {
|
||||
if Telegram.CanEmit("TaskCreated") || Ntfy.CanEmit("ApprovalRequested") {
|
||||
@@ -13,3 +17,32 @@ func TestSurfaceCapabilities(t *testing.T) {
|
||||
t.Fatal("mcp gate is wrong")
|
||||
}
|
||||
}
|
||||
|
||||
// TestSystemSurfaceDowngradedByHTTPMiddleware guards half of B8: System
|
||||
// means "the plane itself, in-process" and is always FullControl with no
|
||||
// token gate, since no deployment configures a token for a surface no HTTP
|
||||
// caller is meant to use. HTTP() must never let a request pass through
|
||||
// treated as System, or a caller declaring X-Orchestra-Surface: system gets
|
||||
// an unconditional, unauthenticated bypass of the token check below it.
|
||||
// (Downstream handlers must independently avoid re-deriving System from the
|
||||
// raw header themselves — see cmd/orchestra/main.go's `surface` closure,
|
||||
// which this package cannot test directly.)
|
||||
func TestSystemSurfaceDowngradedByHTTPMiddleware(t *testing.T) {
|
||||
tokens := map[Surface]string{System: "should-never-be-checked"}
|
||||
h := HTTP(tokens, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/tasks/t1/complete", nil)
|
||||
req.Header.Set("X-Orchestra-Surface", "system")
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, req)
|
||||
// System's token check is intentionally skipped by HTTP() (it downgrades
|
||||
// to Web before the token comparison), so the request reaching the
|
||||
// handler at all is expected here — the guard that matters is that
|
||||
// nothing downstream can observe "system" as the resolved surface. This
|
||||
// test documents the middleware's half of the fix; main.go's `surface`
|
||||
// closure carries the other half.
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("unexpected status %d", rec.Code)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user