diff --git a/poetry.lock b/poetry.lock index 46b2bf72..8e98c988 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1150,7 +1150,7 @@ misp = ["python-magic", "pydeep"] [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "f34e831afe0cbf89d79c5f69d1292b2ec36929d7b752f5887ecd48d1212fb7a5" +content-hash = "e8e3df6d57b274c350ed4df996c608cdf1ecb06ffd3f985c3ed0912a42da310b" [metadata.files] aiohttp = [ diff --git a/website/web/__init__.py b/website/web/__init__.py index d5093b77..38d0f109 100644 --- a/website/web/__init__.py +++ b/website/web/__init__.py @@ -554,6 +554,7 @@ def tree(tree_uuid: str, node_uuid: Optional[str]=None): blur_screenshot=blur_screenshot, urlnode_uuid=hostnode_to_highlight, auto_trigger_modules=auto_trigger_modules, confirm_message=confirm_message if confirm_message else 'Tick to confirm.', + parent_uuid=cache.parent, has_redirects=True if cache.redirects else False) except NoValidHarFile as e: diff --git a/website/web/static/tree.js b/website/web/static/tree.js index 075b9827..7ff7abce 100644 --- a/website/web/static/tree.js +++ b/website/web/static/tree.js @@ -59,6 +59,98 @@ root.y0 = 0; let tree = d3.tree(); update(root); +if (parent_uuid != null) { + node_container.append('rect') + .attr("rx", 6) + .attr("ry", 6) + .attr("transform", `translate(${root.y - 70}, ${root.x - 150})`) + .attr('width', 150) + .attr('height', 65) + .style("opacity", "0.5") + .attr("stroke", 'black') + .attr('stroke-opacity', "0.8") + .attr("stroke-width", "2") + .attr("stroke-linecap", "round") + .attr("fill", "white") + + let text = node_container + .data([ + { + "line1": 'This capture was triggered', + "line2": 'from a previous capture.', + "line3": 'See the parent', + "parent_uuid": parent_uuid + } + ]) + .append('text') + .attr("dy", "0em") + .style("font-size", "12px") + .attr("transform", `translate(${root.y - 67}, ${root.x - 135})`); + + text + .append('tspan') + .text(d => d.line1); + + text + .append('tspan') + .attr("x", 8) + .attr("dy", 18) + .text(d => d.line2); + + text + .append('tspan') + .attr("x", 30) + .attr("dy", 20) + .text(d => d.line3) + .style('fill', '#0000EE') + .attr('cursor', 'pointer') + .on('click', (event, d) => { openTreeInNewTab(d.parent_uuid) } ); + + let line_arrow = node_container + .append('g') + .attr('cursor', 'pointer') + .attr("transform", `translate(${root.y}, ${root.x})`); + + let line = d3.line() + // Other options: http://bl.ocks.org/d3indepth/raw/b6d4845973089bc1012dec1674d3aff8/ + //.curve(d3.curveCardinal) + .curve(d3.curveBundle) + .x(point => point.lx) + .y(point => point.ly); + + let line_tip = d3.symbol() + .type(d3.symbolTriangle) + .size(200); + + line_arrow + .append("path") + .attr('stroke-opacity', "0.7") + .attr("stroke-width", "2") + .attr("stroke", "black") + .attr("fill", "none") + .data([{ + source: {x: 0, y: -85}, + target: {x: 50, y: -node_height/1.12} + }]) + .attr("class", "line") + .attr("d", d => line( + [{lx: d.source.x, ly: d.source.y}, + {lx: d.target.x, ly: d.source.y}, + {lx: d.target.x, ly: d.target.y} + ]) + ); + + line_arrow + .append("path") + .attr("d", line_tip) + .attr("stroke", 'black') + .attr('stroke-opacity', "0.8") + .style('stroke-width', '1.5') + .attr("fill-opacity", '0') + .attr("transform", `translate(50, -${node_height/1.3}) rotate(60)`); +}; + + function openTreeInNewTab(capture_uuid, hostnode_uuid=null) { let url = `/tree/${capture_uuid}`; if (hostnode_uuid != null) { diff --git a/website/web/templates/tree.html b/website/web/templates/tree.html index 3a540865..0b0ea05e 100644 --- a/website/web/templates/tree.html +++ b/website/web/templates/tree.html @@ -116,6 +116,7 @@ var screenshot_thumbnail = "{{ screenshot_thumbnail }}"; var enable_bookmark = {{ enable_bookmark|tojson }}; var treeData = {{ tree_json | safe }}; + var parent_uuid = {{ parent_uuid|tojson }}; var capture_starttime = new Date(Date.parse("{{ start_time }}")); window.addEventListener('DOMContentLoaded', (event) => { document.getElementById("start_time").innerHTML =