Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions src/utils/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ export async function fileExists(filePath: string): Promise<boolean> {
await fs.promises.access(filePath, fs.constants.R_OK);
return true;
} catch (err) {
logger.debug({ path: filePath, err }, 'File does not exist or is not readable');
if ((err as NodeJS.ErrnoException)?.code === 'ENOENT') {
logger.trace({ path: filePath }, 'File does not exist or is not readable');
} else {
logger.debug({ path: filePath, err }, 'File does not exist or is not readable');
}
return false;
}
}
Expand All @@ -27,7 +31,11 @@ export async function readFile(filePath: string): Promise<string | null> {
try {
return await fs.promises.readFile(filePath, 'utf-8');
} catch (err) {
logger.debug({ path: filePath, err }, 'Failed to read file');
if ((err as NodeJS.ErrnoException)?.code === 'ENOENT') {
logger.trace({ path: filePath }, 'Failed to read file');
} else {
logger.debug({ path: filePath, err }, 'Failed to read file');
}
return null;
}
}
Expand Down Expand Up @@ -94,7 +102,11 @@ export async function directoryExists(dirPath: string): Promise<boolean> {
const stats = await fs.promises.stat(dirPath);
return stats.isDirectory();
} catch (err) {
logger.debug({ path: dirPath, err }, 'Directory does not exist');
if ((err as NodeJS.ErrnoException)?.code === 'ENOENT') {
logger.trace({ path: dirPath }, 'Directory does not exist');
} else {
logger.debug({ path: dirPath, err }, 'Directory does not exist');
}
return false;
}
}
Expand Down
36 changes: 35 additions & 1 deletion test/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,19 @@
import { describe, it } from 'node:test';
import * as assert from 'node:assert';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';

import { fileExists, readFileCached, findFiles, relativePath, safePath } from '../src/utils/fs.js';
import {
fileExists,
readFile,
readFileCached,
findFiles,
relativePath,
safePath,
} from '../src/utils/fs.js';
import { logger } from '../src/utils/logger.js';
import { isUnsafeRegex, safeRegex, safeRegexTest } from '../src/utils/regex.js';

const __filename = fileURLToPath(import.meta.url);
Expand Down Expand Up @@ -38,6 +47,31 @@ describe('fs utilities', () => {
});
});

describe('ENOENT probes', () => {
it('should not log error stacks or absolute paths for missing files', async () => {
const missing = path.join(os.tmpdir(), `agent-ready-i82-does-not-exist-${process.pid}`);
const records: Record<string, unknown>[] = [];
const originalDebug = logger.debug;
logger.debug = (obj: unknown) => {
records.push(obj as Record<string, unknown>);
};
try {
await fileExists(missing);
await readFile(missing);
} finally {
logger.debug = originalDebug;
}
for (const record of records) {
const err = record.err as { stack?: string } | undefined;
assert.strictEqual(err?.stack, undefined, 'ENOENT probe must not log err.stack');
assert.ok(
!(typeof record.path === 'string' && path.isAbsolute(record.path)),
'ENOENT probe must not log an absolute path'
);
}
});
});

describe('readFileCached', () => {
it('should read file contents', async () => {
const readmePath = path.join(FIXTURES_DIR, 'minimal-repo/README.md');
Expand Down
Loading