Skip to content

Commit 7c92fa8

Browse files
committed
Options get plumbed through to the builder.
1 parent 7551f76 commit 7c92fa8

File tree

5 files changed

+53
-7
lines changed

5 files changed

+53
-7
lines changed

packages/angular/build/src/builders/application/options.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ export async function normalizeOptions(
398398
partialSSRBuild = false,
399399
externalRuntimeStyles,
400400
instrumentForCoverage,
401+
security,
401402
} = options;
402403

403404
// Return all the normalized options
@@ -461,6 +462,7 @@ export async function normalizeOptions(
461462
partialSSRBuild: usePartialSsrBuild || partialSSRBuild,
462463
externalRuntimeStyles,
463464
instrumentForCoverage,
465+
security,
464466
};
465467
}
466468

packages/angular/build/src/builders/application/schema.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,33 @@
3737
"type": "string",
3838
"description": "Customize the base path for the URLs of resources in 'index.html' and component stylesheets. This option is only necessary for specific deployment scenarios, such as with Angular Elements or when utilizing different CDN locations."
3939
},
40+
"security": {
41+
"description": "Security features to protect against XSS and other common attacks",
42+
"type": "object",
43+
"additionalProperties": false,
44+
"properties": {
45+
"autoCsp": {
46+
"description": "Enables auto-CSP generation. Will default to true once we are out of experimental/preview phases.",
47+
"default": false,
48+
"oneOf": [
49+
{
50+
"type": "object",
51+
"properties": {
52+
"unsafeEval": {
53+
"type": "boolean",
54+
"description": "Include the `unsafe-eval` directive in the auto-CSP. Please only enable this if you are absolutely sure that you need to, as allowing calls to eval will weaken the XSS defenses provided by the auto-CSP.",
55+
"default": false
56+
}
57+
},
58+
"additionalProperties": false
59+
},
60+
{
61+
"type": "boolean"
62+
}
63+
]
64+
}
65+
}
66+
},
4067
"scripts": {
4168
"description": "Global scripts to be included in the build.",
4269
"type": "array",

packages/angular/build/src/tools/esbuild/index-html-generator.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ export async function generateIndexHtml(
8080
throw new Error(`Output file does not exist: ${relativefilePath}`);
8181
};
8282

83+
// Read the Auto CSP options.
84+
const autoCsp = buildOptions.security?.autoCsp;
85+
const autoCspOptions =
86+
autoCsp === true
87+
? { unsafeEval: false }
88+
: autoCsp
89+
? { unsafeEval: !!autoCsp.unsafeEval }
90+
: undefined;
91+
8392
// Create an index HTML generator that reads from the in-memory output files
8493
const indexHtmlGenerator = new IndexHtmlGenerator({
8594
indexPath: indexHtmlOptions.input,
@@ -94,7 +103,7 @@ export async function generateIndexHtml(
94103
buildOptions.prerenderOptions ||
95104
buildOptions.appShellOptions
96105
),
97-
autoCsp: true,
106+
autoCsp: autoCspOptions,
98107
});
99108

100109
indexHtmlGenerator.readAsset = readAsset;

packages/angular/build/src/utils/index-file/auto-csp.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export function hashTextContent(scriptText: string): string {
104104
* @param html Markup that should be processed.
105105
* @returns The transformed HTML that contains the `<meta>` tag CSP and dynamic loader scripts.
106106
*/
107-
export async function autoCsp(html: string): Promise<string> {
107+
export async function autoCsp(html: string, unsafeEval = false): Promise<string> {
108108
const { rewriter, transformedContent } = await htmlRewritingStream(html);
109109

110110
let openedScriptTag: StartTag | undefined = undefined;
@@ -190,7 +190,11 @@ export async function autoCsp(html: string): Promise<string> {
190190
if (tag.tagName === 'head') {
191191
// See what hashes we came up with!
192192
secondPass.rewriter.emitRaw(
193-
`<meta http-equiv="Content-Security-Policy" content="${getStrictCsp(hashes)}">`,
193+
`<meta http-equiv="Content-Security-Policy" content="${getStrictCsp(hashes, {
194+
enableBrowserFallbacks: true,
195+
enableTrustedTypes: false,
196+
enableUnsafeEval: unsafeEval,
197+
})}">`,
194198
);
195199
}
196200
});

packages/angular/build/src/utils/index-file/index-html-generator.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ export interface IndexHtmlGeneratorProcessOptions {
3333
hints?: { url: string; mode: HintMode; as?: string }[];
3434
}
3535

36+
export interface AutoCspOptions {
37+
unsafeEval: boolean;
38+
}
39+
3640
export interface IndexHtmlGeneratorOptions {
3741
indexPath: string;
3842
deployUrl?: string;
@@ -44,7 +48,7 @@ export interface IndexHtmlGeneratorOptions {
4448
cache?: NormalizedCachedOptions;
4549
imageDomains?: string[];
4650
generateDedicatedSSRContent?: boolean;
47-
autoCsp?: boolean;
51+
autoCsp?: AutoCspOptions;
4852
}
4953

5054
export type IndexHtmlTransform = (content: string) => Promise<string>;
@@ -91,7 +95,7 @@ export class IndexHtmlGenerator {
9195

9296
// Auto-CSP (as the last step)
9397
if (options.autoCsp) {
94-
this.csrPlugins.push(autoCspPlugin());
98+
this.csrPlugins.push(autoCspPlugin(options.autoCsp.unsafeEval));
9599
}
96100
}
97101

@@ -205,8 +209,8 @@ function addNoncePlugin(): IndexHtmlGeneratorPlugin {
205209
return (html) => addNonce(html);
206210
}
207211

208-
function autoCspPlugin(): IndexHtmlGeneratorPlugin {
209-
return (html) => autoCsp(html);
212+
function autoCspPlugin(unsafeEval: boolean): IndexHtmlGeneratorPlugin {
213+
return (html) => autoCsp(html, unsafeEval);
210214
}
211215

212216
function postTransformPlugin({ options }: IndexHtmlGenerator): IndexHtmlGeneratorPlugin {

0 commit comments

Comments
 (0)