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}}} <a href="/">Back</a></td>
114
+ <td id="footerLeft">{{{footer}}}</td>
115
+ <td id="footerRight">{{{rootCertLink}}} <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
♦
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}}} <a href=../../terms>Terms & Privacy</a></div>
97
+ <div id="flink1">{{{footer}}}</div>
98
+ <div id="flink2">{{{rootCertLink}}} <a href=../../terms>Terms & 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