mirror of https://github.com/MISP/misp-modules
				
				
				
			
		
			
				
	
	
		
			180 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
			
		
		
	
	
			180 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
| #!/usr/bin/env python\
 | |
| 
 | |
| import json
 | |
| import base64
 | |
| import pandoc
 | |
| import random
 | |
| import string
 | |
| import subprocess
 | |
| import os
 | |
| import shutil
 | |
| 
 | |
| 
 | |
| installationNotes = '''
 | |
| 1. Install pandoc for your distribution
 | |
| 2. Install wkhtmltopdf
 | |
|     - Ensure You have install the version with patched qt
 | |
|     - Ensure it supports margin options
 | |
|     - You can check the above by inspecting the extended help `wkhtmltopdf --extended-help`
 | |
| 3. Install mermaid
 | |
|     - `npm install --global @mermaid-js/mermaid-cli`
 | |
| 4. Install the pandoc-mermaid-filter from https://github.com/DavidCruciani/pandoc-mermaid-filter
 | |
|     - Easiest is to install the following:
 | |
|     ```bash
 | |
|         pip3 install git+https://github.com/DavidCruciani/pandoc-mermaid-filter
 | |
|     ```
 | |
| '''
 | |
| 
 | |
| misperrors = {'error': 'Error'}
 | |
| mispattributes = {'input': ['text'], 'output': ['text']}
 | |
| moduleinfo = {
 | |
|     'version': '0.3',
 | |
|     'author': 'Sami Mokaddem',
 | |
|     'description': 'Render the markdown (under GFM) into PDF. Requires pandoc (https://pandoc.org/), wkhtmltopdf (https://wkhtmltopdf.org/) and mermaid dependencies.',
 | |
|     'module-type': ['expansion'],
 | |
|     'name': 'Markdown to PDF converter',
 | |
|     'logo': '',
 | |
|     'requirements': ['pandoc'],
 | |
|     'features': '',
 | |
|     'references': [installationNotes],
 | |
|     'input': '',
 | |
|     'output': '',
 | |
| }
 | |
| 
 | |
| moduleconfig = [
 | |
| ]
 | |
| 
 | |
| def randomFilename(length=10):
 | |
|     characters = string.ascii_lowercase + string.digits  # Lowercase letters and digits
 | |
|     return ''.join(random.choices(characters, k=length))
 | |
| 
 | |
| def convert(markdown, margin='3'):
 | |
|     doc = pandoc.read(markdown, format='gfm')
 | |
| 
 | |
|     elt = doc
 | |
| 
 | |
|     # wrap/unwrap Inline or MetaInlines into [Inline]
 | |
|     if isinstance(elt, pandoc.types.Inline):
 | |
|         inline = elt
 | |
|         elt = [inline]
 | |
|     elif isinstance(elt, pandoc.types.MetaInlines):
 | |
|         meta_inlines = elt
 | |
|         elt = meta_inlines[0]
 | |
| 
 | |
|     # wrap [Inline] into a Plain element
 | |
|     if isinstance(elt, list) and all(isinstance(elt_, pandoc.types.Inline) for elt_ in elt):
 | |
|         inlines = elt
 | |
|         elt = pandoc.types.Plain(inlines)
 | |
| 
 | |
|     # wrap/unwrap Block or MetaBlocks into [Block]
 | |
|     if isinstance(elt, pandoc.types.Block):
 | |
|         block = elt
 | |
|         elt = [block]
 | |
|     elif isinstance(elt, pandoc.types.MetaBlocks):
 | |
|         meta_blocks = elt
 | |
|         elt = meta_blocks[0]
 | |
| 
 | |
|     # wrap [Block] into a Pandoc element
 | |
|     if isinstance(elt, list) and all(isinstance(elt_, pandoc.types.Block) for elt_ in elt):
 | |
|         blocks = elt
 | |
|         elt = pandoc.types.Pandoc(pandoc.types.Meta({}), blocks)
 | |
| 
 | |
|     if not isinstance(elt, pandoc.types.Pandoc):
 | |
|         raise TypeError(f"{elt!r} is not a Pandoc, Block or Inline instance.")
 | |
| 
 | |
|     doc = elt
 | |
| 
 | |
|     # options = [
 | |
