Skip to content

Commit 17045d6

Browse files
authored
fix(ses): to workaround #2908 use current console (#2934)
Closes: #2908 Refs: #636 #944 #945 nodejs/node#59456 ## Description Due to timing, reporter.js runs before lockdown replaces the system console with our causal console. Thus, when `makeReporter` makes a reporter that uses some global console, it uses the platform's original global console rather than the causal console. I don't know if this was purposeful or good enough at the time, so I don't know whether this PR is a workaround or a proper fix. In any case, it fixes the case of immediate interest by using the current global console to report rather than the original one. ### Security Considerations @gibson042 checked below and found that the `console` property of the global of the start compartments remains mutable after lockdown. This is a security hazard, but is consistent with our stance on the privileges available in the start compartment. In any case, this hazard is not affected by this PR. ### Scaling Considerations none ### Documentation Considerations none ### Testing Considerations Hard to write a regression test to ensure that a stack is output since there is no portable agreement on what stacks look like. But not impossible. Manually running the test case from #2908 , we get the correct outputs at #2908 (comment) . Running that test case from #2908 on Node 22 and 24, we find that the bug does not manifest there either. The problem is ***only*** on Node 20. I don't understand how that can be. In any case, this PR does fix the behavior across all these versions. ### Compatibility Considerations If someone suppresses the `console` replacement with `consoleTaming: 'unsafe'`, then this PR in its current state fails to workaround #2908 If some code depends on reporters using the original `console` rather than the current one, that code might break or misbehave. ### Upgrade Considerations none
1 parent a905de7 commit 17045d6

File tree

3 files changed

+64
-26
lines changed

3 files changed

+64
-26
lines changed

packages/ses/docs/lockdown.md

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -492,19 +492,22 @@ LOCKDOWN_REPORTING=none
492492
- The default behavior is `'platform'` which will detect the platform and
493493
report warnings according to whether a web `console`, Node.js `console`, or
494494
`print` are available.
495-
The web platform is distinguished by the existence of `window` or
496-
`importScripts` (WebWorker).
497-
The Node.js behavior is to report all warnings to `stderr` visually
498-
consistent with use of a console group.
499-
SES will use `print` in the absence of a `console`.
500-
Captures the platform `console` at the time `lockdown` or `repairIntrinsics`
501-
are called, not at the time `ses` initializes.
502-
- The `'console'` option forces the web platform behavior.
503-
On Node.js, this results in group labels being reported to `stdout`.
504-
The global `console` can be replaced before `lockdown` so using this option
495+
- The web platform is distinguished by the existence of `window` or
496+
`importScripts` (WebWorker), in which case the current console (at the time
497+
of reporting) is used.
498+
- The Node.js behavior is to report all warnings to `stderr` visually
499+
consistent with use of a console group. To do this, it actually
500+
reports using the `console.error` method of the current console (at
501+
the time of reporting).
502+
- SES will use `print` in the absence of a `console`.
503+
- The `'console'` option forces the web platform behavior, in which the current
504+
console (at time of reporting) is used directly.
505+
On Node.js, this results in group labels being reported to `stdout`, because
506+
that is the unalterable behavior of Node's `console.group*` methods.
507+
The global `console` can be replaced, so using this option
505508
will drive use of `console.groupCollapsed`, `console.groupEnd`,
506509
`console.warn`, and `console.error` assuming that console is suited for
507-
reporting arbitrary diagnostics rather than also being suited to generate
510+
reporting arbitrary diagnostics, rather than also being suited to generate
508511
machine-readable `stdout`.
509512
- The `'none'` option mutes warnings.
510513

packages/ses/src/reporting.js

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,45 @@
1-
import { functionBind, globalThis } from './commons.js';
1+
import { globalThis } from './commons.js';
22
import { assert } from './error/assert.js';
33

44
/**
55
* @import {Reporter, GroupReporter} from './reporting-types.js'
66
*/
77

8+
/* eslint-disable @endo/no-polymorphic-call */
9+
/**
10+
* To address https://github.com/endojs/endo/issues/2908,
11+
* the `consoleReporter` uses the current `console` rather
12+
* than the original one.
13+
*
14+
* @type {GroupReporter}
15+
*/
16+
const consoleReporter = {
17+
warn(...args) {
18+
globalThis.console.warn(...args);
19+
},
20+
error(...args) {
21+
globalThis.console.error(...args);
22+
},
23+
...(globalThis.console?.groupCollapsed
24+
? {
25+
groupCollapsed(...args) {
26+
globalThis.console.groupCollapsed(...args);
27+
},
28+
}
29+
: undefined),
30+
...(globalThis.console?.groupEnd
31+
? {
32+
groupEnd() {
33+
globalThis.console.groupEnd();
34+
},
35+
}
36+
: undefined),
37+
};
38+
/* eslint-enable @endo/no-polymorphic-call */
39+
840
/**
941
* Creates a suitable reporter for internal errors and warnings out of the
10-
* Node.js console.error to ensure all messages to go stderr, including the
42+
* Node.js console.error to ensure all messages go to stderr, including the
1143
* group label.
1244
* Accounts for the extra space introduced by console.error as a delimiter
1345
* between the indent and subsequent arguments.
@@ -51,18 +83,22 @@ export const chooseReporter = reporting => {
5183
if (reporting === 'none') {
5284
return makeReportPrinter(mute);
5385
}
54-
if (
55-
reporting === 'console' ||
56-
globalThis.window === globalThis ||
57-
globalThis.importScripts !== undefined
58-
) {
59-
return console;
60-
}
6186
if (globalThis.console !== undefined) {
87+
if (
88+
reporting === 'console' || // asks for console explicitly
89+
globalThis.window === globalThis || // likely on browser
90+
globalThis.importScripts !== undefined // likely on worker
91+
) {
92+
// reporter just delegates directly to the current console
93+
return consoleReporter;
94+
}
95+
assert(reporting === 'platform');
6296
// On Node.js, we send all feedback to stderr, regardless of purported level.
63-
const console = globalThis.console;
64-
const error = functionBind(console.error, console);
65-
return makeReportPrinter(error);
97+
// This uses `consoleReporter.error` instead of `console.error` because we
98+
// want the constructed reporter to use the `console.error` of the current
99+
// `console`, not the `console` that was installed when the reporter
100+
// was created.
101+
return makeReportPrinter(consoleReporter.error);
66102
}
67103
if (globalThis.print !== undefined) {
68104
return makeReportPrinter(globalThis.print);

packages/ses/test/error/_throws-and-logs.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ import {
1212
// const internalDebugConsole = console;
1313

1414
const defaultCompareLogs = freeze((t, log, goldenLog) => {
15-
// For our internal debugging purposes
16-
// internalDebugConsole.log('LOG', log);
17-
1815
t.is(log.length, goldenLog.length, 'wrong log length');
1916
log.forEach((logRecord, i) => {
2017
const goldenRecord = goldenLog[i];
@@ -107,6 +104,8 @@ export const assertLogs = freeze((t, thunk, goldenLog, options = {}) => {
107104
globalThis.console = priorConsole;
108105
if (checkLogs) {
109106
const log = takeLog();
107+
// For our internal debugging purposes
108+
// internalDebugConsole.log('LOG', log);
110109
compareLogs(t, log, goldenLog);
111110
}
112111
}

0 commit comments

Comments
 (0)