diff --git a/pymisp/tools/reportlab_generator.py b/pymisp/tools/reportlab_generator.py
index eb5f6a7..cd418e9 100644
--- a/pymisp/tools/reportlab_generator.py
+++ b/pymisp/tools/reportlab_generator.py
@@ -148,6 +148,7 @@ TEXT_FONT_SIZE = 8
LEADING_SPACE = 7
EXPORT_DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
COL_WIDTHS = ['30%', '75%'] # colWidths='*' # Not documented but does exist
+# COL_WIDTHS = ['20%', '80%'] # colWidths='*' # Not documented but does exist
ROW_HEIGHT = 5 * mm # 4.5 * mm (a bit too short to allow vertical align TODO : Fix it)
ROW_HEIGHT_FOR_TAGS = 4 * mm # 4.5 * mm (a bit too short to allow vertical align TODO : Fix it)
@@ -234,7 +235,7 @@ def uuid_to_url(baseurl, uuid):
return baseurl + "events/view/" + uuid
-def create_flowable_table_from_data(data, col_w=COL_WIDTHS):
+def create_flowable_table_from_data(data, col_w=COL_WIDTHS, color_alternation=None, line_alternation=None):
'''
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)
@@ -248,8 +249,8 @@ def create_flowable_table_from_data(data, col_w=COL_WIDTHS):
# 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)
+ alternate_colors_style = alternate_colors_style_generator(data,color_alternation)
+ lines_style = lines_style_generator(data,line_alternation)
general_style = general_style_generator()
# Make the table nicer
@@ -258,11 +259,13 @@ def create_flowable_table_from_data(data, col_w=COL_WIDTHS):
return curr_table
-def alternate_colors_style_generator(data):
+def alternate_colors_style_generator(data, color_alternation):
'''
Create a style, applicable on a table that will be built with parameter's data, with alternated
background color for each line.
Modified from : https://gist.github.com/chadcooper/5798392
+ :param color_alternation: Allow to control the color scheme. e.g. [0,0,0,1,1,0 ... will produce 3 lines of a color,
+ 2 lines of another, 1 of the first one ...
:param data: list of list of items (2D table) to be displayed in the pdf
:return: A list of 'BACKGROUND' properties, usable in a TableStyle, with alternated colours
'''
@@ -270,33 +273,60 @@ def alternate_colors_style_generator(data):
data_len = len(data)
color_list = []
- # For each line, generate a tuple giving to a line a color
- for each in range(data_len):
- if each % 2 == 0:
- bg_color = EVEN_COLOR
- else:
- bg_color = ODD_COLOR
- color_list.append(('BACKGROUND', (0, each), (-1, each), bg_color))
+ if color_alternation is None:
+ # For each line, generate a tuple giving to a line a color
+ for each in range(data_len):
+ if each % 2 == 0:
+ bg_color = EVEN_COLOR
+ else:
+ bg_color = ODD_COLOR
+ color_list.append(('BACKGROUND', (0, each), (-1, each), bg_color))
+ else:
+ if data_len > len(color_alternation) :
+ logger.warning("Line alternation for PDF display isn't correctly set. Looping on given values only.")
+
+ # For each line, generate a tuple giving to a line a color
+ for each in range(data_len):
+ if color_alternation[each%len(color_alternation)] % 2 == 0:
+ bg_color = EVEN_COLOR
+ else:
+ bg_color = ODD_COLOR
+ color_list.append(('BACKGROUND', (0, each), (-1, each), bg_color))
return color_list
-def lines_style_generator(data):
+def lines_style_generator(data, line_alternation):
'''
Create a style, applicable on a table that will be built with parameter's data,
that draw colored lines above and below each line of the table
+ :param line_alternation: Allow to control the color scheme. e.g. [0,0,0,1,1,0 ... will produce with a line up it,
+ 2 lines without, 1 of the first one ...
:param data: list of list of items (2D table) to be displayed in the pdf
:return: A list of 'LINE****' properties, usable in a TableStyle, that are drawing lines
'''
data_len = len(data)
lines_list = []
- # For each line, generate a tuple giving to a line a color
- for each in range(data_len):
- lines_list.append(('LINEABOVE', (0, each), (-1, each), LINE_THICKNESS, LINE_COLOR))
+ if line_alternation is None:
+ # For each line, generate a tuple giving to a line a color
+ for each in range(data_len):
+ lines_list.append(('LINEABOVE', (0, each), (-1, each), LINE_THICKNESS, LINE_COLOR))
+
+ # Last line
+ lines_list.append(('LINEBELOW', (0, len(data) - 1), (-1, len(data) - 1), LINE_THICKNESS, LINE_COLOR))
+ else:
+ if data_len > len(line_alternation) :
+ logger.warning("Line alternation for PDF display isn't correctly set. Looping on given values only.")
+
+ # For each line, generate a tuple giving to a line a color
+ for each in range(data_len):
+ if each == 0 or line_alternation[each%len(line_alternation)] != line_alternation[(each-1)%len(line_alternation)]:
+ lines_list.append(('LINEABOVE', (0, each), (-1, each), LINE_THICKNESS, LINE_COLOR))
+
+ # Last line
+ lines_list.append(('LINEBELOW', (0, len(data) - 1), (-1, len(data) - 1), LINE_THICKNESS, LINE_COLOR))
- # Last line
- lines_list.append(('LINEBELOW', (0, len(data) - 1), (-1, len(data) - 1), LINE_THICKNESS, LINE_COLOR))
return lines_list
@@ -366,9 +396,11 @@ 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
+
########################################################################
# Functions grouped by misp object type
@@ -379,6 +411,7 @@ class Value_Formatter():
["Name to be print in the pdf", "json property access name",
" Name to be display if no values found in the misp_event"]
'''
+
# ----------------------------------------------------------------------
def __init__(self, config, col1_style, col2_style):
self.config = config
@@ -391,7 +424,7 @@ class Value_Formatter():
def get_col1_paragraph(self, dirty_string):
return self.get_unoverflowable_paragraph(dirty_string, self.col1_style)
- def get_unoverflowable_paragraph(self, dirty_string, curr_style = None, do_escape_string=True):
+ 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.)
@@ -425,7 +458,8 @@ class Value_Formatter():
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)
+ 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 w <= FRAME_MAX_WIDTH and h <= FRAME_MAX_HEIGHT:
@@ -436,8 +470,7 @@ class Value_Formatter():
return answer_paragraph
-
- def get_value_link_to_event(self, misp_event, item, curr_style = None, color=True):
+ 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)
@@ -456,7 +489,7 @@ class Value_Formatter():
# 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):
+ 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]]
@@ -495,7 +528,6 @@ class Value_Formatter():
return self.get_unoverflowable_paragraph(safe_string(getattr(misp_event, item[1])))
return self.get_unoverflowable_paragraph(item[2])
-
def get_owner_value(self, misp_event, item):
'''
Returns a flowable paragraph to add to the pdf given the misp_event owner
@@ -508,7 +540,6 @@ class Value_Formatter():
return self.get_unoverflowable_paragraph(safe_string(getattr(misp_event, item[1])))
return self.get_unoverflowable_paragraph(item[2])
-
def get_threat_value(self, misp_event, item):
'''
Returns a flowable paragraph to add to the pdf given the misp_event threat
@@ -518,10 +549,10 @@ class Value_Formatter():
: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(threat_map[safe_string(getattr(misp_event, item[1]))],
+ do_escape_string=False)
return self.get_unoverflowable_paragraph(item[2])
-
def get_analysis_value(self, misp_event, item):
'''
Returns a flowable paragraph to add to the pdf given the misp_event analysis
@@ -531,10 +562,10 @@ class Value_Formatter():
: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(analysis_map[safe_string(getattr(misp_event, item[1]))],
+ do_escape_string=False)
return self.get_unoverflowable_paragraph(item[2])
-
def get_timestamp_value(self, misp_event, item):
'''
Returns a flowable paragraph to add to the pdf given the misp_event timestamp
@@ -544,10 +575,10 @@ class Value_Formatter():
: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(
+ safe_string(getattr(misp_event, item[1]).strftime(EXPORT_DATE_FORMAT)))
return self.get_unoverflowable_paragraph(item[2])
-
def get_creator_organisation_value(self, misp_event, item):
'''
Returns a flowable paragraph to add to the pdf given the misp_event creator organisation
@@ -562,7 +593,6 @@ class Value_Formatter():
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
@@ -575,8 +605,6 @@ class Value_Formatter():
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
@@ -601,7 +629,9 @@ class Value_Formatter():
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)
+ 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)
@@ -615,7 +645,6 @@ class Value_Formatter():
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
@@ -641,7 +670,7 @@ class Value_Formatter():
return answer
- def get_good_link(self,misp_attribute, item):
+ 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
@@ -653,8 +682,7 @@ class Value_Formatter():
"" + getattr(
misp_attribute, item[1]) + "", do_escape_string=False)
-
- def get_bad_link(self,misp_attribute, item):
+ 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
@@ -664,10 +692,11 @@ class Value_Formatter():
'''
return self.get_unoverflowable_paragraph(
"" + getattr(misp_attribute,
- item[1]) + "", do_escape_string=False)
+ item[
+ 1]) + "",
+ do_escape_string=False)
-
- def get_good_or_bad_link(self,misp_attribute, item):
+ 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
@@ -693,7 +722,8 @@ class Event_Metadata():
# ----------------------------------------------------------------------
def __init__(self, config, value_formatter):
self.config = config
- self.curr_val_f = value_formatter
+ self.value_formatter = value_formatter
+ self.sample_style_sheet = getSampleStyleSheet()
# ----------------------------------------------------------------------
@@ -708,61 +738,76 @@ class Event_Metadata():
'''
data = []
+ flowable_table = []
# 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)])
+ data.append([self.value_formatter.get_col1_paragraph(item[0]),
+ self.value_formatter.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)])
+ data.append(
+ [self.value_formatter.get_col1_paragraph(item[0]), self.value_formatter.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)])
+ data.append(
+ [self.value_formatter.get_col1_paragraph(item[0]), self.value_formatter.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)])
+ data.append(
+ [self.value_formatter.get_col1_paragraph(item[0]), self.value_formatter.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)])
+ data.append([self.value_formatter.get_col1_paragraph(item[0]),
+ self.value_formatter.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)])
+ data.append([self.value_formatter.get_col1_paragraph(item[0]),
+ self.value_formatter.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)])
+ data.append([self.value_formatter.get_col1_paragraph(item[0]),
+ self.value_formatter.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)])
+ data.append([self.value_formatter.get_col1_paragraph(item[0]),
+ self.value_formatter.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)])
+ data.append([self.value_formatter.get_col1_paragraph(item[0]),
+ self.value_formatter.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)])
+ data.append([self.value_formatter.get_col1_paragraph(item[0]),
+ self.value_formatter.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)])
+ curr_Tags = Tags(self.config, self.value_formatter)
+ data.append([self.value_formatter.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)])
+ flowable_table.append(create_flowable_table_from_data(data))
- return create_flowable_table_from_data(data)
+ # Galaxies
+ item = ["Related Galaxies", 'Galaxy', "None"]
+ curr_Galaxy = Galaxy(self.config, self.value_formatter)
+ if is_safe_attribute_table(misp_event, item[1]) and is_in_config(self.config, 3):
+ galaxy_title = Paragraph(item[0], self.sample_style_sheet['Heading5'])
+
+ flowable_table.append(galaxy_title)
+ flowable_table.append(curr_Galaxy.get_galaxy_value(misp_event, item))
+
+ return flowable_table
def create_flowable_description_from_event(self, misp_event):
'''
@@ -863,12 +908,12 @@ class Event_Metadata():
'''
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)
+ description_style = ParagraphStyle(name='Description', parent=self.value_formatter.col2_style,
+ alignment=TA_JUSTIFY)
return Paragraph(text, description_style)
-
class Attributes():
# ----------------------------------------------------------------------
@@ -876,6 +921,7 @@ class Attributes():
self.config = config
self.value_formatter = value_formatter
self.sample_style_sheet = getSampleStyleSheet()
+
# ----------------------------------------------------------------------
def create_flowable_table_from_attributes(self, misp_event):
@@ -893,7 +939,7 @@ class Attributes():
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))
+ flowable_table += self.create_flowable_table_from_one_attribute(item)
i += 1
else:
# No attributes for this object
@@ -909,7 +955,7 @@ class Attributes():
'''
data = []
- col1_style, col2_style = get_table_styles()
+ flowable_table = []
# 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
@@ -939,18 +985,21 @@ class Attributes():
# 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)])
+ 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)])
+ 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([self.value_formatter.get_col1_paragraph(item[0]), curr_Tags.get_tag_value(misp_attribute, item)])
+ data.append(
+ [self.value_formatter.get_col1_paragraph(item[0]), curr_Tags.get_tag_value(misp_attribute, item)])
# Sighting
item = ["Sighting", 'Sighting', "None"]
@@ -959,14 +1008,18 @@ class Attributes():
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)])
+ flowable_table.append(create_flowable_table_from_data(data))
- return create_flowable_table_from_data(data)
+ # Galaxies
+ item = ["Related Galaxies", 'Galaxy', "None"]
+ curr_Galaxy = Galaxy(self.config, self.value_formatter)
+ if is_safe_attribute_table(misp_attribute, item[1]) and is_in_config(self.config, 3):
+ galaxy_title = Paragraph(item[0], self.sample_style_sheet['Heading5'])
+
+ flowable_table.append(galaxy_title)
+ flowable_table.append(curr_Galaxy.get_galaxy_value(misp_attribute, item))
+
+ return flowable_table
class Tags():
@@ -1001,7 +1054,7 @@ class Tags():
flowable_table = []
i = 0
- if is_safe_attribute_table(misp_event,"Tag"):
+ 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))
@@ -1059,7 +1112,7 @@ class Sightings():
# 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"):
+ 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
@@ -1077,6 +1130,7 @@ class Sightings():
return answer_sighting
+
class Object():
# ----------------------------------------------------------------------
@@ -1176,13 +1230,13 @@ class Galaxy():
["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"
+ :return: a Flowable 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.create_flowable_table_from_galaxies(misp_event)
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
@@ -1191,23 +1245,39 @@ class Galaxy():
'''
flowable_table = []
- col1_style, col2_style = get_table_styles()
- i = 0
+ scheme_alternation = []
+ curr_color = 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%"])
+
+ for curr_galaxy in getattr(misp_event, "Galaxy"):
+ # For each galaxy of the misp object
+
+ # Add metadata about the Galaxy
+ galaxy_metadata, nb_added_item = self.create_flowable_table_from_one_galaxy(curr_galaxy)
+ flowable_table += galaxy_metadata
+
+ # Construct the line color scheme and line scheme
+ scheme_alternation += [curr_color] * nb_added_item
+
+ # Add metadata about clusters
+ curr_cluster = Galaxy_cluster(self.config, self.value_formatter)
+ clusters_metadata, nb_added_item = curr_cluster.create_flowable_table_from_galaxy_clusters(curr_galaxy)
+ flowable_table += clusters_metadata
+
+ # Construct the line color scheme and line scheme
+ scheme_alternation += [curr_color] * nb_added_item
+ curr_color += 1 if curr_color == 0 else 0
+
+ # Apply the scheme
+ answer_tags = create_flowable_table_from_data(flowable_table, color_alternation=scheme_alternation , line_alternation=scheme_alternation)
else:
# No galaxies for this object
- answer_tags = [Paragraph("No galaxies", col2_style)]
+ answer_tags = [self.value_formatter.get_unoverflowable_paragraph("No galaxies")]
return answer_tags
-
def create_flowable_table_from_one_galaxy(self, misp_galaxy):
'''
Returns a table (flowable) representing the galaxy
@@ -1215,7 +1285,7 @@ class 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()
+ nb_added_item = 0
# 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
@@ -1230,15 +1300,11 @@ class Galaxy():
# 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]])])
+ nb_added_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]
+ return data, nb_added_item
class Galaxy_cluster():
@@ -1249,40 +1315,44 @@ class Galaxy_cluster():
self.value_formatter = value_formatter
# ----------------------------------------------------------------------
- def create_flowable_table_from_galaxy_clusters(self, misp_event):
+ def create_flowable_table_from_galaxy_clusters(self, misp_galaxy):
'''
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
'''
- flowable_table = []
- i = 0
+ data = []
+ nb_added_item = 0
+
+ if is_safe_dict_attribute(misp_galaxy, "GalaxyCluster"):
+ # There is some clusters for this object
+ for curr_cluster in misp_galaxy["GalaxyCluster"]:
+
+ # For each cluster
+ tmp_data, curr_added_items = self.create_flowable_table_from_one_galaxy_cluster(curr_cluster)
+ data += tmp_data
+ nb_added_item += curr_added_items
- 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")]
+ data = [self.value_formatter.get_unoverflowable_paragraph("No galaxy cluster")]
+ nb_added_item += 1
- return answer_tags
+ return data, nb_added_item
def create_flowable_table_from_one_galaxy_cluster(self, misp_cluster):
'''
- Returns a table (flowable) representing the galaxy
+ Returns a table (flowable) representing a galaxy cluster
:param misp_attribute: A misp galaxy
- :return: a table representing this misp's galaxy's attributes, to add to the pdf as a flowable
+ :return: a table representing this misp's galaxy's cluster attributes, to add to the pdf as a flowable
'''
data = []
+ nb_added_item = 0
# 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"],
+ list_attr_automated = [["Cluster #", 'tag_name', "None"],
["Type", 'type', "None"],
["Description", 'description', "None"],
["NameSpace", 'namespace', "None"]]
@@ -1293,14 +1363,13 @@ class Galaxy_cluster():
# 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]])])
+ nb_added_item += 1
- tmp_table = Table(data, ["25%", "75%"])
- print(tmp_table)
+ # 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 data, nb_added_item
########################################################################
@@ -1322,8 +1391,7 @@ class Statics_Drawings():
# TODO : add_header()
# TODO : add_footer()
-
- def add_metadata(self,canvas, doc):
+ 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
@@ -1342,7 +1410,7 @@ class Statics_Drawings():
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) :
+ if is_in_config(self.config, 1):
canvas.setCreator(self.config[moduleconfig[1]])
else:
canvas.setCreator(getattr(getattr(self.misp_event, 'Orgc'), 'name'))
@@ -1350,8 +1418,7 @@ class Statics_Drawings():
if is_safe_attribute(self.misp_event, 'uuid'):
canvas.setKeywords(getattr(self.misp_event, 'uuid'))
-
- def add_page_number(self,canvas, doc):
+ def add_page_number(self, canvas, doc):
'''
Draw the page number on each page
:param canvas: / Automatically filled during pdf compilation
@@ -1401,7 +1468,7 @@ def collect_parts(misp_event, config=None):
curr_attr = Attributes(config, curr_val_f)
curr_object = Object(config, curr_val_f)
- if is_in_config(config,2) : # If description is activated
+ if is_in_config(config, 2): # If description is activated
description = Paragraph("Description", sample_style_sheet['Heading2'])
description_text = curr_event.create_flowable_description_from_event(misp_event)
flowables.append(description)
@@ -1410,7 +1477,7 @@ def collect_parts(misp_event, config=None):
subtitle = Paragraph("General information", sample_style_sheet['Heading2'])
table_general_metainformation = curr_event.create_flowable_table_from_event(misp_event)
flowables.append(subtitle)
- flowables.append(table_general_metainformation)
+ flowables += table_general_metainformation
flowables.append(PageBreak())
@@ -1449,7 +1516,8 @@ def export_flowables_to_pdf(document, misp_event, flowables, config):
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
+ # 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.
@@ -1484,7 +1552,6 @@ def convert_event_in_pdf_buffer(misp_event, config=None):
return pdf_value
-
def get_values_from_buffer(pdf_buffer):
return pdf_buffer.value()