[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
+11 -10
View File
@@ -156,18 +156,19 @@ describe('Error Handler', () => {
});
it('logs non-operational errors as FATAL', () => {
const origError = console.error;
console.error = jest.fn();
const stderrSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
const err = new Error('programming bug');
errorMiddleware(err, req, res, next);
try {
const err = new Error('programming bug');
errorMiddleware(err, req, res, next);
expect(console.error).toHaveBeenCalledWith(
'FATAL: Non-operational error detected',
expect.any(Object)
);
console.error = origError;
const calls = stderrSpy.mock.calls.map(c => String(c[0]));
const fatalLine = calls.find(l => l.includes('FATAL'));
expect(fatalLine).toBeDefined();
expect(fatalLine).toContain('programming bug');
} finally {
stderrSpy.mockRestore();
}
});
});
+1 -1
View File
@@ -775,7 +775,7 @@ async function getStorageInfo() {
: 0;
}
} catch (error) {
console.error('[BackupsRouter] Error getting storage info:', error.message);
process.stderr.write(`[BackupsRouter] Error getting storage info: ${error.message}\n`);
}
return result;
+1 -2
View File
@@ -410,8 +410,7 @@ class EmailMagicLinkProvider extends AuthProvider {
if (this.deps.log && typeof this.deps.log.warn === 'function') {
this.deps.log.warn('auth-magic-dev', marker);
} else {
// eslint-disable-next-line no-console
console.warn(marker);
process.stderr.write(`${marker}\n`);
}
}
@@ -16,7 +16,7 @@ class DNSProviderRegistry {
const instance = new adapterClass({}, {});
const id = instance.providerId;
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);
}
@@ -88,7 +88,7 @@ class DNSProviderRegistry {
}
}
} 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();
this.ctx = ctx;
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.healthChecker = ctx.healthChecker;
this.notification = ctx.notification;
@@ -41,7 +41,7 @@ class ConfigDriftDetector extends EventEmitter {
super();
this.ctx = ctx;
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.servicesStateManager = ctx.servicesStateManager;
this.notification = ctx.notification;
+3 -3
View File
@@ -184,10 +184,10 @@ class AuditLogger {
});
} catch (e) {
// 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) {
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);
} catch (e) {
console.error('[AuditLogger] Failed to read:', e.message);
process.stderr.write(`[AuditLogger] Failed to read: ${e.message}\n`);
return [];
}
}
@@ -216,14 +216,14 @@ function csrfValidationMiddleware(req, res, next) {
// Validate both values exist
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', {
message: 'CSRF cookie not found. Please refresh the page (Ctrl+Shift+R) and try again.'
});
}
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', {
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();
} 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', {
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,
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
const isOperational = err.isOperational || err instanceof AppError;
@@ -65,11 +65,7 @@ function errorMiddleware(err, req, res, next) {
// For non-operational errors, log as fatal
if (!isOperational) {
console.error('FATAL: Non-operational error detected', {
error: err.message,
stack: err.stack,
path: req.path
});
process.stderr.write(`[FATAL] Non-operational error detected: ${JSON.stringify({ error: err.message, stack: err.stack, path: req.path })}\n`);
}
}
+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
// strip it, which masked the issue. Now we surface it in logs and strip it.
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;
opts = rest;
}