Mercurial > hg > orthanc
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> » '); | 337 $('.orthanc-name').append($('<a>') |
338 .addClass('ui-link') | |
339 .attr('href', 'explorer.html') | |
340 .text(s.Name) | |
341 .append(' » ')); | |
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 '&': '&', | |
532 '<': '<', | |
533 '>': '>', | |
534 '"': '"', | |
535 "'": ''', | |
536 '/': '/', | |
537 '`': '`', | |
538 '=': '=' | |
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, |