Skip to content

Commit cdadbde

Browse files
committed
better URL safety checks; smaller tweaks
1 parent b6d5988 commit cdadbde

File tree

8 files changed

+132
-34
lines changed

8 files changed

+132
-34
lines changed

www/css/sepiaFW-skin-alabaster-mystical.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ html.landscape-small #sepiaFW-chat-controls {
177177

178178
#sepiaFW-chat-output article {
179179
border: 1px solid rgb(245, 245, 245);
180+
border-left-color: unset !important;
180181
background: rgba(250, 250, 250, 0.60);
181182
color: #000;
182183
padding-left: 9px;

www/css/sepiaFW-skin-myMessage.css

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ html {
3131
#sepiaFW-statC { background-color: #000; }
3232

3333
#sepiaFW-login-box input, #sepiaFW-main-window input {
34-
border: 1px solid #EBEBFF;
34+
border: 1px solid #eee;
3535
}
3636
#sepiaFW-login-box, #sepiaFW-nav-bar, #sepiaFW-chat-controls-form, #sepiaFW-chat-controls-right {
3737
background: rgba(255, 255, 255, 0.85);
3838
color: #000;
39-
border: 1px solid #EBEBFF;
39+
border: 1px solid #eee;
4040
}
4141
#sepiaFW-chat-controls {
4242
border: 2px solid transparent;
@@ -85,12 +85,15 @@ html {
8585
backdrop-filter: blur(5px);
8686
}
8787
#sepiaFW-assist-btn-area.speaking button {
88-
border: 4px solid #5cfc5c;
88+
border: 4px solid #5af59b;
8989
}
9090
#sepiaFW-assist-btn-area.listening button {
91-
border: 4px solid #f00;
91+
border: 4px solid #ff282d;
9292
color: #fff;
9393
}
94+
#sepiaFW-assist-btn-area.awaitDialog button {
95+
border: 4px solid #f5df1e;
96+
}
9497
#sepiaFW-assist-btn-area button:hover {
9598
background: linear-gradient(45deg, #027bff, #3e98ff);
9699
color: #fff;
@@ -193,7 +196,7 @@ html {
193196
color: #fff;
194197
}
195198
#sepiaFW-chat-output article.chatAssistant {
196-
border: 1px solid #EBEBFF;
199+
border: 1px solid #eee;
197200
background: rgba(255, 255, 255, 0.85);
198201
/*backdrop-filter: blur(5px);*/
199202
color: #2f3035;
@@ -202,9 +205,9 @@ html {
202205
color: #2f3035;
203206
}
204207
#sepiaFW-chat-output article.chatAssistant.chatPm {
205-
border-left-color: rgba(0, 100, 0, 0.15);
206-
border-left-width: 5px;
207-
padding-left: 5px;
208+
border-left-color: #eee;
209+
border-left-width: 4px;
210+
padding-left: 6px;
208211
}
209212
#sepiaFW-chat-output article.chatMe.chatPm {
210213
border-left-color: rgba(255, 255, 255, 0.50);
@@ -213,15 +216,15 @@ html {
213216
border-left-color: rgba(0, 0, 0, 0.15);
214217
}
215218
.chat-buttons-area button {
216-
border: 1px solid #EBEBFF !important;
219+
border: 1px solid #eee !important;
217220
}
218221

