Improved 404 pages to support tight Content-Security-Policy HTTP headers.

Ylian Saint-Hilaire committed Jan 10, 2022 at 01:26 UTC ccd04ed573b2b5a25f8b85deb451d52796f90502
5 files changed +297 -76
views/error404-mobile.handlebars
+84 -20
@@ -9,7 +9,11 @@
9 <meta name="robots" content="noindex,nofollow" />
10 <link rel="apple-touch-icon" href="/favicon-303x303.png" />
11 <title>{{{title}}}</title>
12 - <style type="text/css">
12 + <style nonce="{{{cspNonce}}}">
13 + body {
14 + overflow-y:hidden;max-width:100%;margin:0;padding:0;border:0;color:black;font-size:13px;font-family:\'Trebuchet MS\', Arial, Helvetica, sans-serif
15 + }
16 +
17 a {
18 color: #036;
19 text-decoration: underline;
@@ -20,35 +24,95 @@
24 text-decoration: underline;
25 }
26
23 - #footer a:hover {
24 - color: #fff;
25 - text-decoration: none;
26 - }
27 + #footer a:hover {
28 + color: #fff;
29 + text-decoration: none;
30 + }
31 +
32 + #masthead {
33 + background:url(logo.png) 0px 0px;background-size:341px 50px;background-color:#036;background-repeat:no-repeat;height:50px;width:100%;overflow:hidden
34 + }
35 +
36 + #title1 {
37 + float:left;height:66px;color:#c8c8c8;padding-left:10px;padding-top:4px
38 + }
39 +
40 + #title1a {
41 + font-size:36px;font-family:Arial,Helvetica,sans-serif
42 + }
43 +
44 + #title2 {
45 + float:left;height:66px;color:#c8c8c8;padding-left:5px;padding-top:7px
46 + }
47 +
48 + #title2a {
49 + font-size:12px;font-family:Arial,Helvetica,sans-serif
50 + }
51 +
52 + #page_content {
53 + overflow-y:scroll;position:absolute;bottom:32px;top:50px;width:100%
54 + }
55 +
56 + #column_l {
57 + padding-left:10px;padding-right:10px
58 + }
59 +
60 + #text1 {
61 + text-align:center;padding-top:30px;font-size:100px;font-family:Arial;color:#bbb
62 + }
63 +
64 + #text2 {
65 + text-align:center;font-size:16px;font-family:Arial;color:#999
66 + }
67 +
68 + #text3 {
69 + text-align:center;padding-top:16px;font-size:20px;font-family:Arial;color:#999
70 + }
71 +
72 + #text3l {
73 + text-decoration:none
74 + }
75 +
76 + #footer {
77 + height:32px;width:100%;text-align:center;background-color:#113962;position:absolute;bottom:0px
78 + }
79 +
80 + #footertable {
81 + width:100%
82 + }
83 +
84 + #footerLeft {
85 + text-align:left;color:white
86 + }
87 +
88 + #footerRight {
89 + text-align:right
90 + }
91 </style>
92 </head>
29 -<body onload="if (typeof(startup) !== 'undefined') startup();" style="overflow-y:hidden;max-width:100%;margin:0;padding:0;border:0;color:black;font-size:13px;font-family:\'Trebuchet MS\', Arial, Helvetica, sans-serif">
93 +<body>
94 <div id="container">
95 <!-- Begin Masthead -->
32 - <div id=masthead style="background:url(logo.png) 0px 0px;background-size:341px 50px;background-color:#036;background-repeat:no-repeat;height:50px;width:100%;overflow:hidden">
33 - <div style="float:left;height:66px;color:#c8c8c8;padding-left:10px;padding-top:4px">
34 - <strong><font style="font-size:36px;font-family:Arial,Helvetica,sans-serif">{{{title}}}</font></strong>
96 + <div id=masthead>
97 + <div id="title1">
98 + <strong><font id="title1a">{{{title}}}</font></strong>
99 </div>
36 - <div style="float:left;height:66px;color:#c8c8c8;padding-left:5px;padding-top:7px">
37 - <strong><font style="font-size:12px;font-family:Arial,Helvetica,sans-serif">{{{title2}}}</font></strong>
100 + <div id="title2">
101 + <strong><font id="title2a">{{{title2}}}</font></strong>
102 </div>
103 </div>
40 - <div id=page_content style="overflow-y:scroll;position:absolute;bottom:32px;top:50px;width:100%">
41 - <div id="column_l" style="padding-left:10px;padding-right:10px">
42 - <div style="text-align:center;padding-top:30px;font-size:100px;font-family:Arial;color:#bbb"><b>404</b></div>
43 - <div style="text-align:center;font-size:16px;font-family:Arial;color:#999">This page does not exist</div>
44 - <div style="text-align:center;padding-top:16px;font-size:20px;font-family:Arial;color:#999"><a href="/" style="text-decoration:none"><b>Go to main site</b></a></div>
104 + <div id=page_content>
105 + <div id="column_l">
106 + <div id="text1"><b>404</b></div>
107 + <div id="text2">This page does not exist</div>
108 + <div id="text3"><a id="text3l "href="/"><b>Go to main site</b></a></div>
109 </div>
110 </div>
47 - <div id=footer style="height:32px;width:100%;text-align:center;background-color:#113962;position:absolute;bottom:0px">
48 - <table cellpadding=0 cellspacing=6 style=width:100%>
111 + <div id=footer>
112 + <table id=footertable cellpadding=0 cellspacing=6>
113 <tr>
50 - <td style=text-align:left;color:white>{{{footer}}}</td>
51 - <td style=text-align:right>{{{rootCertLink}}}&nbsp;<a href="/">Back</a></td>
114 + <td id="footerLeft">{{{footer}}}</td>
115 + <td id="footerRight">{{{rootCertLink}}}&nbsp;<a href="/">Back</a></td>
116 </tr>
117 </table>
118 </div>
views/error404.handlebars
+109 -33
@@ -10,53 +10,128 @@
10 <link type="text/css" href="/styles/style.css" media="screen" rel="stylesheet" title="CSS" />
11 <link rel="apple-touch-icon" href="/favicon-303x303.png" />
12 <title>{{{title}}}</title>
13 + <style nonce="{{{cspNonce}}}">
14 + #xbody {
15 + overflow:hidden;
16 + }
17 +
18 + #masthead {
19 + background:url(logo.png) 0px 0px;background-color:#036;background-repeat:no-repeat;height:66px;width:100%;overflow:hidden;
20 + }
21 +
22 + #title1 {
23 + float:left;text-shadow: 1px 1px 2px #000
24 + }
25 +
26 + #title2 {
27 + float:left; height: 66px; color:#c8c8c8; padding-left:16px; padding-top:7px
28 + }
29 +
30 + #title2a {
31 + font-size:46px; font-family: Arial, Helvetica, sans-serif;text-shadow: 1px 1px 2px #000
32 + }
33 +
34 + #title3 {
35 + float:left; height: 66px; color:#c8c8c8; padding-left:5px; padding-top:14px
36 + }
37 +
38 + #title3a {
39 + font-size:14px; font-family: Arial, Helvetica, sans-serif;text-shadow: 1px 1px 2px #000
40 + }
41 +
42 + #footerLeft {
43 + text-align:left
44 + }
45 +
46 + #footerRight {
47 + text-align:right
48 + }
49 +
50 + #topbar {
51 + height:24px;position:relative
52 + }
53 +
54 + #page_leftbar_div {
55 + height:16px
56 + }
57 +
58 + #uiMenu {
59 +
60 + }
61 +
62 + #text1 {
63 + text-align:center;padding-top:30px;font-size:200px;font-family:Arial;color:#bbb
64 + }
65 +
66 + #text2 {
67 + text-align:center;font-size:20px;font-family:Arial;color:#999
68 + }
69 +
70 + #text3 {
71 + text-align:center;padding-top:20px;font-size:20px;font-family:Arial;color:#999
72 + }
73 +
74 + #column_l {
75 + max-height:calc(100vh - 135px);overflow-y:auto
76 + }
77 + </style>
78 </head>
14 -<body id="body" onload="if (typeof(startup) !== 'undefined') startup();" style="display:none;overflow:hidden">
79 +<body id="xbody">
80 <div id="container">
81 <!-- Begin Masthead -->
17 - <div id="masthead" class=noselect style="background:url(logo.png) 0px 0px;background-color:#036;background-repeat:no-repeat;height:66px;width:100%;overflow:hidden;">
18 - <div style="float:left;text-shadow: 1px 1px 2px #000">{{{titlehtml}}}</div>
19 - <div style="float:left; height: 66px; color:#c8c8c8; padding-left:16px; padding-top:7px">
20 - <strong><font style="font-size:46px; font-family: Arial, Helvetica, sans-serif;text-shadow: 1px 1px 2px #000">{{{title1}}}</font></strong>
82 + <div id="masthead" class=noselect>
83 + <div id="title1">{{{titlehtml}}}</div>
84 + <div id="title2">
85 + <strong><font id="title2a">{{{title1}}}</font></strong>
86 </div>
22 - <div style="float:left; height: 66px; color:#c8c8c8; padding-left:5px; padding-top:14px">
23 - <strong><font style="font-size:14px; font-family: Arial, Helvetica, sans-serif;text-shadow: 1px 1px 2px #000">{{{title2}}}</font></strong>
87 + <div id="title3">
88 + <strong><font id="title3a">{{{title2}}}</font></strong>
89 </div>
90 </div>
91 <div id="page_leftbar">
27 - <div style="height:16px"></div>
92 + <div id="page_leftbar_div"></div>
93 </div>
29 - <div id=topbar class="noselect style3" style="height:24px;position:relative">
30 - <div id=uiMenuButton title="User interface selection" onclick="showUserInterfaceSelectMenu()">
94 + <div id=topbar class="noselect style3">
95 + <div id=uiMenuButton title="User interface selection">
96 &diams;
32 - <div id=uiMenu style="display:none">
33 - <div id=uiViewButton1 class=uiSelector onclick=userInterfaceSelectMenu(1) title="Left bar interface"><div class="uiSelector1"></div></div>
34 - <div id=uiViewButton2 class=uiSelector onclick=userInterfaceSelectMenu(2) title="Top bar interface"><div class="uiSelector2"></div></div>
35 - <div id=uiViewButton3 class=uiSelector onclick=userInterfaceSelectMenu(3) title="Fixed width interface"><div class="uiSelector3"></div></div>
36 - <div id=uiViewButton4 class=uiSelector onclick=toggleNightMode() title="Toggle night mode"><div class="uiSelector4"></div></div>
97 + <div id=uiMenu>
98 + <div id=uiViewButton1 class=uiSelector title="Left bar interface"><div class="uiSelector1"></div></div>
99 + <div id=uiViewButton2 class=uiSelector title="Top bar interface"><div class="uiSelector2"></div></div>
100 + <div id=uiViewButton3 class=uiSelector title="Fixed width interface"><div class="uiSelector3"></div></div>
101 + <div id=uiViewButton4 class=uiSelector title="Toggle night mode"><div class="uiSelector4"></div></div>
102 </div>
103 </div>
104 </div>
40 - <div id="column_l" style="max-height:calc(100vh - 135px);overflow-y:auto">
41 - <div style="text-align:center;padding-top:30px;font-size:200px;font-family:Arial;color:#bbb"><b>404</b></div>
42 - <div style="text-align:center;font-size:20px;font-family:Arial;color:#999">This page does not exist</div>
43 - <div style="text-align:center;padding-top:20px;font-size:20px;font-family:Arial;color:#999"><a href="/" style="text-decoration:none"><b>Go to main site</b></a></div>
105 + <div id="column_l">
106 + <div id="text1"><b>404</b></div>
107 + <div id="text2">This page does not exist</div>
108 + <div id="text3"><a href="/" style="text-decoration:none"><b>Go to main site</b></a></div>
109 </div>
110 <div id="footer">
111 <table cellpadding="0" cellspacing="10" style="width: 100%">
112 <tr>
48 - <td style="text-align:left"></td>
49 - <td style="text-align:right"><a href="/">Back</a></td>
113 + <td id="footerLeft"></td>
114 + <td id="footerRight"><a href="/">Back</a></td>
115 </tr>
116 </table>
117 </div>
118 </div>
54 - <script>
119 + <script nonce="{{{cspNonce}}}">
120 'use strict';
121 var uiMode = parseInt(getstore('uiMode', 1));
122 var webPageStackMenu = false;
123 var webPageFullScreen = true;
124 var nightMode = (getstore('_nightMode', '0') == '1');
125 + var menuVisible = false;
126 + QV('uiMenu', menuVisible);
127 + if (nightMode) { QC('xbody').add('night'); } else { QC('xbody').remove('night'); }
128 +
129 + // Setup handlers
130 + Q('uiMenuButton').onclick = showUserInterfaceSelectMenu;
131 + Q('uiViewButton1').onclick = function () { userInterfaceSelectMenu(1); }
132 + Q('uiViewButton2').onclick = function () { userInterfaceSelectMenu(2); }
133 + Q('uiViewButton3').onclick = function () { userInterfaceSelectMenu(3); }
134 + Q('uiViewButton4').onclick = toggleNightMode;
135
136 var terms = '{{{terms}}}';
137 if (terms != '') { QH('column_l', decodeURIComponent(terms)); }
@@ -70,7 +145,8 @@
145 Q('uiViewButton3').classList.remove('uiSelectorSel');
146 Q('uiViewButton4').classList.remove('uiSelectorSel');
147 try { Q('uiViewButton' + uiMode).classList.add('uiSelectorSel'); } catch (ex) { }
73 - QV('uiMenu', (QS('uiMenu').display == 'none'));
148 + menuVisible = !menuVisible;
149 + QV('uiMenu', menuVisible);
150 if (nightMode) { Q('uiViewButton4').classList.add('uiSelectorSel'); }
151 }
152
@@ -85,7 +161,7 @@
161
162 function toggleNightMode() {
163 nightMode = !nightMode;
88 - if (nightMode) { QC('body').add('night'); } else { QC('body').remove('night'); }
164 + if (nightMode) { QC('xbody').add('night'); } else { QC('xbody').remove('night'); }
165 putstore('_nightMode', nightMode ? '1' : '0');
166 }
167
@@ -95,19 +171,19 @@
171 var hide = 0;
172 //if (args.hide) { hide = parseInt(args.hide); }
173 if (webPageFullScreen == false) {
98 - QC('body').remove('menu_stack');
99 - QC('body').remove('fullscreen');
100 - QC('body').remove('arg_hide');
174 + QC('xbody').remove('menu_stack');
175 + QC('xbody').remove('fullscreen');
176 + QC('xbody').remove('arg_hide');
177 //if (xxcurrentView >= 10) QC('column_l').add('room4submenu');
178 //QV('UserDummyMenuSpan', false);
179 //QV('page_leftbar', false);
180 } else {
105 - QC('body').add('fullscreen');
106 - if (hide & 16) QC('body').add('arg_hide'); // This is replacement for QV('page_leftbar', !(hide & 16));
181 + QC('xbody').add('fullscreen');
182 + if (hide & 16) QC('xbody').add('arg_hide'); // This is replacement for QV('page_leftbar', !(hide & 16));
183 //QV('UserDummyMenuSpan', (xxcurrentView < 10) && webPageFullScreen);
184 //QV('page_leftbar', true);
185 }
110 - QV('body', true);
186 + QV('xbody', true);
187 }
188
189 // If FullScreen, toggle menu to be horisontal or vertical
@@ -118,9 +194,9 @@
194 putstore('webPageStackMenu', webPageStackMenu);
195 }
196 if (webPageStackMenu == false) {
121 - QC('body').remove('menu_stack');
197 + QC('xbody').remove('menu_stack');
198 } else {
123 - QC('body').add('menu_stack');
199 + QC('xbody').add('menu_stack');
200 //if (xxcurrentView >= 10) QC('column_l').remove('room4submenu');
201 }
202 }
@@ -130,7 +206,7 @@
206 function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } }
207 function Q(x) { return document.getElementById(x); } // "Q"
208 function QS(x) { try { return Q(x).style; } catch (x) { } } // "Q" style
133 - function QV(x, y) { try { QS(x).display = (y ? '' : 'none'); } catch (x) { } } // "Q" visible
209 + function QV(x, y) { try { QS(x).display = (y ? '' : 'none'); } catch (ex) { console.log(ex); } } // "Q" visible
210 function QH(x, y) { Q(x).innerHTML = y; } // "Q" html
211 function QC(x) { try { return Q(x).classList; } catch (x) { } } // "Q" class
212 </script>
views/error4042.handlebars
+69 -12
@@ -10,7 +10,7 @@
10 <link type=text/css href="/styles/style.css" media="screen" rel="stylesheet" title="CSS" />
11 <link rel="apple-touch-icon" href="/favicon-303x303.png" />
12 <title>{{{title1}}} - Download</title>
13 - <style>
13 + <style nonce="{{{cspNonce}}}">
14 body {
15 background-color: cadetblue;
16 background: linear-gradient(to bottom right, #369, #036);
@@ -18,27 +18,84 @@
18 overflow: hidden;
19 }
20
21 - a:link { color: #c8c8c8; }
22 - a:visited { color: #c8c8c8; }
23 - a:hover { color: #c8c8c8; }
24 - a:active { color: #c8c8c8; }
21 + a:link {
22 + color: #c8c8c8;
23 + }
24 +
25 + a:visited {
26 + color: #c8c8c8;
27 + }
28 +
29 + a:hover {
30 + color: #c8c8c8;
31 + }
32 +
33 + a:active {
34 + color: #c8c8c8;
35 + }
36 +
37 + #backImage {
38 + position: absolute;
39 + left: 0;
40 + bottom: 0;
41 + z-index: -1;
42 + height: 60%;
43 + opacity: 0.1;
44 + }
45 +
46 + #centralTable {
47 + height: 100%;
48 + z-index: 1;
49 + }
50 +
51 + #text1 {
52 + text-align: center;
53 + padding-top: 30px;
54 + font-size: 160px;
55 + font-family: Arial;
56 + color: #bbb;
57 + }
58 +
59 + #text2 {
60 + text-align: center;
61 + font-size: 20px;
62 + font-family: Arial;
63 + color: #999;
64 + }
65 +
66 + #text3 {
67 + text-align: center;
68 + padding-top: 20px;
69 + font-size: 20px;
70 + font-family: Arial;
71 + color: #999;
72 + }
73 +
74 + #flink1 {
75 + margin-left: 4px;
76 + }
77 +
78 + #flink2 {
79 + float: right;
80 + margin-right: 4px;
81 + }
82 </style>
83 </head>
84 <body>
28 - <img style="position:absolute;left:0;bottom:0;z-index:-1;height:60%;opacity:0.1" src="../../images/login/back.png" />
29 - <table id="centralTable" class="container" style="height:100%;z-index:1">
85 + <img id="backImage" src="../../images/login/back.png" />
86 + <table id="centralTable" class="container">
87 <tr>
88 <td style="width:100%;text-align:center">
32 - <div style="text-align:center;padding-top:30px;font-size:160px;font-family:Arial;color:#bbb"><b>404</b></div>
33 - <div style="text-align:center;font-size:20px;font-family:Arial;color:#999">This page does not exist</div>
34 - <div style="text-align:center;padding-top:20px;font-size:20px;font-family:Arial;color:#999"><a href="/"><b>Go to main site</b></a></div>
89 + <div id="text1"><b>404</b></div>
90 + <div id="text2">This page does not exist</div>
91 + <div id="text3"><a href="/"><b>Go to main site</b></a></div>
92 </td>
93 </tr>
94 <tr style="height:20px">
95 <td>
96 <div>
40 - <div id="flink" style="margin-left:4px">{{{footer}}}</div>
41 - <div id="flink" style="float:right;margin-right:4px">{{{rootCertLink}}}&nbsp;<a href=../../terms>Terms &amp; Privacy</a></div>
97 + <div id="flink1">{{{footer}}}</div>
98 + <div id="flink2">{{{rootCertLink}}}&nbsp;<a href=../../terms>Terms &amp; Privacy</a></div>
99 </div>
100 </td>
101 </tr>
views/login2.handlebars
+31 -9
@@ -19,15 +19,37 @@
19 background: linear-gradient(to bottom right, #369, #036);
20 }
21
22 - #flink a:link { color: #c8c8c8; }
23 - #flink a:visited { color: #c8c8c8; }
24 - #flink a:hover { color: #c8c8c8; }
25 - #flink a:active { color: #c8c8c8; }
26 -
27 - #welcomeText a:link { color: #FFF; }
28 - #welcomeText a:visited { color: #FFF; }
29 - #welcomeText a:hover { color: #FFF; }
30 - #welcomeText a:active { color: #FFF; }
22 + #flink a:link {
23 + color: #c8c8c8;
24 + }
25 +
26 + #flink a:visited {
27 + color: #c8c8c8;
28 + }
29 +
30 + #flink a:hover {
31 + color: #c8c8c8;
32 + }
33 +
34 + #flink a:active {
35 + color: #c8c8c8;
36 + }
37 +
38 + #welcomeText a:link {
39 + color: #FFF;
40 + }
41 +
42 + #welcomeText a:visited {
43 + color: #FFF;
44 + }
45 +
46 + #welcomeText a:hover {
47 + color: #FFF;
48 + }
49 +
50 + #welcomeText a:active {
51 + color: #FFF;
52 + }
53 </style>
54 </head>
55 <body id="body" onload="if (typeof(startup) !== 'undefined') startup();" class="arg_hide login">
webserver.js
+4 -2
@@ -6359,8 +6359,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
6359 parent.debug('web', '404 Error ' + req.url);
6360 var domain = getDomain(req);
6361 if ((domain == null) || (domain.auth == 'sspi')) { res.sendStatus(404); return; }
6362 - if ((domain.loginkey != null) && (domain.loginkey.indexOf(req.query.key) == -1)) { res.sendStatus(404); return; } // Check 3FA URL key
6363 - res.status(404).render(getRenderPage((domain.sitestyle == 2) ? 'error4042' : 'error404', req, domain), getRenderArgs({}, req, domain));
6362 + if ((domain.loginkey != null) && (domain.loginkey.indexOf(req.query.key) == -1)) { res.sendStatus(404); return; } // Check 3FA URL
6363 + const cspNonce = obj.crypto.randomBytes(15).toString('base64');
6364 + res.set({ 'Content-Security-Policy': "default-src 'none'; script-src 'self' 'nonce-" + cspNonce + "'; img-src 'self'; style-src 'self' 'nonce-" + cspNonce + "';" });
6365 + res.status(404).render(getRenderPage((domain.sitestyle == 2) ? 'error4042' : 'error404', req, domain), getRenderArgs({ cspNonce: cspNonce }, req, domain));
6366 });
6367 }
6368