Skip to content

Commit 9280a67

Browse files
committed
fix(memory-wiki): make wiki_lint report path tool-safe
1 parent bfbb9c6 commit 9280a67

2 files changed

Lines changed: 68 additions & 3 deletions

File tree

extensions/memory-wiki/src/tool.test.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
import fs from "node:fs/promises";
2+
import path from "node:path";
13
import { describe, expect, it } from "vitest";
24
import type { ResolvedMemoryWikiConfig } from "./config.js";
3-
import { createWikiApplyTool } from "./tool.js";
5+
import { renderWikiMarkdown } from "./markdown.js";
6+
import { createMemoryWikiTestHarness } from "./test-helpers.js";
7+
import { createWikiApplyTool, createWikiLintTool } from "./tool.js";
8+
9+
const { createVault } = createMemoryWikiTestHarness();
410

511
function asSchemaObject(value: unknown): Record<string, unknown> {
612
if (typeof value !== "object" || value === null || Array.isArray(value)) {
@@ -10,6 +16,44 @@ function asSchemaObject(value: unknown): Record<string, unknown> {
1016
}
1117

1218
describe("memory-wiki tools", () => {
19+
it("returns relative bounded details from wiki_lint", async () => {
20+
const { rootDir, config } = await createVault({
21+
prefix: "memory-wiki-tool-lint-",
22+
config: {
23+
vault: { renderMode: "obsidian" },
24+
},
25+
});
26+
await fs.mkdir(path.join(rootDir, "entities"), { recursive: true });
27+
await fs.writeFile(
28+
path.join(rootDir, "entities", "alpha.md"),
29+
renderWikiMarkdown({
30+
frontmatter: {
31+
pageType: "entity",
32+
id: "entity.alpha",
33+
title: "Alpha",
34+
},
35+
body: "# Alpha\n\n[[missing-page]]\n",
36+
}),
37+
"utf8",
38+
);
39+
40+
const tool = createWikiLintTool(config);
41+
const result = await tool.execute("call-1", {});
42+
const text = result.content
43+
.map((entry) => (entry.type === "text" ? entry.text : ""))
44+
.join("\n");
45+
const details = asSchemaObject(result.details);
46+
47+
expect(text).toContain("Report: reports/lint.md");
48+
expect(text).not.toContain(rootDir);
49+
expect(details.reportPath).toBe("reports/lint.md");
50+
expect(details).not.toHaveProperty("vaultRoot");
51+
expect(JSON.stringify(details)).not.toContain(rootDir);
52+
expect(asSchemaObject(details.issuesByCategory).links).toEqual(
53+
expect.arrayContaining([expect.objectContaining({ code: "broken-wikilink" })]),
54+
);
55+
});
56+
1357
it("allows provenance metadata in wiki_apply claim evidence", () => {
1458
const tool = createWikiApplyTool({} as ResolvedMemoryWikiConfig);
1559
const applyProperties = asSchemaObject(asSchemaObject(tool.parameters).properties);

extensions/memory-wiki/src/tool.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import path from "node:path";
12
import { Type } from "typebox";
23
import type { AnyAgentTool, OpenClawConfig } from "../api.js";
34
import { applyMemoryWikiMutation, normalizeMemoryWikiMutationInput } from "./apply.js";
@@ -92,6 +93,20 @@ type WikiToolMemoryContext = {
9293
sandboxed?: boolean;
9394
};
9495

96+
function formatWikiToolReportPath(config: ResolvedMemoryWikiConfig, reportPath: string): string {
97+
const vaultRoot = path.resolve(config.vault.path);
98+
const resolvedReportPath = path.resolve(reportPath);
99+
const relativeReportPath = path.relative(vaultRoot, resolvedReportPath);
100+
if (
101+
!relativeReportPath ||
102+
relativeReportPath.startsWith("..") ||
103+
path.isAbsolute(relativeReportPath)
104+
) {
105+
return reportPath;
106+
}
107+
return relativeReportPath.replace(/\\/g, "/");
108+
}
109+
95110
export function createWikiStatusTool(
96111
config: ResolvedMemoryWikiConfig,
97112
appConfig?: OpenClawConfig,
@@ -182,6 +197,7 @@ export function createWikiLintTool(
182197
const provenance = result.issuesByCategory.provenance.length;
183198
const errors = result.issues.filter((issue) => issue.severity === "error").length;
184199
const warnings = result.issues.filter((issue) => issue.severity === "warning").length;
200+
const reportPath = formatWikiToolReportPath(config, result.reportPath);
185201
const summary =
186202
result.issueCount === 0
187203
? "No wiki lint issues."
@@ -190,11 +206,16 @@ export function createWikiLintTool(
190206
`Contradictions: ${contradictions}`,
191207
`Open questions: ${openQuestions}`,
192208
`Provenance gaps: ${provenance}`,
193-
`Report: ${result.reportPath}`,
209+
`Report: ${reportPath}`,
194210
].join("\n");
195211
return {
196212
content: [{ type: "text", text: summary }],
197-
details: result,
213+
details: {
214+
issueCount: result.issueCount,
215+
issues: result.issues,
216+
issuesByCategory: result.issuesByCategory,
217+
reportPath,
218+
},
198219
};
199220
},
200221
};

0 commit comments

Comments
 (0)