fix(security): GLOBAL memory delimiter spoofing + pin MCP npm version

SAFE-T1201 (#807): Escape [MEMORY prefix in GLOBAL memory content on
write to prevent delimiter-spoofing prompt injection. Content stored
as "[_MEMORY " so it renders as text, not structure, when wrapped with
the real delimiter on read.

SAFE-T1102 (#805): Pin @molecule-ai/mcp-server@1.0.0 in .mcp.json.example.
Prevents supply-chain attacks via unpinned npx -y.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hongming Wang 2026-04-18 11:09:24 -07:00
parent 04292f419c
commit 5be8ba4b45
2 changed files with 9 additions and 1 deletions

View File

@ -3,7 +3,7 @@
"molecule": { "molecule": {
"type": "stdio", "type": "stdio",
"command": "npx", "command": "npx",
"args": ["-y", "@molecule-ai/mcp-server"], "args": ["-y", "@molecule-ai/mcp-server@1.0.0"],
"env": { "env": {
"MOLECULE_URL": "http://localhost:8080" "MOLECULE_URL": "http://localhost:8080"
} }

View File

@ -179,6 +179,14 @@ func (h *MemoriesHandler) Commit(c *gin.Context) {
content := body.Content content := body.Content
content, _ = redactSecrets(workspaceID, content) content, _ = redactSecrets(workspaceID, content)
// SAFE-T1201: prevent delimiter spoofing in GLOBAL memories (#807).
// If content contains the delimiter prefix "[MEMORY ", an attacker could
// craft a fake nested delimiter to inject instructions when the memory
// is read back. Escape the bracket so it renders as text, not structure.
if body.Scope == "GLOBAL" {
content = strings.ReplaceAll(content, "[MEMORY ", "[_MEMORY ")
}
var memoryID string var memoryID string
err := db.DB.QueryRowContext(ctx, ` err := db.DB.QueryRowContext(ctx, `
INSERT INTO agent_memories (workspace_id, content, scope, namespace) INSERT INTO agent_memories (workspace_id, content, scope, namespace)