diff --git a/pymisp/tools/reportlab_generator.py b/pymisp/tools/reportlab_generator.py index b1d4e06..62747c5 100644 --- a/pymisp/tools/reportlab_generator.py +++ b/pymisp/tools/reportlab_generator.py @@ -161,10 +161,16 @@ FRAME_MAX_HEIGHT = 500 # 650 # Ad hoc value for a A4 page FRAME_MAX_WIDTH = 356 STR_TOO_LONG_WARNING = "
[Too long to fit on a single page. Cropped]" +# == Parameters for error handling for image too big to fit on a page == +FRAME_PICTURE_MAX_WIDTH = 88 * mm +FRAME_PICTURE_MAX_HEIGHT = 195 * mm + # == Parameters for links management == LINK_TYPE = "link" # Name of the type that define 'good' links URL_TYPE = "url" # Name of the type that define 'bad' links +IMAGE_TYPE = "attachment" # /!\ Not only pictures ! Can be PDF, ... WARNING_MESSAGE_URL = "'https://Please_consider_that_this_may_be_a_harmful_link'" +NOT_A_PICTURE_MESSAGE = "This attachment is not recognized as an image. Please access this attachment directly from your MISP instance." GOOD_LINK_COLOR = 'blue' BAD_LINK_COLOR = 'red' @@ -423,13 +429,14 @@ def get_published_value(misp_event, item, col2_style): col2_style) else: # Published without published date - answer = YES_ANSWER + "no date)" + answer = Paragraph(YES_ANSWER + "no date)",col2_style) + else: # Not published - answer = NO_ANSWER + answer = Paragraph(NO_ANSWER,col2_style) else: # Does not have a published attribute - answer = item[2] + answer = Paragraph(item[2],col2_style) return answer @@ -456,11 +463,11 @@ def create_flowable_table_from_one_attribute(misp_attribute): ["Type", 'type', "None"], ["Value", 'value', "None"]] - IMAGE_TYPE = "attachment" + # Handle the special case of links STANDARD_TYPE = True - if hasattr(misp_attribute, 'type') and (getattr(misp_attribute, 'type') in [LINK_TYPE, URL_TYPE, IMAGE_TYPE]): + if hasattr(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 @@ -480,41 +487,32 @@ def create_flowable_table_from_one_attribute(misp_attribute): item = ["Value", 'value', "None"] if is_safe_attribute(misp_attribute, item[1]): + # Handle "Good" links if getattr(misp_attribute, 'type') == LINK_TYPE: data.append([Paragraph(item[0], col1_style), get_unoverflowable_paragraph( "" + getattr( misp_attribute, item[1]) + "", col2_style, False)]) + # Handle "bad "links elif getattr(misp_attribute, 'type') == URL_TYPE: data.append([Paragraph(item[0], col1_style), get_unoverflowable_paragraph( - "" + getattr(misp_attribute,item[1]) + "",col2_style, False)]) - elif getattr(misp_attribute, 'type') == IMAGE_TYPE: - # Get the image - buf = getattr(misp_attribute, 'data') - - # Scale down (or up ?) the image to fit the maximum frame size - img_size = ImageReader(buf).getSize() - w_scale = FRAME_MAX_WIDTH / img_size[0] - h_scale = FRAME_MAX_HEIGHT / img_size[1] - scale_down = min(w_scale,h_scale) - - print(FRAME_MAX_HEIGHT,FRAME_MAX_WIDTH) - print(img_size) - print(w_scale) - print(h_scale) - print(scale_down) - print(img_size[0]*scale_down, img_size[1]*scale_down) - - # img_width = 88 * mm # max image height - # print(img_size) - FRAME_PICTURE_MAX_WIDTH = 88*mm - FRAME_PICTURE_MAX_HEIGHT = 195*mm - # return Image(path, width=width, height=(width * aspect)) - img = Image(buf,width=FRAME_PICTURE_MAX_WIDTH,height=FRAME_PICTURE_MAX_HEIGHT,kind='bound') - # width, height = img.getSize() - # aspect = height / float(width) - data.append([Paragraph('data', col1_style),img]) + "" + getattr(misp_attribute, + item[ + 1]) + "", + col2_style, False)]) + item = ["Data", 'data', "None"] + # Handle pictures + if is_safe_attribute(misp_attribute, item[1]) and getattr(misp_attribute, 'type') == IMAGE_TYPE: + 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') + data.append([Paragraph(item[0], col1_style), img]) + except OSError : + logger.error("Trying to add an attachment during PDF export generation. Attachement joining failed. Attachmement may not be an image.") + data.append([Paragraph(item[0], col1_style),get_unoverflowable_paragraph("" + NOT_A_PICTURE_MESSAGE + "", col2_style, False) ]) # Tags item = ["Tags", 'Tag', "None"] diff --git a/tests/test_reportlab.py b/tests/test_reportlab.py index b2b968f..f93e1e7 100644 --- a/tests/test_reportlab.py +++ b/tests/test_reportlab.py @@ -10,7 +10,7 @@ import sys import os import time -manual_testing = True +manual_testing = False class TestMISPEvent(unittest.TestCase): @@ -23,7 +23,9 @@ class TestMISPEvent(unittest.TestCase): self.root = "" self.test_folder = self.root + "reportlab_testfiles/" self.test_batch_folder = self.root + "OSINT_output/" + self.test_image_folder = self.root + "image_json/" self.storage_folder = self.root + "reportlab_testoutputs/" + self.storage_image_folder = self.root + "reportlab_test_image_outputs/" def init_event(self): self.mispevent.info = 'This is a test' @@ -141,6 +143,35 @@ class TestMISPEvent(unittest.TestCase): reportlab_generator.register_value_to_file(reportlab_generator.convert_event_in_pdf_buffer(self.mispevent, config), self.storage_folder + "image_event.pdf") + + def test_batch_image_events(self): + # Test case ONLY for manual testing. Needs to download a full list of image events ! + + if self.check_python_2(): + self.assertTrue(True) + elif not manual_testing : + self.assertTrue(True) + else: + self.init_event() + + file_nb = str(len(os.listdir(self.test_image_folder))) + i = 0 + t = time.time() + for curr_file in os.listdir(self.test_image_folder): + self.mispevent = MISPEvent() + file_path = self.test_image_folder + curr_file + + print("Current file : " + file_path + " " + str(i) + " over " + file_nb) + i += 1 + + self.mispevent.load_file(file_path) + + reportlab_generator.register_value_to_file( + reportlab_generator.convert_event_in_pdf_buffer(self.mispevent), + self.storage_image_folder + curr_file + ".pdf") + print("Elapsed time : " + str(time.time() - t)) + # Local run : 1958.930s for 1064 files + def test_batch_OSINT_events(self): # Test case ONLY for manual testing. Needs to download a full list of OSINT events !