feat(capture): I1 sovereignty gate + server-derived origin (#53)
Adds the trust-zone Origin to CaptureContext and the I1 gate to the use-case: a confidential effective classification through a us-nexus origin is refused before ANY write (ErrSovereigntyRefused), and the refusal is itself audited. A caller-asserted harness label that names a different zone than the server-derived origin is logged as a security event — context.Harness is descriptive-only, never a gate input. The gate triggers only on an explicit ZoneUSNexus, so the unset default (ZoneUnknown) can never make it fire on caller-controllable input; the REST adapter always sets a concrete zone. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -329,3 +329,82 @@ func TestCaptureSummaryPathAndFidelity(t *testing.T) {
|
||||
assert.Contains(t, sw.paths[0], "session-wrap")
|
||||
assert.Contains(t, sw.content[0], "fidelity: transcript-parse", "fidelity stamped in frontmatter")
|
||||
}
|
||||
|
||||
// --- I1 sovereignty gate (#53) ---
|
||||
|
||||
func TestCaptureRefusesConfidentialViaUSNexus(t *testing.T) {
|
||||
b := &fakeBrain{}
|
||||
tr := &fakeTracker{}
|
||||
au := &fakeAudit{}
|
||||
pol := fakePolicy{tags: map[string]classification.Level{"client-seb": classification.Confidential}}
|
||||
svc := newSvc(b, tr, nil, pol, au)
|
||||
|
||||
ctx := baseCtx()
|
||||
ctx.Classification = "confidential"
|
||||
ctx.Origin = ZoneUSNexus
|
||||
_, err := svc.Capture(context.Background(), CaptureInput{
|
||||
Context: ctx,
|
||||
Insights: []Insight{{Text: "x", Wing: "client-seb", Hall: "facts"}},
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.ErrorIs(t, err, ErrSovereigntyRefused)
|
||||
// Refused before any write.
|
||||
assert.Empty(t, b.writes)
|
||||
assert.Empty(t, tr.created)
|
||||
// Refusal is audited.
|
||||
require.Len(t, au.entries, 1)
|
||||
assert.Empty(t, au.entries[0].Items, "no items landed on refusal")
|
||||
}
|
||||
|
||||
func TestCaptureAllowsConfidentialViaSovereign(t *testing.T) {
|
||||
b := &fakeBrain{}
|
||||
pol := fakePolicy{tags: map[string]classification.Level{"client-seb": classification.Confidential}}
|
||||
svc := newSvc(b, &fakeTracker{}, nil, pol, &fakeAudit{})
|
||||
|
||||
ctx := baseCtx()
|
||||
ctx.Classification = "confidential"
|
||||
ctx.Origin = ZoneSovereign
|
||||
rec, err := svc.Capture(context.Background(), CaptureInput{
|
||||
Context: ctx,
|
||||
Insights: []Insight{{Text: "x", Wing: "client-seb", Hall: "facts"}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.True(t, rec.Insights[0].OK)
|
||||
assert.Len(t, b.writes, 1)
|
||||
}
|
||||
|
||||
func TestCaptureAssertedLabelIgnoredAndLogged(t *testing.T) {
|
||||
// Caller asserts harness "sovereign-soil" but principal resolves to
|
||||
// us-nexus; confidential ⇒ refused, and the discrepancy is a security event.
|
||||
au := &fakeAudit{}
|
||||
pol := fakePolicy{tags: map[string]classification.Level{"client-seb": classification.Confidential}}
|
||||
svc := newSvc(&fakeBrain{}, &fakeTracker{}, nil, pol, au)
|
||||
|
||||
ctx := baseCtx()
|
||||
ctx.Harness = "sovereign-soil" // asserted
|
||||
ctx.Origin = ZoneUSNexus // server-derived
|
||||
ctx.Classification = "confidential"
|
||||
_, err := svc.Capture(context.Background(), CaptureInput{
|
||||
Context: ctx,
|
||||
Insights: []Insight{{Text: "x", Wing: "client-seb", Hall: "facts"}},
|
||||
})
|
||||
require.ErrorIs(t, err, ErrSovereigntyRefused)
|
||||
require.Len(t, au.entries, 1)
|
||||
joined := strings.Join(au.entries[0].SecurityEvents, " | ")
|
||||
assert.Contains(t, joined, "asserted-vs-derived origin mismatch")
|
||||
assert.Contains(t, joined, "I1 refusal")
|
||||
}
|
||||
|
||||
func TestCaptureInternalViaUSNexusAllowed(t *testing.T) {
|
||||
// us-nexus origin is fine for non-confidential data.
|
||||
b := &fakeBrain{}
|
||||
svc := newSvc(b, &fakeTracker{}, nil, fakePolicy{}, &fakeAudit{})
|
||||
ctx := baseCtx()
|
||||
ctx.Origin = ZoneUSNexus // internal classification, so gate doesn't fire
|
||||
rec, err := svc.Capture(context.Background(), CaptureInput{
|
||||
Context: ctx,
|
||||
Insights: []Insight{{Text: "x", Wing: "hyperguild", Hall: "facts"}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.True(t, rec.Insights[0].OK)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user