diff --git a/pymisp/tools/reportlab_generator.py b/pymisp/tools/reportlab_generator.py
index 9cf748d..eb5f6a7 100644
--- a/pymisp/tools/reportlab_generator.py
+++ b/pymisp/tools/reportlab_generator.py
@@ -6,12 +6,8 @@ import base64
import logging
import pprint
from io import BytesIO
-from functools import partial
-
import sys
-from orca.cmdnames import TABLE_CELL_FIRST
-
if sys.version_info.major >= 3:
from html import escape
# import PIL
@@ -223,7 +219,44 @@ def get_sample_styles():
sample_style_sheet.list()
-# "INTERNAL" METHODS. Not meant to be used outside of this class.
+########################################################################
+# General Event's Attributes formater tools
+
+def uuid_to_url(baseurl, uuid):
+ '''
+ Return an url constructed from the MISP baseurl and the uuid of the event, to go to this event on this MISP
+ :param baseurl: the baseurl of the MISP instnce e.g. http://localhost:8080 or http://localhost:8080/
+ :param uuid: the uuid of the event that we want to have a link to
+ :return: the complete URL to go to this event on this MISP instance
+ '''
+ if baseurl[len(baseurl) - 1] != "/":
+ baseurl += "/"
+ return baseurl + "events/view/" + uuid
+
+
+def create_flowable_table_from_data(data, col_w=COL_WIDTHS):
+ '''
+ Given a list of flowables items (2D/list of list), creates a Table with styles.
+ :param data: list of list of items (flowables is better)
+ :return: a Table - with styles - to add to the pdf
+ '''
+ # Create the table
+ curr_table = Table(data, col_w)
+
+ # Aside notes :
+ # colWidths='*' does a 100% and share the space automatically
+ # rowHeights=ROW_HEIGHT if you want a fixed height. /!\ Problems with paragraphs that are spreading everywhere
+
+ # Create styles and set parameters
+ alternate_colors_style = alternate_colors_style_generator(data)
+ lines_style = lines_style_generator(data)
+ general_style = general_style_generator()
+
+ # Make the table nicer
+ curr_table.setStyle(TableStyle(general_style + alternate_colors_style + lines_style))
+
+ return curr_table
+
def alternate_colors_style_generator(data):
'''
@@ -318,6 +351,7 @@ def get_table_styles():
def safe_string(bad_str):
return escape(str(bad_str))
+
def is_safe_attribute(curr_object, attribute_name):
return hasattr(curr_object, attribute_name) and getattr(curr_object, attribute_name) is not None and getattr(
curr_object, attribute_name) != ""
@@ -332,946 +366,1011 @@ def is_safe_attribute_table(curr_object, attribute_name):
return hasattr(curr_object, attribute_name) and getattr(curr_object, attribute_name) is not None and getattr(
curr_object, attribute_name) != []
+def is_in_config(config, index):
+ return config is not None and moduleconfig[index] in config
+
########################################################################
-# General attribut formater
+# Functions grouped by misp object type
-def get_unoverflowable_paragraph(dirty_string, curr_style, do_escape_string=True):
+
+class Value_Formatter():
'''
- Create a paragraph that can fit on a cell of one page. Mostly hardcoded values.
- This method can be improved (get the exact size of the current frame, and limit the paragraph to this size.)
- This might be worst look at KeepInFrame (which hasn't went well so far)
- :param do_escape_string: Activate the escaping (may be useful to add inline HTML, e.g. hyperlinks)
- :param dirty_string: String to transform
- :param curr_style: Style to apply to the returned paragraph
- :return:
+ "item" parameter should be as follow, a list of name, in order :
+ ["Name to be print in the pdf", "json property access name",
+ " Name to be display if no values found in the misp_event"]
'''
- if do_escape_string:
- sanitized_str = str(escape(str(dirty_string)))
- else:
- sanitized_str = dirty_string
+ # ----------------------------------------------------------------------
+ def __init__(self, config, col1_style, col2_style):
+ self.config = config
+ self.col1_style = col1_style
+ self.col2_style = col2_style
- # Get the space that the paragraph needs to be printed
- w, h = Paragraph(sanitized_str, curr_style).wrap(FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT)
+ # ----------------------------------------------------------------------
+ ########################################################################
+ # General attribut formater
+ def get_col1_paragraph(self, dirty_string):
+ return self.get_unoverflowable_paragraph(dirty_string, self.col1_style)
- # If there is enough space, directly send back the sanitized paragraph
- if w <= FRAME_MAX_WIDTH and h <= FRAME_MAX_HEIGHT:
- answer_paragraph = Paragraph(sanitized_str, curr_style)
- else:
- # Otherwise, cut the content to fit the paragraph (Dichotomy)
- max_carac_amount = int((FRAME_MAX_HEIGHT / (h * 1.0)) * len(sanitized_str))
+ def get_unoverflowable_paragraph(self, dirty_string, curr_style = None, do_escape_string=True):
+ '''
+ Create a paragraph that can fit on a cell displayed one page maximum.
+ This method can be improved (get the exact size of the current frame, and limit the paragraph to this size.)
+ KeepInFrame may give a nicer solution (not for me so far)
+ :param do_escape_string: Activate the escaping (may be useful to add inline HTML, e.g. hyperlinks)
+ :param dirty_string: String to transform
+ :param curr_style: Style to apply to the returned paragraph
+ :return:
+ '''
+ if do_escape_string:
+ sanitized_str = safe_string(dirty_string)
+ else:
+ sanitized_str = dirty_string
- i = 0
- MAX_ITERATION = 10
- limited_string = ""
- while (w > FRAME_MAX_WIDTH or h > FRAME_MAX_HEIGHT) and i < MAX_ITERATION:
- i += 1
- limited_string = sanitized_str[:max_carac_amount] # .replace("\n", "").replace("\r", "")
- w, h = Paragraph(limited_string + STR_TOO_LONG_WARNING, curr_style).wrap(FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT)
- max_carac_amount = int(max_carac_amount / 2)
+ if curr_style is None:
+ curr_style = self.col2_style
+ # Get the space that the paragraph needs to be printed
+ w, h = Paragraph(sanitized_str, curr_style).wrap(FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT)
+
+ # If there is enough space, directly send back the sanitized paragraph
if w <= FRAME_MAX_WIDTH and h <= FRAME_MAX_HEIGHT:
- answer_paragraph = Paragraph(limited_string + STR_TOO_LONG_WARNING, curr_style)
+ answer_paragraph = Paragraph(sanitized_str, curr_style)
else:
- # We may still end with a not short enough string
- answer_paragraph = Paragraph(STR_TOO_LONG_WARNING, curr_style)
+ # Otherwise, cut the content to fit the paragraph (Dichotomy)
+ max_carac_amount = int((FRAME_MAX_HEIGHT / (h * 1.0)) * len(sanitized_str))
- return answer_paragraph
+ i = 0
+ MAX_ITERATION = 10
+ limited_string = ""
+ while (w > FRAME_MAX_WIDTH or h > FRAME_MAX_HEIGHT) and i < MAX_ITERATION:
+ i += 1
+ limited_string = sanitized_str[:max_carac_amount] # .replace("\n", "").replace("\r", "")
+ w, h = Paragraph(limited_string + STR_TOO_LONG_WARNING, curr_style).wrap(FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT)
+ max_carac_amount = int(max_carac_amount / 2)
-########################################################################
-# Specific attribute formater
-
-def get_value_link_to_event(misp_event, item, col2_style, config=None, color=True):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_event uuid, with or without link
- :param config: Config dictionnary provided by MISP instance, via misp-modules (with baseurl)
- :param misp_event: A misp event with or without "uuid" attributes
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of "uuid"
- '''
-
- # Does MispEven has the attribute ?
- if is_safe_attribute(misp_event, item[1]):
- # It has the requested attribute .. building upon it.
-
- # Does misp_object has an uuid and do we know the baseurl ?
- if is_safe_attribute(misp_event, "uuid") and config is not None and moduleconfig[0] in config:
- # We can build links
- curr_uuid = str(getattr(misp_event, "uuid"))
- curr_baseurl = config[moduleconfig[0]]
- curr_url = uuid_to_url(curr_baseurl, curr_uuid)
- html_url = "" + safe_string(getattr(misp_event, item[1])) + ""
-
- if color:
- # They want fancy colors
- html_url = "" + html_url + ""
-
- # Construct final paragraph
- answer = get_unoverflowable_paragraph(html_url, col2_style, False)
-
- else:
- # We can't build links
- answer = get_unoverflowable_paragraph(getattr(misp_event, item[1]), col2_style)
-
- else:
- # No it doesn't, so we directly give the default answer
- answer = Paragraph(item[2], col2_style)
-
- return answer
-
-
-def get_date_value(misp_event, item, col2_style):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_event date
- :param misp_event: A misp event with or without "date" attributes
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of "date"
- '''
- if is_safe_attribute(misp_event, item[1]):
- return Paragraph(safe_string(getattr(misp_event, item[1])), col2_style)
- return Paragraph(item[2], col2_style)
-
-
-def get_owner_value(misp_event, item, col2_style):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_event owner
- :param misp_event: A misp event with or without "owner" attributes
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of "owner"
- '''
- if is_safe_attribute(misp_event, item[1]):
- return Paragraph(safe_string(getattr(misp_event, item[1])), col2_style)
- return Paragraph(item[2], col2_style)
-
-
-def get_threat_value(misp_event, item, col2_style):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_event threat
- :param misp_event: A misp event with or without "threat" attributes
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of "threat"
- '''
- if is_safe_attribute(misp_event, item[1]) and str(getattr(misp_event, item[1])) in threat_map:
- return Paragraph(threat_map[safe_string(getattr(misp_event, item[1]))], col2_style)
- return Paragraph(item[2], col2_style)
-
-
-def get_analysis_value(misp_event, item, col2_style):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_event analysis
- :param misp_event: A misp event with or without "analysis" attributes
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of "analysis"
- '''
- if is_safe_attribute(misp_event, item[1]) and str(getattr(misp_event, item[1])) in analysis_map:
- return Paragraph(analysis_map[safe_string(getattr(misp_event, item[1]))], col2_style)
- return Paragraph(item[2], col2_style)
-
-
-def get_timestamp_value(misp_event, item, col2_style):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_event timestamp
- :param misp_event: A misp event with or without "timestamp" attributes
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of "timestamp"
- '''
- if is_safe_attribute(misp_event, item[1]):
- return Paragraph(safe_string(getattr(misp_event, item[1]).strftime(EXPORT_DATE_FORMAT)), col2_style)
- return Paragraph(item[2], col2_style)
-
-
-def get_creator_organisation_value(misp_event, item, col2_style):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_event creator organisation
- :param misp_event: A misp event with or without "timestamp" attributes
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event", "json property access name (second level)"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of "creator organisation"
- '''
- if is_safe_attribute(misp_event, item[1]):
- return Paragraph(safe_string(getattr(getattr(misp_event, item[1]), item[3])), col2_style)
- return Paragraph(item[2], col2_style)
-
-
-def get_attributes_number_value(misp_event, item, col2_style):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_event attributes
- :param misp_event: A misp event with or without "attributes" attributes
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of "attributes"
- '''
- if is_safe_attribute(misp_event, item[1]):
- return Paragraph(safe_string(len(getattr(misp_event, item[1]))), col2_style)
- return Paragraph(item[2], col2_style)
-
-
-def get_tag_value(misp_event, item, col2_style):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_event tags
- :param misp_event: A misp event with or without "tags" attributes
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of "tags"
- '''
- if is_safe_attribute_table(misp_event, item[1]):
- table_event_tags = create_flowable_table_from_tags(misp_event)
- return table_event_tags
- return Paragraph(item[2], col2_style)
-
-
-def get_galaxy_value(misp_event, item, col2_style):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_event galaxies
- :param misp_event: A misp event with or without "galaxies" attributes
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of "galaxies"
- '''
- if is_safe_attribute_table(misp_event, item[1]):
- table_event_tags = create_flowable_table_from_galaxies(misp_event)
- return table_event_tags
- return Paragraph(item[2], col2_style)
-
-
-def get_published_value(misp_event, item, col2_style):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_event published/published_time
- More information on how to play with paragraph into reportlab cells :
- https://stackoverflow.com/questions/11810008/reportlab-add-two-paragraphs-into-one-table-cell
- :param misp_event: A misp event with or without "published"/"publish_timestamp" attributes
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event", json property access name (for timestamp")]
- e.g. item = ["Published", 'published', "None", "publish_timestamp"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of "published"/"publish_timestamp"
- '''
-
- RED_COLOR = '#ff0000'
- GREEN_COLOR = '#008000'
- YES_ANSWER = " Yes ("
- NO_ANSWER = "No"
-
- # Formatting similar to MISP Event web view
- if is_safe_attribute(misp_event, item[1]):
- if getattr(misp_event, item[1]): # == True
- if is_safe_attribute(misp_event, item[3]):
- # Published and have published date
- answer = Paragraph(YES_ANSWER + getattr(misp_event, item[3]).strftime(EXPORT_DATE_FORMAT) + ")",
- col2_style)
+ if w <= FRAME_MAX_WIDTH and h <= FRAME_MAX_HEIGHT:
+ answer_paragraph = Paragraph(limited_string + STR_TOO_LONG_WARNING, curr_style)
else:
- # Published without published date
- answer = Paragraph(YES_ANSWER + "no date)", col2_style)
+ # We may still end with a not short enough string
+ answer_paragraph = Paragraph(STR_TOO_LONG_WARNING, curr_style)
+
+ return answer_paragraph
+
+
+ def get_value_link_to_event(self, misp_event, item, curr_style = None, color=True):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_event uuid, with or without link
+ :param color: Boolean to give a color or not to the generate link (good link color)
+ :param config: Config dictionnary provided by MISP instance, via misp-modules (with baseurl)
+ :param misp_event: A misp event with or without "uuid" attributes
+ :param item: as defined in class definition
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of "uuid"
+ '''
+
+ if curr_style is None:
+ curr_style = self.col2_style
+
+ # Does MispEven has the attribute ?
+ if is_safe_attribute(misp_event, item[1]):
+ # It has the requested attribute .. building upon it.
+
+ # Does misp_object has an uuid and do we know the baseurl ?
+ if is_safe_attribute(misp_event, "uuid") and is_in_config(self.config,0):
+ # We can build links
+ curr_uuid = str(getattr(misp_event, "uuid"))
+ curr_baseurl = self.config[moduleconfig[0]]
+ curr_url = uuid_to_url(curr_baseurl, curr_uuid)
+ html_url = "" + safe_string(getattr(misp_event, item[1])) + ""
+
+ if color:
+ # They want fancy colors
+ html_url = "" + html_url + ""
+
+ # Construct final paragraph
+ answer = self.get_unoverflowable_paragraph(html_url, curr_style=curr_style, do_escape_string=False)
+
+ else:
+ # We can't build links
+ answer = self.get_unoverflowable_paragraph(getattr(misp_event, item[1]), curr_style=curr_style)
else:
- # Not published
- answer = Paragraph(NO_ANSWER, col2_style)
- else:
- # Does not have a published attribute
- answer = Paragraph(item[2], col2_style)
+ # No it doesn't, so we directly give the default answer
+ answer = self.get_unoverflowable_paragraph(item[2], curr_style=curr_style)
- return answer
+ return answer
-def create_flowable_table_from_one_attribute(misp_attribute, config=None):
- '''
- Returns a table (flowalbe) representing the attribute
- :param misp_attribute: A misp attribute
- :return: a table representing this misp's attribute's attributes, to add to the pdf as a flowable
- '''
+ ########################################################################
+ # Specific attribute formater
- data = []
- col1_style, col2_style = get_table_styles()
+ def get_date_value(self, misp_event, item):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_event date
+ :param misp_event: A misp event with or without "date" attributes
+ :param item: as defined in class definition
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of "date"
+ '''
+ if is_safe_attribute(misp_event, item[1]):
+ return self.get_unoverflowable_paragraph(safe_string(getattr(misp_event, item[1])))
+ return self.get_unoverflowable_paragraph(item[2])
- # To reduce code size, and automate it a bit, triplet (Displayed Name, object_attribute_name,
- # to_display_if_not_present) are store in the following list
- list_attr_automated = [["UUID", 'uuid', "None"],
- ["Category", 'category', "None"],
- ["Comment", 'comment', "None"],
- ["Type", 'type', "None"],
- ["Value", 'value', "None"]]
- # Handle the special case of links
- STANDARD_TYPE = True
- if is_safe_attribute(misp_attribute, 'type') and (getattr(misp_attribute, 'type') in [LINK_TYPE, URL_TYPE]):
- # getattr(misp_attribute, 'type') == LINK_TYPE or getattr(misp_attribute, 'type') == URL_TYPE):
- # Special case for links
- STANDARD_TYPE = False
+ def get_owner_value(self, misp_event, item):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_event owner
+ :param misp_event: A misp event with or without "owner" attributes
+ :param item: as defined in class definition
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of "owner"
+ '''
+ if is_safe_attribute(misp_event, item[1]):
+ return self.get_unoverflowable_paragraph(safe_string(getattr(misp_event, item[1])))
+ return self.get_unoverflowable_paragraph(item[2])
- # Automated adding of standard (python) attributes of the misp event
- for item in list_attr_automated:
- if is_safe_attribute(misp_attribute, item[1]) and (STANDARD_TYPE or item[1] != 'value'):
- # The attribute exists, we fetch it and create the row
- data.append([Paragraph(item[0], col1_style),
- get_unoverflowable_paragraph(getattr(misp_attribute, item[1]), col2_style)])
- # The attribute does not exist, you may want to print a default text on the row. Then use as a else case :
- # data.append([Paragraph(item[0], col1_style), Paragraph(item[2], col2_style)])
+ def get_threat_value(self, misp_event, item):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_event threat
+ :param misp_event: A misp event with or without "threat" attributes
+ :param item: as defined in class definition
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of "threat"
+ '''
+ if is_safe_attribute(misp_event, item[1]) and str(getattr(misp_event, item[1])) in threat_map:
+ return self.get_unoverflowable_paragraph(threat_map[safe_string(getattr(misp_event, item[1]))], do_escape_string=False)
+ return self.get_unoverflowable_paragraph(item[2])
- # Handle Special case for links (Value) - There were not written in the previous loop
- item = ["Value", 'value', "None"]
- if not STANDARD_TYPE and is_safe_attribute(misp_attribute, item[1]):
- data.append([Paragraph(item[0], col1_style), get_good_or_bad_link(misp_attribute, item, col2_style)])
- # Handle pictures
- item = ["Data", 'data', "None"]
- if is_safe_attribute(misp_attribute, item[1]) and getattr(misp_attribute, 'type') == IMAGE_TYPE:
- data.append([Paragraph(item[0], col1_style), get_image_value(misp_attribute, item, col2_style)])
+ def get_analysis_value(self, misp_event, item):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_event analysis
+ :param misp_event: A misp event with or without "analysis" attributes
+ :param item: as defined in class definition
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of "analysis"
+ '''
+ if is_safe_attribute(misp_event, item[1]) and str(getattr(misp_event, item[1])) in analysis_map:
+ return self.get_unoverflowable_paragraph(analysis_map[safe_string(getattr(misp_event, item[1]))], do_escape_string=False)
+ return self.get_unoverflowable_paragraph(item[2])
- # Tags
- item = ["Tags", 'Tag', "None"]
- if is_safe_attribute_table(misp_attribute, item[1]):
- data.append([Paragraph(item[0], col1_style), get_tag_value(misp_attribute, item, col2_style)])
- # Sighting
- item = ["Sighting", 'Sighting', "None"]
- if is_safe_attribute_table(misp_attribute, item[1]):
- data.append([Paragraph(item[0], col1_style),
- create_flowable_paragraph_from_sightings(misp_attribute, item, col2_style)])
+ def get_timestamp_value(self, misp_event, item):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_event timestamp
+ :param misp_event: A misp event with or without "timestamp" attributes
+ :param item: as defined in class definition
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of "timestamp"
+ '''
+ if is_safe_attribute(misp_event, item[1]):
+ return self.get_unoverflowable_paragraph(safe_string(getattr(misp_event, item[1]).strftime(EXPORT_DATE_FORMAT)))
+ return self.get_unoverflowable_paragraph(item[2])
- if config is not None and moduleconfig[3] in config:
- # Galaxies
- item = ["Galaxies", 'Galaxy', "None"]
+
+ def get_creator_organisation_value(self, misp_event, item):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_event creator organisation
+ :param misp_event: A misp event with or without "timestamp" attributes
+ :param item: a list of name, in order :
+ ["Name to be print in the pdf", "json property access name",
+ " Name to be display if no values found in the misp_event", "json property access name (second level)"]
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of "creator organisation"
+ '''
+ if is_safe_attribute(misp_event, item[1]):
+ return self.get_unoverflowable_paragraph(safe_string(getattr(getattr(misp_event, item[1]), item[3])))
+ return self.get_unoverflowable_paragraph(item[2])
+
+
+ def get_attributes_number_value(self, misp_event, item):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_event attributes
+ :param misp_event: A misp event with or without "attributes" attributes
+ :param item: as defined in class definition
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of "attributes"
+ '''
+ if is_safe_attribute(misp_event, item[1]):
+ return self.get_unoverflowable_paragraph(safe_string(len(getattr(misp_event, item[1]))))
+ return self.get_unoverflowable_paragraph(item[2])
+
+
+
+ def get_published_value(self, misp_event, item):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_event published/published_time
+ More information on how to play with paragraph into reportlab cells :
+ https://stackoverflow.com/questions/11810008/reportlab-add-two-paragraphs-into-one-table-cell
+ :param misp_event: A misp event with or without "published"/"publish_timestamp" attributes
+ :param item: a list of name, in order :
+ ["Name to be print in the pdf", "json property access name",
+ " Name to be display if no values found in the misp_event", json property access name (for timestamp")]
+ e.g. item = ["Published", 'published', "None", "publish_timestamp"]
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of "published"/"publish_timestamp"
+ '''
+
+ RED_COLOR = '#ff0000'
+ GREEN_COLOR = '#008000'
+ YES_ANSWER = " Yes ("
+ NO_ANSWER = "No"
+
+ # Formatting similar to MISP Event web view
+ if is_safe_attribute(misp_event, item[1]):
+ if getattr(misp_event, item[1]): # == True
+ if is_safe_attribute(misp_event, item[3]):
+ # Published and have published date
+ answer = self.get_unoverflowable_paragraph(YES_ANSWER + getattr(misp_event, item[3]).strftime(EXPORT_DATE_FORMAT) + ")", do_escape_string=False)
+ else:
+ # Published without published date
+ answer = self.get_unoverflowable_paragraph(YES_ANSWER + "no date)", do_escape_string=False)
+
+ else:
+ # Not published
+ answer = self.get_unoverflowable_paragraph(NO_ANSWER, do_escape_string=False)
+ else:
+ # Does not have a published attribute
+ answer = self.get_unoverflowable_paragraph(item[2], do_escape_string=False)
+
+ return answer
+
+
+ def get_image_value(self, misp_attribute, item):
+ '''
+ Returns a flowable image to add to the pdf given the misp attribute type and data
+ :param misp_attribute: A misp attribute with type="attachement" and data
+ :param item: as defined in class definition
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a flowable image to add in the pdf, regarding the values of "data"
+ '''
+
+ try:
+ # Get the image
+ buf = getattr(misp_attribute, item[1])
+
+ # Create image within a bounded box (to allow pdf creation)
+ img = Image(buf, width=FRAME_PICTURE_MAX_WIDTH, height=FRAME_PICTURE_MAX_HEIGHT, kind='bound')
+ answer = img
+
+ except OSError:
+ logger.error(
+ "Trying to add an attachment during PDF export generation. Attachement joining failed. Attachement may not be an image.")
+ answer = self.get_unoverflowable_paragraph(
+ "" + NOT_A_PICTURE_MESSAGE + "", do_escape_string=False)
+
+ return answer
+
+ def get_good_link(self,misp_attribute, item):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_attribute value, if this is a link
+ :param misp_attribute: A misp attribute with a link
+ :param item: as defined in class definition
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of this "link" attribute
+ '''
+ return self.get_unoverflowable_paragraph(
+ "" + getattr(
+ misp_attribute, item[1]) + "", do_escape_string=False)
+
+
+ def get_bad_link(self,misp_attribute, item):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_attribute value, if this is a link
+ :param misp_attribute: A misp event with an url
+ :param item: as defined in class definition
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of this "url" attribute
+ '''
+ return self.get_unoverflowable_paragraph(
+ "" + getattr(misp_attribute,
+ item[1]) + "", do_escape_string=False)
+
+
+ def get_good_or_bad_link(self,misp_attribute, item):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_attribute value, if this is a link or an url
+ :param misp_attribute: A misp attribute with a link or an url
+ :param item: as defined in class definition
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of this "link" or "url" attribute
+ '''
+
+ answer = self.get_unoverflowable_paragraph("Not an URL")
+
+ # Handle "Good" links
+ if getattr(misp_attribute, 'type') == LINK_TYPE:
+ answer = self.get_good_link(misp_attribute, item)
+ # Handle "bad "links
+ elif getattr(misp_attribute, 'type') == URL_TYPE:
+ answer = self.get_bad_link(misp_attribute, item)
+
+ return answer
+
+
+class Event_Metadata():
+
+ # ----------------------------------------------------------------------
+ def __init__(self, config, value_formatter):
+ self.config = config
+ self.curr_val_f = value_formatter
+
+ # ----------------------------------------------------------------------
+
+ ########################################################################
+ # General Event's Attributes formater
+
+ def create_flowable_table_from_event(self, misp_event):
+ '''
+ Returns Table presenting a MISP event
+ :param misp_event: A misp event (complete or not)
+ :return: a table that can be added to a pdf
+ '''
+
+ data = []
+
+ # Manual addition
+ # UUID
+ item = ["UUID", 'uuid', "None"]
+ data.append([self.curr_val_f.get_col1_paragraph(item[0]), self.curr_val_f.get_value_link_to_event(misp_event, item)])
+
+ # Date
+ item = ["Date", 'date', "None"]
+ data.append([self.curr_val_f.get_col1_paragraph(item[0]), self.curr_val_f.get_date_value(misp_event, item)])
+
+ # Owner
+ item = ["Owner org", 'owner', "None"]
+ data.append([self.curr_val_f.get_col1_paragraph(item[0]), self.curr_val_f.get_owner_value(misp_event, item)])
+
+ # Threat
+ item = ["Threat level", 'threat_level_id', "None"]
+ data.append([self.curr_val_f.get_col1_paragraph(item[0]), self.curr_val_f.get_threat_value(misp_event, item)])
+
+ # Analysis
+ item = ["Analysis", 'analysis', "None"]
+ data.append([self.curr_val_f.get_col1_paragraph(item[0]), self.curr_val_f.get_analysis_value(misp_event, item)])
+
+ # Info
+ item = ["Info", 'info', "None"]
+ data.append([self.curr_val_f.get_col1_paragraph(item[0]), self.curr_val_f.get_value_link_to_event(misp_event, item)])
+
+ # Timestamp
+ item = ["Event date", 'timestamp', "None"]
+ data.append([self.curr_val_f.get_col1_paragraph(item[0]), self.curr_val_f.get_timestamp_value(misp_event, item)])
+
+ # Published
+ item = ["Published", 'published', "None", "publish_timestamp"]
+ data.append([self.curr_val_f.get_col1_paragraph(item[0]), self.curr_val_f.get_published_value(misp_event, item)])
+
+ # Creator organisation
+ item = ["Creator Org", 'Orgc', "None", "name"]
+ data.append([self.curr_val_f.get_col1_paragraph(item[0]), self.curr_val_f.get_creator_organisation_value(misp_event, item)])
+
+ # Number of Attributes
+ item = ["# Attributes", 'Attribute', "None"]
+ data.append([self.curr_val_f.get_col1_paragraph(item[0]), self.curr_val_f.get_attributes_number_value(misp_event, item)])
+
+ # Tags
+ item = ["Tags", 'Tag', "None"]
+ curr_Tags = Tags(self.config, self.curr_val_f)
+ data.append([self.curr_val_f.get_col1_paragraph(item[0]), curr_Tags.get_tag_value(misp_event, item)])
+
+ if is_in_config(self.config, 3):
+ # Galaxies
+ item = ["Galaxies", 'Galaxy', "None"]
+ if is_safe_attribute_table(misp_event, item[1]):
+ curr_Galaxy = Galaxy(self.config, self.curr_val_f)
+ data.append([self.curr_val_f.get_col1_paragraph(item[0]), curr_Galaxy.get_galaxy_value(misp_event, item)])
+
+ return create_flowable_table_from_data(data)
+
+ def create_flowable_description_from_event(self, misp_event):
+ '''
+ Returns a Paragraph presenting a MISP event
+ :param misp_event: A misp event (complete or not)
+ :return: a paragraph that can be added to a pdf
+ '''
+
+ '''
+ The event "{EventName}" | that occurred on {EventDate}, | had been shared by {Organisation Name} | on the {Date}.
+ '''
+
+ text = ""
+
+ item = ["Info", 'info', "None"]
+ if is_safe_attribute(misp_event, item[1]):
+ text += "The event '"
+ text += safe_string(getattr(misp_event, item[1]))
+ text += "'"
+ else:
+ text += "This event"
+
+ item = ["Event date", 'timestamp', "None"]
+ if is_safe_attribute(misp_event, item[1]):
+ text += " that occurred on "
+ text += safe_string(getattr(misp_event, item[1]).strftime(EXPORT_DATE_FORMAT))
+ text += ","
+
+ item = ["Creator Org", 'Orgc', "None", "name"]
+ text += " had been shared by "
+ if is_safe_attribute(misp_event, item[1]):
+ text += safe_string(getattr(getattr(misp_event, item[1]), item[3]))
+ else:
+ text += " an unknown organisation"
+
+ item = ["Date", 'date', "None"]
+ if is_safe_attribute(misp_event, item[1]):
+ text += " on the "
+ text += safe_string(getattr(misp_event, item[1]))
+ else:
+ text += " on an unknown date"
+ text += "."
+
+ '''
+ The threat level of this event is {ThreatLevel} and the analysis that was made of this event is {AnalysisLevel}.
+ '''
+
+ item = ["Threat level", 'threat_level_id', "None"]
+ text += " The threat level of this event is "
+ if is_safe_attribute(misp_event, item[1]) and safe_string(getattr(misp_event, item[1])) in threat_map:
+ text += threat_map[safe_string(getattr(misp_event, item[1]))]
+ else:
+ text += " unknown"
+
+ item = ["Analysis", 'analysis', "None"]
+ text += " and the analysis that was made of this event is "
+ if is_safe_attribute(misp_event, item[1]) and safe_string(getattr(misp_event, item[1])) in analysis_map:
+ text += analysis_map[safe_string(getattr(misp_event, item[1]))]
+ else:
+ text += " undefined"
+ text += "."
+
+ '''
+ The event is currently {Published} and has associated attributes {Attribute Number}.
+ '''
+
+ item = ["Published", 'published', "None", "publish_timestamp"]
+ text += " The event is currently "
+ if is_safe_attribute(misp_event, item[1]) and getattr(misp_event, item[1]):
+ text += " published"
+ if is_safe_attribute(misp_event, item[3]):
+ text += " since " + getattr(misp_event, item[3]).strftime(EXPORT_DATE_FORMAT)
+ else:
+ text += " private"
+
+ # Number of Attributes
+ item = ["# Attributes", 'Attribute', "None"]
+ text += ", has "
+ if is_safe_attribute_table(misp_event, item[1]):
+ text += safe_string(len(getattr(misp_event, item[1])))
+ else:
+ text += " 0"
+
+ text += " associated attributes"
+
+ # Number of Objects
+ item = ["# Objects", 'Object', "None"]
+ text += " and has "
+ if is_safe_attribute_table(misp_event, item[1]):
+ text += safe_string(len(getattr(misp_event, item[1])))
+ else:
+ text += " 0"
+
+ text += " associated objects."
+
+ '''
+ For more information on the event, please consult the rest of the document
+ '''
+ text += "
For more information on the event, please consult following information."
+
+ description_style = ParagraphStyle(name='Description', parent=self.curr_val_f.col2_style, alignment=TA_JUSTIFY)
+
+ return Paragraph(text, description_style)
+
+
+
+class Attributes():
+
+ # ----------------------------------------------------------------------
+ def __init__(self, config, value_formatter):
+ self.config = config
+ self.value_formatter = value_formatter
+ self.sample_style_sheet = getSampleStyleSheet()
+ # ----------------------------------------------------------------------
+
+ def create_flowable_table_from_attributes(self, misp_event):
+ '''
+ Returns a list of flowables representing the list of attributes of a misp event.
+ The list is composed alternatively of headers and tables, to add to the pdf
+ :param misp_event: A misp event
+ :return: a table of flowables
+ '''
+ flowable_table = []
+ i = 0
+
+ if is_safe_attribute_table(misp_event, "Attribute"):
+ # There is some attributes for this object
+ for item in getattr(misp_event, "Attribute"):
+ # you can use a spacer instead of title to separate paragraph: flowable_table.append(Spacer(1, 5 * mm))
+ flowable_table.append(Paragraph("Attribute #" + str(i), self.sample_style_sheet['Heading4']))
+ flowable_table.append(self.create_flowable_table_from_one_attribute(item))
+ i += 1
+ else:
+ # No attributes for this object
+ flowable_table.append(Paragraph("No attributes", self.sample_style_sheet['Heading4']))
+
+ return flowable_table
+
+ def create_flowable_table_from_one_attribute(self, misp_attribute):
+ '''
+ Returns a table (flowalbe) representing the attribute
+ :param misp_attribute: A misp attribute
+ :return: a table representing this misp's attribute's attributes, to add to the pdf as a flowable
+ '''
+
+ data = []
+ col1_style, col2_style = get_table_styles()
+
+ # To reduce code size, and automate it a bit, triplet (Displayed Name, object_attribute_name,
+ # to_display_if_not_present) are store in the following list
+ list_attr_automated = [["UUID", 'uuid', "None"],
+ ["Category", 'category', "None"],
+ ["Comment", 'comment', "None"],
+ ["Type", 'type', "None"],
+ ["Value", 'value', "None"]]
+
+ # Handle the special case of links
+ STANDARD_TYPE = True
+ if is_safe_attribute(misp_attribute, 'type') and (getattr(misp_attribute, 'type') in [LINK_TYPE, URL_TYPE]):
+ # getattr(misp_attribute, 'type') == LINK_TYPE or getattr(misp_attribute, 'type') == URL_TYPE):
+ # Special case for links
+ STANDARD_TYPE = False
+
+ # Automated adding of standard (python) attributes of the misp event
+ for item in list_attr_automated:
+ if is_safe_attribute(misp_attribute, item[1]) and (STANDARD_TYPE or item[1] != 'value'):
+ # The attribute exists, we fetch it and create the row
+ data.append([self.value_formatter.get_col1_paragraph(item[0]),
+ self.value_formatter.get_unoverflowable_paragraph(getattr(misp_attribute, item[1]))])
+
+ # The attribute does not exist, you may want to print a default text on the row. Then use as a else case :
+ # data.append([Paragraph(item[0], col1_style), Paragraph(item[2], col2_style)])
+
+ # Handle Special case for links (Value) - There were not written in the previous loop
+ item = ["Value", 'value', "None"]
+ if not STANDARD_TYPE and is_safe_attribute(misp_attribute, item[1]):
+ data.append([self.value_formatter.get_col1_paragraph(item[0]), self.value_formatter.get_good_or_bad_link(misp_attribute, item)])
+
+ # Handle pictures
+ item = ["Data", 'data', "None"]
+ if is_safe_attribute(misp_attribute, item[1]) and getattr(misp_attribute, 'type') == IMAGE_TYPE:
+ data.append([self.value_formatter.get_col1_paragraph(item[0]), self.value_formatter.get_image_value(misp_attribute, item)])
+
+ # Tags
+ item = ["Tags", 'Tag', "None"]
+ curr_Tags = Tags(self.config, self.value_formatter)
if is_safe_attribute_table(misp_attribute, item[1]):
- data.append([Paragraph(item[0], col1_style), get_galaxy_value(misp_attribute, item, col2_style)])
+ data.append([self.value_formatter.get_col1_paragraph(item[0]), curr_Tags.get_tag_value(misp_attribute, item)])
- return create_flowable_table_from_data(data)
+ # Sighting
+ item = ["Sighting", 'Sighting', "None"]
+ curr_Sighting = Sightings(self.config, self.value_formatter)
+ if is_safe_attribute_table(misp_attribute, item[1]):
+ data.append([self.value_formatter.get_col1_paragraph(item[0]),
+ curr_Sighting.create_flowable_paragraph_from_sightings(misp_attribute, item)])
+
+ if is_in_config(self.config, 3):
+ # Galaxies
+ item = ["Galaxies", 'Galaxy', "None"]
+ curr_Galaxy = Galaxy(self.config, self.value_formatter)
+ if is_safe_attribute_table(misp_attribute, item[1]):
+ data.append([Paragraph(item[0], col1_style), curr_Galaxy.get_galaxy_value(misp_attribute, item)])
+
+ return create_flowable_table_from_data(data)
-def create_flowable_table_from_one_object(misp_object, config=None):
- '''
- Returns a table (flowable) representing the object
- :param misp_attribute: A misp object
- :return: a table representing this misp's object's attributes, to add to the pdf as a flowable
- '''
- data = []
- col1_style, col2_style = get_table_styles()
+class Tags():
- # To reduce code size, and automate it a bit, triplet (Displayed Name, object_attribute_name,
- # to_display_if_not_present) are store in the following list
- list_attr_automated = [["UUID", 'uuid', "None"],
- ["Description", 'description', "None"],
- ["Meta Category", 'meta-category', "None"],
- ["Object Name", 'name', "None"],
- ["Comment", 'comment', "None"],
- ["Type", 'type', "None"]]
+ # ----------------------------------------------------------------------
+ def __init__(self, config, value_formatter):
+ self.config = config
+ self.value_formatter = value_formatter
- # Automated adding of standard (python) attributes of the misp object
- for item in list_attr_automated:
- if is_safe_attribute(misp_object, item[1]):
- # The attribute exists, we fetch it and create the row
- data.append([Paragraph(item[0], col1_style),
- get_unoverflowable_paragraph(getattr(misp_object, item[1]), col2_style)])
+ # ----------------------------------------------------------------------
+ def get_tag_value(self, misp_event, item):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_event tags
+ :param misp_event: A misp event with or without "tags" attributes
+ :param item: as defined in class definition
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of "tags"
+ '''
+ if is_safe_attribute_table(misp_event, item[1]):
+ table_event_tags = self.create_flowable_table_from_tags(misp_event)
+ return table_event_tags
+ return self.value_formatter.get_unoverflowable_paragraph(item[2])
+
+ def create_flowable_table_from_tags(self, misp_event):
+ '''
+ Returns a Table (flowable) to add to a pdf, representing the list of tags of an event or a misp event
+ :param misp_event: A misp event
+ :return: a table of flowable to add to the pdf
+ '''
+
+ flowable_table = []
+ i = 0
+
+ if is_safe_attribute_table(misp_event,"Tag"):
+ # There is some tags for this object
+ for item in getattr(misp_event, "Tag"):
+ flowable_table.append(create_flowable_tag(item))
+ i += 1
+ answer_tags = self.create_tags_table_from_data(flowable_table)
+ else:
+ # No tags for this object
+ answer_tags = [self.value_formatter.get_unoverflowable_paragraph("No tags")]
+
+ return answer_tags
+
+ def create_tags_table_from_data(self, data):
+ '''
+ Given a list of flowables tags (2D/list of list), creates a Table with styles adapted to tags.
+ :param data: list of list of tags (flowables)
+ :return: a Table - with styles - to add to another table
+ '''
+
+ # Create the table
+ curr_table = Table(data, COL_WIDTHS, rowHeights=ROW_HEIGHT_FOR_TAGS)
+
+ # Create styles and set parameters
+ general_style = general_style_generator()
+
+ # Make the table nicer
+ curr_table.setStyle(TableStyle(general_style))
+
+ return curr_table
+
+
+class Sightings():
+
+ # ----------------------------------------------------------------------
+ def __init__(self, config, value_formatter):
+ self.config = config
+ self.value_formatter = value_formatter
+
+ # ----------------------------------------------------------------------
+
+ def create_flowable_paragraph_from_sightings(self, misp_attribute, item):
+ '''
+ Returns a Table (flowable) to add to a pdf, representing the list of sightings of an event or a misp event
+ :param misp_event: A misp event
+ :return: a table of flowable to add to the pdf
+ '''
+
+ col1_style, col2_style = get_table_styles()
+ i = 0
+ POSITIVE_SIGHT_COLOR = 'green'
+ NEGATIVE_SIGHT_COLOR = 'red'
+ MISC_SIGHT_COLOR = 'orange'
+
+ list_sighting = [0, 0, 0]
+ if is_safe_attribute_table(misp_attribute, item[1]):
+ # There is some tags for this object
+ for curr_item in getattr(misp_attribute, item[1]):
+ # TODO : When Sightings will be object : if is_safe_attribute(item, "type"):
+ if is_safe_dict_attribute(curr_item,"type"):
+ # Store the likes/dislikes depending on their types
+ list_sighting[int(curr_item["type"])] += 1
+ i += 1
+
+ # Create the sighting text
+ sight_text = " Positive : " + str(list_sighting[0]) + ""
+ sight_text += " / " + " Negative : " + str(
+ list_sighting[1]) + ""
+ sight_text += " / " + " Misc. : " + str(list_sighting[2]) + ""
+
+ answer_sighting = self.value_formatter.get_unoverflowable_paragraph(sight_text)
+ else:
+ # No tags for this object
+ answer_sighting = self.value_formatter.get_unoverflowable_paragraph("No sighting")
+
+ return answer_sighting
+
+class Object():
+
+ # ----------------------------------------------------------------------
+ def __init__(self, config, value_formatter):
+ self.config = config
+ self.value_formatter = value_formatter
+ self.sample_style_sheet = getSampleStyleSheet()
+
+ # ----------------------------------------------------------------------
+
+ def create_flowable_table_from_objects(self, misp_event, config=None):
+ '''
+ Returns a list of flowables representing the list of objects of a misp event.
+ The list is composed of a serie of
+ [ header object, table of object information, [ header of attribute, table of attribute]*] to add to the pdf
+ :param misp_event: A misp event
+ :return: a table of flowables
+ '''
+
+ flowable_table = []
+ i = 0
+
+ if is_safe_attribute_table(misp_event, "Object"):
+
+ # There is a list of objects
+ for item in getattr(misp_event, "Object"):
+ # you can use a spacer instead of title to separate paragraph: flowable_table.append(Spacer(1, 5 * mm))
+ flowable_table.append(Paragraph("Object #" + str(i), self.sample_style_sheet['Heading3']))
+ flowable_table += self.create_flowable_table_from_one_object(item, config)
+ i += 1
+ else:
+ # No object found
+ flowable_table.append(Paragraph("No object", self.sample_style_sheet['Heading3']))
+
+ return flowable_table
+
+ def create_flowable_table_from_one_object(self, misp_object, config=None):
+ '''
+ Returns a table (flowable) representing the object
+ :param misp_attribute: A misp object
+ :return: a table representing this misp's object's attributes, to add to the pdf as a flowable
+ '''
+ data = []
+ col1_style, col2_style = get_table_styles()
+
+ # To reduce code size, and automate it a bit, triplet (Displayed Name, object_attribute_name,
+ # to_display_if_not_present) are store in the following list
+ list_attr_automated = [["UUID", 'uuid', "None"],
+ ["Description", 'description', "None"],
+ ["Meta Category", 'meta-category', "None"],
+ ["Object Name", 'name', "None"],
+ ["Comment", 'comment', "None"],
+ ["Type", 'type', "None"]]
+
+ # Automated adding of standard (python) attributes of the misp object
+ for item in list_attr_automated:
+ if is_safe_attribute(misp_object, item[1]):
+ # The attribute exists, we fetch it and create the row
+ data.append([self.value_formatter.get_col1_paragraph(item[0]),
+ self.value_formatter.get_unoverflowable_paragraph(getattr(misp_object, item[1]))])
+
+ # The attribute does not exist, you may want to print a default text on the row. Then use as a else case :
+ # data.append([Paragraph(item[0], col1_style), Paragraph(item[2], col2_style)])
+
+ # Timestamp
+ item = ["Object date", 'timestamp', "None"]
+ data.append([Paragraph(item[0], col1_style), self.value_formatter.get_timestamp_value(misp_object, item)])
+
+ # Transform list of value in a table
+ data = [create_flowable_table_from_data(data)]
+
+ # Handle all the attributes
+ if is_safe_attribute(misp_object, "Attribute"):
+ curr_attributes = Attributes(self.config, self.value_formatter)
+ data += curr_attributes.create_flowable_table_from_attributes(misp_object)
+
+ # Add a page break at the end of an object
+ data.append(PageBreak())
+
+ return data
+
+
+class Galaxy():
+
+ # ----------------------------------------------------------------------
+ def __init__(self, config, value_formatter):
+ self.config = config
+ self.value_formatter = value_formatter
+
+ # ----------------------------------------------------------------------
+
+ def get_galaxy_value(self, misp_event, item):
+ '''
+ Returns a flowable paragraph to add to the pdf given the misp_event galaxies
+ :param misp_event: A misp event with or without "galaxies" attributes
+ :param item: a list of name, in order :
+ ["Name to be print in the pdf", "json property access name",
+ " Name to be display if no values found in the misp_event"]
+ :param col2_style: style to be applied on the returned paragraph
+ :return: a Paragraph to add in the pdf, regarding the values of "galaxies"
+ '''
+ if is_safe_attribute_table(misp_event, item[1]):
+ table_event_tags = self.create_flowable_table_from_galaxies(misp_event)
+ return table_event_tags
+ return self.value_formatter.get_unoverflowable_paragraph(item[2])
+
+ def create_flowable_table_from_galaxies(self, misp_event):
+ '''
+ Returns a Table (flowable) to add to a pdf, representing the list of galaxies of an event or a misp event
+ :param misp_event: A misp event
+ :return: a table of flowables to add to the pdf
+ '''
+
+ flowable_table = []
+ col1_style, col2_style = get_table_styles()
+ i = 0
+
+ if is_safe_attribute_table(misp_event, "Galaxy"):
+ # There is some galaxies for this object
+ for item in getattr(misp_event, "Galaxy"):
+ # flowable_table.append([get_unoverflowable_paragraph(item["name"],col2_style)])
+ flowable_table.append(self.create_flowable_table_from_one_galaxy(item))
+ i += 1
+ answer_tags = create_flowable_table_from_data(flowable_table, ["99%"])
+ else:
+ # No galaxies for this object
+ answer_tags = [Paragraph("No galaxies", col2_style)]
+
+ return answer_tags
+
+
+ def create_flowable_table_from_one_galaxy(self, misp_galaxy):
+ '''
+ Returns a table (flowable) representing the galaxy
+ :param misp_attribute: A misp galaxy
+ :return: a table representing this misp's galaxy's attributes, to add to the pdf as a flowable
+ '''
+ data = []
+ col1_style, col2_style = get_table_styles()
+
+ # To reduce code size, and automate it a bit, triplet (Displayed Name, object_attribute_name,
+ # to_display_if_not_present) are store in the following list
+ list_attr_automated = [["Name", 'name', "None"],
+ ["Type", 'type', "None"],
+ ["Description", 'description', "None"],
+ ["NameSpace", 'namespace', "None"]]
+
+ # Automated adding of standard (python) attributes of the misp object
+ for item in list_attr_automated:
+ if is_safe_dict_attribute(misp_galaxy, item[1]):
+ # The attribute exists, we fetch it and create the row
+ data.append([self.value_formatter.get_col1_paragraph(item[0]),
+ self.value_formatter.get_unoverflowable_paragraph(misp_galaxy[item[1]])])
+
+ # Clusters
+ item = ["Clusters", 'GalaxyCluster', "None"]
+ # data.append([Paragraph(item[0], col1_style), create_flowable_table_from_galaxy_clusters(misp_galaxy)])
+
+ tmp_table = Table(data, ["25%", "75%"])
# The attribute does not exist, you may want to print a default text on the row. Then use as a else case :
# data.append([Paragraph(item[0], col1_style), Paragraph(item[2], col2_style)])
+ return [tmp_table]
- # Timestamp
- item = ["Object date", 'timestamp', "None"]
- data.append([Paragraph(item[0], col1_style), get_timestamp_value(misp_object, item, col2_style)])
- # Transform list of value in a table
- data = [create_flowable_table_from_data(data)]
+class Galaxy_cluster():
- # Handle all the attributes
- if is_safe_attribute(misp_object, "Attribute"):
- data += create_flowable_table_from_attributes(misp_object, config)
+ # ----------------------------------------------------------------------
+ def __init__(self, config, value_formatter):
+ self.config = config
+ self.value_formatter = value_formatter
- # Add a page break at the end of an object
- data.append(PageBreak())
+ # ----------------------------------------------------------------------
+ def create_flowable_table_from_galaxy_clusters(self, misp_event):
+ '''
+ Returns a Table (flowable) to add to a pdf, representing the list of galaxy clusters of a galaxy
+ :param misp_event: A misp event
+ :return: a table of flowables to add to the pdf
+ '''
- return data
+ flowable_table = []
+ i = 0
-def create_flowable_table_from_one_galaxy(misp_galaxy):
- '''
- Returns a table (flowable) representing the galaxy
- :param misp_attribute: A misp galaxy
- :return: a table representing this misp's galaxy's attributes, to add to the pdf as a flowable
- '''
- data = []
- col1_style, col2_style = get_table_styles()
+ if is_safe_dict_attribute(misp_event, "GalaxyCluster"):
+ # There is some galaxies for this object
+ for item in misp_event["GalaxyCluster"]:
+ # flowable_table.append([get_unoverflowable_paragraph(item["name"],col2_style)])
+ flowable_table.append(self.create_flowable_table_from_one_galaxy_cluster(item))
+ i += 1
+ answer_tags = create_flowable_table_from_data(flowable_table, ["99%"])
+ else:
+ # No galaxies for this object
+ answer_tags = [self.value_formatter.get_unoverflowable_paragraph("No galaxy cluster")]
- # To reduce code size, and automate it a bit, triplet (Displayed Name, object_attribute_name,
- # to_display_if_not_present) are store in the following list
- list_attr_automated = [["Name", 'name', "None"],
- ["Type", 'type', "None"],
- ["Description", 'description', "None"],
- ["NameSpace", 'namespace', "None"]]
+ return answer_tags
- # Automated adding of standard (python) attributes of the misp object
- for item in list_attr_automated:
- if is_safe_dict_attribute(misp_galaxy, item[1]):
- # The attribute exists, we fetch it and create the row
- data.append([Paragraph(item[0], col1_style),
- get_unoverflowable_paragraph(misp_galaxy[item[1]], col2_style)])
+ def create_flowable_table_from_one_galaxy_cluster(self, misp_cluster):
+ '''
+ Returns a table (flowable) representing the galaxy
+ :param misp_attribute: A misp galaxy
+ :return: a table representing this misp's galaxy's attributes, to add to the pdf as a flowable
+ '''
+ data = []
- # Clusters
- item = ["Clusters", 'GalaxyCluster', "None"]
- data.append([Paragraph(item[0], col1_style), create_flowable_table_from_one_galaxy_cluster(misp_galaxy, item, col2_style)])
+ # To reduce code size, and automate it a bit, triplet (Displayed Name, object_attribute_name,
+ # to_display_if_not_present) are store in the following list
+ list_attr_automated = [["Name", 'name', "None"],
+ ["Type", 'type', "None"],
+ ["Description", 'description', "None"],
+ ["NameSpace", 'namespace', "None"]]
- tmp_table = Table(data, ["25%","75%"])
+ # Automated adding of standard (python) attributes of the misp object
+ for item in list_attr_automated:
+ if is_safe_dict_attribute(misp_cluster, item[1]):
+ # The attribute exists, we fetch it and create the row
+ data.append([self.value_formatter.get_col1_paragraph(item[0]),
+ self.value_formatter.get_unoverflowable_paragraph(misp_cluster[item[1]])])
+
+ tmp_table = Table(data, ["25%", "75%"])
+ print(tmp_table)
# The attribute does not exist, you may want to print a default text on the row. Then use as a else case :
# data.append([Paragraph(item[0], col1_style), Paragraph(item[2], col2_style)])
- return [tmp_table]
+ return [tmp_table]
-def create_flowable_table_from_one_galaxy_cluster(misp_galaxy):
- '''
- Returns a table (flowable) representing the galaxy
- :param misp_attribute: A misp galaxy
- :return: a table representing this misp's galaxy's attributes, to add to the pdf as a flowable
- '''
- data = []
- col1_style, col2_style = get_table_styles()
- # To reduce code size, and automate it a bit, triplet (Displayed Name, object_attribute_name,
- # to_display_if_not_present) are store in the following list
- list_attr_automated = [["Name", 'name', "None"],
- ["Type", 'type', "None"],
- ["Description", 'description', "None"],
- ["NameSpace", 'namespace', "None"]]
-
- # Automated adding of standard (python) attributes of the misp object
- for item in list_attr_automated:
- if is_safe_dict_attribute(misp_galaxy, item[1]):
- # The attribute exists, we fetch it and create the row
- data.append([Paragraph(item[0], col1_style),
- get_unoverflowable_paragraph(misp_galaxy[item[1]], col2_style)])
-
- # Clusters
- item = ["Object date", 'timestamp', "None"]
- data.append([Paragraph(item[0], col1_style), get_timestamp_value(misp_object, item, col2_style)])
-
-
- tmp_table = Table(data, ["25%","75%"])
- # The attribute does not exist, you may want to print a default text on the row. Then use as a else case :
- # data.append([Paragraph(item[0], col1_style), Paragraph(item[2], col2_style)])
- return [tmp_table]
-
-def get_image_value(misp_attribute, item, col2_style):
- '''
- Returns a flowable image to add to the pdf given the misp attribute type and data
- :param misp_attribute: A misp attribute with type="attachement" and data
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a flowable image to add in the pdf, regarding the values of "data"
- '''
-
- try:
- # Get the image
- buf = getattr(misp_attribute, item[1])
-
- # Create image within a bounded box (to allow pdf creation)
- img = Image(buf, width=FRAME_PICTURE_MAX_WIDTH, height=FRAME_PICTURE_MAX_HEIGHT, kind='bound')
- answer = img
- except OSError:
- logger.error(
- "Trying to add an attachment during PDF export generation. Attachement joining failed. Attachement may not be an image.")
- answer = get_unoverflowable_paragraph(
- "" + NOT_A_PICTURE_MESSAGE + "", col2_style, False)
-
- return answer
-
-
-
-
-########################################################################
-# General Event's Attributes formater tools
-
-def uuid_to_url(baseurl, uuid):
- '''
- Return an url constructed from the MISP baseurl and the uuid of the event, to go to this event on this MISP
- :param baseurl: the baseurl of the MISP instnce e.g. http://localhost:8080 or http://localhost:8080/
- :param uuid: the uuid of the event that we want to have a link to
- :return: the complete URL to go to this event on this MISP instance
- '''
- if baseurl[len(baseurl) - 1] != "/":
- baseurl += "/"
- return baseurl + "events/view/" + uuid
-
-
-def create_flowable_table_from_data(data, col_w = COL_WIDTHS):
- '''
- Given a list of flowables items (2D/list of list), creates a Table with styles.
- :param data: list of list of items (flowables is better)
- :return: a Table - with styles - to add to the pdf
- '''
- # Create the table
- curr_table = Table(data, col_w)
-
- # Aside notes :
- # colWidths='*' does a 100% and share the space automatically
- # rowHeights=ROW_HEIGHT if you want a fixed height. /!\ Problems with paragraphs that are spreading everywhere
-
- # Create styles and set parameters
- alternate_colors_style = alternate_colors_style_generator(data)
- lines_style = lines_style_generator(data)
- general_style = general_style_generator()
-
- # Make the table nicer
- curr_table.setStyle(TableStyle(general_style + alternate_colors_style + lines_style))
-
- return curr_table
-
-def create_tags_table_from_data(data):
- '''
- Given a list of flowables tags (2D/list of list), creates a Table with styles adapted to tags.
- :param data: list of list of tags (flowables)
- :return: a Table - with styles - to add to another table
- '''
-
- # Create the table
- curr_table = Table(data, COL_WIDTHS, rowHeights=ROW_HEIGHT_FOR_TAGS)
-
- # Create styles and set parameters
- general_style = general_style_generator()
-
- # Make the table nicer
- curr_table.setStyle(TableStyle(general_style))
-
- return curr_table
-
-
-def get_good_link(misp_attribute, item, col2_style):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_attribute value, if this is a link
- :param misp_attribute: A misp attribute with a link
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of this "link" attribute
- '''
- return get_unoverflowable_paragraph(
- "" + getattr(
- misp_attribute, item[1]) + "", col2_style, False)
-
-
-def get_bad_link(misp_attribute, item, col2_style):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_attribute value, if this is a link
- :param misp_attribute: A misp event with an url
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of this "url" attribute
- '''
- return get_unoverflowable_paragraph(
- "" + getattr(misp_attribute,
- item[1]) + "",
- col2_style, False)
-
-
-def get_good_or_bad_link(misp_attribute, item, col2_style):
- '''
- Returns a flowable paragraph to add to the pdf given the misp_attribute value, if this is a link or an url
- :param misp_attribute: A misp attribute with a link or an url
- :param item: a list of name, in order :
- ["Name to be print in the pdf", "json property access name",
- " Name to be display if no values found in the misp_event"]
- :param col2_style: style to be applied on the returned paragraph
- :return: a Paragraph to add in the pdf, regarding the values of this "link" or "url" attribute
- '''
-
- # Handle "Good" links
- if getattr(misp_attribute, 'type') == LINK_TYPE:
- answer = get_good_link(misp_attribute, item, col2_style)
- # Handle "bad "links
- elif getattr(misp_attribute, 'type') == URL_TYPE:
- answer = get_bad_link(misp_attribute, item, col2_style)
-
- return answer
-
-
-########################################################################
-# General Event's Attributes formater
-
-def create_flowable_table_from_event(misp_event, config=None):
- '''
- Returns Table presenting a MISP event
- :param misp_event: A misp event (complete or not)
- :return: a table that can be added to a pdf
- '''
-
- data = []
- col1_style, col2_style = get_table_styles()
-
- # Manual addition
- # UUID
- item = ["UUID", 'uuid', "None"]
- data.append([Paragraph(item[0], col1_style), get_value_link_to_event(misp_event, item, col2_style, config)])
-
- # Date
- item = ["Date", 'date', "None"]
- data.append([Paragraph(item[0], col1_style), get_date_value(misp_event, item, col2_style)])
-
- # Owner
- item = ["Owner org", 'owner', "None"]
- data.append([Paragraph(item[0], col1_style), get_owner_value(misp_event, item, col2_style)])
-
- # Threat
- item = ["Threat level", 'threat_level_id', "None"]
- data.append([Paragraph(item[0], col1_style), get_threat_value(misp_event, item, col2_style)])
-
- # Analysis
- item = ["Analysis", 'analysis', "None"]
- data.append([Paragraph(item[0], col1_style), get_analysis_value(misp_event, item, col2_style)])
-
- # Info
- item = ["Info", 'info', "None"]
- data.append([Paragraph(item[0], col1_style), get_value_link_to_event(misp_event, item, col2_style, config)])
-
- # Timestamp
- item = ["Event date", 'timestamp', "None"]
- data.append([Paragraph(item[0], col1_style), get_timestamp_value(misp_event, item, col2_style)])
-
- # Published
- item = ["Published", 'published', "None", "publish_timestamp"]
- data.append([Paragraph(item[0], col1_style), get_published_value(misp_event, item, col2_style)])
-
- # Creator organisation
- item = ["Creator Org", 'Orgc', "None", "name"]
- data.append([Paragraph(item[0], col1_style), get_creator_organisation_value(misp_event, item, col2_style)])
-
- # Number of Attributes
- item = ["# Attributes", 'Attribute', "None"]
- data.append([Paragraph(item[0], col1_style), get_attributes_number_value(misp_event, item, col2_style)])
-
- # Tags
- item = ["Tags", 'Tag', "None"]
- data.append([Paragraph(item[0], col1_style), get_tag_value(misp_event, item, col2_style)])
-
- return create_flowable_table_from_data(data)
-
-
-def create_flowable_description_from_event(misp_event, config=None):
- '''
- Returns a Paragraph presenting a MISP event
- :param misp_event: A misp event (complete or not)
- :return: a paragraph that can be added to a pdf
- '''
-
- '''
- The event "{EventName}" | that occurred on {EventDate}, | had been shared by {Organisation Name} | on the {Date}.
- '''
-
- text = ""
-
- item = ["Info", 'info', "None"]
- if is_safe_attribute(misp_event, item[1]):
- text += "The event '"
- text += str(getattr(misp_event, item[1]))
- text += "'"
- else:
- text += "This event"
-
- item = ["Event date", 'timestamp', "None"]
- if is_safe_attribute(misp_event, item[1]):
- text += " that occurred on "
- text += str(getattr(misp_event, item[1]).strftime(EXPORT_DATE_FORMAT))
- text += ","
-
- item = ["Creator Org", 'Orgc', "None", "name"]
- text += " had been shared by "
- if is_safe_attribute(misp_event, item[1]):
- text += safe_string(getattr(getattr(misp_event, item[1]), item[3]))
- else:
- text += " an unknown organisation"
-
- item = ["Date", 'date', "None"]
- if is_safe_attribute(misp_event, item[1]):
- text += " on the "
- text += str(getattr(misp_event, item[1]))
- else:
- text += " on an unknown date"
- text += "."
-
- '''
- The threat level of this event is {ThreatLevel} and the analysis that was made of this event is {AnalysisLevel}.
- '''
-
- item = ["Threat level", 'threat_level_id', "None"]
- text += " The threat level of this event is "
- if is_safe_attribute(misp_event, item[1]) and str(getattr(misp_event, item[1])) in threat_map:
- text += threat_map[str(getattr(misp_event, item[1]))]
- else:
- text += " unknown"
-
- item = ["Analysis", 'analysis', "None"]
- text += " and the analysis that was made of this event is "
- if is_safe_attribute(misp_event, item[1]) and str(getattr(misp_event, item[1])) in analysis_map:
- text += analysis_map[str(getattr(misp_event, item[1]))]
- else:
- text += " undefined"
- text += "."
-
- '''
- The event is currently {Published} and has associated attributes {Attribute Number}.
- '''
-
- item = ["Published", 'published', "None", "publish_timestamp"]
- text += " The event is currently "
- if is_safe_attribute(misp_event, item[1]) and getattr(misp_event, item[1]):
- text += " published"
- if is_safe_attribute(misp_event, item[3]):
- text += " since " + getattr(misp_event, item[3]).strftime(EXPORT_DATE_FORMAT)
- else:
- text += " private"
-
- # Number of Attributes
- item = ["# Attributes", 'Attribute', "None"]
- text += ", has "
- if is_safe_attribute_table(misp_event, item[1]):
- text += str(len(getattr(misp_event, item[1])))
- else:
- text += " 0"
-
- text += " associated attributes"
-
- # Number of Objects
- item = ["# Objects", 'Object', "None"]
- text += " and has "
- if is_safe_attribute_table(misp_event, item[1]):
- text += str(len(getattr(misp_event, item[1])))
- else:
- text += " 0"
-
- text += " associated objects."
-
- '''
- For more information on the event, please consult the rest of the document
- '''
- text += "
For more information on the event, please consult following information."
-
- col1_style, col2_style = get_table_styles()
- description_style = ParagraphStyle(name='Description', parent=col2_style, alignment=TA_JUSTIFY)
-
- return Paragraph(text, description_style)
-
-
-def create_flowable_table_from_attributes(misp_event, config=None):
- '''
- Returns a list of flowables representing the list of attributes of a misp event.
- The list is composed alternatively of headers and tables, to add to the pdf
- :param misp_event: A misp event
- :return: a table of flowables
- '''
- flowable_table = []
- sample_style_sheet = getSampleStyleSheet()
- i = 0
-
- if is_safe_attribute_table(misp_event, "Attribute"):
- # There is some attributes for this object
- for item in getattr(misp_event, "Attribute"):
- # you can use a spacer instead of title to separate paragraph: flowable_table.append(Spacer(1, 5 * mm))
- flowable_table.append(Paragraph("Attribute #" + str(i), sample_style_sheet['Heading4']))
- flowable_table.append(create_flowable_table_from_one_attribute(item, config))
- i += 1
- else:
- # No attributes for this object
- flowable_table.append(Paragraph("No attributes", sample_style_sheet['Heading4']))
-
- return flowable_table
-
-
-def create_flowable_table_from_tags(misp_event):
- '''
- Returns a Table (flowable) to add to a pdf, representing the list of tags of an event or a misp event
- :param misp_event: A misp event
- :return: a table of flowable to add to the pdf
- '''
-
- flowable_table = []
- col1_style, col2_style = get_table_styles()
- i = 0
-
- if is_safe_attribute_table(misp_event,
- "Tag"): # and len(getattr(misp_event, "Tag")) > 1: # 'Tag' can exist and be empty
- # There is some tags for this object
- for item in getattr(misp_event, "Tag"):
- flowable_table.append(create_flowable_tag(item))
- i += 1
- answer_tags = create_tags_table_from_data(flowable_table)
- else:
- # No tags for this object
- answer_tags = [Paragraph("No tags", col2_style)]
-
- return answer_tags
-
-def create_flowable_table_from_objects(misp_event, config=None):
- '''
- Returns a list of flowables representing the list of objects of a misp event.
- The list is composed of a serie of
- [ header object, table of object information, [ header of attribute, table of attribute]*] to add to the pdf
- :param misp_event: A misp event
- :return: a table of flowables
- '''
-
- flowable_table = []
- sample_style_sheet = getSampleStyleSheet()
- i = 0
-
- if is_safe_attribute_table(misp_event, "Object"):
-
- # There is a list of objects
- for item in getattr(misp_event, "Object"):
- # you can use a spacer instead of title to separate paragraph: flowable_table.append(Spacer(1, 5 * mm))
- flowable_table.append(Paragraph("Object #" + str(i), sample_style_sheet['Heading3']))
- flowable_table += create_flowable_table_from_one_object(item, config)
- i += 1
- else:
- # No object found
- flowable_table.append(Paragraph("No object", sample_style_sheet['Heading3']))
-
- return flowable_table
-
-
-def create_flowable_paragraph_from_sightings(misp_attribute, item, col2_style):
- '''
- Returns a Table (flowable) to add to a pdf, representing the list of sightings of an event or a misp event
- :param misp_event: A misp event
- :return: a table of flowable to add to the pdf
- '''
-
- col1_style, col2_style = get_table_styles()
- i = 0
- POSITIVE_SIGHT_COLOR = 'green'
- NEGATIVE_SIGHT_COLOR = 'red'
- MISC_SIGHT_COLOR = 'orange'
-
- list_sighting = [0, 0, 0]
- if is_safe_attribute_table(misp_attribute, "Sighting"):
- # There is some tags for this object
- for curr_item in getattr(misp_attribute, "Sighting"):
- # TODO : When Sightings will be object : if is_safe_attribute(item, "type"):
- if "type" in curr_item:
- # Store the likes/dislikes depending on their types
- list_sighting[int(curr_item["type"])] += 1
- i += 1
-
- # Create the sighting text
- sight_text = " Positive : " + str(list_sighting[0]) + ""
- sight_text += " / " + " Negative : " + str(
- list_sighting[1]) + ""
- sight_text += " / " + " Misc. : " + str(list_sighting[2]) + ""
-
- answer_sighting = Paragraph(sight_text, col2_style)
- else:
- # No tags for this object
- answer_sighting = Paragraph("No sighting", col2_style)
-
- return answer_sighting
-
-
-def create_flowable_table_from_galaxies(misp_event):
- '''
- Returns a Table (flowable) to add to a pdf, representing the list of galaxies of an event or a misp event
- :param misp_event: A misp event
- :return: a table of flowables to add to the pdf
- '''
-
- flowable_table = []
- col1_style, col2_style = get_table_styles()
- i = 0
-
- if is_safe_attribute_table(misp_event, "Galaxy"):
- # There is some galaxies for this object
- for item in getattr(misp_event, "Galaxy"):
- # flowable_table.append([get_unoverflowable_paragraph(item["name"],col2_style)])
- flowable_table.append(create_flowable_table_from_one_galaxy(item))
- i += 1
- answer_tags = create_flowable_table_from_data(flowable_table, ["99%"])
- else:
- # No galaxies for this object
- answer_tags = [Paragraph("No galaxies", col2_style)]
-
- return answer_tags
########################################################################
# Handling static parts drawn on the upper layer
-def set_template(canvas, doc, misp_event, config=None):
- add_page_number(canvas, doc)
- add_metadata(canvas, doc, misp_event, config)
- # TODO : add_header()
- # TODO : add_footer()
-def add_metadata(canvas, doc, misp_event, config=None):
- '''
- Allow to add metadata to the pdf. Would need deeper digging to change other metadata.
- :param canvas: / Automatically filled during pdf compilation
- :param doc: / Automatically filled during pdf compilation
- :param misp_event: To send trough "partial", to get information to complete metadaa
- :return: / Automatically filled during pdf compilation
- '''
+class Statics_Drawings():
- if hasattr(misp_event, 'info'):
- canvas.setTitle(getattr(misp_event, 'info'))
+ # ----------------------------------------------------------------------
+ def __init__(self, config, misp_event):
+ self.config = config
+ self.misp_event = misp_event
- if hasattr(misp_event, 'info'):
- canvas.setSubject(getattr(misp_event, 'info'))
+ # ----------------------------------------------------------------------
- if hasattr(misp_event, 'Orgc'):
- if hasattr(getattr(misp_event, 'Orgc'), 'name'):
- canvas.setAuthor(getattr(getattr(misp_event, 'Orgc'), 'name'))
-
- if config is not None and moduleconfig[1] in config:
- canvas.setCreator(config[moduleconfig[1]])
- else:
- canvas.setCreator(getattr(getattr(misp_event, 'Orgc'), 'name'))
-
- if hasattr(misp_event, 'uuid'):
- canvas.setKeywords(getattr(misp_event, 'uuid'))
+ def set_template(self, canvas, doc):
+ self.add_page_number(canvas, doc)
+ self.add_metadata(canvas, doc)
+ # TODO : add_header()
+ # TODO : add_footer()
-def add_page_number(canvas, doc):
- '''
- Draw the page number on each page
- :param canvas: / Automatically filled during pdf compilation
- :param doc: / Automatically filled during pdf compilation
- :return: / Automatically filled during pdf compilation
- '''
- canvas.saveState()
- canvas.setFont('Times-Roman', 10)
- page_number_text = "%d" % (doc.page)
+ def add_metadata(self,canvas, doc):
+ '''
+ Allow to add metadata to the pdf. Would need deeper digging to change other metadata.
+ :param canvas: / Automatically filled during pdf compilation
+ :param doc: / Automatically filled during pdf compilation
+ :param misp_event: To send trough "partial", to get information to complete metadaa
+ :return: / Automatically filled during pdf compilation
+ '''
- curr_spacing = 4 * mm # 0.75 * inch
+ if is_safe_attribute(self.misp_event, 'info'):
+ canvas.setTitle(getattr(self.misp_event, 'info'))
- canvas.drawCentredString(
- curr_spacing,
- curr_spacing,
- page_number_text
- )
+ if is_safe_attribute(self.misp_event, 'info'):
+ canvas.setSubject(getattr(self.misp_event, 'info'))
- canvas.restoreState()
+ if is_safe_attribute(self.misp_event, 'Orgc'):
+ if is_safe_attribute(getattr(self.misp_event, 'Orgc'), 'name'):
+ canvas.setAuthor(getattr(getattr(self.misp_event, 'Orgc'), 'name'))
+
+ if is_in_config(self.config,1) :
+ canvas.setCreator(self.config[moduleconfig[1]])
+ else:
+ canvas.setCreator(getattr(getattr(self.misp_event, 'Orgc'), 'name'))
+
+ if is_safe_attribute(self.misp_event, 'uuid'):
+ canvas.setKeywords(getattr(self.misp_event, 'uuid'))
+
+
+ def add_page_number(self,canvas, doc):
+ '''
+ Draw the page number on each page
+ :param canvas: / Automatically filled during pdf compilation
+ :param doc: / Automatically filled during pdf compilation
+ :return: / Automatically filled during pdf compilation
+ '''
+ canvas.saveState()
+ canvas.setFont('Times-Roman', 10)
+ page_number_text = "%d" % (doc.page)
+
+ curr_spacing = 4 * mm # 0.75 * inch
+
+ canvas.drawCentredString(
+ curr_spacing,
+ curr_spacing,
+ page_number_text
+ )
+
+ canvas.restoreState()
########################################################################
@@ -1285,37 +1384,45 @@ def collect_parts(misp_event, config=None):
'''
# List of elements/content we want to add
flowables = []
+
# Get the list of available styles
sample_style_sheet = getSampleStyleSheet()
+ col1_style, col2_style = get_table_styles()
+ curr_val_f = Value_Formatter(config, col1_style, col2_style)
# Create stuff
title_style = ParagraphStyle(name='Column_1', parent=sample_style_sheet['Heading1'], alignment=TA_CENTER)
- title = get_value_link_to_event(misp_event, ["Info", 'info', "None"], title_style, config, False)
+ title = curr_val_f.get_value_link_to_event(misp_event, ["Info", 'info', "None"], title_style, color=False)
# Add all parts to final PDF
flowables.append(title)
- if config is not None and moduleconfig[2] in config:
+ # Creation of handling objects
+ curr_event = Event_Metadata(config, curr_val_f)
+ curr_attr = Attributes(config, curr_val_f)
+ curr_object = Object(config, curr_val_f)
+
+ if is_in_config(config,2) : # If description is activated
description = Paragraph("Description", sample_style_sheet['Heading2'])
- description_text = create_flowable_description_from_event(misp_event, config)
+ description_text = curr_event.create_flowable_description_from_event(misp_event)
flowables.append(description)
flowables.append(description_text)
subtitle = Paragraph("General information", sample_style_sheet['Heading2'])
- table_general_metainformation = create_flowable_table_from_event(misp_event, config)
+ table_general_metainformation = curr_event.create_flowable_table_from_event(misp_event)
flowables.append(subtitle)
flowables.append(table_general_metainformation)
flowables.append(PageBreak())
event_attributes_title = Paragraph("Attributes", sample_style_sheet['Heading2'])
- table_direct_attributes = create_flowable_table_from_attributes(misp_event, config)
+ table_direct_attributes = curr_attr.create_flowable_table_from_attributes(misp_event)
flowables.append(event_attributes_title)
flowables += table_direct_attributes
flowables.append(PageBreak())
event_objects_title = Paragraph("Objects", sample_style_sheet['Heading2'])
- table_objects = create_flowable_table_from_objects(misp_event, config)
+ table_objects = curr_object.create_flowable_table_from_objects(misp_event)
flowables.append(event_objects_title)
flowables += table_objects
@@ -1325,7 +1432,7 @@ def collect_parts(misp_event, config=None):
return flowables
-def export_flowables_to_pdf(document, misp_event, flowables, config=None):
+def export_flowables_to_pdf(document, misp_event, flowables, config):
'''
Export function : creates a pdf from a list of flowables, adding page numbers, etc.
:param document: A document template
@@ -1334,13 +1441,15 @@ def export_flowables_to_pdf(document, misp_event, flowables, config=None):
:return:
'''
+ static_drawer = Statics_Drawings(config, misp_event)
+
document.build(
flowables,
# Partial used to set the metadata
- onFirstPage=partial(set_template, misp_event=misp_event, config=config), # Pagination for first page
- onLaterPages=partial(set_template, misp_event=misp_event, config=config), # Pagination for all other page
+ onFirstPage=static_drawer.set_template, # Pagination for first page
+ onLaterPages=static_drawer.set_template, # Pagination for all other page
)
-
+ # Old way : onLaterPages=partial(static_drawer.set_template, misp_event=misp_event), # Pagination for all other page
########################################################################
# "EXTERNAL" exposed METHODS. Meant to be used outside of this class.
@@ -1375,6 +1484,7 @@ def convert_event_in_pdf_buffer(misp_event, config=None):
return pdf_value
+
def get_values_from_buffer(pdf_buffer):
return pdf_buffer.value()