react-on-rails 16.2.0-beta.10 → 16.2.0-beta.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/RenderUtils.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare function wrapInScriptTags(scriptId: string, scriptBody: string): string;
|
|
1
|
+
export declare function wrapInScriptTags(scriptId: string, scriptBody: string, nonce?: string): string;
|
|
2
2
|
//# sourceMappingURL=RenderUtils.d.ts.map
|
package/lib/RenderUtils.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
// eslint-disable-next-line import/prefer-default-export -- only one export for now, but others may be added later
|
|
2
|
-
export function wrapInScriptTags(scriptId, scriptBody) {
|
|
2
|
+
export function wrapInScriptTags(scriptId, scriptBody, nonce) {
|
|
3
3
|
if (!scriptBody) {
|
|
4
4
|
return '';
|
|
5
5
|
}
|
|
6
|
+
// Sanitize nonce to prevent attribute injection attacks
|
|
7
|
+
// CSP nonces should be base64 strings, so only allow alphanumeric, +, /, =, -, and _
|
|
8
|
+
const sanitizedNonce = nonce?.replace(/[^a-zA-Z0-9+/=_-]/g, '');
|
|
9
|
+
const nonceAttr = sanitizedNonce ? ` nonce="${sanitizedNonce}"` : '';
|
|
6
10
|
return `
|
|
7
|
-
<script id="${scriptId}">
|
|
11
|
+
<script id="${scriptId}"${nonceAttr}>
|
|
8
12
|
${scriptBody}
|
|
9
13
|
</script>`;
|
|
10
14
|
}
|
package/lib/base/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Authenticity from "../Authenticity.js";
|
|
2
|
-
import buildConsoleReplay from "../buildConsoleReplay.js";
|
|
2
|
+
import buildConsoleReplay, { consoleReplay } from "../buildConsoleReplay.js";
|
|
3
3
|
import reactHydrateOrRender from "../reactHydrateOrRender.js";
|
|
4
4
|
import createReactOutput from "../createReactOutput.js";
|
|
5
5
|
const DEFAULT_OPTIONS = {
|
|
@@ -107,6 +107,9 @@ Fix: Use only react-on-rails OR react-on-rails-pro, not both.`);
|
|
|
107
107
|
buildConsoleReplay() {
|
|
108
108
|
return buildConsoleReplay();
|
|
109
109
|
},
|
|
110
|
+
getConsoleReplayScript() {
|
|
111
|
+
return consoleReplay();
|
|
112
|
+
},
|
|
110
113
|
resetOptions() {
|
|
111
114
|
this.options = { ...DEFAULT_OPTIONS };
|
|
112
115
|
},
|
|
@@ -6,7 +6,11 @@ declare global {
|
|
|
6
6
|
}[];
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Returns the console replay JavaScript code without wrapping it in script tags.
|
|
11
|
+
* This is useful when you want to wrap the code in script tags yourself (e.g., with a CSP nonce).
|
|
12
|
+
* @internal Exported for tests and for Ruby helper to wrap with nonce
|
|
13
|
+
*/
|
|
10
14
|
export declare function consoleReplay(customConsoleHistory?: (typeof console)['history'] | undefined, numberOfMessagesToSkip?: number): string;
|
|
11
|
-
export default function buildConsoleReplay(customConsoleHistory?: (typeof console)['history'] | undefined, numberOfMessagesToSkip?: number): string;
|
|
15
|
+
export default function buildConsoleReplay(customConsoleHistory?: (typeof console)['history'] | undefined, numberOfMessagesToSkip?: number, nonce?: string): string;
|
|
12
16
|
//# sourceMappingURL=buildConsoleReplay.d.ts.map
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { wrapInScriptTags } from "./RenderUtils.js";
|
|
2
2
|
import scriptSanitizedVal from "./scriptSanitizedVal.js";
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Returns the console replay JavaScript code without wrapping it in script tags.
|
|
5
|
+
* This is useful when you want to wrap the code in script tags yourself (e.g., with a CSP nonce).
|
|
6
|
+
* @internal Exported for tests and for Ruby helper to wrap with nonce
|
|
7
|
+
*/
|
|
4
8
|
export function consoleReplay(customConsoleHistory = undefined, numberOfMessagesToSkip = 0) {
|
|
5
9
|
// console.history is a global polyfill used in server rendering.
|
|
6
10
|
const consoleHistory = customConsoleHistory ?? console.history;
|
|
@@ -34,11 +38,11 @@ export function consoleReplay(customConsoleHistory = undefined, numberOfMessages
|
|
|
34
38
|
});
|
|
35
39
|
return lines.join('\n');
|
|
36
40
|
}
|
|
37
|
-
export default function buildConsoleReplay(customConsoleHistory = undefined, numberOfMessagesToSkip = 0) {
|
|
41
|
+
export default function buildConsoleReplay(customConsoleHistory = undefined, numberOfMessagesToSkip = 0, nonce) {
|
|
38
42
|
const consoleReplayJS = consoleReplay(customConsoleHistory, numberOfMessagesToSkip);
|
|
39
43
|
if (consoleReplayJS.length === 0) {
|
|
40
44
|
return '';
|
|
41
45
|
}
|
|
42
|
-
return wrapInScriptTags('consoleReplayLog', consoleReplayJS);
|
|
46
|
+
return wrapInScriptTags('consoleReplayLog', consoleReplayJS, nonce);
|
|
43
47
|
}
|
|
44
48
|
//# sourceMappingURL=buildConsoleReplay.js.map
|
|
@@ -2,7 +2,7 @@ import { isValidElement } from 'react';
|
|
|
2
2
|
// ComponentRegistry is accessed via globalThis.ReactOnRails.getComponent for cross-bundle compatibility
|
|
3
3
|
import createReactOutput from "./createReactOutput.js";
|
|
4
4
|
import { isPromise, isServerRenderHash } from "./isServerRenderResult.js";
|
|
5
|
-
import
|
|
5
|
+
import { consoleReplay } from "./buildConsoleReplay.js";
|
|
6
6
|
import handleError from "./handleError.js";
|
|
7
7
|
import { renderToString } from "./ReactDOMServer.cjs";
|
|
8
8
|
import { createResultObject, convertToError, validateComponent } from "./serverRenderUtils.js";
|
|
@@ -80,12 +80,12 @@ async function createPromiseResult(renderState, componentName, throwJsErrors) {
|
|
|
80
80
|
const consoleHistory = console.history;
|
|
81
81
|
try {
|
|
82
82
|
const html = await renderState.result;
|
|
83
|
-
const consoleReplayScript =
|
|
83
|
+
const consoleReplayScript = consoleReplay(consoleHistory);
|
|
84
84
|
return createResultObject(html, consoleReplayScript, renderState);
|
|
85
85
|
}
|
|
86
86
|
catch (e) {
|
|
87
87
|
const errorRenderState = handleRenderingError(e, { componentName, throwJsErrors });
|
|
88
|
-
const consoleReplayScript =
|
|
88
|
+
const consoleReplayScript = consoleReplay(consoleHistory);
|
|
89
89
|
return createResultObject(errorRenderState.result, consoleReplayScript, errorRenderState);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
@@ -94,7 +94,7 @@ function createFinalResult(renderState, componentName, throwJsErrors) {
|
|
|
94
94
|
if (isPromise(result)) {
|
|
95
95
|
return createPromiseResult({ ...renderState, result }, componentName, throwJsErrors);
|
|
96
96
|
}
|
|
97
|
-
const consoleReplayScript =
|
|
97
|
+
const consoleReplayScript = consoleReplay();
|
|
98
98
|
return JSON.stringify(createResultObject(result, consoleReplayScript, renderState));
|
|
99
99
|
}
|
|
100
100
|
function serverRenderReactComponentInternal(options) {
|
package/lib/types/index.d.ts
CHANGED
|
@@ -324,8 +324,14 @@ export interface ReactOnRailsInternal extends ReactOnRails {
|
|
|
324
324
|
handleError(options: ErrorOptions): string | undefined;
|
|
325
325
|
/**
|
|
326
326
|
* Used by Rails server rendering to replay console messages.
|
|
327
|
+
* Returns the console replay script wrapped in script tags.
|
|
327
328
|
*/
|
|
328
329
|
buildConsoleReplay(): string;
|
|
330
|
+
/**
|
|
331
|
+
* Returns the console replay JavaScript code without wrapping it in script tags.
|
|
332
|
+
* Useful when you need to add CSP nonce or other attributes to the script tag.
|
|
333
|
+
*/
|
|
334
|
+
getConsoleReplayScript(): string;
|
|
329
335
|
/**
|
|
330
336
|
* Get a Map containing all registered components. Useful for debugging.
|
|
331
337
|
*/
|