mirror of
https://github.com/hanzoai/base.git
synced 2026-10-21 03:41:50 +00:00
Base renders standalone at its own host and as a section inside a Hanzo surface. One bundle serves both: the same bytes the binary already embeds, so a host is current the moment the pin moves, rather than a component every host recompiles with its own copy of a transport that only works against the Base that served it. Who may frame the admin is a CSP source list, BASE_FRAME_ANCESTORS, default 'self' — which is what the deployment did before. It is stated in one place, on the admin, the only surface a frame can be aimed at to any effect. X-Frame-Options goes rather than sitting beside it: it can say "nobody" or "same origin" and cannot name a host, so an allow-list is the one thing it cannot express, and a second weaker copy of a rule drifts from the first. The ingress already says it this way in front of hanzo.id. Auth is untouched. The framed admin runs its own PKCE, a live IAM session returns a code without rendering anything, and no token crosses an origin. The frame drops the brand and the account because the host answers both already, read from window.self !== window.top — where a document is, is a fact the browser holds, and a flag is a second answer that can be wrong. The nav was a 14rem column at every width, which on a 390px viewport left 166px and put the table past the right edge, inside a pane that scrolls — so the page never grew and no check written against the document could see it. The strip is now the base layout and the column is what a wide page of our own opts into, each declared once. smoke gates all three.
52 lines
1.8 KiB
Go
52 lines
1.8 KiB
Go
package apis
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// The admin renders standalone at its own host and inside the Hanzo surfaces
|
|
// that offer Base as a section, so who may frame it is deployment data. These
|
|
// pin the two halves of that: the default is this origin and nobody else, and
|
|
// what a deployment says is what the header carries.
|
|
func TestFrameAncestorsDefaultsToThisOriginAlone(t *testing.T) {
|
|
t.Setenv("BASE_FRAME_ANCESTORS", "")
|
|
|
|
if got := frameAncestors(); got != "'self'" {
|
|
t.Fatalf("expected the default to name this origin alone, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestFrameAncestorsCarriesTheHostsADeploymentNames(t *testing.T) {
|
|
const hosts = "'self' https://console.hanzo.ai https://hanzo.app"
|
|
|
|
t.Setenv("BASE_FRAME_ANCESTORS", hosts)
|
|
|
|
if got := frameAncestors(); got != hosts {
|
|
t.Fatalf("expected %q, got %q", hosts, got)
|
|
}
|
|
}
|
|
|
|
// Whitespace is what an env var picks up from YAML block scalars, and a value
|
|
// that is only whitespace is a deployment that said nothing.
|
|
func TestFrameAncestorsTreatsBlankAsUnset(t *testing.T) {
|
|
t.Setenv("BASE_FRAME_ANCESTORS", " ")
|
|
|
|
if got := frameAncestors(); got != "'self'" {
|
|
t.Fatalf("expected blank to read as unset, got %q", got)
|
|
}
|
|
}
|
|
|
|
// The directive has to reach the policy the admin is actually served with —
|
|
// a value read into a string nothing concatenates protects nothing. This is
|
|
// the same composition apis/serve.go performs on the admin response.
|
|
func TestAdminPolicyCarriesTheFrameRule(t *testing.T) {
|
|
t.Setenv("BASE_FRAME_ANCESTORS", "'self' https://console.hanzo.ai")
|
|
|
|
policy := "default-src 'self'; script-src 'self' 'unsafe-inline'; frame-ancestors " + frameAncestors()
|
|
|
|
if !strings.Contains(policy, "frame-ancestors 'self' https://console.hanzo.ai") {
|
|
t.Fatalf("frame rule missing from the served policy: %q", policy)
|
|
}
|
|
}
|