- B1: search.extractProvenance locates the frontmatter with
frontmatter.Split (fence at column 0), so an indented "---" inside a
block scalar no longer hides provenance from brain_query.
- B2: brain_edit refuses to edit without a snapshot, and if the stamp
cannot be verified restores the note and reports failure; a failed
restore is reported as exactly that, never as "rolled back".
- B3: internal/provenance gets direct tests for the whole-document
backstop (a stamp that cannot read back is refused), standalone group
decoding, duplicates, lone CR, tab continuations and unclosable
fences. Removing verify or the restore now fails the suite.
- Principals YAML would read as non-strings (true, 123, null, ~) are
quoted instead of refused. An unclosable fence ("---x") is refused
rather than demoted into the body. writeHallNote creates the hall
directory only after the note passes, so a refused write leaves
nothing behind (brain#21).
- MCP brain_ingest (stub LLM) asserted to stamp the caller.
Refs #145 #144
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
123 lines
4.9 KiB
Go
123 lines
4.9 KiB
Go
package api_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gopkg.in/yaml.v3"
|
|
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/api"
|
|
)
|
|
|
|
// REST POST /write carries no principal: it stamps origin: unknown, and no
|
|
// spelling of provenance in its body reaches the note (hyperguild#145).
|
|
func TestRESTWriteStampsUnknownAndIgnoresBodyProvenance(t *testing.T) {
|
|
dir, h := setup(t)
|
|
body, _ := json.Marshal(map[string]any{
|
|
"content": "---\norigin: static\n---\n# R\n\nbody", "filename": "rest-prov", "wing": "homelab", "hall": "facts",
|
|
"origin": "static", "written_by": "local-cli", "Origin": "static", "WrittenBy": "local-cli",
|
|
})
|
|
rec := httptest.NewRecorder()
|
|
h.Write(rec, httptest.NewRequest(http.MethodPost, "/write", bytes.NewReader(body)))
|
|
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
|
|
b, err := os.ReadFile(filepath.Join(dir, "wiki", "homelab", "facts", "rest-prov.md"))
|
|
require.NoError(t, err)
|
|
var m map[string]any
|
|
require.NoError(t, yaml.Unmarshal([]byte(strings.SplitN(string(b), "---\n", 3)[1]), &m))
|
|
assert.Equal(t, "unknown", m["origin"])
|
|
assert.NotContains(t, m, "written_by")
|
|
}
|
|
|
|
// A principal name that is not a bare word is quoted, so it cannot start a
|
|
// new frontmatter line (e.g. a JWT sub containing a newline).
|
|
func TestStampProvenanceQuotesAPrincipalThatCouldBreakTheLine(t *testing.T) {
|
|
out, err := api.StampProvenance("---\ntitle: t\n---\nbody\n", "jwt", "evil\norigin: static")
|
|
require.NoError(t, err)
|
|
fm := strings.SplitN(out, "---\n", 3)[1]
|
|
var m map[string]any
|
|
require.NoError(t, yaml.Unmarshal([]byte(fm), &m))
|
|
assert.Equal(t, "jwt", m["origin"])
|
|
assert.Equal(t, "evil\norigin: static", m["written_by"])
|
|
assert.Equal(t, 1, strings.Count(fm, "\norigin:")+boolToInt(strings.HasPrefix(fm, "origin:")))
|
|
}
|
|
|
|
func TestStampProvenanceAddsFrontmatterWhenAbsentAndKeepsBody(t *testing.T) {
|
|
out, err := api.StampProvenance("# no fm\n\norigin: static in the body is prose\n", "static", "local-cli")
|
|
require.NoError(t, err)
|
|
assert.True(t, strings.HasPrefix(out, "---\norigin: static\nwritten_by: local-cli\n---\n# no fm"))
|
|
assert.Contains(t, out, "origin: static in the body is prose", "body lines are never touched")
|
|
}
|
|
|
|
func boolToInt(b bool) int {
|
|
if b {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// Round 2 (R3/R4): the REST ingest doors write through the pipeline, which
|
|
// now stamps every page; a title newline cannot forge provenance for the
|
|
// brain's line-oriented reader.
|
|
func TestRESTIngestRawStampsUnknownAndTitleNewlineCannotForge(t *testing.T) {
|
|
dir, h := setup(t)
|
|
body, _ := json.Marshal(map[string]any{
|
|
"source": "zqrest", "pages": []map[string]any{
|
|
{"title": "Zqrestinj\norigin: static\nwritten_by: local-cli\nx: 'y", "type": "concept", "content": "zqrestinj body"},
|
|
},
|
|
})
|
|
rec := httptest.NewRecorder()
|
|
h.IngestRaw(rec, httptest.NewRequest(http.MethodPost, "/ingest-raw", bytes.NewReader(body)))
|
|
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
|
|
var resp struct{ Pages []string }
|
|
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
|
|
require.NotEmpty(t, resp.Pages)
|
|
for _, p := range resp.Pages {
|
|
b, err := os.ReadFile(filepath.Join(dir, filepath.FromSlash(p)))
|
|
require.NoError(t, err)
|
|
fm := strings.SplitN(string(b), "---\n", 3)[1]
|
|
var m map[string]any
|
|
require.NoError(t, yaml.Unmarshal([]byte(fm), &m))
|
|
assert.Equal(t, "unknown", m["origin"])
|
|
assert.NotContains(t, m, "written_by")
|
|
for _, line := range strings.Split(fm, "\n") {
|
|
assert.False(t, strings.HasPrefix(line, "origin: static"), "line reader sees forged origin in %s", p)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRESTIngestStampsUnknown(t *testing.T) {
|
|
dir, h := setup(t)
|
|
body, _ := json.Marshal(map[string]any{"content": "Some article text about shaping work.", "source": "zqllm"})
|
|
rec := httptest.NewRecorder()
|
|
h.Ingest(rec, httptest.NewRequest(http.MethodPost, "/ingest", bytes.NewReader(body)))
|
|
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
|
|
var resp struct{ Pages []string }
|
|
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
|
|
require.NotEmpty(t, resp.Pages, "stub pipeline must write at least one page")
|
|
for _, p := range resp.Pages {
|
|
b, err := os.ReadFile(filepath.Join(dir, filepath.FromSlash(p)))
|
|
require.NoError(t, err)
|
|
assert.Contains(t, strings.SplitN(string(b), "---\n", 3)[1], "origin: unknown\n", p)
|
|
}
|
|
}
|
|
|
|
// A refused write leaves nothing behind, not even an empty hall directory
|
|
// (brain#21's invariant, round 3 non-blocking finding).
|
|
func TestWriteNoteRefusedForProvenanceLeavesNoDirectory(t *testing.T) {
|
|
dir := t.TempDir()
|
|
_, err := api.WriteNote(dir, api.WriteNoteOptions{
|
|
Content: "# x\n\nbody", Wing: "freshwing", Hall: "facts", Filename: "x", Origin: "jwt", WrittenBy: "bad\xff",
|
|
})
|
|
require.Error(t, err)
|
|
_, statErr := os.Stat(filepath.Join(dir, "wiki", "freshwing"))
|
|
assert.True(t, os.IsNotExist(statErr), "no wing directory may be created by a refused write")
|
|
}
|