Skip to content

Commit 1d981a8

Browse files
RTLRTL
RTL
authored and
RTL
committed
Dashboard security improvements and fixes
1 parent c1fd4c2 commit 1d981a8

File tree

6 files changed

+112
-91
lines changed

6 files changed

+112
-91
lines changed

Light-Dashboard/README.md

+9
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ Here's how the authentication works: if no users are found in the database, auth
7171

7272
The Users.html page provides a simple interface for managing the user database, allowing the addition and removal of users. For simplicity, the authentication code focuses purely on user authentication, without any authorization mechanisms.
7373

74+
## Security Policies
75+
76+
The following default security policies are set to enhance security by controlling content sources and protecting against MIME-type sniffing:
77+
78+
- Content-Security-Policy: Limits resources (scripts, styles) to load only from trusted sources. By default, it restricts all resources to 'self', with an exception for scripts and styles from cdn.jsdelivr.net. You may need to adjust this policy to include additional trusted domains based on your application's requirements.
79+
- X-Content-Type-Options: Set to "nosniff" to prevent browsers from interpreting files as a different MIME type than declared. This helps prevent certain attacks that rely on MIME-type misinterpretation.
80+
81+
**Note:** These policies are examples and may require customization to meet the specific needs of your deployment environment and any third-party services you integrate. See www/.lua/cms.lua and 'securityPolicies' for details.
82+
7483

7584
## Notes:
7685

Light-Dashboard/www/.lua/cms.lua

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
-- Mini Content Management System (CMS) designed for the Pure.css dashboard.
32

43
-- All LSP applications have a predefined 'dir' object, which is a resrdr instance.
@@ -19,6 +18,13 @@ local rw=require"rwfile"
1918
-- Closure def: https://en.wikipedia.org/wiki/Closure_(computer_programming)
2019
local app,io,dir=app,app.io,app.dir
2120

21+
-- Set on 'dir' and used by function cmsfunc()
22+
local securityPolicies={
23+
["Content-Security-Policy"]= "default-src 'self'; script-src 'self' cdn.jsdelivr.net; style-src 'self' cdn.jsdelivr.net",
24+
["X-Content-Type-Options"]="nosniff",
25+
}
26+
-- Doc: https://realtimelogic.com/ba/doc/en/lua/lua.html#rsrdr_header
27+
dir:header(securityPolicies)
2228

2329
-- Takes the data content of an LSP page (HTML with LSP tags) as
2430
-- argument, converts the LSP page to Lua code, and compiles the Lua
@@ -69,6 +75,7 @@ local pagesT={}
6975
-- See the following for details on the request/response environment:
7076
-- https://realtimelogic.com/ba/doc/en/lua/lua.html#CMDE
7177
local function cmsfunc(_ENV, relpath, notInMenuOK)
78+
local response=response -- e.g. = _ENV.response. Now faster.
7279

7380
-- Translate to (path/)index.html if only directory name is provided.
7481
if #relpath == 0 or relpath:find"/$" then
@@ -96,6 +103,8 @@ local function cmsfunc(_ENV, relpath, notInMenuOK)
96103
--Remove the following line and xrsp:finalize() if you do not want to
97104
--compress the response.
98105
local xrsp = response:setresponse() -- Activate compression
106+
response:setdefaultheaders()
107+
for k,v in pairs(securityPolicies) do response:setheader(k,v) end
99108

100109
-- Make the following available to template.lsp
101110
-- Note, we explicitly use the _ENV tab for code readability.
+2-90
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,12 @@
1-
<?lsp
2-
3-
if require"smq.hub".isSMQ(request) then
4-
-- Upgrade HTTP(S) request to SMQ connection
5-
-- See code in the .preload script (the app).
6-
app.connectClientSlider(request)
7-
response:abort() -- stop executing page
8-
end
9-
?>
10-
111
<!--
122
Slider documentation: https://roundsliderui.com/
133
-->
144
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/roundslider.min.css" rel="stylesheet" />
5+
<link href="/static/WebSockets.css" rel="stylesheet" />
156
<script src="/rtl/jquery.js"></script>
167
<script src="/rtl/smq.js"></script>
178
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/roundslider.min.js"></script>
18-
<style>
19-
#SliderContainer {
20-
position:relative;
21-
height: 200px;
22-
}
23-
#Slider {
24-
width: 260px;
25-
height: 130px;
26-
padding: 20px;
27-
position: absolute;
28-
top: 50%;
29-
left: 50%;
30-
margin: -85px 0 0 -130px;
31-
}
32-
#Slider .rs-handle {
33-
background-color: transparent;
34-
border: 8px solid transparent;
35-
border-right-color: black;
36-
margin: -8px 0 0 14px !important;
37-
}
38-
#Slider .rs-handle:before {
39-
display: block;
40-
content: " ";
41-
position: absolute;
42-
height: 12px;
43-
width: 12px;
44-
background: black;
45-
right: -6px;
46-
bottom: -6px;
47-
border-radius: 100%;
48-
}
49-
#Slider .rs-handle:after {
50-
display: block;
51-
content: " ";
52-
width: 106px;
53-
position: absolute;
54-
top: -1px;
55-
right: 0px;
56-
border-top: 2px solid black;
57-
}
58-
</style>
9+
<script src="/static/WebSockets.js"></script>
5910

