diff --git a/website/web/__init__.py b/website/web/__init__.py index 9942d5c0..aabb4bbd 100644 --- a/website/web/__init__.py +++ b/website/web/__init__.py @@ -397,29 +397,57 @@ def get_all_urls(capture_uuid: str, /) -> dict[str, dict[str, int | list[URLNode return to_return -def get_hostname_investigator(hostname: str, offset: int | None=None, limit: int | None=None) -> tuple[int, list[tuple[str, str, str, datetime, set[str]]]]: +def get_hostname_investigator(hostname: str, offset: int | None=None, limit: int | None=None) -> tuple[int, list[tuple[str, str, str, datetime, list[tuple[str, str]]]]]: '''Returns all the captures loading content from that hostname, used in the web interface.''' total, entries = get_indexing(flask_login.current_user).get_captures_hostname(hostname=hostname, offset=offset, limit=limit) cached_captures = lookyloo.sorted_capture_cache([uuid for uuid, _ in entries]) - return total, [(cache.uuid, cache.title, cache.redirects[-1], cache.timestamp, - get_indexing(flask_login.current_user).get_capture_hostname_nodes(cache.uuid, hostname) - ) for cache in cached_captures] + _captures = [(cache.uuid, cache.title, cache.redirects[-1], cache.timestamp, get_indexing(flask_login.current_user).get_capture_hostname_nodes(cache.uuid, hostname)) for cache in cached_captures] + captures = [] + for capture_uuid, capture_title, landing_page, capture_ts, nodes in _captures: + nodes_info: list[tuple[str, str]] = [] + for urlnode_uuid in nodes: + try: + urlnode = lookyloo.get_urlnode_from_tree(capture_uuid, urlnode_uuid) + nodes_info.append((urlnode.name, urlnode_uuid)) + except IndexError: + continue + captures.append((capture_uuid, capture_title, landing_page, capture_ts, nodes_info)) + return total, captures -def get_url_investigator(url: str, offset: int | None=None, limit: int | None=None) -> tuple[int, list[tuple[str, str, str, datetime, set[str]]]]: +def get_url_investigator(url: str, offset: int | None=None, limit: int | None=None) -> tuple[int, list[tuple[str, str, str, datetime, list[tuple[str, str]]]]]: '''Returns all the captures loading content from that url, used in the web interface.''' total, entries = get_indexing(flask_login.current_user).get_captures_url(url=url, offset=offset, limit=limit) cached_captures = lookyloo.sorted_capture_cache([uuid for uuid, _ in entries]) - return total, [(cache.uuid, cache.title, cache.redirects[-1], cache.timestamp, - get_indexing(flask_login.current_user).get_capture_url_nodes(cache.uuid, url) - ) for cache in cached_captures] + _captures = [(cache.uuid, cache.title, cache.redirects[-1], cache.timestamp, get_indexing(flask_login.current_user).get_capture_url_nodes(cache.uuid, url)) for cache in cached_captures] + captures = [] + for capture_uuid, capture_title, landing_page, capture_ts, nodes in _captures: + nodes_info: list[tuple[str, str]] = [] + for urlnode_uuid in nodes: + try: + urlnode = lookyloo.get_urlnode_from_tree(capture_uuid, urlnode_uuid) + nodes_info.append((urlnode.name, urlnode_uuid)) + except IndexError: + continue + captures.append((capture_uuid, capture_title, landing_page, capture_ts, nodes_info)) + return total, captures -def get_cookie_name_investigator(cookie_name: str, offset: int | None=None, limit: int | None=None) -> tuple[int, list[tuple[str, str, str, datetime, set[str]]]]: +def get_cookie_name_investigator(cookie_name: str, offset: int | None=None, limit: int | None=None) -> tuple[int, list[tuple[str, str, str, datetime, list[tuple[str, str]]]]]: '''Returns all the captures related to a cookie name entry, used in the web interface.''' total, entries = get_indexing(flask_login.current_user).get_captures_cookies_name(cookie_name=cookie_name) cached_captures = lookyloo.sorted_capture_cache([uuid for uuid, _ in entries]) - captures = [(cache.uuid, cache.title, cache.redirects[-1], cache.timestamp, get_indexing(flask_login.current_user).get_capture_cookie_name_nodes(cache.uuid, cookie_name)) for cache in cached_captures] + _captures = [(cache.uuid, cache.title, cache.redirects[-1], cache.timestamp, get_indexing(flask_login.current_user).get_capture_cookie_name_nodes(cache.uuid, cookie_name)) for cache in cached_captures] + captures = [] + for capture_uuid, capture_title, landing_page, capture_ts, nodes in _captures: + nodes_info: list[tuple[str, str]] = [] + for urlnode_uuid in nodes: + try: + urlnode = lookyloo.get_urlnode_from_tree(capture_uuid, urlnode_uuid) + nodes_info.append((urlnode.name, urlnode_uuid)) + except IndexError: + continue + captures.append((capture_uuid, capture_title, landing_page, capture_ts, nodes_info)) return total, captures @@ -445,20 +473,21 @@ def get_favicon_investigator(favicon_sha512: str, offset: int | None=None, limit return total, captures -def get_hhh_investigator(hhh: str, offset: int | None=None, limit: int | None=None) -> tuple[int, list[tuple[str, str, datetime, str, str]]]: +def get_hhh_investigator(hhh: str, offset: int | None=None, limit: int | None=None) -> tuple[int, list[tuple[str, str, str, datetime, list[tuple[str, str]]]]]: '''Returns all the captures related to a cookie name entry, used in the web interface.''' total, entries = get_indexing(flask_login.current_user).get_captures_hhhash(hhh, offset=offset, limit=limit) cached_captures = lookyloo.sorted_capture_cache([uuid for uuid, _ in entries]) + _captures = [(cache.uuid, cache.title, cache.redirects[-1], cache.timestamp, get_indexing(flask_login.current_user).get_capture_hhhash_nodes(cache.uuid, hhh)) for cache in cached_captures] captures = [] - for cache in cached_captures: - if not cache: - continue - for urlnode_uuid in get_indexing(flask_login.current_user).get_capture_hhhash_nodes(cache.uuid, hhh): + for capture_uuid, capture_title, landing_page, capture_ts, nodes in _captures: + nodes_info: list[tuple[str, str]] = [] + for urlnode_uuid in nodes: try: - urlnode = lookyloo.get_urlnode_from_tree(cache.uuid, urlnode_uuid) + urlnode = lookyloo.get_urlnode_from_tree(capture_uuid, urlnode_uuid) + nodes_info.append((urlnode.name, urlnode_uuid)) except IndexError: continue - captures.append((cache.uuid, cache.title, cache.timestamp, urlnode.hostnode_uuid, urlnode.name)) + captures.append((capture_uuid, capture_title, landing_page, capture_ts, nodes_info)) return total, captures @@ -1929,15 +1958,15 @@ def add_context(tree_uuid: str, node_uuid: str) -> WerkzeugResponse | None: return None -def __prepare_node_view(capture_uuid: str, nodes: set[str], from_popup: bool=False) -> str: +def __prepare_node_view(capture_uuid: str, nodes: list[tuple[str, str]], from_popup: bool=False) -> str: to_return = f'The capture contains this value in {len(nodes)} nodes, click below to see them on the tree:' to_return += '' return to_return @@ -1949,20 +1978,22 @@ def post_table(table_name: str, value: str) -> Response: draw = request.form.get('draw', type=int) start = request.form.get('start', type=int) length = request.form.get('length', type=int) - captures: list[tuple[str, str, datetime, str, str]] | list[tuple[str, str, str, datetime, set[str]]] | list[tuple[str, str, str, datetime]] + captures: list[tuple[str, str, datetime, str, str]] | list[tuple[str, str, str, datetime, list[tuple[str, str]]]] | list[tuple[str, str, str, datetime]] if table_name == 'HHHDetailsTable': hhh = value.strip() total, captures = get_hhh_investigator(hhh, offset=start, limit=length) prepared_captures = [] - for capture_uuid, title, capture_time, hostnode_uuid, url in captures: + for capture_uuid, title, landing_page, capture_time, nodes in captures: + _nodes = __prepare_node_view(capture_uuid, nodes, from_popup) to_append = { 'capture_time': capture_time.isoformat(), - 'url': f"""{url}""" + 'landing_page': f"""{landing_page}""" } if from_popup: - to_append['capture_title'] = f""" {title}""" + to_append['capture_title'] = f""" {title}""" else: - to_append['capture_title'] = f"""{title}""" + to_append['capture_title'] = f"""{title}""" + to_append['capture_title'] += f'
{_nodes}' prepared_captures.append(to_append) return jsonify({'draw': draw, 'recordsTotal': total, 'recordsFiltered': total, 'data': prepared_captures}) @@ -1991,7 +2022,7 @@ def post_table(table_name: str, value: str) -> Response: for capture_uuid, title, capture_time, hostnode_uuid, url in captures: to_append = { 'capture_time': capture_time.isoformat(), - 'url': f"""{url}""" + 'landing_page': f"""{url}""" } if from_popup: to_append['capture_title'] = f""" {title}""" @@ -2042,12 +2073,16 @@ def post_table(table_name: str, value: str) -> Response: total, captures = get_hostname_investigator(value.strip(), offset=start, limit=length) prepared_captures = [] for capture_uuid, title, landing_page, capture_time, nodes in captures: - _nodes = __prepare_node_view(capture_uuid, nodes) + _nodes = __prepare_node_view(capture_uuid, nodes, from_popup) to_append = { 'capture_time': capture_time.isoformat(), - 'capture_title': f"""{title}
{_nodes}""", 'landing_page': f"""{landing_page}""" } + if from_popup: + to_append['capture_title'] = f"""{title}""" + else: + to_append['capture_title'] = f"""{title}""" + to_append['capture_title'] += f'
{_nodes}' prepared_captures.append(to_append) return jsonify({'draw': draw, 'recordsTotal': total, 'recordsFiltered': total, 'data': prepared_captures}) @@ -2056,12 +2091,16 @@ def post_table(table_name: str, value: str) -> Response: total, captures = get_url_investigator(url, offset=start, limit=length) prepared_captures = [] for capture_uuid, title, landing_page, capture_time, nodes in captures: - _nodes = __prepare_node_view(capture_uuid, nodes) + _nodes = __prepare_node_view(capture_uuid, nodes, from_popup) to_append = { 'capture_time': capture_time.isoformat(), - 'capture_title': f"""{title}
{_nodes}""", 'landing_page': f"""{landing_page}""" } + if from_popup: + to_append['capture_title'] = f"""{title}""" + else: + to_append['capture_title'] = f"""{title}""" + to_append['capture_title'] += f'
{_nodes}' prepared_captures.append(to_append) return jsonify({'draw': draw, 'recordsTotal': total, 'recordsFiltered': total, 'data': prepared_captures}) return jsonify({}) diff --git a/website/web/sri.txt b/website/web/sri.txt index e806730c..9533aad0 100644 --- a/website/web/sri.txt +++ b/website/web/sri.txt @@ -20,7 +20,7 @@ "favicon.ico": "KOmrfwRbOQqhhwSeBkNpMRAxSVMmmLg+2kRMg9iSv7OWjE9spJc7x4MKB4AE/hi0knaV7UBVctAU6XZ7AC72ZA==", "font.png": "RwoQkj9dT9SLUL2F7cAA16Nat9t2hDb58eQlHF9ThUar829p0INUXG+5XuDaFOC8SsmCZK5vw2f+YAQ6mLC1Qw==", "generic.css": "Sh/BcxFMLYYaLdCluVt9efGvJ9CF5d+YJ7lkL2M24PRGu8VZHI9lJiUlFObIocjQgwss3Ve2U5cUAE5WiAdpQQ==", - "generic.js": "pGtPn+FrS0QgPQq793fcOQ1gFQvt2nhXR34HgbjHAp+BsLYxFhCaOnrBWgsH8VRaR58hvQvyMn2ahI5s6C+s9w==", + "generic.js": "zalLApmHMQrFs+7aj5UCXHSuqLDr2IgOWgBtzDIRpUsO7JVcg4mxtI3lQNN/RwLvuL/Q5AOkDRMeLFiTtFptCA==", "hostname_popup.js": "froqRK2HEphJSU++PkPfEvaztrzH05QwZ4q2wEBAL6JDinpOCZqW9GMMV6oBhpTmyu5zfsz6ZpqrfaB0C0iIwg==", "html.png": "T7pZrb8MMDsA/JV/51hu+TOglTqlxySuEVY0rpDjTuAEyhzk2v+W4kYrj7vX+Tp3n2d2lvVD08PwhCG62Yfbzg==", "ifr.png": "rI5YJypmz1QcULRf9UaOYSqV4tPUSxUdLAycoYzCwywt4Pw4eWzBg9SUr769VyIimoiIyJR+aNuoIA4p5WO2fQ==", @@ -33,7 +33,7 @@ "loader.gif": "ZZKD5vLSKBWKeUpa2KI9qheUJ49iTI/UULmVU/AX28fBfH00K3lLc2v5pVJZ4qXG1BbB13LTXzRKKU35H2XfNg==", "lookyloo.jpeg": "i6wBj8CsIM5YAQLEMQfhs3CNOSKkErF8AMqqM6ZygSwCyQgv9CU8xt94veMZhM/ufBWoz7kAXmR+yywmxsTxug==", "redirect.png": "PAjzlPV97rEFvH55mG1ZC9wRl98be3yMeX/nENuFkJcds6/AXgSR2ig/QyPULgobSnNgiYieLVWY/oqsgeywrQ==", - "render_tables.js": "2J/KmrgCb6RG760S9ykgD1VzftrSXcQVE1HlPJ6wCDU35KEg3qPEu847VT3w7MSkGZ/1xO0n6OaTskpagRwAcA==", + "render_tables.js": "dx/SKBsKQK/dKAUZbqO3+/4YllmCyVXLuEm0uGaWbI6U5RqysUKMDmKFhe0+YI4CfNZV6AtC4NDdsVCRiKD6kA==", "secure.svg": "H8ni7t0d60nCJDVGuZpuxC+RBy/ipAjWT627D12HlZGg6LUmjSwPTQTUekm3UJupEP7TUkhXyq6WHc5gy7QBjg==", "stats.css": "/kY943FwWBTne4IIyf7iBROSfbGd82TeBicEXqKkRwawMVRIvM/Pk5MRa7okUyGIxaDjFQGmV/U1vy+PhN6Jbw==", "stats_graph.js": "S/sMNQK1UMMLD0xQeEa7sq3ce8o6oPxwxGlyKVtaHOODjair86dbBDm7cu6pa/elMRDJT1j09jEFjWp+5GbhTw==", diff --git a/website/web/static/generic.js b/website/web/static/generic.js index 1bda2690..f77bf36b 100644 --- a/website/web/static/generic.js +++ b/website/web/static/generic.js @@ -66,6 +66,12 @@ function newTabClickListener() { })); }; +function downloadFaviconListener() { + document.querySelectorAll(".downloadFaviconButton").forEach(el => el.addEventListener('click', event => { + downloadBase64File(el.dataset.mimetype, el.dataset.b64favicon, el.dataset.filename); + })) +}; + document.addEventListener("DOMContentLoaded", () => { document.querySelectorAll('.goBack').forEach(el => el.addEventListener('click', event => { diff --git a/website/web/static/render_tables.js b/website/web/static/render_tables.js index 580dfef5..295b560c 100644 --- a/website/web/static/render_tables.js +++ b/website/web/static/render_tables.js @@ -5,6 +5,7 @@ processing: true, serverSide: true, retrieve: true, + ordering: false, drawCallback: function (settings) { newTabClickListener() }, ajax: { url: `/tables/HHHDetailsTable/${hhh}${window.location.search}`, @@ -13,7 +14,7 @@ columns : [ { data: 'capture_time', width: '20%', render: DataTable.render.datetime_with_tz() }, { data: 'capture_title', width: '40%' }, - { data: 'url', width: '40%' } + { data: 'landing_page', width: '40%' } ], }) } @@ -23,6 +24,7 @@ processing: true, serverSide: true, retrieve: true, + ordering: false, drawCallback: function (settings) { newTabClickListener() }, ajax: { url: `/tables/bodyHashDetailsTable/${bodyhash}${window.location.search}`, @@ -31,7 +33,7 @@ columns : [ { data: 'capture_time', width: '20%', render: DataTable.render.datetime_with_tz() }, { data: 'capture_title', width: '40%' }, - { data: 'url', width: '40%' } + { data: 'landing_page', width: '40%' } ], }) } @@ -41,9 +43,10 @@ processing: true, serverSide: true, retrieve: true, + ordering: false, drawCallback: function (settings) { newTabClickListener() }, ajax: { - url: `/tables/hashTypeDetailsTable/${hash_value}`, + url: `/tables/hashTypeDetailsTable/${hash_value}${window.location.search}`, type: 'POST' }, columns : [ @@ -60,9 +63,10 @@ processing: true, serverSide: true, retrieve: true, + ordering: false, drawCallback: function (settings) { newTabClickListener() }, ajax: { - url: `/tables/identifierDetailsTable/${identifier_value}`, + url: `/tables/identifierDetailsTable/${identifier_value}${window.location.search}`, type: 'POST' }, columns : [ @@ -77,10 +81,10 @@ retrieve: true, drawCallback: function (settings) { newTabClickListener() }, order: [[ 0, "desc" ]], - columnDefs: [{ width: '10%', targets: 0 }, - { width: '10%', targets: 1 }, - { width: '60%', targets: 2 }, - { width: '20%', targets: 3 }], + columns: [{ width: '10%' }, + { width: '10%' }, + { width: '60%' }, + { width: '20%' }], initComplete: function (settings, json) { $('[data-bs-toggle="tooltip"]').tooltip({html: true}); } @@ -91,20 +95,20 @@ if (document.getElementById('faviconsTable')) { new DataTable('#faviconsTable', { retrieve: true, - drawCallback: function (settings) { newTabClickListener() }, - columnDefs: [{ width: '10%', targets: 0 }, - { width: '40%', targets: 1 }, - { width: '40%', targets: 2 }, - { width: '10%', targets: 3 }], + drawCallback: function (settings) { newTabClickListener(); downloadFaviconListener(); }, + columns: [{ width: '10%' }, + { width: '40%' }, + { width: '40%' }, + { width: '10%' }], }); } if (document.getElementById('treeHashesTable')) { new DataTable('#treeHashesTable', { retrieve: true, drawCallback: function (settings) { newTabClickListener() }, - columnDefs: [{ width: '20%', targets: 0 }, - { width: '40%', targets: 1 }, - { width: '40%', targets: 2 }], + column: [{ width: '20%' }, + { width: '40%' }, + { width: '40%' }], }); } if (document.getElementById('hostnamesTable')) { @@ -112,9 +116,9 @@ retrieve: true, drawCallback: function (settings) { newTabClickListener() }, order: [[ 0, "desc" ]], - columnDefs: [{ width: '10%', targets: 0 }, - { width: '40%', targets: 1 }, - { width: '50%', targets: 2 }], + columns: [{ width: '10%' }, + { width: '40%' }, + { width: '50%' }], initComplete: function (settings, json) { $('[data-bs-toggle="tooltip"]').tooltip({html: true}); } @@ -126,9 +130,9 @@ new DataTable('#identifiersTable', { retrieve: true, drawCallback: function (settings) { newTabClickListener() }, - columnDefs: [{ width: '20%', targets: 0 }, - { width: '40%', targets: 1 }, - { width: '40%', targets: 2 }], + columns: [{ width: '20%' }, + { width: '40%' }, + { width: '40%' }], }); } if (document.getElementById('urlsTable')) { @@ -136,8 +140,8 @@ retrieve: true, drawCallback: function (settings) { newTabClickListener() }, order: [[ 0, "desc" ]], - columnDefs: [{ width: '10%', targets: 0 }, - { width: '90%', targets: 1 }], + columns: [{ width: '10%' }, + { width: '90%' }], initComplete: function (settings, json) { $('[data-bs-toggle="tooltip"]').tooltip({html: true}); } @@ -151,6 +155,7 @@ processing: true, serverSide: true, retrieve: true, + ordering: false, drawCallback: function (settings) { newTabClickListener() }, ajax: { url: `/tables/cookieNameTable/${cookieName}${window.location.search}`, @@ -170,9 +175,10 @@ processing: true, serverSide: true, retrieve: true, + ordering: false, drawCallback: function (settings) { newTabClickListener() }, ajax: { - url: `/tables/hostnameTable/${hostname}`, + url: `/tables/hostnameTable/${hostname}${window.location.search}`, type: 'POST' }, columns : [ @@ -189,9 +195,10 @@ processing: true, serverSide: true, retrieve: true, + ordering: false, drawCallback: function (settings) { newTabClickListener() }, ajax: { - url: `/tables/urlTable/${url}`, + url: `/tables/urlTable/${url}${window.location.search}`, type: 'POST' }, columns : [ @@ -208,9 +215,10 @@ processing: true, serverSide: true, retrieve: true, + ordering: false, drawCallback: function (settings) { newTabClickListener() }, ajax: { - url: `/tables/faviconDetailsTable/${favicon}`, + url: `/tables/faviconDetailsTable/${favicon}${window.location.search}`, type: 'POST' }, columns : [ @@ -220,9 +228,4 @@ ], }); } - - // Other things to trigger in modals - document.querySelectorAll(".downloadFaviconButton").forEach(el => el.addEventListener('click', event => { - downloadBase64File(el.dataset.mimetype, el.dataset.b64favicon, el.dataset.filename); - })) })); diff --git a/website/web/templates/bulk_captures.html b/website/web/templates/bulk_captures.html index 82133dd4..bd795073 100644 --- a/website/web/templates/bulk_captures.html +++ b/website/web/templates/bulk_captures.html @@ -10,9 +10,7 @@