2020-11-17 19:36:19 +01:00
# -*- coding: utf-8 -*-
import os
2023-05-31 15:25:08 +02:00
from pathlib import Path
2024-08-12 11:23:10 +02:00
import importlib
import copy
2020-11-17 19:36:19 +01:00
2024-08-12 11:23:10 +02:00
module_types = [ ' expansion ' , ' export_mod ' , ' import_mod ' , ' action_mod ' ]
titles = [ ' Expansion Modules ' , ' Export Modules ' , ' Import Modules ' , ' Action Modules ' ]
2020-11-17 19:36:19 +01:00
githublink = ' https://github.com/MISP/misp-modules/tree/main/misp_modules/modules '
2024-08-12 11:23:10 +02:00
moduleinfo_to_ignore = [ ' module-type ' , ' author ' , ' version ' ]
_all_moduleinfo = { }
def get_all_moduleinfo ( ) :
'''
Get all module information from the modules .
Behaves like a singleton , so it will only load the modules once .
'''
if not _all_moduleinfo :
for module_type in module_types :
_all_moduleinfo [ module_type ] = { }
module_type_module = importlib . import_module ( f " misp_modules.modules. { module_type } " )
module_type_module . __all__ . sort ( )
for module_name in module_type_module . __all__ :
module_package_name = f " misp_modules.modules. { module_type } . { module_name } "
try :
module = importlib . import_module ( module_package_name )
moduleinfo = copy . deepcopy ( module . version ( ) )
except Exception :
continue # skip if we have issues loading the module
moduleinfo = dict ( sorted ( moduleinfo . items ( ) ) )
_all_moduleinfo [ module_type ] [ module_name ] = moduleinfo
return _all_moduleinfo
2020-11-17 19:36:19 +01:00
def generate_doc ( module_type , root_path , logo_path = ' logos ' ) :
markdown = [ ]
2024-08-12 11:23:10 +02:00
# current_path = os.path.join(root_path, 'website', module_type)
# files = sorted(os.listdir(current_path))
2020-11-17 19:36:19 +01:00
githubpath = f ' { githublink } / { module_type } '
2024-08-12 11:23:10 +02:00
for module_name , moduleinfo in get_all_moduleinfo ( ) [ module_type ] . items ( ) :
githubref = f ' { githubpath } / { module_name } .py '
moduleinfo = copy . deepcopy ( moduleinfo ) # ensure to not modify the original data
for i in moduleinfo_to_ignore :
moduleinfo . pop ( i )
2024-08-13 08:32:07 +02:00
try :
module_name_pretty = moduleinfo . pop ( ' name ' )
except KeyError :
exit ( f " ERROR: Issue with module { module_name } - no field ' name ' provided " )
2024-08-12 11:23:10 +02:00
if module_name_pretty == ' ' :
module_name_pretty = module_name
markdown . append ( f ' \n #### [ { module_name_pretty } ]( { githubref } ) \n ' )
if moduleinfo [ ' logo ' ] != ' ' :
logo = os . path . join ( logo_path , moduleinfo . pop ( ' logo ' ) )
2020-11-17 19:36:19 +01:00
markdown . append ( f " \n <img src= { logo } height=60> \n " )
2024-08-12 11:23:10 +02:00
if ' description ' in moduleinfo :
markdown . append ( f " \n { moduleinfo . pop ( ' description ' ) } \n " )
if ' features ' in moduleinfo :
markdown . append ( get_single_value ( ' features ' , str ( moduleinfo . pop ( ' features ' ) ) . replace ( ' \n ' , ' \n > ' ) ) )
for field , value in sorted ( moduleinfo . items ( ) ) :
2020-11-17 19:36:19 +01:00
if not value :
continue
if isinstance ( value , list ) :
markdown . append ( handle_list ( field , value ) )
continue
2024-08-12 11:23:10 +02:00
markdown . append ( get_single_value ( field , str ( value ) . replace ( ' \n ' , ' \n > ' ) ) )
2020-11-17 19:36:19 +01:00
markdown . append ( ' \n ----- \n ' )
return markdown
2024-08-12 11:23:10 +02:00
def generate_index_doc ( module_type , root_path ) :
markdown = [ ]
githubpath = f ' { githublink } / { module_type } '
for module_name , moduleinfo in get_all_moduleinfo ( ) [ module_type ] . items ( ) :
module_name_pretty = moduleinfo . get ( ' name ' )
if module_name_pretty == ' ' :
module_name_pretty = module_name
githubref = f ' { githubpath } / { module_name } .py '
2024-08-13 08:32:07 +02:00
markdown . append ( f ' * [ { module_name_pretty } ]( { githubref } ) - { moduleinfo . get ( " description " ) . replace ( ' \n ' , ' ' ) } \n ' )
2024-08-12 11:23:10 +02:00
return markdown
2020-11-17 19:36:19 +01:00
def get_single_value ( field , value ) :
return f " - ** { field } **: \n > { value } \n "
def handle_list ( field , values ) :
if len ( values ) == 1 :
return get_single_value ( field , values [ 0 ] )
values = ' \n > - ' . join ( values )
return f " - ** { field } **: \n > - { values } \n "
2024-08-12 11:23:10 +02:00
def write_doc_for_readme ( root_path ) :
2020-11-17 19:36:19 +01:00
markdown = [ " # MISP modules documentation \n " ]
for _path , title in zip ( module_types , titles ) :
markdown . append ( f ' \n ## { title } \n ' )
markdown . extend ( generate_doc ( _path , root_path ) )
2023-05-31 15:25:08 +02:00
with open ( root_path / ' README.md ' , ' w ' ) as w :
2020-11-17 19:36:19 +01:00
w . write ( ' ' . join ( markdown ) )
def write_docs_for_mkdocs ( root_path ) :
for _path , title in zip ( module_types , titles ) :
markdown = generate_doc ( _path , root_path , logo_path = ' ../logos ' )
with open ( os . path . join ( root_path , ' mkdocs ' , f ' { _path } .md ' ) , ' w ' ) as w :
w . write ( ' ' . join ( markdown ) )
2024-08-12 11:23:10 +02:00
def update_docs_for_mkdocs_index ( root_path ) :
with open ( root_path / ' mkdocs ' / ' index.md ' , ' r ' ) as r :
old_doc = r . readlines ( )
new_doc = [ ]
skip = False
for line in old_doc :
if skip and not line . startswith ( ' ## ' ) : # find next title
continue # skip lines, as we're in the block that we're auto-generating
skip = False
new_doc . append ( line )
if line . startswith ( ' ## Existing MISP modules ' ) :
skip = True
# generate the updated content
for _path , title in zip ( module_types , titles ) :
new_doc . append ( f ' \n ### { title } \n ' )
new_doc . extend ( generate_index_doc ( _path , root_path ) )
new_doc . append ( ' \n \n ' )
with open ( root_path / ' mkdocs ' / ' index.md ' , ' w ' ) as w :
w . write ( ' ' . join ( new_doc ) )
pass
def update_readme ( root_path ) :
with open ( root_path / ' README.md ' , ' r ' ) as r :
old_readme = r . readlines ( )
new_doc = [ ]
skip = False
for line in old_readme :
if skip and not line . startswith ( ' # Existing MISP modules ' ) and not line . startswith ( ' # How to add your own MISP modules? ' ) and not line . startswith ( ' # Installation ' ) : # find next title
continue # skip lines, as we're in the block that we're auto-generating
new_doc . append ( line )
if line . startswith ( ' # Existing MISP modules ' ) :
skip = True
# generate the updated content
for _path , title in zip ( module_types , titles ) :
new_doc . append ( f ' \n ## { title } \n ' )
new_doc . extend ( generate_index_doc ( _path , root_path ) )
new_doc . append ( ' \n \n ' )
elif line . startswith ( ' # How to add your own MISP modules? ' ) :
skip = True
# copy over the contribute.md file
with open ( root_path / ' documentation ' / ' mkdocs ' / ' contribute.md ' , ' r ' ) as f :
f . readline ( ) # skip the title
new_doc . extend ( f . readlines ( ) )
elif line . startswith ( ' # Installation ' ) :
skip = True
new_doc . append ( ' \n ' )
# copy over the install.md file
with open ( root_path / ' documentation ' / ' mkdocs ' / ' install.md ' , ' r ' ) as f :
new_doc . extend ( f . readlines ( ) )
new_doc . append ( ' \n ' )
with open ( root_path / ' README.md ' , ' w ' ) as w :
w . write ( ' ' . join ( new_doc ) )
pass
2020-11-17 19:36:19 +01:00
if __name__ == ' __main__ ' :
2023-05-31 15:25:08 +02:00
root_path = Path ( __file__ ) . resolve ( ) . parent
2024-08-12 11:23:10 +02:00
write_doc_for_readme ( root_path )
2020-11-17 19:36:19 +01:00
write_docs_for_mkdocs ( root_path )
2024-08-12 11:23:10 +02:00
update_docs_for_mkdocs_index ( root_path )
update_readme ( root_path . parent )