WebClient: improve HTML escaping

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2022-09-12 20:09:14 +02:00
parent 4a34ae6662
commit cbef217cfa
No known key found for this signature in database
GPG key ID: 2F1FB59433D5A8CB
5 changed files with 65 additions and 32 deletions

View file

@ -265,8 +265,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<script src="{{.StaticURL}}/js/sb-admin-2.min.js"></script> <script src="{{.StaticURL}}/js/sb-admin-2.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
function escapeHTML(str) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
}
function unescapeHTML(escapedStr) {
var div = document.createElement('div');
div.innerHTML = escapedStr;
var child = div.childNodes[0];
return child ? child.nodeValue : '';
}
function fixedEncodeURIComponent(str) { function fixedEncodeURIComponent(str) {
return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { return encodeURIComponent(unescapeHTML(str)).replace(/[!'()*]/g, function (c) {
return '%' + c.charCodeAt(0).toString(16); return '%' + c.charCodeAt(0).toString(16);
}); });
} }

View file

@ -219,8 +219,30 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<script src="{{.StaticURL}}/js/sb-admin-2.min.js"></script> <script src="{{.StaticURL}}/js/sb-admin-2.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
function escapeHTML(str) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
}
function unescapeHTML(escapedStr) {
var div = document.createElement('div');
div.innerHTML = escapedStr;
var child = div.childNodes[0];
return child ? child.nodeValue : '';
}
function escapeHTMLForceSafe(str) {
return str
.replace(/&/g, '_')
.replace(/</g, '_')
.replace(/>/g, '_')
.replace(/\"/g, '_')
.replace(/\'/g, '_');
}
function fixedEncodeURIComponent(str) { function fixedEncodeURIComponent(str) {
return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { return encodeURIComponent(unescapeHTML(str)).replace(/[!'()*]/g, function (c) {
return '%' + c.charCodeAt(0).toString(16); return '%' + c.charCodeAt(0).toString(16);
}); });
} }
@ -229,13 +251,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
return str.replace(/\//g,'\u2215'); return str.replace(/\//g,'\u2215');
} }
var escapeHTML = function ( t ) { function b64EncodeUnicode(str) {
return t return btoa(encodeURIComponent(str));
.replace( /&/g, '&amp;' ) }
.replace( /</g, '&lt;' )
.replace( />/g, '&gt;' ) function UnicodeDecodeB64(str) {
.replace( /"/g, '&quot;' ); return decodeURIComponent(atob(str));
}; }
</script> </script>
<!-- Page level plugins --> <!-- Page level plugins -->

View file

@ -239,7 +239,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
if (childReference == null || childReference.closed) { if (childReference == null || childReference.closed) {
childProps.set('link', fileLink); childProps.set('link', fileLink);
childProps.set('url', url); childProps.set('url', url);
childProps.set('file_name', fileName); childProps.set('file_name', UnicodeDecodeB64(fileName));
childReference = window.open(url, '_blank'); childReference = window.open(url, '_blank');
if (!checkerStarted){ if (!checkerStarted){
keepAlive(); keepAlive();
@ -366,7 +366,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
async function saveBlob() { async function saveBlob() {
var errorMessage = "Error saving external file"; var errorMessage = "Error saving external file";
var uploadPath = '{{.FileURL}}?path={{.CurrentDir}}'+encodeURIComponent("/"+childProps.get('file_name')); var uploadPath = '{{.FileURL}}?path={{.CurrentDir}}'+encodeURIComponent("/"+unescapeHTML(childProps.get('file_name')));
let response; let response;
try { try {
response = await fetch(uploadPath, { response = await fetch(uploadPath, {
@ -447,7 +447,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
} }
function openVideoPlayer(name, url, videoType){ function openVideoPlayer(name, url, videoType){
$("#video_title").text(name); $("#video_title").text(UnicodeDecodeB64(name));
$('#videoModal').modal('show'); $('#videoModal').modal('show');
player.src({ player.src({
type: videoType, type: videoType,
@ -995,8 +995,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
var title = ""; var title = "";
var cssClass = ""; var cssClass = "";
var shortened = shortenData(data, 70); var shortened = shortenData(data, 70);
data = escapeHTML(data);
if (shortened != data){ if (shortened != data){
title = escapeHTML(data); title = data;
cssClass = "ellipsis"; cssClass = "ellipsis";
} }
@ -1017,7 +1018,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
{ "data": "edit_url", { "data": "edit_url",
"render": function (data, type, row) { "render": function (data, type, row) {
if (type === 'display') { if (type === 'display') {
var filename = row["name"]; var filename = escapeHTML(row["name"]);
var extension = filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase(); var extension = filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
if (data){ if (data){
if (extension == "csv" || extension == "bat" || CodeMirror.findModeByExtension(extension) != null){ if (extension == "csv" || extension == "bat" || CodeMirror.findModeByExtension(extension) != null){
@ -1039,15 +1040,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
case "svg": case "svg":
case "ico": case "ico":
var view_url = row['url']+"&inline=1"; var view_url = row['url']+"&inline=1";
return `<a href="${view_url}" data-lightbox="image-gallery" data-title="${filename}"><i class="fas fa-eye"></i></a>`; var title = escapeHTMLForceSafe(row["name"])
return `<a href="${view_url}" data-lightbox="image-gallery" data-title="${title}"><i class="fas fa-eye"></i></a>`;
case "mp4": case "mp4":
case "mov": case "mov":
return `<a href="#" onclick="openVideoPlayer('${row["name"]}', '${row['url']}', 'video/mp4');"><i class="fas fa-eye"></i></a>`; var name = b64EncodeUnicode(row["name"]);
return `<a href="#" onclick="openVideoPlayer('${name}', '${row['url']}', 'video/mp4');"><i class="fas fa-eye"></i></a>`;
case "webm": case "webm":
return `<a href="#" onclick="openVideoPlayer('${row["name"]}', '${row['url']}', 'video/webm');"><i class="fas fa-eye"></i></a>`; var name = b64EncodeUnicode(row["name"]);
return `<a href="#" onclick="openVideoPlayer('${name}', '${row['url']}', 'video/webm');"><i class="fas fa-eye"></i></a>`;
case "ogv": case "ogv":
case "ogg": case "ogg":
return `<a href="#" onclick="openVideoPlayer('${row["name"]}', '${row['url']}', 'video/ogg');"><i class="fas fa-eye"></i></a>`; var name = b64EncodeUnicode(row["name"]);
return `<a href="#" onclick="openVideoPlayer('${name}}', '${row['url']}', 'video/ogg');"><i class="fas fa-eye"></i></a>`;
case "pdf": case "pdf":
if (PDFObject.supportsPDFs){ if (PDFObject.supportsPDFs){
var view_url = row['url']; var view_url = row['url'];
@ -1065,7 +1070,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
{{if .HasIntegrations}} {{if .HasIntegrations}}
if (type === 'display') { if (type === 'display') {
if (data){ if (data){
return `<a href="#" onclick="openExternalURL('${data}', '${row["ext_link"]}', '${row["name"]}');"><i class="fas fa-external-link-alt"></i></a>`; var name = b64EncodeUnicode(escapeHTML(row["name"]));
return `<a href="#" onclick="openExternalURL('${data}', '${row["ext_link"]}', '${name}');"><i class="fas fa-external-link-alt"></i></a>`;
} }
} }
{{end}} {{end}}

View file

@ -94,26 +94,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<script src="{{.StaticURL}}/vendor/datatables/dataTables.responsive.min.js"></script> <script src="{{.StaticURL}}/vendor/datatables/dataTables.responsive.min.js"></script>
<script src="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.js"></script> <script src="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
var escapeHTML = function ( t ) {
return t
.replace( /&/g, '&amp;' )
.replace( /</g, '&lt;' )
.replace( />/g, '&gt;' )
.replace( /"/g, '&quot;' );
};
function shortenData(d, cutoff) { function shortenData(d, cutoff) {
if ( typeof d !== 'string' ) { if ( typeof d !== 'string' ) {
return d; return d;
} }
if ( d.length <= cutoff ) { if ( d.length <= cutoff ) {
return d; return escapeHTML(d);
} }
var shortened = d.substr(0, cutoff-1); var shortened = d.substr(0, cutoff-1);
return shortened+'&#8230;'; return escapeHTML(shortened)+'&#8230;';
} }
function getIconForFile(filename) { function getIconForFile(filename) {
@ -250,7 +241,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
let response; let response;
try { try {
var f = files[index]; var f = files[index];
var uploadPath = '{{.UploadBaseURL}}'+fixedEncodeURIComponent("/"+f.name); var uploadPath = '{{.UploadBaseURL}}'+fixedEncodeURIComponent("/"+escapeHTML(f.name));
var lastModified; var lastModified;
try { try {
lastModified = f.lastModified; lastModified = f.lastModified;
@ -384,6 +375,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
var title = ""; var title = "";
var cssClass = ""; var cssClass = "";
var shortened = shortenData(data, 70); var shortened = shortenData(data, 70);
data = escapeHTML(data);
if (shortened != data){ if (shortened != data){
title = escapeHTML(data); title = escapeHTML(data);
cssClass = "ellipsis"; cssClass = "ellipsis";

View file

@ -99,7 +99,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
let response; let response;
try { try {
var f = files[index]; var f = files[index];
var uploadPath = '{{.UploadBasePath}}/'+fixedEncodeURIComponent(f.name); var uploadPath = '{{.UploadBasePath}}/'+fixedEncodeURIComponent(escapeHTML(f.name));
var lastModified; var lastModified;
try { try {
lastModified = f.lastModified; lastModified = f.lastModified;