import io import zipfile from pychrysalide.analysis.scan import ScanRegisteredItem from pychrysalide.analysis.scan.exprs import ScanLiteralExpression from pychrysalide.core import get_rost_root_namespace from pychrysalide.plugins import PluginModule class ZipBombKeyword(ScanRegisteredItem): """Introduce a new keyword in the ROST grammar.""" _name = 'is_zip_bomb' def _reduce(self, ctx, scope): expr = None try: has_bomb = False # compress < 10% plain => bomb ZIP_BOMB_RATIO = 0.01 data = io.BytesIO(ctx.content.data) zf = zipfile.ZipFile(data) info = zf.infolist() for i in info: ratio = i.compress_size / i.file_size has_bomb = ratio < ZIP_BOMB_RATIO if has_bomb: break expr = ScanLiteralExpression(has_bomb) except: pass return expr class ZipBombChecker(PluginModule): """Check for ZIP bombs.""" _name = 'ZipBombChecker' _desc = 'Check if a ZIP archive may contain heavy files on extraction' _version = '0.1' _url = 'https://www.chrysalide.re/' _actions = ( ) def __init__(self): """Create a ZipBombChecker plugin instance.""" super().__init__() ns = get_rost_root_namespace() kw = ZipBombKeyword() ns.register_item(kw)