mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
Allow passing CSP nonce to exported html (#12088)
* Allow passing CSP nonce to exported html * test: Add nonce regression test, drop options from tags Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -33,10 +33,18 @@ const getBucketOrigin = () => {
|
||||
}
|
||||
};
|
||||
|
||||
interface CSPOptions {
|
||||
/** Additional origins to allow as script sources. */
|
||||
extraScriptSrc?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Content Security Policy middleware for the application.
|
||||
*
|
||||
* @param options Optional configuration for the CSP middleware.
|
||||
* @returns A Koa middleware function that applies the CSP headers.
|
||||
*/
|
||||
export default function createCSPMiddleware() {
|
||||
export default function createCSPMiddleware(options?: CSPOptions) {
|
||||
// Construct scripts CSP based on options in use
|
||||
const defaultSrc: string[] = ["'self'"];
|
||||
const scriptSrc: string[] = [];
|
||||
@@ -83,6 +91,7 @@ export default function createCSPMiddleware() {
|
||||
styleSrc,
|
||||
scriptSrc: [
|
||||
...uniq(scriptSrc),
|
||||
...(options?.extraScriptSrc ?? []),
|
||||
env.DEVELOPMENT_UNSAFE_INLINE_CSP
|
||||
? "'unsafe-inline'"
|
||||
: `'nonce-${ctx.state.cspNonce}'`,
|
||||
|
||||
@@ -100,6 +100,32 @@ describe("DocumentHelper", () => {
|
||||
expect(result).toContain('<p dir="auto">This is a test paragraph</p>');
|
||||
});
|
||||
|
||||
it("should apply the cspNonce to the injected mermaid script", async () => {
|
||||
const document = await buildDocument({
|
||||
text: "```mermaid\ngraph TD;\nA-->B;\n```",
|
||||
});
|
||||
const result = await DocumentHelper.toHTML(document, {
|
||||
includeTitle: false,
|
||||
includeStyles: false,
|
||||
includeMermaid: true,
|
||||
cspNonce: "test-nonce-123",
|
||||
});
|
||||
expect(result).toMatch(/<script[^>]*nonce="test-nonce-123"/);
|
||||
expect(result).toContain('window.status = "ready"');
|
||||
});
|
||||
|
||||
it("should not set a nonce attribute when cspNonce is not provided", async () => {
|
||||
const document = await buildDocument({
|
||||
text: "```mermaid\ngraph TD;\nA-->B;\n```",
|
||||
});
|
||||
const result = await DocumentHelper.toHTML(document, {
|
||||
includeTitle: false,
|
||||
includeStyles: false,
|
||||
includeMermaid: true,
|
||||
});
|
||||
expect(result).not.toMatch(/<script[^>]*nonce="/);
|
||||
});
|
||||
|
||||
it("should render diff classes when changes provided", async () => {
|
||||
const doc1 = await buildDocument({ text: "Hello world" });
|
||||
const doc2 = await buildDocument({ text: "Hello modified world" });
|
||||
|
||||
@@ -68,6 +68,8 @@ type HTMLOptions = {
|
||||
baseUrl?: string;
|
||||
/** Changes to highlight in the document */
|
||||
changes?: readonly ExtendedChange[];
|
||||
/** CSP nonce to apply to injected inline scripts */
|
||||
cspNonce?: string;
|
||||
};
|
||||
|
||||
@trace()
|
||||
@@ -257,12 +259,12 @@ export class DocumentHelper {
|
||||
centered: options?.centered,
|
||||
baseUrl: options?.baseUrl,
|
||||
changes: options?.changes,
|
||||
cspNonce: options?.cspNonce,
|
||||
});
|
||||
|
||||
addTags({
|
||||
collectionId: model instanceof Collection ? model.id : undefined,
|
||||
documentId: !(model instanceof Collection) ? model.id : undefined,
|
||||
options,
|
||||
});
|
||||
|
||||
if (options?.signedUrls) {
|
||||
|
||||
@@ -50,6 +50,8 @@ export type HTMLOptions = {
|
||||
baseUrl?: string;
|
||||
/** Changes to highlight in the document */
|
||||
changes?: readonly ExtendedChange[];
|
||||
/** CSP nonce to apply to injected inline scripts */
|
||||
cspNonce?: string;
|
||||
};
|
||||
|
||||
export type MentionAttrs = {
|
||||
@@ -558,6 +560,10 @@ export class ProsemirrorHelper extends SharedProsemirrorHelper {
|
||||
const element = dom.window.document.createElement("script");
|
||||
element.setAttribute("type", "module");
|
||||
|
||||
if (options?.cspNonce) {
|
||||
element.setAttribute("nonce", options.cspNonce);
|
||||
}
|
||||
|
||||
// Inject Mermaid script
|
||||
if (mermaidElements.length) {
|
||||
element.innerHTML = `
|
||||
|
||||
Reference in New Issue
Block a user