6011
<div class="header">
6112
<h1>WebSockets</h1>
@@ -74,42 +25,3 @@ <h2>Real Time Slider Via WebSockets (SMQ)</h2>
7425

7526
<p>See our <a target="_blank" href="https://tutorial.realtimelogic.com/WebSockets.lsp">online tutorial : WebSockets</a> for a WebSockets introduction.</p>
7627
</div>
77-
78-
<script>
79-
$(function() {
80-
var smq = SMQ.Client(); // No args: connect back to 'origin' i.e. the same page.
81-
let active=true;
82-
83-
//SMQ callback for data sent to the topic "slider" and "self"
84-
function onSmqMsg(d,ptid) {
85-
if(ptid != smq.gettid()) { //Ignore messages from 'self'
86-
active=true;
87-
$("#Slider").roundSlider("option", "value", Math.floor(d.angle * 100 / 180));
88-
active=false;
89-
}
90-
}
91-
92-
smq.subscribe("self",{datatype:"json",onmsg:onSmqMsg});
93-
smq.subscribe("slider",{datatype:"json",onmsg:onSmqMsg});
94-
95-
function onChange (e) {
96-
if(!active)
97-
smq.pubjson({angle:Math.floor(e.value * 180 / 100)}, "slider");
98-
}
99-
100-
$("#Slider").roundSlider({
101-
animation:false,
102-
sliderType: "min-range",
103-
radius: 130,
104-
showTooltip: false,
105-
width: 16,
106-
value: 0,
107-
handleSize: 0,
108-
handleShape: "square",
109-
circleShape: "half-top",
110-
change: onChange,
111-
tooltipFormat: onChange
112-
});
113-
active=false;
114-
});
115-
</script>

Light-Dashboard/www/SMQ/index.lsp

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?lsp
2+
3+
-- JS code in the WebSockets.html page (in WebSockets.js) connects to
4+
-- this page to set up an SMQ WebSocket Connection.
5+
6+
-- Doc: https://realtimelogic.com/ba/doc/en/SMQ.html
7+
if require"smq.hub".isSMQ(request) then
8+
-- Upgrade HTTP(S) request to SMQ connection
9+
-- See code in the .preload script (the app).
10+
app.connectClientSlider(request) -- Function in the .preload script
11+
response:abort() -- stop executing page
12+
end
13+
14+
?>
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#SliderContainer {
2+
position:relative;
3+
height: 200px;
4+
}
5+
#Slider {
6+
width: 260px;
7+
height: 130px;
8+
padding: 20px;
9+
position: absolute;
10+
top: 50%;
11+
left: 50%;
12+
margin: -85px 0 0 -130px;
13+
}
14+
#Slider .rs-handle {
15+
background-color: transparent;
16+
border: 8px solid transparent;
17+
border-right-color: black;
18+
margin: -8px 0 0 14px !important;
19+
}
20+
#Slider .rs-handle:before {
21+
display: block;
22+
content: " ";
23+
position: absolute;
24+
height: 12px;
25+
width: 12px;
26+
background: black;
27+
right: -6px;
28+
bottom: -6px;
29+
border-radius: 100%;
30+
}
31+
#Slider .rs-handle:after {
32+
display: block;
33+
content: " ";
34+
width: 106px;
35+
position: absolute;
36+
top: -1px;
37+
right: 0px;
38+
border-top: 2px solid black;
39+
}
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
$(function() {
2+
//SMQ Doc: https://realtimelogic.com/ba/doc/en/JavaScript/SMQ.html
3+
var smq = SMQ.Client("/SMQ/"); // Connect to /SMQ/index.lsp
4+
let active=true;
5+
6+
//SMQ callback for data sent to the topic "slider" and "self"
7+
function onSmqMsg(d,ptid) {
8+
if(ptid != smq.gettid()) { //Ignore messages from 'self'
9+
active=true;
10+
$("#Slider").roundSlider("option", "value", Math.floor(d.angle * 100 / 180));
11+
active=false;
12+
}
13+
}
14+
15+
smq.subscribe("self",{datatype:"json",onmsg:onSmqMsg});
16+
smq.subscribe("slider",{datatype:"json",onmsg:onSmqMsg});
17+
18+
function onChange (e) {
19+
if(!active)
20+
smq.pubjson({angle:Math.floor(e.value * 180 / 100)}, "slider");
21+
}
22+
23+
$("#Slider").roundSlider({
24+
animation:false,
25+
sliderType: "min-range",
26+
radius: 130,
27+
showTooltip: false,
28+
width: 16,
29+
value: 0,
30+
handleSize: 0,
31+
handleShape: "square",
32+
circleShape: "half-top",
33+
change: onChange,
34+
tooltipFormat: onChange
35+
});
36+
active=false;
37+
});
38+

0 commit comments

Comments
 (0)