mirror of https://github.com/CIRCL/lookyloo
Add tooltip for URLs
parent
678985c94a
commit
0ac38a0f2c
|
@ -6,15 +6,23 @@ import json
|
|||
from har2tree import CrawledTree
|
||||
from scrapysplashwrapper import crawl
|
||||
|
||||
from flask import Flask, render_template, request
|
||||
from flask import Flask, render_template, request, session
|
||||
from flask_bootstrap import Bootstrap
|
||||
|
||||
from glob import glob
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
import pickle
|
||||
import tempfile
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
app.secret_key = 'changeme'
|
||||
|
||||
if app.secret_key == 'changeme':
|
||||
raise Exception('FFS, please set a proper secret key...')
|
||||
|
||||
Bootstrap(app)
|
||||
app.config['BOOTSTRAP_SERVE_LOCAL'] = True
|
||||
app.debug = True
|
||||
|
@ -23,11 +31,25 @@ HAR_DIR = 'scraped'
|
|||
SPLASH = 'http://127.0.0.1:8050'
|
||||
|
||||
|
||||
@app.before_request
|
||||
def session_management():
|
||||
# make the session last indefinitely until it is cleared
|
||||
session.permanent = True
|
||||
|
||||
|
||||
def load_tree(report_dir):
|
||||
if session.get('tree'):
|
||||
# TODO delete file
|
||||
pass
|
||||
session.clear()
|
||||
har_files = sorted(glob(os.path.join(HAR_DIR, report_dir, '*.har')))
|
||||
ct = CrawledTree(har_files)
|
||||
ct.find_parents()
|
||||
ct.join_trees()
|
||||
temp = tempfile.NamedTemporaryFile(delete=False)
|
||||
pickle.dump(ct, temp)
|
||||
temp.close()
|
||||
session["tree"] = temp.name
|
||||
return ct.jsonify(), ct.start_time.isoformat(), ct.user_agent, ct.root_url
|
||||
|
||||
|
||||
|
@ -63,6 +85,25 @@ def get_report_dirs():
|
|||
return sorted(os.listdir(HAR_DIR), reverse=True)
|
||||
|
||||
|
||||
@app.route('/tree/hostname/<node_uuid>', methods=['GET'])
|
||||
def hostnode_details(node_uuid):
|
||||
with open(session["tree"], 'rb') as f:
|
||||
ct = pickle.load(f)
|
||||
hostnode = ct.root_hartree.get_host_node_by_uuid(node_uuid)
|
||||
urls = []
|
||||
for url in hostnode.urls:
|
||||
urls.append(url.jsonify())
|
||||
return json.dumps(urls)
|
||||
|
||||
|
||||
@app.route('/tree/url/<node_uuid>', methods=['GET'])
|
||||
def urlnode_details(node_uuid):
|
||||
with open(session["tree"], 'rb') as f:
|
||||
ct = pickle.load(f)
|
||||
urlnode = ct.root_hartree.get_url_node_by_uuid(node_uuid)
|
||||
return urlnode.jsonify()
|
||||
|
||||
|
||||
@app.route('/tree/<int:tree_id>', methods=['GET'])
|
||||
def tree(tree_id):
|
||||
report_dir = get_report_dirs()[tree_id]
|
||||
|
|
|
@ -49,3 +49,24 @@
|
|||
padding-bottom: 5px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
text-align: left;
|
||||
padding: 2px;
|
||||
font: 12px sans-serif;
|
||||
background: lightsteelblue;
|
||||
border: 0px;
|
||||
border-radius: 8px;
|
||||
z-index: 1;
|
||||
border: 2px solid;
|
||||
padding-top: 5px;
|
||||
padding-right: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.tooltip text {
|
||||
font: 15px sans-serif;
|
||||
z-index: 2;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,11 @@ var margin = {top: 20, right: 200, bottom: 30, left: 90},
|
|||
height = 10000 - margin.top - margin.bottom;
|
||||
|
||||
var node_width = 0;
|
||||
var node_height = 35;
|
||||
var node_height = 45;
|
||||
|
||||
var hostnode_tooltip = d3.select("body").append("div")
|
||||
.attr("class", "tooltip")
|
||||
.style("opacity", 0);
|
||||
|
||||
var init = d3.select("body").append("svg")
|
||||
.attr("width", width + margin.right + margin.left)
|
||||
|
@ -29,7 +33,12 @@ var background = init.append('rect')
|
|||
.attr('y', 0)
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.style('fill', "url(#backstripes)");
|
||||
.style('fill', "url(#backstripes)")
|
||||
.on('click', function(d) {
|
||||
hostnode_tooltip.transition()
|
||||
.duration(500)
|
||||
.style("opacity", 0);
|
||||
});
|
||||
|
||||
// append the svg object to the body of the page
|
||||
// appends a 'group' element to 'svg'
|
||||
|
@ -71,6 +80,41 @@ function getBB(selection) {
|
|||
})
|
||||
}
|
||||
|
||||
function urlnode_click(uuid) {
|
||||
var url = "url/" + uuid;
|
||||
d3.json(url, function(error, u) {
|
||||
if (error) throw error;
|
||||
console.log(u)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function hostnode_click(d) {
|
||||
// Modal display
|
||||
var url = "hostname/" + d.data.uuid;
|
||||
var pageX=d3.event.pageX;
|
||||
var pageY=d3.event.pageY;
|
||||
hostnode_tooltip.selectAll("ul").remove();
|
||||
d3.json(url, function(error, urls) {
|
||||
if (error) throw error;
|
||||
hostnode_tooltip.transition()
|
||||
.duration(200)
|
||||
.style("opacity", .9)
|
||||
.style("left", (pageX) + "px")
|
||||
.style("top", (pageY - 28) + "px");
|
||||
var list = hostnode_tooltip.append('ul')
|
||||
.attr("class", "list-group");
|
||||
urls.forEach(function(url){
|
||||
jdata = JSON.parse(url)
|
||||
var entry = list.append('li')
|
||||
.attr("class", "list-group-item")
|
||||
.attr("url_uuid", jdata['uuid'])
|
||||
.text(jdata['name'])
|
||||
.on('click', urlnode_click(jdata['uuid']));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function update(source) {
|
||||
|
||||
// reinitialize max_depth
|
||||
|
@ -101,8 +145,7 @@ function update(source) {
|
|||
.attr('class', 'node')
|
||||
.attr("transform", function(d) {
|
||||
return "translate(" + source.y0 + "," + source.x0 + ")";
|
||||
})
|
||||
.on('click', click);
|
||||
});
|
||||
|
||||
// Add Circle for the nodes
|
||||
nodeEnter.append('circle')
|
||||
|
@ -110,11 +153,13 @@ function update(source) {
|
|||
.attr('r', 1e-6)
|
||||
.style("fill", function(d) {
|
||||
return d._children ? "lightsteelblue" : "#fff";
|
||||
});
|
||||
})
|
||||
.on('click', click);
|
||||
|
||||
// Avoid hiding the content after the circle
|
||||
var nodeContent = nodeEnter
|
||||
.append('svg')
|
||||
.attr('height',node_height)
|
||||
.attr('x', 10)
|
||||
.attr('y', -20);
|
||||
|
||||
|
@ -128,7 +173,8 @@ function update(source) {
|
|||
.text(function(d) {
|
||||
d.data.total_width = 0; // reset total_width
|
||||
return d.data.name;
|
||||
});
|
||||
})
|
||||
.on('click', hostnode_click);
|
||||
|
||||
// This value has to be set once for all for the whole tree and cannot be updated
|
||||
// on click as clicking only updates a part of the tree
|
||||
|
@ -361,4 +407,5 @@ function update(source) {
|
|||
}
|
||||
update(d);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue