#!/usr/bin/env python import argparse str_to_token = { 'and': lambda left, right: left and right, 'or': lambda left, right: left or right, 'not': 'not', '(': '(', ')': ')' } size_multipliers = { 'Kb': 1000, 'Mb': 1000000, 'Gb': 1000000000, 'Tb': 1000000000000, 'Pb': 1000000000000000, } empty_res = True def human_to_int(val): if val.endswith('b'): mult = val[-2:] if mult not in size_multipliers: return int(val[:-1]) return int(val[:-2]) * size_multipliers[mult] return int(val) def eval_fileext(exp, filename): ext = exp.strip('=') return filename.endswith(ext) def eval_filematch(exp, filename): import re regex = exp.strip('=') ret = re.search(r'{}'.format(regex), filename) is not None return ret def eval_filesize(exp, filesize): tokens = [x for x in exp.split('=') if x] if len(tokens) == 1: token = tokens[0] if token.startswith('>'): return filesize > human_to_int(token[1:]) elif token.startswith('<'): return filesize < human_to_int(token[1:]) else: return filesize == human_to_int(token) elif len(tokens) == 2: comp, val = tokens if comp == '>': return filesize >= human_to_int(val) elif comp == '<': return filesize <= human_to_int(val) return False def eval_token(token, filesize, filename): if token.startswith('file_size'): exp = token[len('file_size'):] return eval_filesize(exp, filesize) elif token.startswith('file_ext'): exp = token[len('file_ext'):] return eval_fileext(exp, filename) elif token.startswith('file_match'): exp = token[len('file_match'):] return eval_filematch(exp, filename) return False def create_token_lst(s, filesize, filename, str_to_token=str_to_token): """create token list: 'file_size>10Kb or file_ext=so' -> [True, lambda..., False]""" s = s.replace('(', ' ( ') s = s.replace(')', ' ) ') ret = [str_to_token.get(it) if it in str_to_token else eval_token(it, filesize, filename) for it in s.split()] return ret def find(lst, what, start=0): return [i for i, it in enumerate(lst) if it == what and i >= start] def parens(token_lst): """returns: (bool)parens_exist, left_paren_pos, right_paren_pos """ left_lst = find(token_lst, '(') if not left_lst: return False, -1, -1 left = left_lst[-1] # can not occur earlier, hence there are args and op. right_lst = find(token_lst, ')', left + 4) if not right_lst: # special case (True) right_lst = find(token_lst, ')', left + 1) right = right_lst[0] return True, left, right def bool_eval(token_lst): """token_lst has length 3 and format: [left_arg, operator, right_arg] operator(left_arg, right_arg) is returned except in case of tokens has length 1 and look like: [True] do to an input expression like: (file_size=10Kb)""" if len(token_lst) == 1: return token_lst[0] elif len(token_lst) == 2: if token_lst[0] == 'not': return not token_lst[1] return token_lst[1] if len(token_lst) > 3: new_tokens = [] negate = False for tok in token_lst: if tok == 'not': negate = not negate else: if negate: new_tokens.append(not tok) negate = False else: new_tokens.append(tok) return new_tokens[1](new_tokens[0], new_tokens[2]) return token_lst[1](token_lst[0], token_lst[2]) def formatted_bool_eval(token_lst, empty_res=empty_res): """eval a formatted (i.e. of the form 'ToFa(ToF)') string""" if not token_lst: return empty_res if len(token_lst) == 1: return token_lst[0] has_parens, l_paren, r_paren = parens(token_lst) if not has_parens: return bool_eval(token_lst) token_lst[l_paren:r_paren + 1] = [bool_eval(token_lst[l_paren+1:r_paren])] return formatted_bool_eval(token_lst, bool_eval) def nested_bool_eval(s, filesize, filename): """The actual 'eval' routine, if 's' is empty, 'True' is returned, otherwise 's' is evaluated according to parentheses nesting. The format assumed: [1] 'LEFT OPERATOR RIGHT', where LEFT and RIGHT can be a subexpression: '(' [1] ')' """ return formatted_bool_eval(create_token_lst(s, filesize, filename)) if __name__ == '__main__': parser = argparse.ArgumentParser(description='Burp advanced expression parser') parser.add_argument('-s', '--size', type=int, help='Input file-size') parser.add_argument('-f', '--filename', help='Input file-name') parser.add_argument('expressions', metavar='EXP', nargs='+', help="An expression to parse") args = parser.parse_args() size = args.size filename = args.filename for exp in args.expressions: print('-' * 20) print("Evaluating '{}' with filename: '{}', size: {} -> {}".format(exp, filename, size, nested_bool_eval(exp, size, filename))) print('-' * 20)