|     #     '--pdf-engine=wkhtmltopdf',
 | |
|     #     f'-V margin-left={margin}',
 | |
|     #     f'-V margin-right={margin}',
 | |
|     #     f'-V margin-top={margin}',
 | |
|     #     f'-V margin-bottom={margin}',
 | |
|     #     '--pdf-engine-opt="--disable-smart-shrinking"',
 | |
|     # ]
 | |
|     randomFn = randomFilename()
 | |
|     command = [
 | |
|         "/usr/bin/pandoc",
 | |
|         "-t", "pdf",
 | |
|         "-o", f"/tmp/{randomFn}/output",
 | |
|         "--pdf-engine=wkhtmltopdf",
 | |
|         "-V", f"margin-left={margin}",
 | |
|         "-V", f"margin-right={margin}",
 | |
|         "-V", f"margin-top={margin}",
 | |
|         "-V", f"margin-bottom={margin}",
 | |
|         "--pdf-engine-opt=--disable-smart-shrinking",
 | |
|         "--filter=pandoc-mermaid",
 | |
|         "-f", "json",
 | |
|         f"/tmp/{randomFn}/input.js"
 | |
|     ]
 | |
|     # try:
 | |
|     #     # For some reasons, options are not passed correctly or not parsed correctly by wkhtmltopdf..
 | |
|     #     # converted = pandoc.write(doc, format='pdf', options=options)
 | |
|     # except Exception as e:
 | |
|     #     print(e)
 | |
| 
 | |
|     os.makedirs(f'/tmp/{randomFn}', exist_ok=True)
 | |
|     # Write parsed file structure to be fed to the converter
 | |
|     with open(f'/tmp/{randomFn}/input.js', 'bw') as f:
 | |
|         configuration = pandoc.configure(read=True)
 | |
|         if pandoc.utils.version_key(configuration["pandoc_types_version"]) < [1, 17]:
 | |
|             json_ = pandoc.write_json_v1(doc)
 | |
|         else:
 | |
|             json_ = pandoc.write_json_v2(doc)
 | |
|         json_str = json.dumps(json_)
 | |
|         f.write(json_str.encode("utf-8"))
 | |
| 
 | |
|     # Do conversion by manually invoking pandoc
 | |
|     try:
 | |
|         subprocess.run(command, check=True)
 | |
|     except subprocess.CalledProcessError as e:
 | |
|         print(f"Command failed with error: {e}")
 | |
| 
 | |
|     # Read output and returns it
 | |
|     with open(f'/tmp/{randomFn}/output', 'br') as f:
 | |
|         converted = f.read()
 | |
| 
 | |
|     # Clean up generated files
 | |
|     folderPath = f'/tmp/{randomFn}'
 | |
|     try:
 | |
|         shutil.rmtree(folderPath)
 | |
|         print(f"Folder '{folderPath}' deleted successfully.")
 | |
|     except FileNotFoundError:
 | |
|         print(f"Folder '{folderPath}' does not exist.")
 | |
|     except Exception as e:
 | |
|         print(f"Error deleting folder '{folderPath}': {e}")
 | |
| 
 | |
|     return base64.b64encode(converted).decode()
 | |
| 
 | |
| def handler(q=False):
 | |
|     if q is False:
 | |
|         return False
 | |
|     request = json.loads(q)
 | |
|     if request.get('text'):
 | |
|         data = request['text']
 | |
|     else:
 | |
|         return False
 | |
|     data = json.loads(data)
 | |
|     markdown = data.get('markdown')
 | |
|     try:
 | |
|         margin = '3'
 | |
|         if 'config' in request['config']:
 | |
|             if request['config'].get('margin'):
 | |
|                 margin = request['config'].get('margin')
 | |
|         rendered = convert(markdown, margin=margin)
 | |
|     except Exception as e:
 | |
|         rendered = f'Error: {e}'
 | |
| 
 | |
|     r = {'results': [{'types': mispattributes['output'],
 | |
|                       'values':[rendered]}]}
 | |
|     return r
 | |
| 
 | |
| 
 | |
| def introspection():
 | |
|     return mispattributes
 | |
| 
 | |
| 
 | |
| def version():
 | |
|     moduleinfo['config'] = moduleconfig
 | |
|     return moduleinfo
 |