219222
.sepiaFW-button {
220223
background-color: linear-gradient(45deg, #027bff, #3e98ff);
221224
color: #fff;
222225
}
223226
.sepiaFW-button:hover {
224-
background-color: #EBEBFF;
227+
background-color: #eee;
225228
color: #000;
226229
}
227230

www/css/sepiaFW-skin-os2.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ html.landscape-small #sepiaFW-chat-controls {
169169
}
170170
#sepiaFW-chat-output article.chatAssistant.chatPm {
171171
/*border-left-color: rgba(243, 214, 104, 0.82);*/
172-
border-left-color: #f4dbc4;
172+
border-left-color: #f3d4b9;
173173
}
174174
#sepiaFW-chat-output article.chatMe {
175175
background: linear-gradient(130deg, #d1d1d1 0%, #e2e2e2 80%, #d1d1d1 100%);

www/css/sepiaFW-style.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,13 @@ body.limit-size {
335335
display: flex;
336336
flex-direction: column;
337337
}
338+
.sepiaFW-input-popup .warn-text {
339+
color: #f00;
340+
}
341+
.sepiaFW-input-popup h3.warn-text {
342+
width: 100%;
343+
text-align: center;
344+
}
338345
.sepiaFW-input-popup div {
339346
margin-bottom: 12px;
340347
}

www/scripts/sepiaFW.app.js

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -375,12 +375,37 @@ function sepiaFW_build_tools(){
375375

376376
//URL is same origin?
377377
Tools.isSameOrigin = function(uri1, uri2){
378-
uri1 = new URL(uri1);
379-
uri2 = new URL(uri2 || window.location.href);
380-
if(uri1.host !== uri2.host) return false;
381-
if(uri1.port !== uri2.port) return false;
382-
if(uri1.protocol !== uri2.protocol) return false;
383-
return true;
378+
try {
379+
uri1 = new URL(uri1);
380+
uri2 = new URL(uri2 || window.location.href);
381+
if(uri1.host !== uri2.host) return false;
382+
if(uri1.port !== uri2.port) return false;
383+
if(uri1.protocol !== uri2.protocol) return false;
384+
return true;
385+
}catch(error){
386+
return false;
387+
}
388+
}
389+
//URL is remote file
390+
Tools.isRemoteFileUrl = function(url, fileEnding){
391+
if (!url) return false;
392+
else url = url.trim().toLowerCase().replace(/\?.*/, "");
393+
if (!url.match(/\.[a-z0-9]+$/)) return false;
394+
if (fileEnding && url.split(".").pop() != fileEnding.toLowerCase()) return false;
395+
return (url.indexOf("http:") == 0) || (url.indexOf("https:") == 0) || (url.indexOf("ftp:") == 0);
396+
}
397+
//URL is local file
398+
Tools.isRelativeFileUrl = function(url, fileEnding){
399+
if (!url) return false;
400+
else url = url.trim().replace(/\?.*/, "");
401+
if (fileEnding && url.toLowerCase().split(".").pop() != fileEnding.toLowerCase()) return false;
402+
return (!!url.match(/^[-+%a-zA-Z0-9_\/]*\.[a-zA-Z0-9]+$/));
403+
}
404+
//URL has valid protocol
405+
Tools.urlHasValidProtocol = function(url){
406+
if (!url) return false;
407+
else url = url.toLowerCase().trim();
408+
return (!!url.match(/^[a-z][-a-z0-9_]*:/) && !url.match(/^(javascript|data):/));
384409
}
385410

386411
//get URL parameters

www/scripts/sepiaFW.frames.js

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -157,22 +157,20 @@ function sepiaFW_build_frames(){
157157
Frames.setup = function(info, finishCallback){
158158
//get HTML - is there a language dependent version?
159159
var framePage = Frames.getLocalOrDefaultPage(info.pageUrl, SepiaFW.config.appLanguage).trim();
160-
var isRemote = (framePage.indexOf("http:") == 0) || (framePage.indexOf("https:") == 0) || (framePage.indexOf("ftp:") == 0);
161-
if (isRemote){
162-
var isSameOrigin = SepiaFW.tools.isSameOrigin(framePage);
163-
var isSepiaFileHost = SepiaFW.config.urlIsSepiaFileHost(framePage);
164-
if (isSameOrigin || isSepiaFileHost) isRemote = false;
165-
}
160+
var isValidLocalURL = SepiaFW.tools.isRelativeFileUrl(framePage, "html");
161+
var isTrustedRemoteUrl = SepiaFW.tools.isRemoteFileUrl(framePage, "html")
162+
&& (SepiaFW.tools.isSameOrigin(framePage) || SepiaFW.config.urlIsSepiaFileHost(framePage));
163+
var isTrusted = isValidLocalURL || isTrustedRemoteUrl;
166164

167165
//$.get(framePage, function(frameHtml){
168166
SepiaFW.files.fetch(framePage, function(frameHtml){
169-
if (isRemote){
167+
if (!isTrusted){
170168
SepiaFW.debug.error("WARNING: Frame page has remote location and was BLOCKED due to security restrictions! - URL: " + framePage);
171-
SepiaFW.ui.showPopup("<h3 style='color:#f00; width:100%; text-align: center;'>Warning</h3>"
172-
+ "<p>SEPIA was asked to open a remote URL in a custom view (frame). The request has been blocked due to security restrictions.</p>"
173-
+ "<p>URL: " + framePage + "</p>"
174-
+ "<p>If you want to use this view please ask an admin to move it to a secure location (e.g. the SEPIA file server).</p>"
175-
);
169+
SepiaFW.ui.showSafeWarningPopup("Warning", [
170+
"SEPIA was asked to open a remote URL in a custom view (frame). The request has been blocked due to security restrictions.",
171+
"If you want to use this view please ask an admin to move it to a secure location (e.g. the SEPIA file server).",
172+
"URL:"
173+
], framePage);
176174
Frames.close();
177175
return;
178176
}else{

www/scripts/sepiaFW.ui.actions.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,15 +230,41 @@ function sepiaFW_build_ui_actions(){
230230
var inAppBrowserOptions = 'location=yes,toolbar=yes,mediaPlaybackRequiresUserAction=yes,allowInlineMediaPlayback=yes,hardwareback=yes,disableswipenavigation=no,clearsessioncache=no,clearcache=no';
231231
Actions.openUrlAutoTarget = function(url, forceExternal){
232232
if (!url) return;
233-
var urlLower = url.toLowerCase();
233+
234+
//tiny and headless do not support URL action
234235
if (SepiaFW.ui.isTinyApp || SepiaFW.ui.isHeadless){
235236
//Tiny and headless apps usually have no ability to open in-app browser
236237
SepiaFW.assistant.waitForOpportunityAndSay("<error_client_support_0a>", function(){
237238
//Fallback after max-wait:
238239
SepiaFW.ui.showInfo(SepiaFW.local.g('no_client_support'));
239240
}, 2000, 30000); //min-wait, max-wait
240-
241-
}else if (SepiaFW.ui.isCordova){
241+
return;
242+
}
243+
244+
//check URL vor sanity (at least a bit)
245+
var hasValidUrlProtocol = SepiaFW.tools.urlHasValidProtocol(url);
246+
if (!hasValidUrlProtocol){
247+
var question = document.createElement("div");
248+
var p = document.createElement("p");
249+
p.textContent = "Open URL:";
250+
question.appendChild(p);
251+
var unsafeContent = document.createElement("textarea");
252+
unsafeContent.textContent = url;
253+
question.appendChild(unsafeContent);
254+
SepiaFW.ui.askForPermissionToExecute(question, function(){
255+
//open
256+
openUrlAutoTargetFun(url, forceExternal);
257+
}, function(){
258+
SepiaFW.debug.error("Action - 'openUrlAutoTarget' blocked due to suspicious URL: " + url);
259+
});
260+
}else{
261+
//open
262+
openUrlAutoTargetFun(url, forceExternal);
263+
}
264+
}
265+
function openUrlAutoTargetFun(url, forceExternal){
266+
var urlLower = url.toLowerCase();
267+
if (SepiaFW.ui.isCordova){
242268
if (forceExternal
243269
|| (urlLower.indexOf('http') !== 0 && !!urlLower.match(/^\w+:/)) //TODO: keep an eye on this! Does it prevent some cool URL scheme features?
244270
|| urlLower.indexOf('https://maps.') === 0 || urlLower.indexOf('http://maps.') === 0

www/scripts/sepiaFW.ui.js

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,6 +1437,35 @@ function sepiaFW_build_ui(){
14371437
messagePopupAutoActionTry = 0;
14381438
}
14391439

1440+
//Show warning
1441+
UI.showSafeWarningPopup = function(headerText, infoTextParagraphs, unsafeString){
1442+
var content = document.createElement("div");
1443+
var header = document.createElement("h3");
1444+
header.className = "warn-text";
1445+
header.textContent = headerText || "Warning";
1446+
content.appendChild(header);
1447+
var info = [];
1448+
if (typeof infoTextParagraphs == "string"){
1449+
info.push(infoTextParagraphs);
1450+
}else if (typeof infoTextParagraphs == "object" && infoTextParagraphs instanceof Node){
1451+
content.appendChild(infoTextParagraphs);
1452+
info = undefined;
1453+
}else{
1454+
info = infoTextParagraphs;
1455+
}
1456+
if (info && info.length) info.forEach(function(it){
1457+
var p = document.createElement("p");
1458+
p.textContent = it || "";
1459+
content.appendChild(p);
1460+
});
1461+
if (unsafeString){
1462+
var unsafeContent = document.createElement("textarea");
1463+
unsafeContent.textContent = unsafeString;
1464+
content.appendChild(unsafeContent);
1465+
}
1466+
SepiaFW.ui.showPopup(content);
1467+
}
1468+
14401469
//Create basic input popup
14411470
UI.showInputPopup = function(infoContent, inputLabel,
14421471
inputCallback, abortCallback, buttonLabel1, buttonLabel2){
@@ -1485,8 +1514,17 @@ function sepiaFW_build_ui(){
14851514

14861515
//Use pop-up to ask for permission
14871516
UI.askForPermissionToExecute = function(question, allowedCallback, refusedCallback){
1488-
var request = SepiaFW.local.g('allowedToExecuteThisCommand') + "<br>" + question;
1489-
UI.showPopup(request, {
1517+
var content;
1518+
if (typeof question == "object" && question instanceof Node){
1519+
var content = document.createElement("div");
1520+
var p = document.createElement("p");
1521+
p.textContent = SepiaFW.local.g('allowedToExecuteThisCommand');
1522+
content.appendChild(p);
1523+
content.appendChild(question);
1524+
}else{
1525+
content = SepiaFW.local.g('allowedToExecuteThisCommand') + "<br>" + question;
1526+
}
1527+
UI.showPopup(content, {
14901528
buttonOneName: SepiaFW.local.g('looksGood'),
14911529
buttonOneAction: function(){
14921530
//yes

0 commit comments

Comments
 (0)