feat(ingestion): extract WriteNote helper and add brain_write MCP tool
api.WriteNote captures the file-write logic that was previously inline in Handler.Write. The existing HTTP endpoint now delegates to it; the new MCP brain_write tool reuses the same function. Path-traversal guard is strengthened to explicitly reject filenames containing path separators or "..", so the rejection is surfaced before filepath.Base strips the suspicious component (the previous defense-in-depth prefix check became unreachable for these inputs after Base normalisation). HTTP error code for caller-input errors shifts from 500 to 400, which is semantically correct and not exercised by any existing test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,3 +50,47 @@ func TestBrainQueryReturnsResults(t *testing.T) {
|
||||
text := content[0].(map[string]any)["text"].(string)
|
||||
assert.Contains(t, text, "tdd.md")
|
||||
}
|
||||
|
||||
func TestBrainWriteCreatesFile(t *testing.T) {
|
||||
brainDir := t.TempDir()
|
||||
srv := mcp.NewServer(brainDir, nil, nil)
|
||||
|
||||
resp := toolCall(t, srv, "brain_write", map[string]any{
|
||||
"content": "# Test\n\nbody",
|
||||
"filename": "test.md",
|
||||
"type": "note",
|
||||
"domain": "personal",
|
||||
})
|
||||
require.Nil(t, resp["error"])
|
||||
|
||||
got, err := os.ReadFile(filepath.Join(brainDir, "knowledge", "test.md"))
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, string(got), "type: note")
|
||||
assert.Contains(t, string(got), "domain: personal")
|
||||
assert.Contains(t, string(got), "# Test")
|
||||
}
|
||||
|
||||
func TestBrainWriteRejectsTraversal(t *testing.T) {
|
||||
brainDir := t.TempDir()
|
||||
srv := mcp.NewServer(brainDir, nil, nil)
|
||||
|
||||
resp := toolCall(t, srv, "brain_write", map[string]any{
|
||||
"content": "x",
|
||||
"filename": "../escape.md",
|
||||
})
|
||||
require.NotNil(t, resp["error"])
|
||||
}
|
||||
|
||||
func TestBrainWriteAcceptsDoubleDotInName(t *testing.T) {
|
||||
brainDir := t.TempDir()
|
||||
srv := mcp.NewServer(brainDir, nil, nil)
|
||||
|
||||
resp := toolCall(t, srv, "brain_write", map[string]any{
|
||||
"content": "x",
|
||||
"filename": "notes..draft.md",
|
||||
})
|
||||
require.Nil(t, resp["error"])
|
||||
|
||||
_, err := os.Stat(filepath.Join(brainDir, "knowledge", "notes..draft.md"))
|
||||
require.NoError(t, err, "filename with embedded .. should be allowed")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user