@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
144 lines (142 loc) • 5.8 kB
JavaScript
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