# HG changeset patch # User Sebastien Jodogne # Date 1498578885 -7200 # Node ID ccd44d546b47ce54dcba800fd27add861e08e693 # Parent b9856b1615d63df00aa23a6db046fb9cbd401bf6 Fix XSS inside DICOM in Orthanc Explorer diff -r b9856b1615d6 -r ccd44d546b47 NEWS --- a/NEWS Tue Jun 27 12:59:36 2017 +0200 +++ b/NEWS Tue Jun 27 17:54:45 2017 +0200 @@ -19,6 +19,7 @@ * Fix issue 44 (Bad interpretation of photometric interpretation MONOCHROME1) * Fix issue 49 (Worklists: accentuated characters are removed from C-Find responses) * Fix Debian #865606 (orthanc FTBFS with libdcmtk-dev 3.6.1~20170228-2) +* Fix XSS inside DICOM in Orthanc Explorer (as reported by Victor Pasnkel, Morphus Labs) Version 1.2.0 (2016/12/13) diff -r b9856b1615d6 -r ccd44d546b47 OrthancExplorer/explorer.js --- a/OrthancExplorer/explorer.js Tue Jun 27 12:59:36 2017 +0200 +++ b/OrthancExplorer/explorer.js Tue Jun 27 17:54:45 2017 +0200 @@ -17,19 +17,6 @@ var currentUuid = ''; -// http://stackoverflow.com/a/4673436 -String.prototype.format = function() { - var args = arguments; - return this.replace(/{(\d+)}/g, function(match, number) { - /*return typeof args[number] != 'undefined' - ? args[number] - : match;*/ - - return args[number]; - }); -}; - - function DeepCopy(obj) { return jQuery.extend(true, {}, obj); @@ -209,29 +196,34 @@ } -function CompleteFormatting(s, link, isReverse) +function CompleteFormatting(node, link, isReverse, count) { + if (count != null) + { + node = node.add($('') + .addClass('ui-li-count') + .text(count)); + } + if (link != null) { - s = 'href="' + link + '">' + s + ''; - + node = $('').attr('href', link).append(node); + if (isReverse) - s = 'data-direction="reverse" '+ s; - - s = '').append(node); + if (isReverse) - return '
  • ' + s + '
  • '; - else - return '
  • ' + s + '
  • '; + node.attr('data-icon', 'back'); + + return node; } -function FormatMainDicomTags(tags, tagsToIgnore) +function FormatMainDicomTags(target, tags, tagsToIgnore) { - var s = ''; - for (var i in tags) { if (tagsToIgnore.indexOf(i) == -1) @@ -250,47 +242,38 @@ v = SplitLongUid(v); } - - s += ('

    {0}: {1}

    ').format(i, v); + target.append($('

    ') + .text(i + ': ') + .append($('').text(v))); } } - - return s; } function FormatPatient(patient, link, isReverse) { - var s = ('

    {0}

    {1}' + - '{2}' - ).format - (patient.MainDicomTags.PatientName, - FormatMainDicomTags(patient.MainDicomTags, [ - "PatientName" - /*"OtherPatientIDs" */ - ]), - patient.Studies.length - ); + var node = $('
    ').append($('

    ').text(patient.MainDicomTags.PatientName)); - return CompleteFormatting(s, link, isReverse); + FormatMainDicomTags(node, patient.MainDicomTags, [ + "PatientName" + // "OtherPatientIDs" + ]); + + return CompleteFormatting(node, link, isReverse, patient.Studies.length); } function FormatStudy(study, link, isReverse) { - var s = ('

    {0}

    {1}' + - '{2}' - ).format - (study.MainDicomTags.StudyDescription, - FormatMainDicomTags(study.MainDicomTags, [ + var node = $('
    ').append($('

    ').text(study.MainDicomTags.StudyDescription)); + + FormatMainDicomTags(node, study.MainDicomTags, [ "StudyDescription", "StudyTime" - ]), - study.Series.length - ); - - return CompleteFormatting(s, link, isReverse); + ]); + + return CompleteFormatting(node, link, isReverse, study.Series.length); } @@ -307,41 +290,39 @@ { c = series.Instances.length + '/' + series.ExpectedNumberOfInstances; } + + var node = $('
    ') + .append($('

    ').text(series.MainDicomTags.SeriesDescription)) + .append($('

    ').append($('') + .text('Status: ') + .append($('').text(series.Status)))); - var s = ('

    {0}

    ' + - '

    Status: {1}

    {2}' + - '{3}').format - (series.MainDicomTags.SeriesDescription, - series.Status, - FormatMainDicomTags(series.MainDicomTags, [ + FormatMainDicomTags(node, series.MainDicomTags, [ "SeriesDescription", "SeriesTime", "Manufacturer", "ImagesInAcquisition", "SeriesDate", "ImageOrientationPatient" - ]), - c - ); - - return CompleteFormatting(s, link, isReverse); + ]); + + return CompleteFormatting(node, link, isReverse, c); } function FormatInstance(instance, link, isReverse) { - var s = ('

    Instance {0}

    {1}').format - (instance.IndexInSeries, - FormatMainDicomTags(instance.MainDicomTags, [ - "AcquisitionNumber", - "InstanceNumber", - "InstanceCreationDate", - "InstanceCreationTime", - "ImagePositionPatient" - ]) - ); + var node = $('
    ').append($('

    ').text('Instance: ' + instance.IndexInSeries)); - return CompleteFormatting(s, link, isReverse); + FormatMainDicomTags(node, instance.MainDicomTags, [ + "AcquisitionNumber", + "InstanceNumber", + "InstanceCreationDate", + "InstanceCreationTime", + "ImagePositionPatient" + ]); + + return CompleteFormatting(node, link, isReverse); } @@ -353,7 +334,11 @@ cache: false, success: function(s) { if (s.Name != "") { - $('.orthanc-name').html('' + s.Name + ' » '); + $('.orthanc-name').append($('') + .addClass('ui-link') + .attr('href', 'explorer.html') + .text(s.Name) + .append(' » ')); } } }); @@ -417,8 +402,9 @@ for (var i = 0; i < studies.length; i++) { if (i == 0 || studies[i].MainDicomTags.StudyDate != studies[i - 1].MainDicomTags.StudyDate) { - target.append('
  • {0}
  • '.format - (FormatDicomDate(studies[i].MainDicomTags.StudyDate))); + target.append($('
  • ') + .attr('data-role', 'list-divider') + .text(FormatDicomDate(studies[i].MainDicomTags.StudyDate))); } target.append(FormatStudy(studies[i], '#study?uuid=' + studies[i].ID)); @@ -477,9 +463,11 @@ for (var i = 0; i < series.length; i++) { if (i == 0 || series[i].MainDicomTags.SeriesDate != series[i - 1].MainDicomTags.SeriesDate) { - target.append('
  • {0}
  • '.format - (FormatDicomDate(series[i].MainDicomTags.SeriesDate))); + target.append($('
  • ') + .attr('data-role', 'list-divider') + .text(FormatDicomDate(series[i].MainDicomTags.SeriesDate))); } + target.append(FormatSeries(series[i], '#series?uuid=' + series[i].ID)); } target.listview('refresh'); @@ -537,6 +525,24 @@ } +function EscapeHtml(value) +{ + var ENTITY_MAP = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/', + '`': '`', + '=': '=' + }; + + return String(value).replace(/[&<>"'`=\/]/g, function (s) { + return ENTITY_MAP[s]; + }); +} + function ConvertForTree(dicom) { @@ -544,12 +550,14 @@ for (var i in dicom) { if (dicom[i] != null) { - var label = i + ' (' + dicom[i]["Name"] + '): '; + var label = (i + ' (' + + EscapeHtml(dicom[i]["Name"]) + + '): '); if (dicom[i]["Type"] == 'String') { result.push({ - label: label + '' + dicom[i]["Value"] + '', + label: label + '' + EscapeHtml(dicom[i]["Value"]) + '', children: [] }); } @@ -797,7 +805,7 @@ var images = []; for (var i = 0; i < instances.length; i++) { images.push([ '../instances/' + instances[i].ID + '/preview', - '{0}/{1}'.format(i + 1, instances.length) ]) + (i + 1).toString() + '/' + instances.length.toString() ]) } jQuery.slimbox(images, 0, {