UNPKG

@microsoft/windows-admin-center-sdk

Version:

Microsoft - Windows Admin Center Shell

144 lines (142 loc) 5.8 kB
import { LogLevel } from '../diagnostics/log-level'; import { Logging } from '../diagnostics/logging'; /** * Class for asset manager */ export class AssetManager { localizationManager; _cssV2; /** * Getter for cssV2. */ get cssV2() { return !!this._cssV2; } fontsKeyword = 'fonts'; constructor(localizationManager, _cssV2) { this.localizationManager = localizationManager; this._cssV2 = _cssV2; } setTheme(theme) { const self = MsftSme.self(); const oldTheme = self.Resources.theme; self.Resources.theme = theme; // apply the theme class to the body of the document document.body.classList.remove(`sme-theme-${oldTheme}`); document.body.classList.add(`sme-theme-${theme}`); } /** * Injects dynamic assets (css, js, etc..) from the shell * This is only meant to be called once during an extensions lifecycle (during init) * @param theme the current theme name * @param assets the assets to process */ loadAssets(theme, assets) { const self = MsftSme.self(); self.Resources.assets = assets; this.setTheme(theme); if (!assets) { return; } // inject css tags into header let cssAssets = assets.css; if (this._cssV2) { if (assets.cssV2) { cssAssets = assets.cssV2; Logging.log({ level: LogLevel.Informational, message: 'Using cssV2', source: 'AssetManager.loadAssets' }); } else if (assets.css) { // Backup solution for scenario where customer using GA release shell before publishing the cssV2 feature with // repository that has been using the cssV2. // In this case, we only allow fonts override to happen. cssAssets = this.updateCssV2Assets(assets.css); Logging.log({ level: LogLevel.Warning, message: 'Empty assets warning. Using older assets for fonts. It is suggested to update the WAC to the latest(2211 release) GA build.', source: 'AssetManager.loadAssets' }); } else { Logging.log({ level: LogLevel.Critical, message: 'Empty assets. Try disable cache and refresh the browser or re-install the latest WAC build', source: 'AssetManager.loadAssets' }); } } this.appendAssets(cssAssets); /** * The js injection mechanism below is subject to the following attack: * * 1. User visits malicious website (MW) from their workstation * 2. MW randomly or sequentially opens hidden iframes to localhost on various ports. * 3. once each iframe loads it send rpc init and impersonates the shell side of the communication channel * 4. The iframe will respond because it trusts * domains for onMessage requests.(this is a basic requirement of our infrastructure) * 5. The MW can then inject any javascript it wants into the module and presumably knows the gateway is running on the same port. * 6. Because we use windows authentication, the MW can execute powershell requests on any servers the user has access to. * 7. The MW has now compromised the server acting as the user. * * How to fix: * In order for this to work, we need an ironclad way of validating that our parent is the shell. * some possibilities are: * * 1. Three way handshake with gateway to discover the only acceptable shell origin. * a. this could be done with javascript or it could be a static file that the module always reads at startup * 2. certificate based authentication before rpc communication * 3. other methods? * * Disabling until we have a more solid use case and we know the most secure way to achieve this functionality. */ // // inject js tags into header // if (assets.js) { // assets.js.forEach(href => { // let script = document.createElement('script'); // script.setAttribute('type', 'text/javascript'); // script.setAttribute('src', href); // head.appendChild(script); // }); // } } appendAssets(cssAssets) { if (!cssAssets) { return; } // get the page header const head = document.getElementsByTagName('head')[0]; cssAssets.forEach(href => { const link = document.createElement('link'); link.rel = 'stylesheet'; link.type = 'text/css'; link.crossOrigin = 'cors'; link.href = href; Logging.log({ level: LogLevel.Informational, message: link, source: 'AssetManager.loadAssets' }); head.appendChild(link); }); } /** * Only push the fonts and let the extension use cssV2. * @param cssAssets the input cssV1 assets. * @returns the cssAssetsV2 including fonts.css only. */ updateCssV2Assets(cssAssets) { const cssAssetsV2 = []; if (!cssAssets) { return cssAssetsV2; } for (let i = 0; i < cssAssets.length; i++) { if (cssAssets[i] && cssAssets[i].includes(this.fontsKeyword)) { cssAssetsV2.push(cssAssets[i]); } } return cssAssetsV2; } } //# sourceMappingURL=asset-manager.js.map