From 68204c268694c7d35c00c56336dde93d9eb8ae48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Tue, 26 Sep 2023 18:08:04 +0200 Subject: [PATCH] new: Support for rendered nodes with HTML and downloaded file --- lookyloo/lookyloo.py | 12 ++- website/web/sri.txt | 3 +- website/web/static/download.png | Bin 0 -> 743 bytes website/web/static/tree.js | 119 ++++++++-------------- website/web/templates/hostname_popup.html | 36 ++++--- 5 files changed, 76 insertions(+), 94 deletions(-) create mode 100644 website/web/static/download.png diff --git a/lookyloo/lookyloo.py b/lookyloo/lookyloo.py index 5132fb19..6d548822 100644 --- a/lookyloo/lookyloo.py +++ b/lookyloo/lookyloo.py @@ -913,9 +913,15 @@ class Lookyloo(): error_img: Path = get_homedir() / 'website' / 'web' / 'static' / 'error_screenshot.png' to_thumbnail = Image.open(error_img) except UnidentifiedImageError as e: - # The capture probably doesn't have a screenshot at all, no need to log that as a warning. - self.logger.debug(f'Unable to generate the screenshot thumbnail of {capture_uuid}: {e}.') - error_img = get_homedir() / 'website' / 'web' / 'static' / 'error_screenshot.png' + # We might have a direct download link, and no screenshot. Assign the thumbnail accordingly. + try: + filename, data = self.get_data(capture_uuid) + self.logger.info(f'{capture_uuid} is is a download link, set thumbnail.') + error_img = get_homedir() / 'website' / 'web' / 'static' / 'download.png' + except Exception: + # The capture probably doesn't have a screenshot at all, no need to log that as a warning. + self.logger.debug(f'Unable to generate the screenshot thumbnail of {capture_uuid}: {e}.') + error_img = get_homedir() / 'website' / 'web' / 'static' / 'error_screenshot.png' to_thumbnail = Image.open(error_img) to_thumbnail.thumbnail(size) diff --git a/website/web/sri.txt b/website/web/sri.txt index 9459e922..afe0a2ae 100644 --- a/website/web/sri.txt +++ b/website/web/sri.txt @@ -12,6 +12,7 @@ "datatables.min.js": "8ua4soo5FXHNIbuX9zDsc7L/JOQE5q5tqJ+GOofQN4/8sDnJ3l5zNj54dIWKoppTfrthPBjetweH/PPf97P/bg==", "down.jpg": "LHRHJ5yCaSjNcDfEoChGIfh7K5HrMYbaGn7EOlxgZ8GoLIwb0nFBkpoOMG9gMHA/pBX2skkXMukvKJC6P6FBGg==", "down_left.jpg": "UwHkJaZGayY1LewuFM3bJHQCUPG1vYyrVeiGG5mCM9MD9FtAhdbD4hBY3JZNDWv93CXeEAbxL1kqEeHTKnyquQ==", + "download.png": "J8y1gDKURf3AhgYDuqCnfaVLKRG2MI6k37xSvR5pJBAZ3aNmA6dDw6+UGf65hLBN3eGksaBJUeroBW/LDlUTqQ==", "download.svg": "8Dmi0Z56+uecmE4mW03JEP6IdxeFFaQcUgdnqAzrYbVLED0YxnQTuiKqn5qeLZNlK1XQpGnDC47YzHvE7zAKig==", "empty.svg": "6tfMLNzDFV9P6t1rC2tDRQtOGzrxi/VtIBc8aV0jo4i3u+dn1fIe3/fySBFA6z13n+XjISF5bTRUNBsN3LWinQ==", "error_screenshot.png": "IkUKnQ47PYYreukA7Byvx+5ACkcCvqk+jYD0GZoQznsD9qDPWrKAMZxlIku7G3Re19vehIlYawep/THcV/ruTA==", @@ -35,7 +36,7 @@ "stats.css": "/kY943FwWBTne4IIyf7iBROSfbGd82TeBicEXqKkRwawMVRIvM/Pk5MRa7okUyGIxaDjFQGmV/U1vy+PhN6Jbw==", "stats_graph.js": "0OEouA6NAxLG2wMd7D2vtGoMrXKna7My98Euc6ecyfdO4/6mIJS87vzISOS4zSZ8u4ehpa+p7E0nWhsXXE7H/Q==", "tree.css": "R/pWQnE8kMacDrzGy/NpA1tJoHAZpOjFiX6dqtBe+PqAnhYMn1CIQzODh8Ifvh6hBTPLRWX3bsabfEvfaI7Z6A==", - "tree.js": "jvaOjno3+4vX3Caam4PSXeZf4G3o9uh8whfOsND2zTfDxLQwo1P+umNQQx7JDPsf5Fan7tHDGOnBTupb9CjLlA==", + "tree.js": "67R8M7znCyonDtD3GdqyF7uq4p18SLWB/BSwR1dK45M63SHmksENlBEPl9WS3OepzmnfW8q00sH/UBEWqGE8rQ==", "up.jpg": "d1ljZJ9f5JekyM6RLFFH2Ua44j6neiQBdUIXOenRTjGppQr3JaeglpQIH6BjPCJL177+TH52U3UIRNS5YAyKIg==", "up_right.jpg": "OMmz+n+MxR34P8/fn5t4DkqKqdJRzQbXQ7fAi2lhkZIJGhVs2vIyY1f2hpYoBxDAX1OcYsSE2lqIR2vXNDGZsA==", "video.png": "gJtmkfr8I1Kw43pYEKjg6CAjgmhl1vIBKBQ3ZkxCu3wvxQm+6kf93iLrrFiY2WuiXzxEn2Leu52GJzmVN5id0g==", diff --git a/website/web/static/download.png b/website/web/static/download.png new file mode 100644 index 0000000000000000000000000000000000000000..953917250cdeec8d110be75ed84944b7bba9377a GIT binary patch literal 743 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9GAd?3ttX4}GMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&3=B*qo-U3d6?5LsKAk1vDAKmy zxN!N7aK~8Ayrb>`dstEurs#wm+4_jN^K>QGX2JW8f_FO9q#pUHRCHSE1?k#O-TB2J z^y~Q}zaHxBT4wQe{`%Fss+Lusx%+ee)nMs>)l31>0p4r}&akC@Q$6DF%(+z1WOMF= z9E(41-aS{bYhiF>D=QEFvEn+zR>rMbh_c*5q1 z)vMZPai%-Wzqr|?RP90{Tfvtz=9a%t)-3lD;khrHTp?d!pwIB&UQ5jP$yI(cbL2QK zGfdI4<8D|dpvI^XStHReS77b3UZkav!ApdH4P&{}8pb^tyx;a*@#$a`iT>Pf?KQcc zW5e~V8>bD9s>Pm6W|&((f%R|C73;4_Q!3Sby60ru$!Xg+SSqGy$8jxCZdr5mG~=0u z344DmWiViLn)qlsn}fk6b~{FS&x(9|eTHlelcI~#vzLnOU%0DiKgZHARtA|Z(r4b> zaFPtZ?a2Q_%5?MII}88- literal 0 HcmV?d00001 diff --git a/website/web/static/tree.js b/website/web/static/tree.js index fda8f2ec..7650b17c 100644 --- a/website/web/static/tree.js +++ b/website/web/static/tree.js @@ -582,82 +582,53 @@ function update(root, computed_node_width=0) { const thumbnail_size = 64; if (d.data.contains_rendered_urlnode) { center_node = d.data.uuid; - if (d.data.downloaded_filename) { - d3.select(this).append("svg").append('rect') - .attr('x', selected_node_bbox.width/3) - .attr('y', node_height - 3) - .attr('width', thumbnail_size) - .attr('height', thumbnail_size) - .attr('fill', 'white') - .attr('stroke', 'black'); - + if (favicon) { d3.select(this).append('image') - .attr('x', selected_node_bbox.width/3) - .attr('y', node_height - 3) - .attr('id', 'screenshot_thumbnail') - .attr("width", thumbnail_size) - .attr("height", thumbnail_size) - .attr("xlink:href", '/static/download.svg') - .on('mouseover', (event, d) => { - d3.select('#tooltip') - .style('opacity', 1) - .style('left', `${event.pageX + 10}px`) - .style('top', `${event.pageY + 10}px`) - .text(`Contains the downloaded file (${d.data.downloaded_filename}).`); - }) - .on('mouseout', (event, d) => { - d3.select('#tooltip').style('opacity', 0) - }); - } else { - if (favicon) { - d3.select(this).append('image') - .attr('x', selected_node_bbox.width/6) - .attr('y', node_height - 1) - .attr('id', 'favicon') - .attr("width", 32) - .attr("height", 32) - .attr("xlink:href", `data:image/png;base64,${favicon}`) - .attr('cursor', 'pointer') - .on('mouseover', (event, d) => { - d3.select('#tooltip') - .style('opacity', 1) - .style('left', `${event.pageX + 10}px`) - .style('top', `${event.pageY + 10}px`) - .text('Potential favicon.'); - }); - } - if (screenshot_thumbnail) { - d3.select(this).append("svg").append('rect') - .attr('x', selected_node_bbox.width/2) - .attr('y', node_height - 3) - .attr('width', thumbnail_size) - .attr('height', thumbnail_size) - .attr('fill', 'white') - .attr('stroke', 'black'); - - d3.select(this).append('image') - .attr('x', selected_node_bbox.width/2) - .attr('y', node_height - 3) - .attr('id', 'screenshot_thumbnail') - .attr("width", thumbnail_size) - .attr("height", thumbnail_size) - .attr("xlink:href", `data:image/png;base64,${screenshot_thumbnail}`) - .attr('cursor', 'pointer') - .on('mouseover', (event, d) => { - d3.select('#tooltip') - .style('opacity', 1) - .style('left', `${event.pageX + 10}px`) - .style('top', `${event.pageY + 10}px`) - .text('Contains the URL rendered in the browser.'); - }) - .on('click', (event, d) => { - $("#screenshotModal").modal('toggle'); - }) - .on('mouseout', (event, d) => { - d3.select('#tooltip').style('opacity', 0) - }); - } + .attr('x', selected_node_bbox.width/6) + .attr('y', node_height - 1) + .attr('id', 'favicon') + .attr("width", 32) + .attr("height", 32) + .attr("xlink:href", `data:image/png;base64,${favicon}`) + .attr('cursor', 'pointer') + .on('mouseover', (event, d) => { + d3.select('#tooltip') + .style('opacity', 1) + .style('left', `${event.pageX + 10}px`) + .style('top', `${event.pageY + 10}px`) + .text('Potential favicon.'); + }); } + d3.select(this).append("svg").append('rect') + .attr('x', selected_node_bbox.width/2) + .attr('y', node_height - 3) + .attr('width', thumbnail_size) + .attr('height', thumbnail_size) + .attr('fill', 'white') + .attr('stroke', 'black'); + + d3.select(this).append('image') + .attr('x', selected_node_bbox.width/2) + .attr('y', node_height - 3) + .attr('id', 'screenshot_thumbnail') + .attr("width", thumbnail_size) + .attr("height", thumbnail_size) + .attr("xlink:href",`data:image/png;base64,${screenshot_thumbnail}`) + .attr('cursor', 'pointer') + .on('mouseover', (event, d) => { + d3.select('#tooltip') + .data(d) + .style('opacity', 1) + .style('left', `${event.pageX + 10}px`) + .style('top', `${event.pageY + 10}px`) + .text(d => d.data.downloaded_filename ? 'Contains the URL rendered in the browser. It also downloaded a file.': 'Contains the URL rendered in the browser.'); + }) + .on('click', (event, d) => { + $("#screenshotModal").modal('toggle'); + }) + .on('mouseout', (event, d) => { + d3.select('#tooltip').style('opacity', 0) + }); }; const http_icon_size = 24; diff --git a/website/web/templates/hostname_popup.html b/website/web/templates/hostname_popup.html index 012b7592..f2ecfe2d 100644 --- a/website/web/templates/hostname_popup.html +++ b/website/web/templates/hostname_popup.html @@ -241,17 +241,20 @@ {{ popup_icons_response(url['url_object'], tree_uuid) }} {% if url['url_object'].downloaded_filename %} +
{% if has_pandora %} -
Downloaded file: {{url['url_object'].downloaded_filename}} ({{sizeof_fmt(url['url_object'].downloaded_file.getbuffer().nbytes)}})
- {{ pandora_submit() }} +
Downloaded file: {{url['url_object'].downloaded_filename}} ({{sizeof_fmt(url['url_object'].downloaded_file.getbuffer().nbytes)}})
+ {{ pandora_submit() }} {% else %} - - Download {{url['url_object'].downloaded_filename}} - ({{sizeof_fmt(url['url_object'].downloaded_file.getbuffer().nbytes)}}) + + Download {{url['url_object'].downloaded_filename}} + ({{sizeof_fmt(url['url_object'].downloaded_file.getbuffer().nbytes)}}) {% endif%} +
+ {% endif%} - {% else %} - {% if url['url_object'].rendered_html %} + {% if url['url_object'].rendered_html %} +
Download rendered HTML page ({{ sizeof_fmt(url['url_object'].rendered_html.getbuffer().nbytes)}}) @@ -259,18 +262,19 @@ Download URLs in rendered HTML page - {% endif %} -
+
+ {% endif %} + +
{% if url['url_object'].empty_response %} - Empty body. + Empty HTML body. {% else %} - {{ ressource_legitimacy_details(url['legitimacy'], url['url_object'].body.getbuffer().nbytes) }} - {% if has_pandora %} - {{ pandora_submit(url['url_object'].uuid) }} + {{ ressource_legitimacy_details(url['legitimacy'], url['url_object'].body.getbuffer().nbytes) }} + {% if has_pandora %} + {{ pandora_submit(url['url_object'].uuid) }} + {% endif %} {% endif %} - {% endif %} -
- {%endif%} +
{% if url['known_content'] %}