mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-02 07:03:28 +08:00
fix: harden error handling, fix TOCTOU races, and improve test accuracy
Core library fixes: - session-manager.js: wrap all statSync calls in try-catch to prevent TOCTOU crashes when files are deleted between readdir and stat - session-manager.js: use birthtime||ctime fallback for Linux compat - session-manager.js: remove redundant existsSync before readFile - utils.js: fix findFiles TOCTOU race on statSync inside readdir loop Hook improvements: - Add 1MB stdin buffer limits to all PostToolUse hooks to prevent unbounded memory growth from large payloads - suggest-compact.js: use fd-based atomic read+write for counter file to reduce race window between concurrent invocations - session-end.js: log when transcript file is missing, check replaceInFile return value for failed timestamp updates - start-observer.sh: log claude CLI failures instead of silently swallowing them, check observations file exists before analysis Test fixes: - Fix blocking hook tests to send matching input (dev server command) and expect correct exit code 2 instead of 1
This commit is contained in:
@@ -58,10 +58,6 @@ function getSessionPath(filename) {
|
||||
* @returns {string|null} Session content or null if not found
|
||||
*/
|
||||
function getSessionContent(sessionPath) {
|
||||
if (!fs.existsSync(sessionPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return readFile(sessionPath);
|
||||
}
|
||||
|
||||
@@ -217,8 +213,14 @@ function getAllSessions(options = {}) {
|
||||
|
||||
const sessionPath = path.join(sessionsDir, filename);
|
||||
|
||||
// Get file stats
|
||||
const stats = fs.statSync(sessionPath);
|
||||
// Get file stats (wrapped in try-catch to handle TOCTOU race where
|
||||
// file is deleted between readdirSync and statSync)
|
||||
let stats;
|
||||
try {
|
||||
stats = fs.statSync(sessionPath);
|
||||
} catch {
|
||||
continue; // File was deleted between readdir and stat
|
||||
}
|
||||
|
||||
sessions.push({
|
||||
...metadata,
|
||||
@@ -226,7 +228,7 @@ function getAllSessions(options = {}) {
|
||||
hasContent: stats.size > 0,
|
||||
size: stats.size,
|
||||
modifiedTime: stats.mtime,
|
||||
createdTime: stats.birthtime
|
||||
createdTime: stats.birthtime || stats.ctime
|
||||
});
|
||||
}
|
||||
|
||||
@@ -278,14 +280,19 @@ function getSessionById(sessionId, includeContent = false) {
|
||||
}
|
||||
|
||||
const sessionPath = path.join(sessionsDir, filename);
|
||||
const stats = fs.statSync(sessionPath);
|
||||
let stats;
|
||||
try {
|
||||
stats = fs.statSync(sessionPath);
|
||||
} catch {
|
||||
return null; // File was deleted between readdir and stat
|
||||
}
|
||||
|
||||
const session = {
|
||||
...metadata,
|
||||
sessionPath,
|
||||
size: stats.size,
|
||||
modifiedTime: stats.mtime,
|
||||
createdTime: stats.birthtime
|
||||
createdTime: stats.birthtime || stats.ctime
|
||||
};
|
||||
|
||||
if (includeContent) {
|
||||
@@ -319,11 +326,12 @@ function getSessionTitle(sessionPath) {
|
||||
* @returns {string} Formatted size (e.g., "1.2 KB")
|
||||
*/
|
||||
function getSessionSize(sessionPath) {
|
||||
if (!fs.existsSync(sessionPath)) {
|
||||
let stats;
|
||||
try {
|
||||
stats = fs.statSync(sessionPath);
|
||||
} catch {
|
||||
return '0 B';
|
||||
}
|
||||
|
||||
const stats = fs.statSync(sessionPath);
|
||||
const size = stats.size;
|
||||
|
||||
if (size < 1024) return `${size} B`;
|
||||
@@ -387,7 +395,11 @@ function deleteSession(sessionPath) {
|
||||
* @returns {boolean} True if session exists
|
||||
*/
|
||||
function sessionExists(sessionPath) {
|
||||
return fs.existsSync(sessionPath) && fs.statSync(sessionPath).isFile();
|
||||
try {
|
||||
return fs.statSync(sessionPath).isFile();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -168,14 +168,19 @@ function findFiles(dir, pattern, options = {}) {
|
||||
const fullPath = path.join(currentDir, entry.name);
|
||||
|
||||
if (entry.isFile() && regex.test(entry.name)) {
|
||||
let stats;
|
||||
try {
|
||||
stats = fs.statSync(fullPath);
|
||||
} catch {
|
||||
continue; // File deleted between readdir and stat
|
||||
}
|
||||
|
||||
if (maxAge !== null) {
|
||||
const stats = fs.statSync(fullPath);
|
||||
const ageInDays = (Date.now() - stats.mtimeMs) / (1000 * 60 * 60 * 24);
|
||||
if (ageInDays <= maxAge) {
|
||||
results.push({ path: fullPath, mtime: stats.mtimeMs });
|
||||
}
|
||||
} else {
|
||||
const stats = fs.statSync(fullPath);
|
||||
results.push({ path: fullPath, mtime: stats.mtimeMs });
|
||||
}
|
||||
} else if (entry.isDirectory() && recursive) {
|
||||
|
||||
Reference in New Issue
Block a user