comparison OrthancExplorer/explorer.js @ 2292:ccd44d546b47

Fix XSS inside DICOM in Orthanc Explorer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 27 Jun 2017 17:54:45 +0200
parents e0517f25919e
children a95beca72e99
comparison
equal deleted inserted replaced
2291:b9856b1615d6 2292:ccd44d546b47
13 //$.mobile.defaultPageTransition = 'slide'; 13 //$.mobile.defaultPageTransition = 'slide';
14 14
15 15
16 var currentPage = ''; 16 var currentPage = '';
17 var currentUuid = ''; 17 var currentUuid = '';
18
19
20 // http://stackoverflow.com/a/4673436
21 String.prototype.format = function() {
22 var args = arguments;
23 return this.replace(/{(\d+)}/g, function(match, number) {
24 /*return typeof args[number] != 'undefined'
25 ? args[number]
26 : match;*/
27
28 return args[number];
29 });
30 };
31 18
32 19
33 function DeepCopy(obj) 20 function DeepCopy(obj)
34 { 21 {
35 return jQuery.extend(true, {}, obj); 22 return jQuery.extend(true, {}, obj);
207 } 194 }
208 }); 195 });
209 } 196 }
210 197
211 198
212 function CompleteFormatting(s, link, isReverse) 199 function CompleteFormatting(node, link, isReverse, count)
213 { 200 {
201 if (count != null)
202 {
203 node = node.add($('<span>')
204 .addClass('ui-li-count')
205 .text(count));
206 }
207
214 if (link != null) 208 if (link != null)
215 { 209 {
216 s = 'href="' + link + '">' + s + '</a>'; 210 node = $('<a>').attr('href', link).append(node);
217 211
218 if (isReverse) 212 if (isReverse)
219 s = 'data-direction="reverse" '+ s; 213 node.attr('data-direction', 'reverse')
220 214 }
221 s = '<a ' + s; 215
222 } 216 node = $('<li>').append(node);
223 217
224 if (isReverse) 218 if (isReverse)
225 return '<li data-icon="back">' + s + '</li>'; 219 node.attr('data-icon', 'back');
226 else 220
227 return '<li>' + s + '</li>'; 221 return node;
228 } 222 }
229 223
230 224
231 function FormatMainDicomTags(tags, tagsToIgnore) 225 function FormatMainDicomTags(target, tags, tagsToIgnore)
232 { 226 {
233 var s = '';
234
235 for (var i in tags) 227 for (var i in tags)
236 { 228 {
237 if (tagsToIgnore.indexOf(i) == -1) 229 if (tagsToIgnore.indexOf(i) == -1)
238 { 230 {
239 var v = tags[i]; 231 var v = tags[i];
248 i == "DicomSeriesInstanceUID") 240 i == "DicomSeriesInstanceUID")
249 { 241 {
250 v = SplitLongUid(v); 242 v = SplitLongUid(v);
251 } 243 }
252 244
253 245 target.append($('<p>')
254 s += ('<p>{0}: <strong>{1}</strong></p>').format(i, v); 246 .text(i + ': ')
255 } 247 .append($('<strong>').text(v)));
256 } 248 }
257 249 }
258 return s;
259 } 250 }
260 251
261 252
262 function FormatPatient(patient, link, isReverse) 253 function FormatPatient(patient, link, isReverse)
263 { 254 {
264 var s = ('<h3>{0}</h3>{1}' + 255 var node = $('<div>').append($('<h3>').text(patient.MainDicomTags.PatientName));
265 '<span class="ui-li-count">{2}</span>' 256
266 ).format 257 FormatMainDicomTags(node, patient.MainDicomTags, [
267 (patient.MainDicomTags.PatientName, 258 "PatientName"
268 FormatMainDicomTags(patient.MainDicomTags, [ 259 // "OtherPatientIDs"
269 "PatientName" 260 ]);
270 /*"OtherPatientIDs" */ 261
271 ]), 262 return CompleteFormatting(node, link, isReverse, patient.Studies.length);
272 patient.Studies.length
273 );
274
275 return CompleteFormatting(s, link, isReverse);
276 } 263 }
277 264
278 265
279 266
280 function FormatStudy(study, link, isReverse) 267 function FormatStudy(study, link, isReverse)
281 { 268 {
282 var s = ('<h3>{0}</h3>{1}' + 269 var node = $('<div>').append($('<h3>').text(study.MainDicomTags.StudyDescription));
283 '<span class="ui-li-count">{2}</span>' 270
284 ).format 271 FormatMainDicomTags(node, study.MainDicomTags, [
285 (study.MainDicomTags.StudyDescription,
286 FormatMainDicomTags(study.MainDicomTags, [
287 "StudyDescription", 272 "StudyDescription",
288 "StudyTime" 273 "StudyTime"
289 ]), 274 ]);
290 study.Series.length 275
291 ); 276 return CompleteFormatting(node, link, isReverse, study.Series.length);
292
293 return CompleteFormatting(s, link, isReverse);
294 } 277 }
295 278
296 279
297 280
298 function FormatSeries(series, link, isReverse) 281 function FormatSeries(series, link, isReverse)
305 } 288 }
306 else 289 else
307 { 290 {
308 c = series.Instances.length + '/' + series.ExpectedNumberOfInstances; 291 c = series.Instances.length + '/' + series.ExpectedNumberOfInstances;
309 } 292 }
310 293
311 var s = ('<h3>{0}</h3>' + 294 var node = $('<div>')
312 '<p><em>Status: <strong>{1}</strong></em></p>{2}' + 295 .append($('<h3>').text(series.MainDicomTags.SeriesDescription))
313 '<span class="ui-li-count">{3}</span>').format 296 .append($('<p>').append($('<em>')
314 (series.MainDicomTags.SeriesDescription, 297 .text('Status: ')
315 series.Status, 298 .append($('<strong>').text(series.Status))));
316 FormatMainDicomTags(series.MainDicomTags, [ 299
300 FormatMainDicomTags(node, series.MainDicomTags, [
317 "SeriesDescription", 301 "SeriesDescription",
318 "SeriesTime", 302 "SeriesTime",
319 "Manufacturer", 303 "Manufacturer",
320 "ImagesInAcquisition", 304 "ImagesInAcquisition",
321 "SeriesDate", 305 "SeriesDate",
322 "ImageOrientationPatient" 306 "ImageOrientationPatient"
323 ]), 307 ]);
324 c 308
325 ); 309 return CompleteFormatting(node, link, isReverse, c);
326
327 return CompleteFormatting(s, link, isReverse);
328 } 310 }
329 311
330 312
331 function FormatInstance(instance, link, isReverse) 313 function FormatInstance(instance, link, isReverse)
332 { 314 {
333 var s = ('<h3>Instance {0}</h3>{1}').format 315 var node = $('<div>').append($('<h3>').text('Instance: ' + instance.IndexInSeries));
334 (instance.IndexInSeries, 316
335 FormatMainDicomTags(instance.MainDicomTags, [ 317 FormatMainDicomTags(node, instance.MainDicomTags, [
336 "AcquisitionNumber", 318 "AcquisitionNumber",
337 "InstanceNumber", 319 "InstanceNumber",
338 "InstanceCreationDate", 320 "InstanceCreationDate",
339 "InstanceCreationTime", 321 "InstanceCreationTime",
340 "ImagePositionPatient" 322 "ImagePositionPatient"
341 ]) 323 ]);
342 ); 324
343 325 return CompleteFormatting(node, link, isReverse);
344 return CompleteFormatting(s, link, isReverse);
345 } 326 }
346 327
347 328
348 $('[data-role="page"]').live('pagebeforeshow', function() { 329 $('[data-role="page"]').live('pagebeforeshow', function() {
349 $.ajax({ 330 $.ajax({
351 dataType: 'json', 332 dataType: 'json',
352 async: false, 333 async: false,
353 cache: false, 334 cache: false,
354 success: function(s) { 335 success: function(s) {
355 if (s.Name != "") { 336 if (s.Name != "") {
356 $('.orthanc-name').html('<a class="ui-link" href="explorer.html">' + s.Name + '</a> &raquo; '); 337 $('.orthanc-name').append($('<a>')
338 .addClass('ui-link')
339 .attr('href', 'explorer.html')
340 .text(s.Name)
341 .append(' &raquo; '));
357 } 342 }
358 } 343 }
359 }); 344 });
360 }); 345 });
361 346
415 $('li', target).remove(); 400 $('li', target).remove();
416 401
417 for (var i = 0; i < studies.length; i++) { 402 for (var i = 0; i < studies.length; i++) {
418 if (i == 0 || studies[i].MainDicomTags.StudyDate != studies[i - 1].MainDicomTags.StudyDate) 403 if (i == 0 || studies[i].MainDicomTags.StudyDate != studies[i - 1].MainDicomTags.StudyDate)
419 { 404 {
420 target.append('<li data-role="list-divider">{0}</li>'.format 405 target.append($('<li>')
421 (FormatDicomDate(studies[i].MainDicomTags.StudyDate))); 406 .attr('data-role', 'list-divider')
407 .text(FormatDicomDate(studies[i].MainDicomTags.StudyDate)));
422 } 408 }
423 409
424 target.append(FormatStudy(studies[i], '#study?uuid=' + studies[i].ID)); 410 target.append(FormatStudy(studies[i], '#study?uuid=' + studies[i].ID));
425 } 411 }
426 412
475 var target = $('#list-series'); 461 var target = $('#list-series');
476 $('li', target).remove(); 462 $('li', target).remove();
477 for (var i = 0; i < series.length; i++) { 463 for (var i = 0; i < series.length; i++) {
478 if (i == 0 || series[i].MainDicomTags.SeriesDate != series[i - 1].MainDicomTags.SeriesDate) 464 if (i == 0 || series[i].MainDicomTags.SeriesDate != series[i - 1].MainDicomTags.SeriesDate)
479 { 465 {
480 target.append('<li data-role="list-divider">{0}</li>'.format 466 target.append($('<li>')
481 (FormatDicomDate(series[i].MainDicomTags.SeriesDate))); 467 .attr('data-role', 'list-divider')
468 .text(FormatDicomDate(series[i].MainDicomTags.SeriesDate)));
482 } 469 }
470
483 target.append(FormatSeries(series[i], '#series?uuid=' + series[i].ID)); 471 target.append(FormatSeries(series[i], '#series?uuid=' + series[i].ID));
484 } 472 }
485 target.listview('refresh'); 473 target.listview('refresh');
486 474
487 currentPage = 'study'; 475 currentPage = 'study';
535 }); 523 });
536 } 524 }
537 } 525 }
538 526
539 527
528 function EscapeHtml(value)
529 {
530 var ENTITY_MAP = {
531 '&': '&amp;',
532 '<': '&lt;',
533 '>': '&gt;',
534 '"': '&quot;',
535 "'": '&#39;',
536 '/': '&#x2F;',
537 '`': '&#x60;',
538 '=': '&#x3D;'
539 };
540
541 return String(value).replace(/[&<>"'`=\/]/g, function (s) {
542 return ENTITY_MAP[s];
543 });
544 }
545
540 546
541 function ConvertForTree(dicom) 547 function ConvertForTree(dicom)
542 { 548 {
543 var result = []; 549 var result = [];
544 550
545 for (var i in dicom) { 551 for (var i in dicom) {
546 if (dicom[i] != null) { 552 if (dicom[i] != null) {
547 var label = i + '<span class="tag-name"> (<i>' + dicom[i]["Name"] + '</i>)</span>: '; 553 var label = (i + '<span class="tag-name"> (<i>' +
554 EscapeHtml(dicom[i]["Name"]) +
555 '</i>)</span>: ');
548 556
549 if (dicom[i]["Type"] == 'String') 557 if (dicom[i]["Type"] == 'String')
550 { 558 {
551 result.push({ 559 result.push({
552 label: label + '<strong>' + dicom[i]["Value"] + '</strong>', 560 label: label + '<strong>' + EscapeHtml(dicom[i]["Value"]) + '</strong>',
553 children: [] 561 children: []
554 }); 562 });
555 } 563 }
556 else if (dicom[i]["Type"] == 'TooLong') 564 else if (dicom[i]["Type"] == 'TooLong')
557 { 565 {
795 Sort(instances, function(x) { return x.IndexInSeries; }, true, false); 803 Sort(instances, function(x) { return x.IndexInSeries; }, true, false);
796 804
797 var images = []; 805 var images = [];
798 for (var i = 0; i < instances.length; i++) { 806 for (var i = 0; i < instances.length; i++) {
799 images.push([ '../instances/' + instances[i].ID + '/preview', 807 images.push([ '../instances/' + instances[i].ID + '/preview',
800 '{0}/{1}'.format(i + 1, instances.length) ]) 808 (i + 1).toString() + '/' + instances.length.toString() ])
801 } 809 }
802 810
803 jQuery.slimbox(images, 0, { 811 jQuery.slimbox(images, 0, {
804 overlayFadeDuration : 1, 812 overlayFadeDuration : 1,
805 resizeDuration : 1, 813 resizeDuration : 1,