Skip to content

Commit

Permalink
Merge pull request #2986 from SCADA-LTS/fix/#2985_Prevent_XSS_for_RES…
Browse files Browse the repository at this point in the history
…T_API_by_escape_String_content

#2985 Prevent XSS for REST API by escape String content:
  • Loading branch information
Limraj authored Sep 2, 2024
2 parents 1fa5836 + 805b4e6 commit 82a18e9
Show file tree
Hide file tree
Showing 93 changed files with 1,460 additions and 1,527 deletions.
10 changes: 3 additions & 7 deletions WebContent/WEB-INF/jsp/dataPointEdit.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -396,13 +396,9 @@
}
}
// const
var pathArray = location.href.split( '/' );
var protocol = pathArray[0];
var host = pathArray[2];
var appScada = pathArray[3];
var myLocation;
if (!myLocation) {
myLocation = protocol + "//" + host + "/" + appScada + "/";
myLocation = getAppLocation();
}
var arrDictLoggingType = ["", "When point value changes", "All data", "Do not log", "Interval", "When point timestamp changes"];
Expand Down Expand Up @@ -813,7 +809,7 @@
jQuery.ajax({
type: "GET",
dataType: "json",
url:myLocation+"/api/point_properties/getPropertiesBaseOnId/"+idPointConfigurationToBaseOnExistingPoint,
url:myLocation+"api/point_properties/getPropertiesBaseOnId/"+idPointConfigurationToBaseOnExistingPoint,
success: function(properties){
setConfig(properties);
},
Expand All @@ -836,7 +832,7 @@
jQuery.ajax({
type: "GET",
dataType: "json",
url:myLocation+"/api/point_properties/getPropertiesBaseOnId/"+idPointConfigurationToBaseOnExistingPoint,
url:myLocation+"api/point_properties/getPropertiesBaseOnId/"+idPointConfigurationToBaseOnExistingPoint,
success: function(properties){
let bCheckedType = checkType(properties.dataTypeId);
Expand Down
9 changes: 9 additions & 0 deletions WebContent/WEB-INF/jsp/include/highlight.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ function updateCodeText(text, destination) {
hljs.highlightElement(result_element);
}
function updateCodeTextEscaped(text, destination) {
let result_element = document.querySelector(destination);
if(text[text.length-1] == "\n") {
text += " ";
}
result_element.innerHTML = text;
hljs.highlightElement(result_element);
}
function syncCodeScroll(element, destination) {
let result_element = document.querySelector(destination);
result_element.scrollTop = element.scrollTop;
Expand Down
9 changes: 5 additions & 4 deletions WebContent/WEB-INF/jsp/systemSettings.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -514,8 +514,9 @@
function initCustomCssData() {
fetchCustomCssConfig().then((val) => {
document.getElementById('cssEditor').value = val;
updateCodeText(val, '#cssHighlightingContent');
let res = JSON.parse(val);
document.getElementById('cssEditor').innerHTML = res.content;
updateCodeTextEscaped(res.content, '#cssHighlightingContent');
});
}
Expand All @@ -525,7 +526,7 @@
req.open('GET', customCssUrl, true);
req.onload = () => {
if (req.status === 200) {
resolve(req.responseText);
resolve(req.response);
} else {
reject(req.status);
}
Expand Down Expand Up @@ -1129,7 +1130,7 @@
id="cssEditor"
class="hgl-editor"
spellcheck="false"
oninput="updateCodeText(this.value, '#cssHighlightingContent');"
oninput="updateCodeTextEscaped(this.value, '#cssHighlightingContent');"
onscroll="syncCodeScroll(this, '#cssHighlightingContent');">
</textarea>
<pre id="cssHighlighting" class="hgl-highlighting" aria-hidden="true">
Expand Down
Binary file not shown.
Binary file removed WebContent/WEB-INF/lib/jackson-annotations-2.8.2.jar
Binary file not shown.
Binary file added WebContent/WEB-INF/lib/jackson-core-2.17.2.jar
Binary file not shown.
Binary file removed WebContent/WEB-INF/lib/jackson-core-2.8.2.jar
Binary file not shown.
Binary file not shown.
Binary file removed WebContent/WEB-INF/lib/jackson-databind-2.8.2.jar
Binary file not shown.
19 changes: 18 additions & 1 deletion WebContent/WEB-INF/springDispatcher-servlet.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">

<context:component-scan base-package="org.scada_lts" />
<mvc:annotation-driven />
<mvc:annotation-driven>
<mvc:message-converters>
<ref bean="messageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>

<context:annotation-config />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
Expand Down Expand Up @@ -215,4 +219,17 @@
<bean id="dataSourceListController" class="org.scada_lts.web.mvc.controller.DataSourceListController" >
<property name="viewName"><value>dataSourceList</value></property>
</bean>

<bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean" scope="prototype">
<property name="serializers">
<array>
<bean class="org.scada_lts.web.security.XssProtectStringSerializer" />
<bean class="org.scada_lts.web.security.XssProtectCssStyleSerializer" />
</array>
</property>
</bean>

<bean id="messageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" scope="prototype">
<constructor-arg ref="objectMapper"/>
</bean>
</beans>
13 changes: 11 additions & 2 deletions WebContent/resources/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -1215,8 +1215,11 @@ function disconnectWebsocket() {
}

function onloadHandlerWebsocket() {
let endpoint = scadalts.websocket.endpoints.main;
scadalts.websocket.client = connectWebsocket(getAppLocation() + endpoint, {}, errorCallbackWebsocket, connectCallbackWebsocket);
let location = window.location.href;
if(!location.includes('app.shtm')) {
let endpoint = scadalts.websocket.endpoints.main;
scadalts.websocket.client = connectWebsocket(getAppLocation() + endpoint, {}, errorCallbackWebsocket, connectCallbackWebsocket);
}
}

function getAppLocation() {
Expand Down Expand Up @@ -1285,4 +1288,10 @@ function unassignEvent(eventId) {

function isEmpty(value) {
return !value || (typeof value === "string" && value.trim() === "");
}

function unescapeHtml(value) {
let div = document.createElement("div");
div.innerHTML = value;
return div.textContent || div.innerText;
}
5 changes: 3 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,11 @@ task stopDebug(type: Exec) {
}

task buildRun(type: GradleBuild) {
tasks = ['clearTomcat', 'clearProject', 'test', 'war', 'deployTomcat', 'run']
tasks = ['clearTomcat', 'clearProject', 'installWebDependency', 'test', 'war', 'deployTomcat', 'run']
}

task buildRunDebug(type: GradleBuild) {
tasks = ['clearTomcat', 'clearProject', 'test', 'war', 'deployTomcat', 'runDebug']
tasks = ['clearTomcat', 'clearProject', 'installWebDependency', 'test', 'war', 'deployTomcat', 'runDebug']
}

task buildRunDebugDev(type: GradleBuild) {
Expand Down Expand Up @@ -232,6 +232,7 @@ test {
includeTestsMatching "com.serotonin.mango.util.AddLimitIfWithoutSqlDataSourceUtilsTest"
includeTestsMatching "com.serotonin.mango.util.StartStopDataPointsUtilsTestsSuite"
includeTestsMatching "org.scada_lts.utils.BlockingQueuesUtilsTest"
includeTestsMatching "org.scada_lts.web.security.XssProtectHtmlEscapeUtilsTest"
includeTestsMatching "org.scada_lts.web.security.XssUtilsTest"
}

Expand Down
10 changes: 5 additions & 5 deletions scadalts-ui/src/layout/lists/events/EventScadaItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>
{{ comment.comment }}
<span v-html="comment.comment"></span>
</v-list-item-title>
<v-list-item-subtitle>
{{ comment.username }},
<span v-html="comment.username"></span>,
{{ new Date(comment.ts).toLocaleString() }}
</v-list-item-subtitle>
</v-list-item-content>
Expand Down Expand Up @@ -157,7 +157,7 @@ export default {
},
methods: {
addComment(e) {
async addComment(e) {
let time = new Date();
let comment = {
userId: this.$store.state.loggedUser.id,
Expand All @@ -166,13 +166,13 @@ export default {
username: this.$store.state.loggedUser.username,
prettyTime: time.toLocaleTimeString(),
};
e.userComments.push(Object.assign({}, comment));
this.$store.dispatch('addUserComment', {
let response = this.$store.dispatch('addUserComment', {
comment: comment,
typeId: 1,
refId: e.id,
});
this.newComment = '';
e.userComments.push(Object.assign({}, response));
},
deleteComment(e, comment) {
Expand Down
16 changes: 8 additions & 8 deletions scadalts-ui/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1065,14 +1065,14 @@
"systemsettings.webresource.uploads.path.wrong":"Uploaded images save path must end with 'uploads' or 'uploads{0}' ",
"systemsettings.webresource.graphics.path.wrong": "Custom Graphics images path must end with 'graphics' or 'graphics{0}' ",
"systemsettings.misc.eventAssignEnabled": "Event Assign enabled",
"systemsettings.misc.eventPendingCacheEnabled": "Event Pending Limit",
"systemsettings.misc.eventPendingLimit": "Enabled Event Pending Cache",
"systemsettings.misc.workItemsReportingEnabled": "Work items reporting enabled",
"systemsettings.misc.workItemsReportingItemsPerSecondEnabled": "Items per second reporting enabled",
"systemsettings.misc.workItemsReportingItemsPerSecondLimit": "Items per second reporting limit",
"systemsettings.misc.threadsNameAdditionalLength": "Thread name length",
"systemsettings.misc.webResourceUploadsPath": "Uploaded images save path",
"systemsettings.misc.webResourceGraphicsPath": "Graphics images path",
"systemsettings.misc.eventPendingCacheEnabled": "Event Pending Cache Enabled",
"systemsettings.misc.eventPendingLimit": "Event Pending Limit",
"systemsettings.misc.workItemsReportingEnabled": "Work Items Reporting Enabled",
"systemsettings.misc.workItemsReportingItemsPerSecondEnabled": "Items Per Second Reporting Enabled",
"systemsettings.misc.workItemsReportingItemsPerSecondLimit": "Items Per Second Reporting Limit",
"systemsettings.misc.threadsNameAdditionalLength": "Thread Name Length",
"systemsettings.misc.webResourceUploadsPath": "Uploads Images Path",
"systemsettings.misc.webResourceGraphicsPath": "Graphics Images Path",
"systemsettings.misc.webResourceUploadsPath.wrong":"Uploaded images save path must end with 'uploads' or 'uploads{0}' ",
"systemsettings.misc.webResourceGraphicsPath.wrong": "Graphics images path must end with 'graphics' or 'graphics{0}' ",
"systemsettings.top.description": "Top description",
Expand Down
6 changes: 6 additions & 0 deletions scadalts-ui/src/utils/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ export function getEventList(event, sortByParam, sortDescParam, eventList) {
}
}

export function unescapeHtml(value) {
let div = document.createElement("div");
div.innerHTML = value;
return div.textContent || div.innerText;
}

function comparatorBy(a, b, sortBy, sortDesc) {
for(let i = 0; i < sortBy.length; i++) {
let by = sortBy[i];
Expand Down
4 changes: 2 additions & 2 deletions scadalts-ui/src/views/Alarms/EventList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@
{{ $t(`eventList.sourceType${item.typeId}`) }}
</template>
<template v-slot:item.message="{ item }">
<a :title="(item.message) | clearHtml">{{ (item.message) | clearHtml | truncate}}</a>
<a :title="(item.message) | clearHtml"><span v-html="item.message"></span></a>
</template>

<template v-slot:item.status="{ item }">
Expand Down Expand Up @@ -621,7 +621,7 @@ export default {
return input;
},
clearHtml(str) {
return str.replace(/<[^>]*>?/gm, '').replaceAll('&nbsp;', ' ')
return str.replace(/<[^>]*>?/gm, '').replaceAll('&nbsp;', ' ').replaceAll('&#39;', ' ').replaceAll('&quot;', ' ')
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>
{{ comment.comment }}
<span v-html="comment.comment"></span>
</v-list-item-title>
<v-list-item-subtitle>
{{ comment.username }}, {{ new Date(comment.ts).toLocaleString() }}
<span v-html="comment.username"></span>,
{{ new Date(comment.ts).toLocaleString() }}
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action
Expand Down Expand Up @@ -87,7 +88,7 @@ export default {
this.activeUserId = this.$store.state.loggedUser.id;
},
addComment() {
async addComment() {
let time = new Date();
let comment = {
userId: this.$store.state.loggedUser.id,
Expand All @@ -96,13 +97,13 @@ export default {
username: this.$store.state.loggedUser.username,
prettyTime: time.toLocaleTimeString(),
};
this.data.comments.push(Object.assign({}, comment));
this.$store.dispatch('addUserComment', {
let response = await this.$store.dispatch('addUserComment', {
comment: comment,
typeId: 2,
refId: this.data.id,
});
this.newComment = '';
this.data.comments.push(Object.assign({}, response));
},
deleteComment(e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>
{{ comment.comment }}
<span v-html="comment.comment"></span>
</v-list-item-title>
<v-list-item-subtitle>
{{ comment.username }},
<span v-html="comment.username"></span>,
{{ new Date(comment.ts).toLocaleString() }}
</v-list-item-subtitle>
</v-list-item-content>
Expand Down Expand Up @@ -245,7 +245,7 @@ export default {
}
},
addComment(e) {
async addComment(e) {
let time = new Date();
let comment = {
userId: this.$store.state.loggedUser.id,
Expand All @@ -254,13 +254,13 @@ export default {
username: this.$store.state.loggedUser.username,
prettyTime: time.toLocaleTimeString(),
};
e.userComments.push(Object.assign({}, comment));
this.$store.dispatch('addUserComment', {
let response = await this.$store.dispatch('addUserComment', {
comment: comment,
typeId: 1,
refId: e.id,
});
this.newComment = '';
e.userComments.push(Object.assign({}, response));
},
deleteComment(e, comment) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,6 @@ export default {
methods: {
updatePointWs(data) {
console.log("updatePointWs", data);
this.pointValue = JSON.parse(data.body).value;
this.fetchData();
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>
{{ comment.comment }}
<span v-html="comment.comment"></span>
</v-list-item-title>
<v-list-item-subtitle>
{{ comment.username }},
<span v-html="comment.username"></span>,
{{ new Date(comment.ts).toLocaleString() }}
</v-list-item-subtitle>
</v-list-item-content>
Expand Down
10 changes: 7 additions & 3 deletions scadalts-ui/src/views/SynopticPanel/SynopticPanelItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<script>
import customComponentMixin from '../../components/SynopticPanel/CustomComponentMixin.js';
import SynopticPanelItemEditor from './SynopticPanelItemEditor.vue';
import {unescapeHtml} from '../../utils/common';
/**
* Synoptic Panel component - Item
Expand Down Expand Up @@ -85,9 +86,10 @@ export default {
if (!!dom && !!dom.firstChild) {
dom.firstChild.remove();
}
let vectorImage = unescapeHtml(this.panel.vectorImage);
this.$svg('panel-canvas')
.size(window.innerWidth, window.innerHeight)
.svg(this.panel.vectorImage);
.svg(vectorImage);
this.$emit('loaded', this.panel.id);
this.initSvgComponentData();
},
Expand Down Expand Up @@ -120,7 +122,8 @@ export default {
initSvgComponentData() {
try {
let graphicItems = this.parseSvgContent();
let componentData = new Map(JSON.parse(this.panel.componentData));
let unescapeComponentData = unescapeHtml(this.panel.componentData);
let componentData = new Map(JSON.parse(unescapeComponentData));
if (componentData !== undefined) {
for (const [key, value] of componentData.entries()) {
graphicItems.set(key, value);
Expand All @@ -136,7 +139,8 @@ export default {
savePanel(childGraphicItems) {
this.graphicItems = childGraphicItems;
this.panel.componentData = JSON.stringify([...this.graphicItems]);
this.panel.vectorImage = unescapeHtml(this.panel.vectorImage);
this.$store.dispatch('updateSynopticPanel', this.panel).then(() => {
this.$emit('updated', true);
}).catch(() => {
Expand Down
Loading

0 comments on commit 82a18e9

Please sign in to comment.