@remnic/core 9.3.583 → 9.3.585

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "9.3.583",
3
+ "version": "9.3.585",
4
4
  "description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -159,6 +159,34 @@ test("generateContextTree rejects symlinked memory category roots", async () =>
159
159
  }
160
160
  });
161
161
 
162
+ test("generateContextTree rejects symlinked memoryDir roots", async () => {
163
+ const root = await mkdtemp(path.join(os.tmpdir(), "remnic-projection-root-symlink-"));
164
+ try {
165
+ const outsideMemoryDir = path.join(root, "outside-memory");
166
+ const memoryDir = path.join(root, "memory");
167
+ const outputDir = path.join(root, "context-tree");
168
+ await writeFact(outsideMemoryDir);
169
+ await symlink(outsideMemoryDir, memoryDir, "dir");
170
+
171
+ await assert.rejects(
172
+ generateContextTree({
173
+ memoryDir,
174
+ outputDir,
175
+ categories: ["fact"],
176
+ includeEntities: false,
177
+ includeQuestions: false,
178
+ }),
179
+ /memoryDir must not be a symlink/,
180
+ );
181
+ await assert.rejects(
182
+ readFile(path.join(outputDir, "fact", "fact-1.md"), "utf-8"),
183
+ /ENOENT/,
184
+ );
185
+ } finally {
186
+ await rm(root, { recursive: true, force: true });
187
+ }
188
+ });
189
+
162
190
  test("generateContextTree rejects symlinked output path components", async () => {
163
191
  const root = await mkdtemp(path.join(os.tmpdir(), "remnic-projection-output-symlink-"));
164
192
  try {
@@ -90,7 +90,7 @@ export async function generateContextTree(options: GenerateOptions): Promise<Gen
90
90
  const resolvedMemoryDir = path.resolve(memoryDir);
91
91
  const resolvedOutputDir = path.resolve(outputDir);
92
92
  const requestedCategories = validateProjectionCategories(filterCategories);
93
- const realMemoryDir = fs.realpathSync(resolvedMemoryDir);
93
+ const realMemoryDir = assertSafeMemoryRoot(resolvedMemoryDir);
94
94
 
95
95
  // Ensure output directory exists
96
96
  assertNotSymlink(resolvedOutputDir, "context tree outputDir");
@@ -240,6 +240,17 @@ function assertNotSymlink(targetPath: string, label: string): void {
240
240
  }
241
241
  }
242
242
 
243
+ function assertSafeMemoryRoot(targetPath: string): string {
244
+ const stat = fs.lstatSync(targetPath);
245
+ if (stat.isSymbolicLink()) {
246
+ throw new Error(`memoryDir must not be a symlink: ${targetPath}`);
247
+ }
248
+ if (!stat.isDirectory()) {
249
+ throw new Error(`memoryDir must be a directory: ${targetPath}`);
250
+ }
251
+ return fs.realpathSync(targetPath);
252
+ }
253
+
243
254
  function assertSafeInputRoot(realMemoryDir: string, targetPath: string, label: string): void {
244
255
  const stat = fs.lstatSync(targetPath);
245
256
  if (stat.isSymbolicLink()) {