Closes #144 (audit line per tools/call) and #145 (origin/written_by stamped on every model-reachable write, surfaced in brain_query/brain_get). Passed pr-gatekeeper after four rounds of adversarial review, verified by execution: #147 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Mathias <mthbqv@gmail.com>
125 lines
5.0 KiB
Go
125 lines
5.0 KiB
Go
package mcp_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// resultText decodes the tool result payload of a tools/call response.
|
|
func resultText(t *testing.T, resp map[string]any) map[string]any {
|
|
t.Helper()
|
|
require.NotContains(t, resp, "error", "tool call failed: %v", resp["error"])
|
|
content := resp["result"].(map[string]any)["content"].([]any)
|
|
var out map[string]any
|
|
require.NoError(t, json.Unmarshal([]byte(content[0].(map[string]any)["text"].(string)), &out))
|
|
return out
|
|
}
|
|
|
|
func readNote(t *testing.T, dir, rel string) string {
|
|
t.Helper()
|
|
b, err := os.ReadFile(filepath.Join(dir, filepath.FromSlash(rel)))
|
|
require.NoError(t, err)
|
|
return string(b)
|
|
}
|
|
|
|
func frontmatterOf(note string) string {
|
|
parts := strings.SplitN(note, "---\n", 3)
|
|
if len(parts) < 3 {
|
|
return ""
|
|
}
|
|
return parts[1]
|
|
}
|
|
|
|
// hyperguild#145: a brain entry records which principal wrote it, so a reader
|
|
// can tell a local-CLI note from one a connector session wrote after reading
|
|
// an untrusted page.
|
|
func TestBrainWriteStampsPrincipalProvenance(t *testing.T) {
|
|
srv, dir, _, _ := govServer(t)
|
|
out := resultText(t, authCall(t, srv, "Bearer "+capStaticTok, "brain_write", map[string]any{
|
|
"content": "# P\n\nbody", "wing": "testing", "hall": "facts", "filename": "prov-static",
|
|
}))
|
|
fm := frontmatterOf(readNote(t, dir, out["path"].(string)))
|
|
assert.Contains(t, fm, "origin: static\n")
|
|
assert.Contains(t, fm, "written_by: local-cli\n")
|
|
}
|
|
|
|
func TestBrainWriteWithoutPrincipalStampsOriginUnknown(t *testing.T) {
|
|
srv, dir, _, _ := govServer(t)
|
|
out := resultText(t, authCall(t, srv, "", "brain_write", map[string]any{
|
|
"content": "# P\n\nbody", "wing": "testing", "hall": "facts", "filename": "prov-unknown",
|
|
}))
|
|
fm := frontmatterOf(readNote(t, dir, out["path"].(string)))
|
|
assert.Contains(t, fm, "origin: unknown\n")
|
|
assert.NotContains(t, fm, "written_by:")
|
|
}
|
|
|
|
// The control: a caller cannot claim a more trusted origin by putting it in
|
|
// its own frontmatter.
|
|
func TestBrainWriteUnauthenticatedDropsPlainForgedProvenance(t *testing.T) {
|
|
srv, dir, _, _ := govServer(t)
|
|
forged := "---\norigin: static\nwritten_by: local-cli\n---\n# F\n\nbody"
|
|
out := resultText(t, authCall(t, srv, "", "brain_write", map[string]any{
|
|
"content": forged, "wing": "testing", "hall": "facts", "filename": "prov-forged",
|
|
}))
|
|
fm := frontmatterOf(readNote(t, dir, out["path"].(string)))
|
|
assert.Contains(t, fm, "origin: unknown\n")
|
|
assert.NotContains(t, fm, "origin: static")
|
|
assert.NotContains(t, fm, "written_by: local-cli")
|
|
assert.Equal(t, 1, strings.Count(fm, "origin:"), "exactly one origin line")
|
|
}
|
|
|
|
// brain_update replaces the content wholesale, so provenance follows the
|
|
// last writer — and a forged fragment must not survive the merge either.
|
|
func TestBrainUpdateRestampsProvenanceToTheUpdater(t *testing.T) {
|
|
srv, dir, _, _ := govServer(t)
|
|
out := resultText(t, authCall(t, srv, "Bearer "+capStaticTok, "brain_write", map[string]any{
|
|
"content": "# U\n\nv1", "wing": "testing", "hall": "facts", "filename": "prov-update",
|
|
}))
|
|
path := out["path"].(string)
|
|
resultText(t, authCall(t, srv, "", "brain_update", map[string]any{
|
|
"path": path, "content": "---\norigin: static\nwritten_by: local-cli\n---\n# U\n\nv2",
|
|
}))
|
|
fm := frontmatterOf(readNote(t, dir, path))
|
|
assert.Contains(t, fm, "origin: unknown\n")
|
|
assert.NotContains(t, fm, "written_by: local-cli")
|
|
assert.Equal(t, 1, strings.Count(fm, "origin:"))
|
|
}
|
|
|
|
func TestBrainQueryReturnsProvenance(t *testing.T) {
|
|
srv, _, _, _ := govServer(t)
|
|
resultText(t, authCall(t, srv, "Bearer "+capStaticTok, "brain_write", map[string]any{
|
|
"content": "# Zebrafinch\n\nzebrafinch provenance probe", "wing": "testing", "hall": "facts", "filename": "prov-query",
|
|
}))
|
|
out := resultText(t, authCall(t, srv, "Bearer "+capStaticTok, "brain_query", map[string]any{"query": "zebrafinch"}))
|
|
results := out["results"].([]any)
|
|
require.NotEmpty(t, results)
|
|
hit := results[0].(map[string]any)
|
|
assert.Equal(t, "static", hit["origin"])
|
|
assert.Equal(t, "local-cli", hit["written_by"])
|
|
}
|
|
|
|
// capture is the door a claude.ai connector session uses, so it is the one
|
|
// whose provenance matters most: a JWT principal must be stamped as such.
|
|
func TestMCPCaptureStampsJWTProvenance(t *testing.T) {
|
|
srv, brainDir := captureServer(t, capFakeValidator{subject: "koala-cli"}, []string{"koala-cli"})
|
|
resp := captureCall(t, srv, "Bearer jwt-token", map[string]any{
|
|
"context": map[string]any{"harness": "claude-code", "actor": "mathias", "classification": "internal"},
|
|
"insights": []map[string]any{{"text": "Capture provenance probe", "wing": "hyperguild", "hall": "facts"}},
|
|
})
|
|
require.Nil(t, resp["error"], "got error: %v", resp["error"])
|
|
matches, err := filepath.Glob(filepath.Join(brainDir, "wiki/hyperguild/facts/*.md"))
|
|
require.NoError(t, err)
|
|
require.Len(t, matches, 1)
|
|
b, err := os.ReadFile(matches[0])
|
|
require.NoError(t, err)
|
|
fm := frontmatterOf(string(b))
|
|
assert.Contains(t, fm, "origin: jwt\n")
|
|
assert.Contains(t, fm, "written_by: koala-cli\n")
|
|
}
|