diff --git a/bin/crawlers/Crawler.py b/bin/crawlers/Crawler.py index fd8a758f..4b499eb9 100755 --- a/bin/crawlers/Crawler.py +++ b/bin/crawlers/Crawler.py @@ -19,6 +19,7 @@ from lib.ConfigLoader import ConfigLoader from lib.objects.Domains import Domain from lib.objects.Items import Item from lib.objects import Screenshots +from lib.objects import Titles logging.config.dictConfig(ail_logger.get_config(name='crawlers')) @@ -252,6 +253,13 @@ class Crawler(AbstractModule): self.root_item = item_id parent_id = item_id + item = Item(item_id) + + title_content = crawlers.extract_title_from_html(entries['html']) + if title_content: + title = Titles.create_title(title_content) + title.add(item.get_date(), item_id) + # SCREENSHOT if self.screenshot: if 'png' in entries and entries['png']: @@ -260,7 +268,6 @@ class Crawler(AbstractModule): if not screenshot.is_tags_safe(): unsafe_tag = 'dark-web:topic="pornography-child-exploitation"' self.domain.add_tag(unsafe_tag) - item = Item(item_id) item.add_tag(unsafe_tag) # Remove Placeholder pages # TODO Replace with warning list ??? if screenshot.id not in self.placeholder_screenshots: diff --git a/bin/lib/ail_core.py b/bin/lib/ail_core.py index 6f4ed42e..a9128489 100755 --- a/bin/lib/ail_core.py +++ b/bin/lib/ail_core.py @@ -15,7 +15,7 @@ config_loader = ConfigLoader() r_serv_db = config_loader.get_db_conn("Kvrocks_DB") config_loader = None -AIL_OBJECTS = sorted({'cve', 'cryptocurrency', 'decoded', 'domain', 'item', 'pgp', 'screenshot', 'username'}) +AIL_OBJECTS = sorted({'cve', 'cryptocurrency', 'decoded', 'domain', 'item', 'pgp', 'screenshot', 'title', 'username'}) def get_ail_uuid(): ail_uuid = r_serv_db.get('ail:uuid') diff --git a/bin/lib/correlations_engine.py b/bin/lib/correlations_engine.py index c0e8d370..f09d5c5f 100755 --- a/bin/lib/correlations_engine.py +++ b/bin/lib/correlations_engine.py @@ -44,11 +44,12 @@ CORRELATION_TYPES_BY_OBJ = { "cryptocurrency": ["domain", "item"], "cve": ["domain", "item"], "decoded": ["domain", "item"], - "domain": ["cve", "cryptocurrency", "decoded", "item", "pgp", "username", "screenshot"], - "item": ["cve", "cryptocurrency", "decoded", "domain", "pgp", "username", "screenshot"], + "domain": ["cve", "cryptocurrency", "decoded", "item", "pgp", "title", "screenshot", "username"], + "item": ["cve", "cryptocurrency", "decoded", "domain", "pgp", "screenshot", "title", "username"], "pgp": ["domain", "item"], - "username": ["domain", "item"], "screenshot": ["domain", "item"], + "title": ["domain", "item"], + "username": ["domain", "item"], } def get_obj_correl_types(obj_type): diff --git a/bin/lib/crawlers.py b/bin/lib/crawlers.py index 2a9c2a01..d8f4c890 100755 --- a/bin/lib/crawlers.py +++ b/bin/lib/crawlers.py @@ -183,6 +183,47 @@ def extract_favicon_from_html(html, url): # # # - - # # # +# # # # # # # # +# # +# TITLE # +# # +# # # # # # # # + +def extract_title_from_html(html): + soup = BeautifulSoup(html, 'html.parser') + title = soup.title + if title: + return str(title.string) + return '' + +def extract_description_from_html(html): + soup = BeautifulSoup(html, 'html.parser') + description = soup.find('meta', attrs={'name': 'description'}) + if description: + return description['content'] + return '' + +def extract_description_from_html(html): + soup = BeautifulSoup(html, 'html.parser') + description = soup.find('meta', attrs={'name': 'description'}) + if description: + return description['content'] + return '' + +def extract_keywords_from_html(html): + soup = BeautifulSoup(html, 'html.parser') + keywords = soup.find('meta', attrs={'name': 'keywords'}) + if keywords: + return keywords['content'] + return '' + +def extract_author_from_html(html): + soup = BeautifulSoup(html, 'html.parser') + keywords = soup.find('meta', attrs={'name': 'author'}) + if keywords: + return keywords['content'] + return '' +# # # - - # # # ################################################################################ @@ -1711,7 +1752,7 @@ def test_ail_crawlers(): load_blacklist() # if __name__ == '__main__': - # task = CrawlerTask('2dffcae9-8f66-4cfa-8e2c-de1df738a6cd') - # print(task.get_meta()) - # _clear_captures() - +# item = Item('crawled/2023/03/06/foo.bec50a87b5-0c21-4ed4-9cb2-2d717a7a6507') +# content = item.get_content() +# r = extract_author_from_html(content) +# print(r) diff --git a/bin/lib/module_extractor.py b/bin/lib/module_extractor.py index 99d51a6b..02f499fd 100755 --- a/bin/lib/module_extractor.py +++ b/bin/lib/module_extractor.py @@ -3,7 +3,6 @@ import json import os import sys -import time import yara @@ -15,6 +14,7 @@ sys.path.append(os.environ['AIL_BIN']) ################################## from lib.objects import ail_objects from lib.objects.Items import Item +from lib.objects.Titles import Title from lib import correlations_engine from lib import regex_helper from lib.ConfigLoader import ConfigLoader @@ -58,18 +58,25 @@ def get_correl_match(extract_type, obj_id, content): correl = correlations_engine.get_correlation_by_correl_type('item', '', obj_id, extract_type) to_extract = [] map_subtype = {} + map_value_id = {} for c in correl: subtype, value = c.split(':', 1) - map_subtype[value] = subtype - to_extract.append(value) + if extract_type == 'title': + title = Title(value).get_content() + to_extract.append(title) + map_value_id[title] = value + else: + map_subtype[value] = subtype + to_extract.append(value) + map_value_id[value] = value if to_extract: objs = regex_helper.regex_finditer(r_key, '|'.join(to_extract), obj_id, content) for obj in objs: - if map_subtype[obj[2]]: + if map_subtype.get(obj[2]): subtype = map_subtype[obj[2]] else: subtype = '' - extracted.append([obj[0], obj[1], obj[2], f'{extract_type}:{subtype}:{obj[2]}']) + extracted.append([obj[0], obj[1], obj[2], f'{extract_type}:{subtype}:{map_value_id[obj[2]]}']) return extracted def _get_yara_match(data): @@ -173,7 +180,7 @@ def extract(obj_id, content=None): if matches: extracted = extracted + matches - for obj_t in ['cve', 'cryptocurrency', 'username']: # Decoded, PGP->extract bloc + for obj_t in ['cve', 'cryptocurrency', 'title', 'username']: # Decoded, PGP->extract bloc matches = get_correl_match(obj_t, obj_id, content) if matches: extracted = extracted + matches diff --git a/bin/lib/objects/Titles.py b/bin/lib/objects/Titles.py new file mode 100755 index 00000000..3c682224 --- /dev/null +++ b/bin/lib/objects/Titles.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +# -*-coding:UTF-8 -* + +import os +import sys + +from hashlib import sha256 +from flask import url_for + +from pymisp import MISPObject + +sys.path.append(os.environ['AIL_BIN']) +################################## +# Import Project packages +################################## +from lib.ConfigLoader import ConfigLoader +from lib.objects.abstract_daterange_object import AbstractDaterangeObject, AbstractDaterangeObjects + +config_loader = ConfigLoader() +r_objects = config_loader.get_db_conn("Kvrocks_Objects") +baseurl = config_loader.get_config_str("Notifications", "ail_domain") +config_loader = None + + +class Title(AbstractDaterangeObject): + """ + AIL Title Object. + """ + + def __init__(self, id): + super(Title, self).__init__('title', id) + + # def get_ail_2_ail_payload(self): + # payload = {'raw': self.get_gzip_content(b64=True), + # 'compress': 'gzip'} + # return payload + + # # WARNING: UNCLEAN DELETE /!\ TEST ONLY /!\ + def delete(self): + # # TODO: + pass + + def get_content(self, r_type='str'): + if r_type == 'str': + return self._get_field('content') + + def get_link(self, flask_context=False): + if flask_context: + url = url_for('correlation.show_correlation', type=self.type, id=self.id) + else: + url = f'{baseurl}/correlation/show?type={self.type}&id={self.id}' + return url + + # TODO # CHANGE COLOR + def get_svg_icon(self): + return {'style': 'fas', 'icon': '\uf1dc', 'color': '#1E88E5', 'radius': 5} + + def get_misp_object(self): + obj_attrs = [] + obj = MISPObject('tsk-web-history') + obj.first_seen = self.get_first_seen() + obj.last_seen = self.get_last_seen() + + obj_attrs.append(obj.add_attribute('title', value=self.get_content())) + for obj_attr in obj_attrs: + for tag in self.get_tags(): + obj_attr.add_tag(tag) + return obj + + def get_meta(self, options=set()): + meta = self._get_meta(options=options) + meta['id'] = self.id + meta['tags'] = self.get_tags(r_list=True) + meta['content'] = self.get_content() + return meta + + def add(self, date, item_id): + self._add(date, item_id) + + def create(self, content, _first_seen=None, _last_seen=None): + self._set_field('content', content) + self._create() + + +def create_title(content): + title_id = sha256(content.encode()).hexdigest() + title = Title(title_id) + if not title.exists(): + title.create(content) + return title + +class Titles(AbstractDaterangeObjects): + """ + Titles Objects + """ + def __init__(self): + super().__init__('title') + + def get_metas(self, obj_ids, options=set()): + return self._get_metas(Title, obj_ids, options=options) + + def sanitize_name_to_search(self, name_to_search): + return name_to_search + + +# if __name__ == '__main__': +# from lib import crawlers +# from lib.objects import Items +# for item in Items.get_all_items_objects(filters={'sources': ['crawled']}): +# title_content = crawlers.extract_title_from_html(item.get_content()) +# if title_content: +# print(item.id, title_content) +# title = create_title(title_content) +# title.add(item.get_date(), item.id) diff --git a/bin/lib/objects/abstract_daterange_object.py b/bin/lib/objects/abstract_daterange_object.py index 882c11c7..59a579f4 100755 --- a/bin/lib/objects/abstract_daterange_object.py +++ b/bin/lib/objects/abstract_daterange_object.py @@ -7,6 +7,7 @@ Base Class for AIL Objects # Import External packages ################################## import os +import re import sys from abc import abstractmethod, ABC @@ -44,8 +45,14 @@ class AbstractDaterangeObject(AbstractObject, ABC): def exists(self): return r_object.exists(f'meta:{self.type}:{self.id}') + def _get_field(self, field): + return r_object.hget(f'meta:{self.type}:{self.id}', field) + + def _set_field(self, field, value): + return r_object.hset(f'meta:{self.type}:{self.id}', field, value) + def get_first_seen(self, r_int=False): - first_seen = r_object.hget(f'meta:{self.type}:{self.id}', 'first_seen') + first_seen = self._get_field('first_seen') if r_int: if first_seen: return int(first_seen) @@ -55,7 +62,7 @@ class AbstractDaterangeObject(AbstractObject, ABC): return first_seen def get_last_seen(self, r_int=False): - last_seen = r_object.hget(f'meta:{self.type}:{self.id}', 'last_seen') + last_seen = self._get_field('last_seen') if r_int: if last_seen: return int(last_seen) @@ -83,10 +90,10 @@ class AbstractDaterangeObject(AbstractObject, ABC): return meta_dict def set_first_seen(self, first_seen): - r_object.hset(f'meta:{self.type}:{self.id}', 'first_seen', first_seen) + self._set_field('first_seen', first_seen) def set_last_seen(self, last_seen): - r_object.hset(f'meta:{self.type}:{self.id}', 'last_seen', last_seen) + self._set_field('last_seen', last_seen) def update_daterange(self, date): date = int(date) @@ -139,11 +146,85 @@ class AbstractDaterangeObject(AbstractObject, ABC): self.add_correlation('domain', '', domain) # TODO:ADD objects + Stats - def _create(self, first_seen, last_seen): - self.set_first_seen(first_seen) - self.set_last_seen(last_seen) + def _create(self, first_seen=None, last_seen=None): + if first_seen: + self.set_first_seen(first_seen) + if last_seen: + self.set_last_seen(last_seen) r_object.sadd(f'{self.type}:all', self.id) # TODO def _delete(self): pass + + +class AbstractDaterangeObjects(ABC): + """ + Abstract Daterange Objects + """ + + def __init__(self, obj_type): + """ Abstract for Daterange Objects + + :param obj_type: object type (item, ...) + """ + self.type = obj_type + + def get_all(self): + return r_object.smembers(f'{self.type}:all') + + def get_by_date(self, date): + return r_object.zrange(f'{self.type}:date:{date}', 0, -1) + + def get_nb_by_date(self, date): + return r_object.zcard(f'{self.type}:date:{date}') + + def get_by_daterange(self, date_from, date_to): + obj_ids = set() + for date in Date.substract_date(date_from, date_to): + obj_ids = obj_ids | set(self.get_by_date(date)) + return obj_ids + + @abstractmethod + def get_metas(self, obj_ids, options=set()): + pass + + def _get_metas(self, obj_class_ref, obj_ids, options=set()): + dict_obj = {} + for obj_id in obj_ids: + obj = obj_class_ref(obj_id) + dict_obj[obj_id] = obj.get_meta(options=options) + return dict_obj + + @abstractmethod + def sanitize_name_to_search(self, name_to_search): + return name_to_search + + def search_by_name(self, name_to_search, r_pos=False): + objs = {} + # for subtype in subtypes: + r_name = self.sanitize_name_to_search(name_to_search) + if not name_to_search or isinstance(r_name, dict): + return objs + r_name = re.compile(r_name) + for title_name in self.get_all(): + res = re.search(r_name, title_name) + if res: + objs[title_name] = {} + if r_pos: + objs[title_name]['hl-start'] = res.start() + objs[title_name]['hl-end'] = res.end() + return objs + + def api_get_chart_nb_by_daterange(self, date_from, date_to): + date_type = [] + for date in Date.substract_date(date_from, date_to): + d = {'date': f'{date[0:4]}-{date[4:6]}-{date[6:8]}', + self.type: self.get_nb_by_date(date)} + date_type.append(d) + return date_type + + def api_get_meta_by_daterange(self, date_from, date_to): + date = Date.sanitise_date_range(date_from, date_to) + return self.get_metas(self.get_by_daterange(date['date_from'], date['date_to']), options={'sparkline'}) + diff --git a/bin/lib/objects/abstract_object.py b/bin/lib/objects/abstract_object.py index b65c9ace..631597c4 100755 --- a/bin/lib/objects/abstract_object.py +++ b/bin/lib/objects/abstract_object.py @@ -187,7 +187,7 @@ class AbstractObject(ABC): pass @staticmethod - def get_misp_object_first_last_seen(misp_obj): + def get_misp_object_first_last_seen(misp_obj): # TODO REMOVE ME ???? """ :type misp_obj: MISPObject """ diff --git a/bin/lib/objects/ail_objects.py b/bin/lib/objects/ail_objects.py index 01445c9e..d52a0bbd 100755 --- a/bin/lib/objects/ail_objects.py +++ b/bin/lib/objects/ail_objects.py @@ -21,6 +21,7 @@ from lib.objects.Domains import Domain from lib.objects.Items import Item, get_all_items_objects, get_nb_items_objects from lib.objects import Pgps from lib.objects.Screenshots import Screenshot +from lib.objects import Titles from lib.objects import Usernames config_loader = ConfigLoader() @@ -59,6 +60,8 @@ def get_object(obj_type, subtype, id): return CryptoCurrencies.CryptoCurrency(id, subtype) elif obj_type == 'pgp': return Pgps.Pgp(id, subtype) + elif obj_type == 'title': + return Titles.Title(id) elif obj_type == 'username': return Usernames.Username(id, subtype) @@ -160,10 +163,12 @@ def get_object_card_meta(obj_type, subtype, id, related_btc=False): obj = get_object(obj_type, subtype, id) meta = obj.get_meta() meta['icon'] = obj.get_svg_icon() - if subtype or obj_type == 'cve': + if subtype or obj_type == 'cve' or obj_type == 'title': meta['sparkline'] = obj.get_sparkline() if obj_type == 'cve': meta['cve_search'] = obj.get_cve_search() + # if obj_type == 'title': + # meta['cve_search'] = obj.get_cve_search() if subtype == 'bitcoin' and related_btc: meta["related_btc"] = btc_ail.get_bitcoin_info(obj.id) if obj.get_type() == 'decoded': diff --git a/bin/modules/Phone.py b/bin/modules/Phone.py index a7537eda..16af8303 100755 --- a/bin/modules/Phone.py +++ b/bin/modules/Phone.py @@ -43,7 +43,7 @@ class Phone(AbstractModule): def extract(self, obj_id, content, tag): extracted = [] - phones = self.regex_phone_iter('US', obj_id, content) + phones = self.regex_phone_iter('ZZ', obj_id, content) for phone in phones: extracted.append([phone[0], phone[1], phone[2], f'tag:{tag}']) return extracted diff --git a/var/www/Flask_server.py b/var/www/Flask_server.py index c6669ef6..63addc22 100755 --- a/var/www/Flask_server.py +++ b/var/www/Flask_server.py @@ -49,6 +49,7 @@ from blueprints.settings_b import settings_b from blueprints.objects_cve import objects_cve from blueprints.objects_decoded import objects_decoded from blueprints.objects_subtypes import objects_subtypes +from blueprints.objects_title import objects_title Flask_dir = os.environ['AIL_FLASK'] @@ -102,6 +103,7 @@ app.register_blueprint(settings_b, url_prefix=baseUrl) app.register_blueprint(objects_cve, url_prefix=baseUrl) app.register_blueprint(objects_decoded, url_prefix=baseUrl) app.register_blueprint(objects_subtypes, url_prefix=baseUrl) +app.register_blueprint(objects_title, url_prefix=baseUrl) # ========= =========# # ========= Cookie name ======== diff --git a/var/www/blueprints/objects_title.py b/var/www/blueprints/objects_title.py new file mode 100644 index 00000000..eef7f69c --- /dev/null +++ b/var/www/blueprints/objects_title.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +# -*-coding:UTF-8 -* + +''' + Blueprint Flask: crawler splash endpoints: dashboard, onion crawler ... +''' + +import os +import sys + +from flask import Flask, render_template, jsonify, request, Blueprint, redirect, url_for, Response, abort, send_file +from flask_login import login_required, current_user + +# Import Role_Manager +from Role_Manager import login_admin, login_analyst, login_read_only + +sys.path.append(os.environ['AIL_BIN']) +################################## +# Import Project packages +################################## +from lib.objects import Titles +from packages import Date + +# ============ BLUEPRINT ============ +objects_title = Blueprint('objects_title', __name__, template_folder=os.path.join(os.environ['AIL_FLASK'], 'templates/objects/title')) + +# ============ VARIABLES ============ +bootstrap_label = ['primary', 'success', 'danger', 'warning', 'info'] + + +# ============ FUNCTIONS ============ +@objects_title.route("/objects/title", methods=['GET']) +@login_required +@login_read_only +def objects_titles(): + date_from = request.args.get('date_from') + date_to = request.args.get('date_to') + show_objects = request.args.get('show_objects') + date = Date.sanitise_date_range(date_from, date_to) + date_from = date['date_from'] + date_to = date['date_to'] + + if show_objects: + dict_objects = Titles.Titles().api_get_meta_by_daterange(date_from, date_to) + else: + dict_objects = {} + + return render_template("TitleDaterange.html", date_from=date_from, date_to=date_to, + dict_objects=dict_objects, show_objects=show_objects) + +@objects_title.route("/objects/title/post", methods=['POST']) +@login_required +@login_read_only +def objects_titles_post(): + date_from = request.form.get('date_from') + date_to = request.form.get('date_to') + show_objects = request.form.get('show_objects') + return redirect(url_for('objects_title.objects_titles', date_from=date_from, date_to=date_to, show_objects=show_objects)) + +@objects_title.route("/objects/title/range/json", methods=['GET']) +@login_required +@login_read_only +def objects_title_range_json(): + date_from = request.args.get('date_from') + date_to = request.args.get('date_to') + date = Date.sanitise_date_range(date_from, date_to) + date_from = date['date_from'] + date_to = date['date_to'] + return jsonify(Titles.Titles().api_get_chart_nb_by_daterange(date_from, date_to)) + +@objects_title.route("/objects/title/search", methods=['POST']) +@login_required +@login_read_only +def objects_title_search(): + to_search = request.form.get('object_id') + + # TODO SANITIZE ID + # TODO Search all + title = Titles.Title(to_search) + if not title.exists(): + abort(404) + else: + return redirect(title.get_link(flask_context=True)) + +# ============= ROUTES ============== + diff --git a/var/www/templates/correlation/metadata_card_cve.html b/var/www/templates/correlation/metadata_card_cve.html index d1cfab9f..1e166ddc 100644 --- a/var/www/templates/correlation/metadata_card_cve.html +++ b/var/www/templates/correlation/metadata_card_cve.html @@ -108,7 +108,7 @@ Tags: {% for tag in dict_object["metadata"]['tags'] %} {% endfor %} diff --git a/var/www/templates/correlation/metadata_card_title.html b/var/www/templates/correlation/metadata_card_title.html new file mode 100644 index 00000000..cd943349 --- /dev/null +++ b/var/www/templates/correlation/metadata_card_title.html @@ -0,0 +1,173 @@ + + + +{% with modal_add_tags=dict_object['metadata_card']['add_tags_modal']%} + {% include 'modals/add_tags.html' %} +{% endwith %} + +{% include 'modals/edit_tag.html' %} + +
+
+

