chg: Improve and normalize hostnode popup

pull/1015/head
Raphaël Vinot 2024-12-11 17:04:00 +01:00
parent 489bb52e12
commit a8b0464afa
No known key found for this signature in database
GPG Key ID: 32E4E1C133B3792F
8 changed files with 169 additions and 166 deletions

View File

@ -279,6 +279,7 @@ def get_icon(icon_id: str) -> Icon | None:
app.jinja_env.globals.update(get_icon=get_icon)
app.jinja_env.globals.update(generic_type=mimetype_to_generic)
def get_tz_info() -> tuple[str | None, str, set[str]]:
@ -366,8 +367,7 @@ def get_all_body_hashes(capture_uuid: str, /) -> dict[str, dict[str, int | str |
continue
if node.body_hash not in to_return:
total_captures = get_indexing(flask_login.current_user).get_captures_body_hash_count(node.body_hash)
generic_type = mimetype_to_generic(node.mimetype)
to_return[node.body_hash] = {'total_captures': total_captures, 'generic_type': generic_type, 'nodes': []}
to_return[node.body_hash] = {'total_captures': total_captures, 'mimetype': node.mimetype, 'nodes': []}
to_return[node.body_hash]['nodes'].append((node, False)) # type: ignore[union-attr]
# get embedded retources (if any) - need their type too
if 'embedded_ressources' in node.features:
@ -375,8 +375,7 @@ def get_all_body_hashes(capture_uuid: str, /) -> dict[str, dict[str, int | str |
for h, blob in blobs:
if h not in to_return:
total_captures = get_indexing(flask_login.current_user).get_captures_body_hash_count(h)
generic_type = mimetype_to_generic(mimetype)
to_return[h] = {'total_captures': total_captures, 'generic_type': generic_type, 'nodes': []}
to_return[h] = {'total_captures': total_captures, 'mimetype': mimetype, 'nodes': []}
to_return[h]['nodes'].append((node, True)) # type: ignore[union-attr]
return to_return
@ -548,7 +547,7 @@ def get_hostnode_investigator(capture_uuid: str, /, node_uuid: str) -> tuple[Hos
# Index lookup
# %%% Full body %%%
if freq := get_indexing(flask_login.current_user).get_captures_body_hash_count(url.body_hash):
to_append['body_hash_details'] = {'hash_freq': freq}
to_append['body_hash_freq'] = freq
# %%% Embedded ressources %%%
if hasattr(url, 'embedded_ressources') and url.embedded_ressources:
@ -1778,7 +1777,18 @@ def favicon_detail(favicon_sha512: str) -> str:
@app.route('/body_hashes/<string:body_hash>', methods=['GET'])
def body_hash_details(body_hash: str) -> str:
from_popup = True if (request.args.get('from_popup') and request.args.get('from_popup') == 'True') else False
return render_template('body_hash.html', body_hash=body_hash, from_popup=from_popup)
filename = ''
mimetype = ''
b64 = ''
if uuids := get_indexing(flask_login.current_user).get_hash_uuids(body_hash):
# got UUIDs for this hash
capture_uuid, urlnode_uuid = uuids
if ressource := lookyloo.get_ressource(capture_uuid, urlnode_uuid, body_hash):
filename, body, mimetype = ressource
if mimetype_to_generic(mimetype) == 'image':
b64 = base64.b64encode(body.read()).decode()
return render_template('body_hash.html', body_hash=body_hash, from_popup=from_popup,
filename=filename, mimetype=mimetype, b64=b64)
@app.route('/urls/<string:url>', methods=['GET'])

View File

@ -128,8 +128,10 @@ def get_indexing(user: User | None) -> Indexing:
return get_indexing_cache(full=bool(user and user.is_authenticated))
def mimetype_to_generic(mimetype: str) -> str:
if 'javascript' in mimetype or 'ecmascript' in mimetype or mimetype.startswith('js'):
def mimetype_to_generic(mimetype: str | None) -> str:
if not mimetype or mimetype == 'none':
return 'unset_mimetype'
elif 'javascript' in mimetype or 'ecmascript' in mimetype or mimetype.startswith('js'):
return 'js'
elif (mimetype.startswith('image')
or mimetype.startswith('img')
@ -180,7 +182,5 @@ def mimetype_to_generic(mimetype: str) -> str:
elif ('application/gzip' in mimetype
or 'application/zip' in mimetype):
return 'archive'
elif not mimetype or mimetype == 'none':
return 'unset_mimetype'
else:
return 'unknown_mimetype'

View File

@ -17,8 +17,24 @@
{%endif%}
<center>
<h6>{{ body_hash }}</h6>
<a href="{{ url_for('ressource_by_hash', sha512=body_hash) }}">Download</a>
<p class="lead">
{% if filename %}
Filename: <b title="The file may have different names across the captures, this is one of them.">{{ filename }}</b><br>
{% endif %}
{% if mimetype %}
Mimetype: <b>{{ mimetype }}</b><br>
{% endif %}
Hash:<br> <span style="font-size: 70%;">{{ body_hash }}</span><br>
{% if b64 %}
Preview:<br>
<a href="{{ url_for('ressource_by_hash', sha512=body_hash) }}">
<img class="border rounded border-3" src="data:{{mimetype}};base64,{{ b64 }}" style="max-width: 256px;max-height: 256px;"
title="Click to download"/>
</a>
{% else %}
<a href="{{ url_for('ressource_by_hash', sha512=body_hash) }}">Download</a>
{% endif %}
</p>
</center>
<table id="bodyHashDetailsTable" class="table table-striped" style="width:100%" data-bodyhash="{{body_hash}}">

View File

@ -2,11 +2,12 @@
{% from "macros.html" import known_content_details %}
{% from "macros.html" import ressource_legitimacy_details %}
{% from "macros.html" import indexed_cookies %}
{% from "macros.html" import popup_icons_request %}
{% from "macros.html" import popup_icons_response %}
{% from "macros.html" import request_cookies_icon %}
{% from "macros.html" import response_cookies_icon %}
{% from "macros.html" import hash_info%}
{% from "macros.html" import redirect_response %}
{% from "macros.html" import shorten_string %}
{% from "macros.html" import other_captures_table %}
{% from "macros.html" import get_ressource_button %}
{% from "macros.html" import context_form %}
{% from "macros.html" import pandora_submit %}
@ -127,7 +128,7 @@
</div>
<br>
{# Start list of URLs #}
<ul class="list-group-flush">
<ul class="list-group list-group-flush">
{% for url in urls %}
{# URL Display #}
<li class="list-group-item">
@ -156,34 +157,47 @@
</div>
{% endif %}
{% if url['url_object'].security_details %}
<div>TLS certificate details:
<ul>
{% for k, v in url['url_object'].security_details.items() %}
<li><b>{{k}}</b>: {{v}}</li>
{% endfor%}
</ul>
<div class="accordion accordion-flush" id="accordionTLS_{{url['url_object'].uuid}}">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTLS_{{url['url_object'].uuid}}" aria-expanded="false" aria-controls="collapseTLS">
Show certificate details
</button>
</h2>
<div id="collapseTLS_{{url['url_object'].uuid}}" class="accordion-collapse collapse" data-bs-parent="#accordionTLS_{{url['url_object'].uuid}}">
<div class="accordion-body">
<ul>
{% for k, v in url['url_object'].security_details.items() %}
<li><b>{{k}}</b>: {{v}}</li>
{% endfor%}
</ul>
</div>
</div>
</div>
</div>
{% endif %}
<div></div>
<ul class="list-group">
{% if url['url_object'].posted_data or url['cookies_sent'] %}
<li class="list-group-item">
<p class="h4">Request</p>
{{ popup_icons_request(url['url_object'], tree_uuid) }}
<p class="h4">Request {{ request_cookies_icon(url['url_object'], tree_uuid) }}</p>
<hr>
<div class="d-grid gap-2">
{% if url['url_object'].posted_data %}
<div>
<a href="{{ url_for('urlnode_post_request', tree_uuid=tree_uuid, node_uuid=url['url_object'].uuid) }}">
Download posted data
</a><br>
{% if url['url_object'].posted_data is string %}
Posted data size: {{ sizeof_fmt(url['url_object'].posted_data|length) }}
{% endif %}
</div>
{% endif %}
{% if url['cookies_sent'] %}
<div>
<p class="h5">This request contains cookies.
<button class="btn btn-primary collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#request_cookies_full_list_{{ url['url_object'].uuid }}" aria-expanded="false" aria-controls="collapseExample">
<p>
<button class="btn btn-primary btn-sm collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#request_cookies_full_list_{{ url['url_object'].uuid }}" aria-expanded="false" aria-controls="collapseExample">
<span class="if-collapsed">Show cookies sent.</span>
<span class="if-not-collapsed">Hide cookies sent.</span>
</button>
@ -195,7 +209,9 @@
</div>
</div>
{% endif %}
</div>
</li>
{% endif %}
<li class="list-group-item">
{# Details of the response #}
@ -206,9 +222,37 @@
</span>
-
<span>Load time: {{ url['url_object'].time.total_seconds() }}s</span>
<span>{{response_cookies_icon(url['url_object'], tree_uuid)}}</span>
</small>
</p>
{{ popup_icons_response(url['url_object'], tree_uuid) }}
<hr>
{% if url['url_object'].rendered_html %}
<div>
<a href="{{ url_for('urlnode_rendered_content', tree_uuid=tree_uuid, node_uuid=url['url_object'].uuid) }}">
Download rendered HTML page
</a>({{ sizeof_fmt(url['url_object'].rendered_html.getbuffer().nbytes)}})
<br>
<a href="{{ url_for('urlnode_urls_in_rendered_content', tree_uuid=tree_uuid, node_uuid=url['url_object'].uuid) }}">
Download URLs in rendered HTML page
</a>
</div>
{% endif %}
{{ redirect_response(url['url_object'], tree_uuid) }}
{% if url['url_object'].empty_response %}
Empty HTML body.
{% else %}
{{ hash_info(tree_uuid, url['url_object'].uuid, url['url_object'].mimetype,
url['url_object'].body_hash, url['url_object'].body.getbuffer().nbytes,
url.get('body_hash_freq', 0), has_pandora) }}
{{ ressource_legitimacy_details(url.get('legitimacy')) }}
{{ known_content_details(url.get('known_content')) }}
{% if enable_context_by_users %}
{{ context_form(tree_uuid, url['url_object'].uuid,
url['url_object'].body_hash, 'hostnode_popup') }}
{% endif %}
{% endif %}
{% if url['url_object'].downloaded_filename %}
<div>
@ -223,65 +267,19 @@
</div>
{% endif%}
{% if url['url_object'].rendered_html %}
<div>
<a href="{{ url_for('urlnode_rendered_content', tree_uuid=tree_uuid, node_uuid=url['url_object'].uuid) }}">
Download rendered HTML page
</a>({{ sizeof_fmt(url['url_object'].rendered_html.getbuffer().nbytes)}})
<br>
<a href="{{ url_for('urlnode_urls_in_rendered_content', tree_uuid=tree_uuid, node_uuid=url['url_object'].uuid) }}">
Download URLs in rendered HTML page
</a>
</div>
{% endif %}
<div>
{% if url['url_object'].empty_response %}
Empty HTML body.
{% else %}
{{ ressource_legitimacy_details(url['legitimacy'], url['url_object'].body.getbuffer().nbytes) }}
{% if has_pandora %}
{{ pandora_submit(tree_uuid, url['url_object'].uuid) }}
{% endif %}
{% endif %}
</div>
{% if url['known_content'] %}
{{ known_content_details(url['known_content']) }}
{% endif %}
{# Everything we know about the response content #}
{% if url['body_hash_details'] and url['body_hash_details']['hash_freq'] %}
<div>
This file can be found <b>{{ url['body_hash_details']['hash_freq'] }}</b> times
across all the captures on this lookyloo instance.
<p>
<a href="{{ url_for('body_hash_details', body_hash=url['url_object'].body_hash, from_popup=True) }}">
Show more information about this response body.
</a>
</p>
</div>
{% if url['url_object'].hhhash %}
<p>
<a href="{{ url_for('hhh_detail', hhh=url['url_object'].hhhash, from_popup=True) }}">
See other captures with the same HTTP Headers Hash
Show other captures with the same HTTP Headers Hash
</a>
</p>
{% endif %}
{% endif %}
{% if enable_context_by_users %}
<br>
{{ context_form(tree_uuid, url['url_object'].uuid,
url['url_object'].body_hash, 'hostnode_popup') }}
{% endif %}
<div class="d-grid gap-2">
{% if url['embedded_ressources'] %}
{# Details on embedded resources #}
<div><b>This response contains embedded ressources</b>
<button class="btn btn-primary collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#embedded_full_list_{{ url['url_object'].uuid }}" aria-expanded="false" aria-controls="collapseExample">
<div>
<button class="btn btn-primary btn-sm collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#embedded_full_list_{{ url['url_object'].uuid }}" aria-expanded="false" aria-controls="collapseExample">
<span class="if-collapsed">Show embedded resources.</span>
<span class="if-not-collapsed">Hide embedded resources.</span>
</button>
@ -290,28 +288,12 @@
<div class="card card-body">
{% for hash, details in url['embedded_ressources'].items() %}
<div>
{% if details['known_content'] %}
{{ known_content_details(details['known_content']) }}
{% endif %}
{{ ressource_legitimacy_details(details['legitimacy'], details['body_size']) }}
</div>
{% if has_pandora %}
{{ pandora_submit(tree_uuid, url['url_object'].uuid, hash) }}
{% endif %}
<div>
This file {% if details['type'] %}(<b>{{ details['type'] }}</b>){% endif %} can be found <b>{{ details['hash_freq'] }}</b> times
across all the captures on this lookyloo instance.
{{ get_ressource_button(tree_uuid, url['url_object'].uuid, hash,
'Download the embedded ressource',
details['type'] and details['type'].startswith('image')) }}
<br>
{{hash_info(tree_uuid, url['url_object'].uuid, details['type'], hash, details['body_size'], details['hash_freq'] ) }}<br>
{{ ressource_legitimacy_details(details.get('legitimacy')) }}
{{ known_content_details(details.get('known_content')) }}
{% if enable_context_by_users %}
{{ context_form(tree_uuid, url['url_object'].uuid, hash, 'hostnode_popup') }}
{% endif %}
<p><a href="{{ url_for('body_hash_details', body_hash=url['url_object'].body_hash, from_popup=True) }}">
Show more information about this embedded content.
</a></p>
</div>
{% endfor %}
</div>
@ -320,8 +302,8 @@
{% if url['cookies_received'] %}
<div>
<p class="h5">This response contains cookies.
<button class="btn btn-primary collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#response_cookies_full_list_{{ url['url_object'].uuid }}" aria-expanded="false" aria-controls="collapseExample">
<p>
<button class="btn btn-primary btn-sm collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#response_cookies_full_list_{{ url['url_object'].uuid }}" aria-expanded="false" aria-controls="collapseExample">
<span class="if-collapsed">Show cookies received.</span>
<span class="if-not-collapsed">Hide cookies received.</span>
</button>
@ -335,6 +317,7 @@
</div>
</div>
{% endif %}
<div>
</li>
</ul>
</li>

View File

@ -81,21 +81,23 @@
{% macro known_content_details(details) %}
<div>
{% if details is string %}
<b>{{ details }} </b>
{% else %}
This file is known as part of <b>{{ details[0] }}</b>
version <b>{{ details[1] }}</b>: <b>{{ details[2] }}</b>.
{% if details[3] > 1%}
It is also present in <b>{{ details[3] -1 }}</b> other libraries.
{% if details %}
<div>
{% if details is string %}
<b>{{ details }} </b>
{% else %}
This file is known as part of <b>{{ details[0] }}</b>
version <b>{{ details[1] }}</b>: <b>{{ details[2] }}</b>.
{% if details[3] > 1%}
It is also present in <b>{{ details[3] -1 }}</b> other libraries.
{%endif%}
{%endif%}
</div>
{%endif%}
</div>
{% endmacro %}
{% macro context_form(tree_uuid, urlnode_uuid, hash, callback_str) %}
<button class="btn btn-primary collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#context_response_{{ urlnode_uuid }}" aria-expanded="false" aria-controls="collapseContextForm">
<button class="btn btn-primary btn-sm collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#context_response_{{ urlnode_uuid }}" aria-expanded="false" aria-controls="collapseContextForm">
<span class="if-collapsed">Add context</span>
<span class="if-not-collapsed">Hide context form</span>
</button>
@ -150,22 +152,10 @@
</div>
{% endmacro %}
{% macro get_ressource_button(capture_uuid, urlnode_uuid, hash, text, can_preview=False) %}
<form method="post" action="{{ url_for('get_ressource', tree_uuid=capture_uuid, node_uuid=urlnode_uuid) }}">
<button class="btn btn-primary" name="ressource_hash" value="{{ hash }}"
{% if can_preview %}
data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-html="true"
title='<img class="ressource_preview" src="{{ url_for('get_ressource_preview', tree_uuid=capture_uuid, node_uuid=urlnode_uuid, h_ressource=hash) }}"/>'
{% endif %}
>{{ text }}</button>
</form>
{% endmacro %}
{% macro ressource_legitimacy_details(details, ressource_size) %}
{% macro ressource_legitimacy_details(details) %}
{% if details and details[0] == False %}
<img src="/static/bomb.svg" title="Known malicious content in the response." width="21" height="21"/>
{%endif%}
<b>Body size</b> (in the HTTP response): {{ sizeof_fmt(ressource_size) }}
{% if details %}
{% if details[0] %}
- This file is known <b>legitimate</b> on the following domains: {{ ', '.join(details[1]) }}.
@ -215,17 +205,15 @@
{% endif %}
{% endmacro %}
{% macro popup_icons_request(urlnode, tree_uuid) %}
<div>
{% macro request_cookies_icon(urlnode, tree_uuid) %}
{% if urlnode.request_cookie %}
{% set icon_info = get_icon("request_cookie") %}
<a href="{{ url_for('urlnode_request_cookies', tree_uuid=tree_uuid, node_uuid=urlnode.uuid) }}"
title="Download all the cookies in the request to the server">
<a href="{{ url_for('urlnode_request_cookies', tree_uuid=tree_uuid, node_uuid=urlnode.uuid) }}">
<img src="{{ url_for('static', filename=icon_info['icon']) }}" alt="{{ icon_info['tooltip'] }}"
width="21" height="21"/>
width="21" height="21"/
title="Download all the cookies in the request to the server">
</a>
{% endif %}
</div>
{% endmacro %}
@ -241,47 +229,57 @@
</div>
{% endmacro %}
{% macro hash_icon(tree_uuid, urlnode_uuid, generic_type, hash) %}
{% if generic_type in ["js", "exe", "css", "font", "html", "json", "image", "video",
"unknown_mimetype", "text", "unset_mimetype", "octet-stream",
"livestream"] %}
{% set icon_info = get_icon(generic_type) %}
<a href="{{ url_for('get_ressource', tree_uuid=tree_uuid, node_uuid=urlnode_uuid) }}">
<img src="{{ url_for('static', filename=icon_info['icon']) }}" alt="{{ icon_info['tooltip'] }}"
width="21" height="21"
{% if generic_type == "image" %}
data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-html="true"
title='<img class="ressource_preview" src="{{ url_for('get_ressource_preview', tree_uuid=tree_uuid, node_uuid=urlnode_uuid, h_ressource=hash) }}"/> <br>Click to download.'
{% else %}
data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-html="true"
title="{{icon_info['tooltip']}} <br/>Click to download."
{% endif %}
/>
</a>
{%endif%}
{% macro hash_info(tree_uuid, urlnode_uuid, mimetype, hash, ressource_size,
nb_occurrences, has_pandora)%}
{{ hash_icon(tree_uuid, urlnode_uuid, mimetype, hash) }}
<b>Body size</b> (in the HTTP response): {{ sizeof_fmt(ressource_size) }}
{% if nb_occurrences > 0 %}
<div>
This file can be found <b>{{ nb_occurrences }}</b> times across all the captures on this lookyloo instance.
<p>
<a href="{{ url_for('body_hash_details', body_hash=hash, from_popup=True) }}">
Show more information about this ressource.
</a>
</p>
</div>
{% endif %}
{% if has_pandora %}
{{ pandora_submit(tree_uuid, urlnode_uuid, hash) }}
{% endif %}
{% endmacro %}
{% macro popup_icons_response(urlnode, tree_uuid) %}
<div>
{% if urlnode.generic_type in ["js", "exe", "css", "font", "html", "json", "image", "video",
"unknown_mimetype", "text", "unset_mimetype", "octet-stream",
"livestream"] %}
{% set icon_info = get_icon(urlnode.generic_type) %}
<a href="{{ url_for('get_ressource', tree_uuid=tree_uuid, node_uuid=urlnode.uuid) }}">
{% macro hash_icon(tree_uuid, urlnode_uuid, mimetype, hash) %}
{% if mimetype %}
{% set gt = generic_type(mimetype) %}
{% if gt in ["js", "exe", "css", "font", "html", "json", "image", "video",
"unknown_mimetype", "text", "unset_mimetype", "octet-stream",
"livestream"] %}
{% set icon_info = get_icon(gt) %}
<a href="{{ url_for('get_ressource', tree_uuid=tree_uuid, node_uuid=urlnode_uuid) }}">
<img src="{{ url_for('static', filename=icon_info['icon']) }}" alt="{{ icon_info['tooltip'] }}"
width="21" height="21"
{% if urlnode.generic_type == "image" %}
{% if gt == "image" %}
data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-html="true"
title='<img class="ressource_preview" src="{{ url_for('get_ressource_preview', tree_uuid=tree_uuid, node_uuid=urlnode.uuid) }}"/> <br>Click to download.'
title='<img class="ressource_preview" src="{{ url_for('get_ressource_preview', tree_uuid=tree_uuid, node_uuid=urlnode_uuid, h_ressource=hash) }}"/> <br>Click to download.'
{% else %}
data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-html="true"
title="{{icon_info['tooltip']}} <br/>Click to download."
{% endif %}
/>
</a>
<small>Mimetype: <b>{{mimetype}}</b></small>
<br>
{%endif%}
{%endif%}
{% endmacro %}
{% macro response_cookies_icon(urlnode, tree_uuid) %}
{% if urlnode.response_cookie %}
{% set icon_info = get_icon("response_cookie") %}
<a href="{{ url_for('urlnode_response_cookies', tree_uuid=tree_uuid, node_uuid=urlnode.uuid) }}"
@ -290,10 +288,9 @@
width="21" height="21"/>
</a>
{% endif %}
{% endmacro %}
</div>
<div>
{% macro redirect_response(urlnode, tree_uuid) %}
{% if urlnode["redirect"] %}
{% set icon_info = get_icon('redirect') %}
{% for child in urlnode.children if child.name == urlnode.redirect_url %}
@ -309,7 +306,6 @@
width="21" height="21"/>
{% endfor %}
{%endif%}
</div>
{% endmacro %}
{% macro shorten_string(string, cut_length, with_title=False) %}

View File

@ -2,7 +2,7 @@
{% from 'bootstrap5/utils.html' import render_messages %}
{% from 'macros.html' import shorten_string %}
{% from 'macros.html' import get_ressource_button %}
{% from 'macros.html' import hash_icon %}
{% from 'macros.html' import context_form %}
{% block title %}Ressources{% endblock %}
@ -42,7 +42,7 @@
<tr>
<td>
<a href="{{ url_for('body_hash_details', body_hash=h) }}">{{ shorten_string(h, 10) }}</a><br>
{{ get_ressource_button(capture_uuid, urlnode_uuid, h, 'Download sample', mimetype and mimetype.startswith('image')) }}
{{ hash_icon(capture_uuid, urlnode_uuid, mimetype, h) }}
</td>
<td>{{ freq }}</td>
<td> {{ context['type'] }} - {{ context['details'] }}<br>

View File

@ -14,7 +14,7 @@
<tr>
<td>{{ info['total_captures'] }}</td>
<td data-order="{{info['generic_type']}}">
{{hash_icon(tree_uuid, info['nodes'][0][0].uuid, info['generic_type'], body_hash)}}
{{hash_icon(tree_uuid, info['nodes'][0][0].uuid, info['mimetype'], body_hash)}}
</td>
<td>
<ul>

View File

@ -1,5 +1,3 @@
{% from "macros.html" import popup_icons_response %}
<table id="hostnamesTable" class="table table-striped" style="width:100%">
<thead>
<tr>