From 8b33208b176e58591439bad3f35829ffe3fb81c9 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Tue, 1 Mar 2022 11:18:42 -0800 Subject: [PATCH] httpheaders now adds to existing headers, set a header to null to force remove it. --- meshcentral.js | 6 ++-- package.json | 18 +++++++++-- public/scripts/agent-desktop-0.0.2-min.js | 2 +- webserver.js | 38 +++++++++++------------ 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/meshcentral.js b/meshcentral.js index dc1ed468..c6b13451 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -679,7 +679,7 @@ function CreateMeshCentralServer(config, args) { obj.args = args = config2.settings; // Lower case all keys in the config file - obj.common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate']); + obj.common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders']); // Grad some of the values from the original config.json file if present. if ((config.settings.vault != null) && (config2.settings != null)) { config2.settings.vault = config.settings.vault; } @@ -1168,7 +1168,7 @@ function CreateMeshCentralServer(config, args) { for (i in args) { config2.settings[i] = args[i]; } // Lower case all keys in the config file - common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate']); + common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders']); // Grad some of the values from the original config.json file if present. config2['mysql'] = config['mysql']; @@ -3279,7 +3279,7 @@ function getConfig(createSampleConfig) { // Lower case all keys in the config file try { - require('./common.js').objKeysToLower(config, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate']); + require('./common.js').objKeysToLower(config, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders']); } catch (ex) { console.log('CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.'); process.exit(); diff --git a/package.json b/package.json index bd813df8..1c7308c7 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,8 @@ "sample-config-advanced.json" ], "dependencies": { + "@yetzt/nedb": "^1.8.0", + "archiver": "^4.0.2", "body-parser": "^1.19.0", "cbor": "~5.2.0", "compression": "^1.7.4", @@ -43,13 +45,25 @@ "express": "^4.17.0", "express-handlebars": "^5.3.5", "express-ws": "^4.0.0", + "html-minifier": "^4.0.0", + "image-size": "^1.0.1", "ipcheck": "^0.1.0", + "jsdom": "^19.0.0", + "loadavg-windows": "^1.1.1", + "minify-js": "^0.0.4", "minimist": "^1.2.5", "multiparty": "^4.2.1", - "@yetzt/nedb": "^1.8.0", "node-forge": "^1.0.0", + "node-rdpjs-2": "^0.3.5", + "node-windows": "^0.1.4", + "otplib": "^10.2.3", + "pg": "^8.7.1", + "pgtools": "^0.3.2", + "ssh2": "^1.6.0", + "web-push": "^3.4.5", "ws": "^5.2.3", - "yauzl": "^2.10.0" + "yauzl": "^2.10.0", + "yubikeyotp": "^0.2.0" }, "engines": { "node": ">=10.0.0" diff --git a/public/scripts/agent-desktop-0.0.2-min.js b/public/scripts/agent-desktop-0.0.2-min.js index 2d618ef5..8496d77a 100644 --- a/public/scripts/agent-desktop-0.0.2-min.js +++ b/public/scripts/agent-desktop-0.0.2-min.js @@ -1 +1 @@ -Uint8Array.prototype.slice||Object.defineProperty(Uint8Array.prototype,"slice",{value:function(e,t){return new Uint8Array(Array.prototype.slice.call(this,e,t))}});var CreateAgentRemoteDesktop=function(e,t){var p={};"string"==typeof(p.CanvasId=e)&&(p.CanvasId=Q(e)),p.Canvas=p.CanvasId.getContext("2d"),p.scrolldiv=t,p.State=0,p.PendingOperations=[],p.tilesReceived=0,p.TilesDrawn=0,p.KillDraw=0,p.ipad=!1,p.tabletKeyboardVisible=!1,p.LastX=0,p.LastY=0,p.touchenabled=0,p.submenuoffset=0,p.touchtimer=null,p.TouchArray={},p.connectmode=0,p.connectioncount=0,p.rotation=0,p.protocol=2,p.debugmode=0,p.firstUpKeys=[],p.stopInput=!1,p.localKeyMap=!0,p.remoteKeyMap=!1,p.pressedKeys=[],p.sessionid=0,p.username,p.oldie=!1,p.ImageType=1,p.CompressionLevel=50,p.ScalingLevel=1024,p.FrameRateTimer=100,p.SwapMouse=!1,p.FirstDraw=!1,p.onRemoteInputLockChanged=null,p.RemoteInputLock=null,p.onKeyboardStateChanged=null,p.KeyboardState=0,p.ScreenWidth=960,p.ScreenHeight=701,p.width=960,p.height=960,p.displays=null,p.selectedDisplay=null,p.onScreenSizeChange=null,p.onMessage=null,p.onConnectCountChanged=null,p.onDebugMessage=null,p.onTouchEnabledChanged=null,p.onDisplayinfo=null;var S=!(p.accumulator=null),v="default";p.mouseCursorActive=function(e){S!=e&&(S=e,p.CanvasId.style.cursor=1==e?v:"default")};var C=["default","progress","crosshair","pointer","help","text","no-drop","move","nesw-resize","ns-resize","nwse-resize","w-resize","alias","wait","none","not-allowed","col-resize","row-resize","copy","zoom-in","zoom-out"];p.Start=function(){p.State=0,p.accumulator=null},p.Stop=function(){p.setRotation(0),p.UnGrabKeyInput(),p.UnGrabMouseInput(),p.touchenabled=0,null!=p.onScreenSizeChange&&p.onScreenSizeChange(p,p.ScreenWidth,p.ScreenHeight,p.CanvasId),p.Canvas.clearRect(0,0,p.CanvasId.width,p.CanvasId.height)},p.xxStateChange=function(e){p.State!=e&&(p.State=e,p.CanvasId.style.cursor="default",0===e&&p.Stop())},p.send=function(e){2>32)+p.intToStr(32&o)):(p.recordedSize+=n.length,p.shortToStr(e)+p.shortToStr(t)+p.intToStr(n.length)+p.intToStr(o>>32)+p.intToStr(32&o)+n)}return p.SendKeyMsg=function(e,t){var n,o;null!=e&&(t=t||window.event,n=!1,0==(n=1!=urlargs.noextkeys&&"string"==typeof t.code&&(t.code.startsWith("Arrow")||0<=r.indexOf(t.code))?!0:n)&&t.code&&0==t.code.startsWith("NumPad")&&0==p.localKeyMap?null!=(o=(o=t).code.startsWith("Key")&&4==o.code.length?o.code.charCodeAt(3):o.code.startsWith("Digit")&&6==o.code.length?o.code.charCodeAt(5):o.code.startsWith("Numpad")&&7==o.code.length?o.code.charCodeAt(6)+48:a[o.code])&&p.SendKeyMsgKC(e,o,n):(59==(o=t.keyCode)?o=186:173==o?o=189:61==o&&(o=187),p.SendKeyMsgKC(e,o,n)))},p.SendRemoteInputLock=function(e){p.send(String.fromCharCode(0,87,0,5,e))},p.SendMessage=function(e){3==p.State&&p.send(String.fromCharCode(0,17)+p.shortToStr(4+e.length)+e)},p.SendKeyMsgKC=function(e,t,n){if(3==p.State)if("object"==typeof e)for(var o in e)p.SendKeyMsgKC(e[o][0],e[o][1],e[o][2]);else{1==e?-1==p.pressedKeys.indexOf(t)&&p.pressedKeys.unshift(t):2==e&&-1!=(o=p.pressedKeys.indexOf(t))&&p.pressedKeys.splice(o,1),0>8),255-(255&Math.abs(r))):(s=r>>8,255&r),String.fromCharCode(0,p.InputType.MOUSE,0,12,0,0,n/256&255,255&n,o/256&255,255&o,s,t)):String.fromCharCode(0,p.InputType.MOUSE,0,10,0,e==p.KeyAction.DOWN?a:2*a&255,n/256&255,255&n,o/256&255,255&o),p.Action==p.KeyAction.NONE?0==p.Alternate||p.ipad?(p.send(i),p.Alternate=1):p.Alternate=0:p.send(i)))},p.GetDisplayNumbers=function(){p.send(String.fromCharCode(0,11,0,4))},p.SetDisplay=function(e){p.send(String.fromCharCode(0,12,0,6,e>>8,255&e))},p.intToStr=function(e){return String.fromCharCode(e>>24&255,e>>16&255,e>>8&255,255&e)},p.shortToStr=function(e){return String.fromCharCode(e>>8&255,255&e)},p.onResize=function(){0!=p.ScreenWidth&&0!=p.ScreenHeight&&(p.Canvas.canvas.width==p.ScreenWidth&&p.Canvas.canvas.height==p.ScreenHeight||(p.FirstDraw&&(p.Canvas.canvas.width=p.ScreenWidth,p.Canvas.canvas.height=p.ScreenHeight,p.Canvas.fillRect(0,0,p.ScreenWidth,p.ScreenHeight),null!=p.onScreenSizeChange&&p.onScreenSizeChange(p,p.ScreenWidth,p.ScreenHeight,p.CanvasId)),p.FirstDraw=!1,1>32)+p.intToStr(32&o)):(p.recordedSize+=n.length,p.shortToStr(e)+p.shortToStr(t)+p.intToStr(n.length)+p.intToStr(o>>32)+p.intToStr(32&o)+n)}return p.SendKeyMsg=function(e,t){var n,o;null!=e&&(t=t||window.event,n=!1,0==(n=(p.UseExtendedKeyFlag||1==urlargs.extkeys)&&"string"==typeof t.code&&(t.code.startsWith("Arrow")||0<=r.indexOf(t.code))?!0:n)&&t.code&&0==t.code.startsWith("NumPad")&&0==p.localKeyMap?null!=(o=(o=t).code.startsWith("Key")&&4==o.code.length?o.code.charCodeAt(3):o.code.startsWith("Digit")&&6==o.code.length?o.code.charCodeAt(5):o.code.startsWith("Numpad")&&7==o.code.length?o.code.charCodeAt(6)+48:a[o.code])&&p.SendKeyMsgKC(e,o,n):(59==(o=t.keyCode)?o=186:173==o?o=189:61==o&&(o=187),p.SendKeyMsgKC(e,o,n)))},p.SendRemoteInputLock=function(e){p.send(String.fromCharCode(0,87,0,5,e))},p.SendMessage=function(e){3==p.State&&p.send(String.fromCharCode(0,17)+p.shortToStr(4+e.length)+e)},p.SendKeyMsgKC=function(e,t,n){if(3==p.State)if("object"==typeof e)for(var o in e)p.SendKeyMsgKC(e[o][0],e[o][1],e[o][2]);else{1==e?-1==p.pressedKeys.indexOf(t)&&p.pressedKeys.unshift(t):2==e&&-1!=(o=p.pressedKeys.indexOf(t))&&p.pressedKeys.splice(o,1),0>8),255-(255&Math.abs(r))):(s=r>>8,255&r),String.fromCharCode(0,p.InputType.MOUSE,0,12,0,0,n/256&255,255&n,o/256&255,255&o,s,t)):String.fromCharCode(0,p.InputType.MOUSE,0,10,0,e==p.KeyAction.DOWN?a:2*a&255,n/256&255,255&n,o/256&255,255&o),p.Action==p.KeyAction.NONE?0==p.Alternate||p.ipad?(p.send(i),p.Alternate=1):p.Alternate=0:p.send(i)))},p.GetDisplayNumbers=function(){p.send(String.fromCharCode(0,11,0,4))},p.SetDisplay=function(e){p.send(String.fromCharCode(0,12,0,6,e>>8,255&e))},p.intToStr=function(e){return String.fromCharCode(e>>24&255,e>>16&255,e>>8&255,255&e)},p.shortToStr=function(e){return String.fromCharCode(e>>8&255,255&e)},p.onResize=function(){0!=p.ScreenWidth&&0!=p.ScreenHeight&&(p.Canvas.canvas.width==p.ScreenWidth&&p.Canvas.canvas.height==p.ScreenHeight||(p.FirstDraw&&(p.Canvas.canvas.width=p.ScreenWidth,p.Canvas.canvas.height=p.ScreenHeight,p.Canvas.fillRect(0,0,p.ScreenWidth,p.ScreenHeight),null!=p.onScreenSizeChange&&p.onScreenSizeChange(p,p.ScreenWidth,p.ScreenHeight,p.CanvasId)),p.FirstDraw=!1,1= 0) || (req.url.indexOf('/agent.ashx/.websocket') >= 0) || (req.url.indexOf('/localrelay.ashx/.websocket') >= 0)) { next(); return; } - // If this domain has configured headers, use them. - // Example headers: { 'Strict-Transport-Security': 'max-age=360000;includeSubDomains' }; - // { 'Referrer-Policy': 'no-referrer', 'x-frame-options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "default-src http: ws: data: 'self';script-src http: 'unsafe-inline';style-src http: 'unsafe-inline'" }; + // Setup security headers + const geourl = (domain.geolocation ? ' *.openstreetmap.org' : ''); + var selfurl = ' wss://' + req.headers.host; + if ((xforwardedhost != null) && (xforwardedhost != req.headers.host)) { selfurl += ' wss://' + xforwardedhost; } + const extraScriptSrc = (parent.config.settings.extrascriptsrc != null) ? (' ' + parent.config.settings.extrascriptsrc) : ''; + const headers = { + 'Referrer-Policy': 'no-referrer', + 'X-XSS-Protection': '1; mode=block', + 'X-Content-Type-Options': 'nosniff', + 'Permissions-Policy': 'interest-cohort=()', // Remove Google's FLoC Network + 'Content-Security-Policy': "default-src 'none'; font-src 'self'; script-src 'self' 'unsafe-inline'" + extraScriptSrc + "; connect-src 'self'" + geourl + selfurl + "; img-src 'self' blob: data:" + geourl + " data:; style-src 'self' 'unsafe-inline'; frame-src 'self' mcrouter:; media-src 'self'; form-action 'self'" + }; + if ((parent.config.settings.allowframing !== true) && (typeof parent.config.settings.allowframing !== 'string')) { headers['X-Frame-Options'] = 'sameorigin'; } + if ((parent.config.settings.stricttransportsecurity === true) || ((parent.config.settings.stricttransportsecurity !== false) && (obj.isTrustedCert(domain)))) { if (typeof parent.config.settings.stricttransportsecurity == 'string') { headers['Strict-Transport-Security'] = parent.config.settings.stricttransportsecurity; } else { headers['Strict-Transport-Security'] = 'max-age=63072000'; } } + + // If this domain has configured headers, add them. If a header is set to null, remove it. if ((domain != null) && (domain.httpheaders != null) && (typeof domain.httpheaders == 'object')) { - res.set(domain.httpheaders); - } else { - // Use default security headers - const geourl = (domain.geolocation ? ' *.openstreetmap.org' : ''); - var selfurl = ' wss://' + req.headers.host; - if ((xforwardedhost != null) && (xforwardedhost != req.headers.host)) { selfurl += ' wss://' + xforwardedhost; } - const extraScriptSrc = (parent.config.settings.extrascriptsrc != null) ? (' ' + parent.config.settings.extrascriptsrc) : ''; - const headers = { - 'Referrer-Policy': 'no-referrer', - 'X-XSS-Protection': '1; mode=block', - 'X-Content-Type-Options': 'nosniff', - 'Permissions-Policy': 'interest-cohort=()', // Remove Google's FLoC Network - 'Content-Security-Policy': "default-src 'none'; font-src 'self'; script-src 'self' 'unsafe-inline'" + extraScriptSrc + "; connect-src 'self'" + geourl + selfurl + "; img-src 'self' blob: data:" + geourl + " data:; style-src 'self' 'unsafe-inline'; frame-src 'self' mcrouter:; media-src 'self'; form-action 'self'" - }; - if ((parent.config.settings.allowframing !== true) && (typeof parent.config.settings.allowframing !== 'string')) { headers['X-Frame-Options'] = 'sameorigin'; } - if ((parent.config.settings.stricttransportsecurity === true) || ((parent.config.settings.stricttransportsecurity !== false) && (obj.isTrustedCert(domain)))) { if (typeof parent.config.settings.stricttransportsecurity == 'string') { headers['Strict-Transport-Security'] = parent.config.settings.stricttransportsecurity; } else { headers['Strict-Transport-Security'] = 'max-age=63072000'; } } - res.set(headers); + for (var i in domain.httpheaders) { if (domain.httpheaders === null) { delete headers[i]; } else { headers[i] = domain.httpheaders[i]; } } } + res.set(headers); // Check the session if bound to the external IP address if ((parent.config.settings.cookieipcheck !== false) && (req.session.ip != null) && (req.clientIp != null) && (req.session.ip != req.clientIp)) { req.session = {}; }