{{ dict_object["metadata"]["content"] }}

+
{{ dict_object["correlation_id"] }}
+ + + {% with obj_type='title', obj_id=dict_object['correlation_id'], obj_subtype='' %} + {% include 'modals/investigations_register_obj.html' %} + {% endwith %} + + +
+
+ + + + + + diff --git a/var/www/templates/correlation/show_correlation.html b/var/www/templates/correlation/show_correlation.html index b8a9dd79..8342e8b6 100644 --- a/var/www/templates/correlation/show_correlation.html +++ b/var/www/templates/correlation/show_correlation.html @@ -113,6 +113,8 @@ {% include 'correlation/metadata_card_domain.html' %} {% elif dict_object["object_type"] == "screenshot" %} {% include 'correlation/metadata_card_screenshot.html' %} + {% elif dict_object["object_type"] == "title" %} + {% include 'correlation/metadata_card_title.html' %} {% elif dict_object["object_type"] == "item" %} {% include 'correlation/metadata_card_item.html' %} {% endif %} diff --git a/var/www/templates/crawler/crawler_splash/showDomain.html b/var/www/templates/crawler/crawler_splash/showDomain.html index 43f00d4b..b2e2f0b3 100644 --- a/var/www/templates/crawler/crawler_splash/showDomain.html +++ b/var/www/templates/crawler/crawler_splash/showDomain.html @@ -347,6 +347,46 @@ {% endif %} + {% if 'title' in dict_domain%} +
+
+
+
+
+
+ Titles   +
{{dict_domain['title']|length}}
+
+
+
+ +
+
+
+
+
+ + + + + + + + {% for title in dict_domain['title']%} + + + + {% endfor %} + +
Tilte
{{ title[1] }}
+
+
+
+
+ {% endif %} + {% if dict_domain["history"] %}
@@ -489,6 +529,9 @@ {% endif %} {% if 'cryptocurrency' in dict_domain%} $('#tablecurrency').DataTable({}); + {% endif %} + {% if 'title' in dict_domain%} + $('#tabletitle').DataTable({}); {% endif %} table = $('#myTable_1').DataTable( { diff --git a/var/www/templates/objects/title/TitleDaterange.html b/var/www/templates/objects/title/TitleDaterange.html new file mode 100644 index 00000000..17efda7c --- /dev/null +++ b/var/www/templates/objects/title/TitleDaterange.html @@ -0,0 +1,611 @@ + + + + + Titles - AIL + + + + + + + + + + + + + + + + + + + + + + + + {% include 'nav_bar.html' %} + +
+
+ + {% include 'sidebars/sidebar_objects.html' %} + +
+ +
+
+
+ +
+
+
Search Title by name:
+
+
+ + +
+
+
+
+
+ + +
+ +
+
+
Select a date range :
+
+
+
+ +
+
+
+ +
+
+ + +
+ +
+
+
+ +
+
+
+
+
+
+ + {% if dict_objects %} + {% if date_from|string == date_to|string %} +

{{ date_from }} Title:

+ {% else %} +

{{ date_from }} to {{ date_to }} Title:

+ {% endif %} + + + + + + + + + + + + {% for obj_id in dict_objects %} + + + + + + + + {% endfor %} + +
First SeenLast SeenTotalLast days
{{ dict_objects[obj_id]['content'] }}{{ dict_objects[obj_id]['first_seen'] }}{{ dict_objects[obj_id]['last_seen'] }}{{ dict_objects[obj_id]['nb_seen'] }}
+ + + {% else %} + {% if show_objects %} + {% if date_from|string == date_to|string %} +

{{ date_from }}, No Title

+ {% else %} +

{{ date_from }} to {{ date_to }}, No Title

+ {% endif %} + {% endif %} + {% endif %} +
+ +
+
+ + + + + + + + + + + + + + + + + diff --git a/var/www/templates/sidebars/sidebar_objects.html b/var/www/templates/sidebars/sidebar_objects.html index 46fe58eb..4f09e138 100644 --- a/var/www/templates/sidebars/sidebar_objects.html +++ b/var/www/templates/sidebars/sidebar_objects.html @@ -34,6 +34,12 @@ CVE +