mirror of
https://github.com/affaan-m/ECC.git
synced 2026-10-26 18:35:05 +00:00
fix(memory): classify directory traversal failures
This commit is contained in:
1 parent
90ef62cb1d
commit
8321021c54
2 files changed
+58
-4
No files matched your search
@@ -443,6 +443,12 @@ function publicMemoryFileError(error) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function incompleteMemoryLookupError() {
|
||||||
|
const error = new Error('Memory lookup is incomplete. Inspect the authorized vault before retrying.');
|
||||||
|
error.code = 'ECC_MEMORY_INCOMPLETE';
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
function readMemoryFiles(options = {}) {
|
function readMemoryFiles(options = {}) {
|
||||||
const roots = options.roots || resolveVaultRoots(options);
|
const roots = options.roots || resolveVaultRoots(options);
|
||||||
const scopes = normalizeScopes(options.scopes || DEFAULT_RECALL_SCOPES);
|
const scopes = normalizeScopes(options.scopes || DEFAULT_RECALL_SCOPES);
|
||||||
@@ -461,7 +467,13 @@ function readMemoryFiles(options = {}) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const root = assertMemoryRootSafe(roots, scope);
|
const root = assertMemoryRootSafe(roots, scope);
|
||||||
const walked = walkMemoryRoot(root, MAX_FILES - visitedCount);
|
let walked;
|
||||||
|
try {
|
||||||
|
walked = walkMemoryRoot(root, MAX_FILES - visitedCount);
|
||||||
|
} catch {
|
||||||
|
// Directory open/read/close failures cannot establish a complete lookup.
|
||||||
|
throw incompleteMemoryLookupError();
|
||||||
|
}
|
||||||
visitedCount += walked.visitedCount;
|
visitedCount += walked.visitedCount;
|
||||||
truncated = truncated || walked.truncated;
|
truncated = truncated || walked.truncated;
|
||||||
skippedSymlinkCount += walked.skippedSymlinkCount;
|
skippedSymlinkCount += walked.skippedSymlinkCount;
|
||||||
@@ -660,9 +672,7 @@ function readMemoryById(id, options = {}) {
|
|||||||
: null;
|
: null;
|
||||||
const loaded = readMemoryFiles(options);
|
const loaded = readMemoryFiles(options);
|
||||||
if (loaded.truncated || loaded.invalidFileCount > 0) {
|
if (loaded.truncated || loaded.invalidFileCount > 0) {
|
||||||
const error = new Error('Memory lookup is incomplete. Inspect the authorized vault before retrying.');
|
throw incompleteMemoryLookupError();
|
||||||
error.code = 'ECC_MEMORY_INCOMPLETE';
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
const matches = loaded.entries
|
const matches = loaded.entries
|
||||||
.filter(entry => entry.memory.id === memoryId)
|
.filter(entry => entry.memory.id === memoryId)
|
||||||
|
|||||||
@@ -75,6 +75,50 @@ async function main() {
|
|||||||
fs.openSync = open;
|
fs.openSync = open;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
function failTraversal(roots, phase, fn) {
|
||||||
|
const open = fs.opendirSync;
|
||||||
|
let closed = false;
|
||||||
|
fs.opendirSync = (directory, ...args) => {
|
||||||
|
if (directory !== roots.project) return open(directory, ...args);
|
||||||
|
const fail = () => {
|
||||||
|
const error = new Error('Synthetic private directory detail.');
|
||||||
|
error.code = 'EACCES';
|
||||||
|
throw error;
|
||||||
|
};
|
||||||
|
if (phase === 'open') return fail();
|
||||||
|
const handle = open(directory, ...args);
|
||||||
|
return {
|
||||||
|
readSync: () => phase === 'read' ? fail() : handle.readSync(),
|
||||||
|
closeSync: () => {
|
||||||
|
handle.closeSync(); closed = true;
|
||||||
|
if (phase === 'close') fail();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
try { fn(); } finally {
|
||||||
|
fs.opendirSync = open;
|
||||||
|
if (phase !== 'open') assert.equal(closed, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const phase of ['open', 'read', 'close']) {
|
||||||
|
check(`direct read classifies directory ${phase} failure`, ({ roots, read }) => {
|
||||||
|
failTraversal(roots, phase, () => {
|
||||||
|
assert.throws(read, error => error.code === 'ECC_MEMORY_INCOMPLETE'
|
||||||
|
&& !error.message.includes('Synthetic private directory detail.'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
check(`MCP read classifies directory ${phase} failure`, ({ roots, mcp }) => {
|
||||||
|
failTraversal(roots, phase, () => {
|
||||||
|
const result = mcp(); assert.equal(result.isError, true);
|
||||||
|
const error = JSON.parse(result.content[0].text).error;
|
||||||
|
assert.equal(error.code, 'MEMORY_READ_INCOMPLETE');
|
||||||
|
assert.equal(error.message.includes('Synthetic private directory detail.'), false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
check('unreadable traversal cannot establish missing memory', ({ roots, read }) => {
|
||||||
|
failTraversal(roots, 'open', () => incomplete(() => read('mem_synthetic_missing')));
|
||||||
|
});
|
||||||
check('MCP incomplete lookup has a distinct bounded error', ({ mcp, truncate }) => {
|
check('MCP incomplete lookup has a distinct bounded error', ({ mcp, truncate }) => {
|
||||||
truncate(); const result = mcp(); assert.equal(result.isError, true);
|
truncate(); const result = mcp(); assert.equal(result.isError, true);
|
||||||
const error = JSON.parse(result.content[0].text).error;
|
const error = JSON.parse(result.content[0].text).error;
|
||||||
|
|||||||
Reference in new issue
Block a user