[grade=A] DC-065: Sweep 15 console.* calls to process.stderr.write
CI / Test & Lint (push) Canceled after 0s
CI / Security audit (push) Canceled after 0s

Replace all non-logger console.error/warn calls with process.stderr.write
using tagged prefixes ([AuditLogger], [CSRF], [DNS Registry], etc.) for
grep-ability. All in fallback/catch paths where structured logger may be
unavailable. Test updated to use jest.spyOn with try/finally for clean
mock restoration.

Codex grade: pass (22,402 tokens). All 1539 tests pass.
This commit is contained in:
Hermes
2026-08-12 04:50:16 -07:00
parent a1d7208686
commit 92482980dd
10 changed files with 26 additions and 30 deletions
@@ -156,18 +156,19 @@ describe('Error Handler', () => {
}); });
it('logs non-operational errors as FATAL', () => { it('logs non-operational errors as FATAL', () => {
const origError = console.error; const stderrSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
console.error = jest.fn();
try {
const err = new Error('programming bug'); const err = new Error('programming bug');
errorMiddleware(err, req, res, next); errorMiddleware(err, req, res, next);
expect(console.error).toHaveBeenCalledWith( const calls = stderrSpy.mock.calls.map(c => String(c[0]));
'FATAL: Non-operational error detected', const fatalLine = calls.find(l => l.includes('FATAL'));
expect.any(Object) expect(fatalLine).toBeDefined();
); expect(fatalLine).toContain('programming bug');
} finally {
console.error = origError; stderrSpy.mockRestore();
}
}); });
}); });
+1 -1
View File
@@ -775,7 +775,7 @@ async function getStorageInfo() {
: 0; : 0;
} }
} catch (error) { } catch (error) {
console.error('[BackupsRouter] Error getting storage info:', error.message); process.stderr.write(`[BackupsRouter] Error getting storage info: ${error.message}\n`);
} }
return result; return result;
+1 -2
View File
@@ -410,8 +410,7 @@ class EmailMagicLinkProvider extends AuthProvider {
if (this.deps.log && typeof this.deps.log.warn === 'function') { if (this.deps.log && typeof this.deps.log.warn === 'function') {
this.deps.log.warn('auth-magic-dev', marker); this.deps.log.warn('auth-magic-dev', marker);
} else { } else {
// eslint-disable-next-line no-console process.stderr.write(`${marker}\n`);
console.warn(marker);
} }
} }
@@ -16,7 +16,7 @@ class DNSProviderRegistry {
const instance = new adapterClass({}, {}); const instance = new adapterClass({}, {});
const id = instance.providerId; const id = instance.providerId;
if (this.providers.has(id)) { if (this.providers.has(id)) {
console.warn(`DNS provider "${id}" already registered, overwriting`); process.stderr.write(`[DNS Registry] Provider "${id}" already registered, overwriting\n`);
} }
this.providers.set(id, adapterClass); this.providers.set(id, adapterClass);
} }
@@ -88,7 +88,7 @@ class DNSProviderRegistry {
} }
} }
} catch (err) { } catch (err) {
console.error(`Failed to load DNS provider from ${file}:`, err.message); process.stderr.write(`[DNS Registry] Failed to load DNS provider from ${file}: ${err.message}\n`);
} }
} }
} }
@@ -50,7 +50,7 @@ class AutoRestartManager extends EventEmitter {
super(); super();
this.ctx = ctx; this.ctx = ctx;
this.log = ctx.log || console; this.log = ctx.log || console;
this.logError = ctx.logError || ((_ctx, err) => console.error(err)); this.logError = ctx.logError || ((_ctx, err) => process.stderr.write(`[auto-restart] ${err?.message || err}\n`));
this.docker = ctx.docker; this.docker = ctx.docker;
this.healthChecker = ctx.healthChecker; this.healthChecker = ctx.healthChecker;
this.notification = ctx.notification; this.notification = ctx.notification;
@@ -41,7 +41,7 @@ class ConfigDriftDetector extends EventEmitter {
super(); super();
this.ctx = ctx; this.ctx = ctx;
this.log = ctx.log || console; this.log = ctx.log || console;
this.logError = ctx.logError || ((_c, err) => console.error(err)); this.logError = ctx.logError || ((_c, err) => process.stderr.write(`[config-drift] ${err?.message || err}\n`));
this.docker = ctx.docker; this.docker = ctx.docker;
this.servicesStateManager = ctx.servicesStateManager; this.servicesStateManager = ctx.servicesStateManager;
this.notification = ctx.notification; this.notification = ctx.notification;
+3 -3
View File
@@ -184,10 +184,10 @@ class AuditLogger {
}); });
} catch (e) { } catch (e) {
// Non-fatal — security store is a best-effort mirror // Non-fatal — security store is a best-effort mirror
console.error('[AuditLogger] Security event emit failed:', e.message); process.stderr.write(`[AuditLogger] Security event emit failed: ${e.message}\n`);
} }
} catch (e) { } catch (e) {
console.error('[AuditLogger] Failed to write entry:', e.message); process.stderr.write(`[AuditLogger] Failed to write entry: ${e.message}\n`);
} }
} }
@@ -199,7 +199,7 @@ class AuditLogger {
} }
return entries.slice(offset, offset + limit); return entries.slice(offset, offset + limit);
} catch (e) { } catch (e) {
console.error('[AuditLogger] Failed to read:', e.message); process.stderr.write(`[AuditLogger] Failed to read: ${e.message}\n`);
return []; return [];
} }
} }
@@ -216,14 +216,14 @@ function csrfValidationMiddleware(req, res, next) {
// Validate both values exist // Validate both values exist
if (!cookieNonce) { if (!cookieNonce) {
console.warn(`[CSRF] Missing CSRF cookie: ${method} ${req.path} from ${req.ip}`); process.stderr.write(`[CSRF] Missing CSRF cookie: ${method} ${req.path} from ${req.ip}\n`);
return errorResponse(res, 403, '[DC-100] CSRF token missing', { return errorResponse(res, 403, '[DC-100] CSRF token missing', {
message: 'CSRF cookie not found. Please refresh the page (Ctrl+Shift+R) and try again.' message: 'CSRF cookie not found. Please refresh the page (Ctrl+Shift+R) and try again.'
}); });
} }
if (!headerToken) { if (!headerToken) {
console.warn(`[CSRF] Missing CSRF header: ${method} ${req.path} from ${req.ip}`); process.stderr.write(`[CSRF] Missing CSRF header: ${method} ${req.path} from ${req.ip}\n`);
return errorResponse(res, 403, '[DC-100] CSRF token missing', { return errorResponse(res, 403, '[DC-100] CSRF token missing', {
message: 'CSRF token not provided in request headers. Please refresh the page (Ctrl+Shift+R) and try again.' message: 'CSRF token not provided in request headers. Please refresh the page (Ctrl+Shift+R) and try again.'
}); });
@@ -247,7 +247,7 @@ function csrfValidationMiddleware(req, res, next) {
next(); next();
} catch (err) { } catch (err) {
console.warn(`[CSRF] Invalid CSRF token: ${method} ${req.path} from ${req.ip} - ${err.message}`); process.stderr.write(`[CSRF] Invalid CSRF token: ${method} ${req.path} from ${req.ip} - ${err.message}\n`);
return errorResponse(res, 403, '[DC-101] CSRF token invalid', { return errorResponse(res, 403, '[DC-101] CSRF token invalid', {
message: 'CSRF token validation failed. Please refresh the page (Ctrl+Shift+R) and try again.' message: 'CSRF token validation failed. Please refresh the page (Ctrl+Shift+R) and try again.'
}); });
+2 -6
View File
@@ -34,7 +34,7 @@ function errorMiddleware(err, req, res, next) {
userId: req.user?.id, userId: req.user?.id,
body: req.body body: req.body
} }
).catch(e => console.error('Failed to write to error log:', e.message)); ).catch(e => process.stderr.write(`[error-handler] Failed to write to error log: ${e.message}\n`));
// Determine if this is an operational error (AppError) or programming error // Determine if this is an operational error (AppError) or programming error
const isOperational = err.isOperational || err instanceof AppError; const isOperational = err.isOperational || err instanceof AppError;
@@ -65,11 +65,7 @@ function errorMiddleware(err, req, res, next) {
// For non-operational errors, log as fatal // For non-operational errors, log as fatal
if (!isOperational) { if (!isOperational) {
console.error('FATAL: Non-operational error detected', { process.stderr.write(`[FATAL] Non-operational error detected: ${JSON.stringify({ error: err.message, stack: err.stack, path: req.path })}\n`);
error: err.message,
stack: err.stack,
path: req.path
});
} }
} }
+1 -1
View File
@@ -43,7 +43,7 @@ function fetchT(url, opts = {}, timeoutMs = TIMEOUTS.HTTP_DEFAULT) {
// passes `timeout: N` here, it's almost certainly a bug — we used to silently // passes `timeout: N` here, it's almost certainly a bug — we used to silently
// strip it, which masked the issue. Now we surface it in logs and strip it. // strip it, which masked the issue. Now we surface it in logs and strip it.
if ('timeout' in opts) { if ('timeout' in opts) {
console.warn(`[fetchT] opts.timeout=${opts.timeout} is ignored — pass timeoutMs as the 3rd arg of fetchT() instead. Called from: ${new Error().stack.split('\n').slice(2, 4).join(' <- ')}`); process.stderr.write(`[fetchT] opts.timeout=${opts.timeout} is ignored — pass timeoutMs as the 3rd arg of fetchT() instead. Called from: ${new Error().stack.split('\n').slice(2, 4).join(' <- ')}\n`);
const { timeout: _timeout, ...rest } = opts; const { timeout: _timeout, ...rest } = opts;
opts = rest; opts = rest;
} }