Skip to content

Commit

Permalink
feat(platform): add API to provide CSP nonce for inline stylesheets […
Browse files Browse the repository at this point in the history
…WIP using constructable stylesheet]

resolves #287
  • Loading branch information
danielwiehl committed Oct 17, 2024
1 parent c10b0c4 commit 9d32613
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 115 deletions.
10 changes: 9 additions & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,10 @@
"buildTarget": "microfrontend-platform-testing-app:build:production-ci"
},
"development": {
"buildTarget": "microfrontend-platform-testing-app:build:development"
"buildTarget": "microfrontend-platform-testing-app:build:development",
"headers": {
"Content-Security-Policy-Report-Only": "default-src 'self'; connect-src 'self' blob: http://localhost:4201 http://localhost:4202 http://localhost:4203 http://localhost:4204 http://localhost:5200; frame-src 'self' http://localhost:4201 http://localhost:4202 http://localhost:4203 http://localhost:4204 http://localhost:5200; font-src 'self' https://fonts.gstatic.com; style-src 'self' https://fonts.googleapis.com 'nonce-test'; script-src 'self' 'nonce-test';"
}
}
},
"defaultConfiguration": "development"
Expand Down Expand Up @@ -323,5 +326,10 @@
}
}
}
},
"cli": {
"cache": {
"enabled": false
}
}
}
2 changes: 1 addition & 1 deletion apps/microfrontend-platform-testing-app/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
<app-root ngCspNonce="test"></app-root>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -26,40 +26,6 @@ const ATTR_NAME = 'name';
const ATTR_SCROLLABLE = 'scrollable';
const ATTR_KEYSTROKES = 'keystrokes';
const HTML_TEMPLATE = `
<style>
:host {
display: block;
overflow: hidden;
position: relative; /* positioning context for splash */
}
iframe {
width: 100%;
height: 100%;
border: none;
margin: 0;
}
/* Ensure transparent router-outlet if empty.
*
* An iframe is transparent only if the embedded content has the same color scheme as the embedding document.
* An empty router-outlet loads the 'about:blank' page. This page has the user's preferred OS color scheme,
* which may be different from the application's color scheme, making the iframe opaque. Therefore, we hide
* the iframe to make the router-outlet transparent again.
*
* More information about iframe transparency:
* - https://github.com/w3c/csswg-drafts/issues/4772#issuecomment-591553929
* - https://fvsch.com/transparent-iframes
*/
:host-context(.sci-empty) iframe {
display: none;
}
div[part="splash"] {
position: absolute;
inset: 0;
}
</style>
<iframe src="about:blank" scrolling="yes" marginheight="0" marginwidth="0"></iframe>
<template id="splash">
Expand All @@ -68,6 +34,40 @@ const HTML_TEMPLATE = `
</div>
</template>
`;
const STYLE_SHEET = `
:host {
display: block;
overflow: hidden;
position: relative; /* positioning context for splash */
}
iframe {
width: 100%;
height: 100%;
border: none;
margin: 0;
}
/* Ensure transparent router-outlet if empty.
*
* An iframe is transparent only if the embedded content has the same color scheme as the embedding document.
* An empty router-outlet loads the 'about:blank' page. This page has the user's preferred OS color scheme,
* which may be different from the application's color scheme, making the iframe opaque. Therefore, we hide
* the iframe to make the router-outlet transparent again.
*
* More information about iframe transparency:
* - https://github.com/w3c/csswg-drafts/issues/4772#issuecomment-591553929
* - https://fvsch.com/transparent-iframes
*/
:host-context(.sci-empty) iframe {
display: none;
}
div[part="splash"] {
position: absolute;
inset: 0;
}
`;

/**
* Web component that allows embedding web content using the {@link OutletRouter}. The content is displayed inside
Expand Down Expand Up @@ -274,6 +274,12 @@ export class SciRouterOutletElement extends HTMLElement {
this._outletName$ = new BehaviorSubject<string>(PRIMARY_OUTLET);
this._shadowRoot = this.attachShadow({mode: 'open'});
this._shadowRoot.innerHTML = HTML_TEMPLATE.trim();

// Add styles using a constructable stylesheet instead of inline styles, as inline styles require a nonce if CSP disallows 'unsafe-inline' styles.
const styleSheet = new CSSStyleSheet({});
styleSheet.replaceSync(STYLE_SHEET);
this._shadowRoot.adoptedStyleSheets.push(styleSheet);

this._iframe = this._shadowRoot.querySelector('iframe')!;
this._contextProvider = new RouterOutletContextProvider(this._iframe);
this._splash = new Splash(this._shadowRoot, this._iframe);
Expand Down
158 changes: 79 additions & 79 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
"ES2022",
"dom"
],
"paths": {
"@scion/microfrontend-platform": [
"./dist/scion/microfrontend-platform"
]
}
// "paths": {
// "@scion/microfrontend-platform": [
// "./dist/scion/microfrontend-platform"
// ]
// }
/*
* =====================================================================+
* | PATH-OVERRIDE-FOR-DEVELOPMENT |
Expand All @@ -41,80 +41,80 @@
* | '../scion-microfrontend-platform/node_module'. |
* =====================================================================+
*/
// "paths": {
// "@scion/microfrontend-platform": [
// "./projects/scion/microfrontend-platform/src/public-api"
// ],
// // "@scion/toolkit/bean-manager": [
// // "../scion-toolkit/projects/scion/toolkit/bean-manager/src/public_api"
// // ],
// // "@scion/toolkit/observable": [
// // "../scion-toolkit/projects/scion/toolkit/observable/src/public_api"
// // ],
// // "@scion/toolkit/operators": [
// // "../scion-toolkit/projects/scion/toolkit/operators/src/public_api"
// // ],
// // "@scion/toolkit/storage": [
// // "../scion-toolkit/projects/scion/toolkit/storage/src/public_api"
// // ],
// // "@scion/toolkit/testing": [
// // "../scion-toolkit/projects/scion/toolkit/testing/src/public_api"
// // ],
// // "@scion/toolkit/util": [
// // "../scion-toolkit/projects/scion/toolkit/util/src/public_api"
// // ],
// // "@scion/toolkit/uuid": [
// // "../scion-toolkit/projects/scion/toolkit/uuid/src/public_api"
// // ],
// // "@scion/components/dimension": [
// // "../scion-toolkit/projects/scion/components/dimension/src/public_api"
// // ],
// // "@scion/components/sashbox": [
// // "../scion-toolkit/projects/scion/components/sashbox/src/public_api"
// // ],
// // "@scion/components/splitter": [
// // "../scion-toolkit/projects/scion/components/splitter/src/public_api"
// // ],
// // "@scion/components/throbber": [
// // "../scion-toolkit/projects/scion/components/throbber/src/public_api"
// // ],
// // "@scion/components/viewport": [
// // "../scion-toolkit/projects/scion/components/viewport/src/public_api"
// // ],
// // "@scion/components.internal/accordion": [
// // "../scion-toolkit/projects/scion/components.internal/accordion/src/public_api"
// // ],
// // "@scion/components.internal/checkbox": [
// // "../scion-toolkit/projects/scion/components.internal/checkbox/src/public_api"
// // ],
// // "@scion/components.internal/filter-field": [
// // "../scion-toolkit/projects/scion/components.internal/filter-field/src/public_api"
// // ],
// // "@scion/components.internal/form-field": [
// // "../scion-toolkit/projects/scion/components.internal/form-field/src/public_api"
// // ],
// // "@scion/components.internal/list": [
// // "../scion-toolkit/projects/scion/components.internal/list/src/public_api"
// // ],
// // "@scion/components.internal/key-value-field": [
// // "../scion-toolkit/projects/scion/components.internal/key-value-field/src/public_api"
// // ],
// // "@scion/components.internal/key-value": [
// // "../scion-toolkit/projects/scion/components.internal/key-value/src/public_api"
// // ],
// // "@scion/components.internal/qualifier-chip-list": [
// // "../scion-toolkit/projects/scion/components.internal/qualifier-chip-list/src/public_api"
// // ],
// // "@scion/components.internal/tabbar": [
// // "../scion-toolkit/projects/scion/components.internal/tabbar/src/public_api"
// // ],
// // "@scion/components.internal/toggle-button": [
// // "../scion-toolkit/projects/scion/components.internal/toggle-button/src/public_api"
// // ],
// // "@scion/components.internal/material-icon": [
// // "../scion-toolkit/projects/scion/components.internal/material-icon/src/public_api"
// // ],
// }
"paths": {
"@scion/microfrontend-platform": [
"./projects/scion/microfrontend-platform/src/public-api"
],
// "@scion/toolkit/bean-manager": [
// "../scion-toolkit/projects/scion/toolkit/bean-manager/src/public_api"
// ],
// "@scion/toolkit/observable": [
// "../scion-toolkit/projects/scion/toolkit/observable/src/public_api"
// ],
// "@scion/toolkit/operators": [
// "../scion-toolkit/projects/scion/toolkit/operators/src/public_api"
// ],
// "@scion/toolkit/storage": [
// "../scion-toolkit/projects/scion/toolkit/storage/src/public_api"
// ],
// "@scion/toolkit/testing": [
// "../scion-toolkit/projects/scion/toolkit/testing/src/public_api"
// ],
// "@scion/toolkit/util": [
// "../scion-toolkit/projects/scion/toolkit/util/src/public_api"
// ],
// "@scion/toolkit/uuid": [
// "../scion-toolkit/projects/scion/toolkit/uuid/src/public_api"
// ],
// "@scion/components/dimension": [
// "../scion-toolkit/projects/scion/components/dimension/src/public_api"
// ],
// "@scion/components/sashbox": [
// "../scion-toolkit/projects/scion/components/sashbox/src/public_api"
// ],
// "@scion/components/splitter": [
// "../scion-toolkit/projects/scion/components/splitter/src/public_api"
// ],
// "@scion/components/throbber": [
// "../scion-toolkit/projects/scion/components/throbber/src/public_api"
// ],
// "@scion/components/viewport": [
// "../scion-toolkit/projects/scion/components/viewport/src/public_api"
// ],
// "@scion/components.internal/accordion": [
// "../scion-toolkit/projects/scion/components.internal/accordion/src/public_api"
// ],
// "@scion/components.internal/checkbox": [
// "../scion-toolkit/projects/scion/components.internal/checkbox/src/public_api"
// ],
// "@scion/components.internal/filter-field": [
// "../scion-toolkit/projects/scion/components.internal/filter-field/src/public_api"
// ],
// "@scion/components.internal/form-field": [
// "../scion-toolkit/projects/scion/components.internal/form-field/src/public_api"
// ],
// "@scion/components.internal/list": [
// "../scion-toolkit/projects/scion/components.internal/list/src/public_api"
// ],
// "@scion/components.internal/key-value-field": [
// "../scion-toolkit/projects/scion/components.internal/key-value-field/src/public_api"
// ],
// "@scion/components.internal/key-value": [
// "../scion-toolkit/projects/scion/components.internal/key-value/src/public_api"
// ],
// "@scion/components.internal/qualifier-chip-list": [
// "../scion-toolkit/projects/scion/components.internal/qualifier-chip-list/src/public_api"
// ],
// "@scion/components.internal/tabbar": [
// "../scion-toolkit/projects/scion/components.internal/tabbar/src/public_api"
// ],
// "@scion/components.internal/toggle-button": [
// "../scion-toolkit/projects/scion/components.internal/toggle-button/src/public_api"
// ],
// "@scion/components.internal/material-icon": [
// "../scion-toolkit/projects/scion/components.internal/material-icon/src/public_api"
// ],
}
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
Expand Down

0 comments on commit 9d32613

Please sign in to comment.