Files
rohitg00__agentmemory/test/mcp-env-placeholder.test.ts
Rohit Ghumare bc64107791 fix: pre-release hardening for 0.9.22 — regex correctness + MCP env guard (#588)
Three concerns surfaced when auditing PRs merged since v0.9.21:

1) Graph parser regex from #494 was correct for self-closing tags ONLY
   when they're the only entity in the document. With a self-closing
   entity followed by an explicit-close entity, the greedy `[^>]*` ate
   the trailing `/` of the self-close, the alternation fell through to
   the explicit-close branch, and `[\s\S]*?` ran ahead to the *next*
   `</entity>` — merging two entity declarations into one match and
   silently dropping a node. Switch to lazy `[^>]*?` so the
   self-closing alternation gets first chance. Test from #494 (which
   was failing on main but I didn't notice) now passes.

2) #386 shipped `${AGENTMEMORY_URL}` / `${AGENTMEMORY_SECRET}`
   placeholders in plugin/.mcp.json. Claude Code and Cursor expand
   those at MCP launch time; some hosts pass the literal string
   through. A truthy literal `"${AGENTMEMORY_URL}"` defeats the
   existing `|| DEFAULT_URL` fallback and would have the shim POST to
   `${AGENTMEMORY_URL}/agentmemory/...` (DNS failure). Add a
   defensive guard in src/mcp/rest-proxy.ts that strips any value of
   the form `${...}` so the fallback engages. Mirror in
   src/mcp/standalone.ts's mode-announce log line.

3) CHANGELOG entries for all PRs landed since v0.9.21 (#321, #325,
   #386, #454, #494, #526, #542, #560, #561, #562, #564, #567) plus
   the regex + env-guard hardening here.

Validation:
- npm test → 98 files, 1091 tests pass
- npm pack → 142 files, fresh install clean, iii-sdk@0.11.2 pinned,
  plugin/ shipped with hooks/scripts/skills/opencode/
- New test file covers 8 placeholder cases (unset, empty,
  `${VAR}` literal, mismatched braces, real values with $,
  unclosed `${`, no-braces `$VAR`).
2026-05-21 10:44:30 +01:00

58 lines
1.8 KiB
TypeScript

import { describe, it, expect, afterEach, beforeEach } from "vitest";
import { resolveEnvOrEmpty } from "../src/mcp/rest-proxy.js";
const VAR = "AGENTMEMORY_TEST_URL";
describe("resolveEnvOrEmpty — guards against literal ${VAR} placeholders", () => {
let original: string | undefined;
beforeEach(() => {
original = process.env[VAR];
delete process.env[VAR];
});
afterEach(() => {
if (original === undefined) delete process.env[VAR];
else process.env[VAR] = original;
});
it("returns '' when env var is unset", () => {
expect(resolveEnvOrEmpty(VAR)).toBe("");
});
it("returns '' when env var is empty string", () => {
process.env[VAR] = "";
expect(resolveEnvOrEmpty(VAR)).toBe("");
});
it("returns '' when env var is literal ${VAR} placeholder", () => {
process.env[VAR] = "${AGENTMEMORY_TEST_URL}";
expect(resolveEnvOrEmpty(VAR)).toBe("");
});
it("returns '' when env var is a different literal placeholder", () => {
process.env[VAR] = "${SOME_OTHER_VAR}";
expect(resolveEnvOrEmpty(VAR)).toBe("");
});
it("preserves a real URL value", () => {
process.env[VAR] = "https://memory.prod.example/api";
expect(resolveEnvOrEmpty(VAR)).toBe("https://memory.prod.example/api");
});
it("preserves a real secret value that happens to contain a $ char", () => {
process.env[VAR] = "secret-with-$dollar";
expect(resolveEnvOrEmpty(VAR)).toBe("secret-with-$dollar");
});
it("does not treat ${ at start without matching } as placeholder", () => {
process.env[VAR] = "${unclosed";
expect(resolveEnvOrEmpty(VAR)).toBe("${unclosed");
});
it("does not treat $VAR (no braces) as placeholder", () => {
process.env[VAR] = "$AGENTMEMORY_URL";
expect(resolveEnvOrEmpty(VAR)).toBe("$AGENTMEMORY_URL");
});
});