def pretty_print_body(fmt, body): try: if fmt.lower() == 'json': d = json.loads(body.strip()) s = json.dumps(d, indent=4, sort_keys=True) print pygments.highlight(s, JsonLexer(), TerminalFormatter()) elif fmt.lower() == 'form': qs = repeatable_parse_qs(body) for k, v in qs.all_pairs(): s = Colors.GREEN s += '%s: ' % urllib.unquote(k) s += Colors.ENDC s += urllib.unquote(v) print s elif fmt.lower() == 'text': print body elif fmt.lower() == 'xml': import xml.dom.minidom xml = xml.dom.minidom.parseString(body) print pygments.highlight(xml.toprettyxml(), XmlLexer(), TerminalFormatter()) else: raise PappyException('"%s" is not a valid format' % fmt) except PappyException as e: raise e except: raise PappyException('Body could not be parsed as "%s"' % fmt)
def load(self): if self.filename: match = re.findall('.*int_(.*).py$', self.filename) if len(match) > 0: self.file_name = match[0] else: self.file_name = self.filename st = os.stat(self.filename) if (st.st_mode & stat.S_IWOTH): raise PappyException("Refusing to load world-writable macro: %s" % self.filename) module_name = os.path.basename(os.path.splitext(self.filename)[0]) self.source = imp.load_source('%s'%module_name, self.filename) self.name = self.source.MACRO_NAME if self.name == '': raise PappyException('Macro in %s cannot have a blank name' % self.filename) if hasattr(self.source, 'SHORT_NAME'): self.short_name = self.source.SHORT_NAME else: self.short_name = None if hasattr(self.source, 'mangle_request') and \ hasattr(self.source, 'async_mangle_request'): raise PappyException('Intercepting macro in %s cannot define both mangle_request and async_mangle_request' % self.filename) if hasattr(self.source, 'mangle_response') and \ hasattr(self.source, 'async_mangle_response'): raise PappyException('Intercepting macro in %s cannot define both mangle_response and async_mangle_response' % self.filename) if hasattr(self.source, 'mangle_ws') and \ hasattr(self.source, 'async_mangle_ws'): raise PappyException('Intercepting macro in %s cannot define both mangle_ws and async_mangle_ws' % self.filename) else: self.source = None # Update what we can do if self.source and hasattr(self.source, 'mangle_request'): self.intercept_requests = True self.async_req = False elif self.source and hasattr(self.source, 'async_mangle_request'): self.intercept_requests = True self.async_req = True else: self.intercept_requests = True if self.source and hasattr(self.source, 'mangle_response'): self.intercept_responses = True self.async_rsp = False elif self.source and hasattr(self.source, 'async_mangle_response'): self.intercept_responses = True self.async_rsp = True else: self.intercept_responses = False if self.source and hasattr(self.source, 'mangle_ws'): self.intercept_ws = True self.async_ws = False elif self.source and hasattr(self.source, 'async_mangle_ws'): self.intercept_ws = True self.async_ws = True else: self.intercept_ws = False
def do_stop_int_macro(self, line): global int_macro_dict global loaded_int_macros if not line: raise PappyException( 'You must give an intercepting macro to run. You can give its short name, or the name in the filename.' ) if line not in int_macro_dict: raise PappyException('%s not a loaded intercepting macro' % line) macro = int_macro_dict[line] pappyproxy.proxy.remove_intercepting_macro(macro.name) print '"%s" stopped' % macro.name
def get(name): if name not in BuiltinFilters._filters: raise PappyException('%s not a bult in filter' % name) if name in BuiltinFilters._filters: filters = [ pappyproxy.context.Filter(f) for f in BuiltinFilters._filters[name][0] ] for f in filters: yield f.generate() defer.returnValue(filters) raise PappyException('"%s" is not a built-in filter' % name)
def do_run_macro(self, line): global macro_dict global loaded_macros args = shlex.split(line) if not args: raise PappyException( 'You must give a macro to run. You can give its short name, or the name in the filename.' ) mname = args[0] if mname not in macro_dict: raise PappyException('%s not a loaded macro' % mname) macro = macro_dict[mname] macro.execute(args[1:])
def stop_int_macro(line): """ Stop a running intercepting macro Usage: stop_int_macro <macro name or macro short name> """ global int_macro_dict global loaded_int_macros if not line: raise PappyException('You must give an intercepting macro to run. You can give its short name, or the name in the filename.') if line not in int_macro_dict: raise PappyException('%s not a loaded intercepting macro' % line) macro = int_macro_dict[line] remove_intercepting_macro(macro.name) print '"%s" stopped' % macro.name
def load_certs_from_dir(cert_dir): try: with open(cert_dir + '/' + config.SSL_CA_FILE, 'rt') as f: ca_raw = f.read() except IOError: raise PappyException("Could not load CA cert!") try: with open(cert_dir + '/' + config.SSL_PKEY_FILE, 'rt') as f: ca_key_raw = f.read() except IOError: raise PappyException("Could not load CA private key!") return (ca_raw, ca_key_raw)
def save_filter_set(line): if line == '': raise PappyException("Must give name to save filters as") strs = yield _save_filters_to(line) print 'Filters saved to %s:' % line for s in strs: print ' %s' % s
def export(line): """ Write the full request/response of a request/response to a file. Usage: export [req|rsp] <reqid(s)> """ args = shlex.split(line) if len(args) < 2: print 'Requires req/rsp and and request id(s)' defer.returnValue(None) if args[0] not in ('req', 'rsp'): raise PappyException('Request or response not specified') reqs = yield load_reqlist(args[1]) for req in reqs: try: if args[0] == 'req': fname = 'req_%s.txt' % req.reqid with open(fname, 'w') as f: f.write(req.full_request) print 'Full request written to %s' % fname elif args[0] == 'rsp': fname = 'rsp_%s.txt' % req.reqid with open(fname, 'w') as f: f.write(req.full_response) print 'Full response written to %s' % fname except PappyException as e: print 'Unable to export %s: %s' % (req.reqid, e)
def load_certs_from_dir(cert_dir): try: with open(cert_dir + '/' + config.SSL_CA_FILE, 'rt') as f: ca_raw = f.read() except IOError: raise PappyException( "Could not load CA cert! Generate certs using the `gencerts` command then add the .crt file to your browser." ) try: with open(cert_dir + '/' + config.SSL_PKEY_FILE, 'rt') as f: ca_key_raw = f.read() except IOError: raise PappyException("Could not load CA private key!") return (ca_raw, ca_key_raw)
def respond_failure(self, message): """ Closes the connection to the remote server and returns an error message. The request object will have a response of None. """ #self.transport.loseConnection() self.data_defer.errback(PappyException(message))
def untag(line): """ Remove a tag from requests Usage: untag <tag> <request ids> You can provide as many request ids as you want and the tag will be removed from all of them. If no ids are given, the tag will be removed from all in-context requests. """ args = shlex.split(line) if len(args) == 0: raise PappyException("Tag and request ids are required") tag = args[0] ids = [] if len(args) > 1: reqids = yield load_reqlist(args[1], False, ids_only=True) print 'Removing tag %s from %s' % (tag, ', '.join(reqids)) else: print "Removing tag %s from all in-context requests" % tag reqids = yield async_main_context_ids() for reqid in reqids: req = yield Request.load_request(reqid) if tag in req.tags: req.tags.discard(tag) if req.saved: yield req.async_save() if ids: print 'Tag %s removed from %s' % (tag, ', '.join(ids))
def tag(line): """ Add a tag to requests. Usage: tag <tag> [request ids] You can tag as many requests as you want at the same time. If no ids are given, the tag will be applied to all in-context requests. """ args = shlex.split(line) if len(args) == 0: raise PappyException('Tag name is required') tag = args[0] if len(args) > 1: reqids = yield load_reqlist(args[1], False, ids_only=True) print 'Tagging %s with %s' % (', '.join(reqids), tag) else: print "Tagging all in-context requests with %s" % tag reqids = yield async_main_context_ids() for reqid in reqids: req = yield Request.load_request(reqid) if tag not in req.tags: req.tags.add(tag) if req.saved: yield req.async_save() else: print 'Request %s already has tag %s' % (req.reqid, tag)
def check_reqid(reqid): # Used for the repeater command. Must not be async try: yield pappyproxy.http.Request.load_request(reqid) except: raise PappyException('"%s" is not a valid request id' % reqid) defer.returnValue(None)
def load_certs_from_dir(cert_dir): from pappyproxy.pappy import session try: with open(cert_dir + '/' + session.config.ssl_ca_file, 'rt') as f: ca_raw = f.read() except IOError: raise PappyException( "Could not load CA cert! Generate certs using the `gencerts` command then add the .crt file to your browser." ) try: with open(cert_dir + '/' + session.config.ssl_pkey_file, 'rt') as f: ca_key_raw = f.read() except IOError: raise PappyException("Could not load CA private key!") return (ca_raw, ca_key_raw)
def do_builtin_filter(self, line): if not line: raise PappyException("Filter name required") filters_to_add = pappyproxy.context.BuiltinFilters.get(line) for f in filters_to_add: print f.filter_string pappyproxy.context.add_filter(f)
def load_macros_cmd(line): """ Load macros from a directory. By default loads macros in the current directory. Usage: load_macros [dir] """ global macro_dict global int_macro_dict global loaded_macros global loaded_int_macros if line: load_dir = line else: load_dir = '.' (to_load, int_to_load) = load_macros(load_dir) if not to_load and not int_to_load: raise PappyException('No macros to load.') macro_dict = {} loaded_macros = [] int_macro_dict = {} loaded_int_macros = [] for macro in to_load: if macro.name in macro_dict: print 'Name conflict in %s! "%s" already in use, not loading.' % ( macro.filename, macro.name) elif macro.short_name and macro.short_name in macro_dict: print 'Name conflict in %s! "%s" already in use, not loading.' % ( macro.filename, macro.short_name) elif macro.file_name in macro_dict: print 'Name conflict in %s! "%s" already in use, not loading.' % ( macro.filename, macro.file_name) else: macro_dict[macro.name] = macro macro_dict[macro.file_name] = macro if macro.short_name: macro_dict[macro.short_name] = macro loaded_macros.append(macro) print 'Loaded "%s"' % macro for macro in int_to_load: if macro.name in int_macro_dict: print 'Name conflict in %s! "%s" already in use, not loading.' % ( macro.filename, macro.name) elif macro.short_name and macro.short_name in int_macro_dict: print 'Name conflict in %s! "%s" already in use, not loading.' % ( macro.filename, macro.short_name) elif macro.file_name in int_macro_dict: print 'Name conflict in %s! "%s" already in use, not loading.' % ( macro.filename, macro.file_name) else: int_macro_dict[macro.name] = macro int_macro_dict[macro.file_name] = macro if macro.short_name: int_macro_dict[macro.short_name] = macro loaded_int_macros.append(macro) print 'Loaded "%s"' % macro
def builtin_filter(line): if not line: raise PappyException("Filter name required") filters_to_add = yield BuiltinFilters.get(line) for f in filters_to_add: print f.filter_string yield pappyproxy.pappy.main_context.add_filter(f) defer.returnValue(None)
def asciihex_decode_helper(s): ret = [] try: for a, b in zip(s[0::2], s[1::2]): c = a + b ret.append(chr(int(c, 16))) return ''.join(ret) except Exception as e: raise PappyException(e)
def do_generate_int_macro(self, line): if line == '': raise PappyException('Macro name is required') args = shlex.split(line) name = args[0] script_str = gen_imacro() fname = 'int_%s.py' % name with open(fname, 'wc') as f: f.write(script_str) print 'Wrote script to %s' % fname
def load_filter_set(line): if line == '': raise PappyException("Must give name to save filters as") strs = yield get_saved_context(line, pappyproxy.http.dbpool) yield _save_filters_to('_') pappyproxy.pappy.main_context.set_filters([]) for s in strs: yield pappyproxy.pappy.main_context.add_filter_string(s) print 'Set the context to:' for s in strs: print ' %s' % s
def load(self): if self.filename: match = re.findall('.*macro_(.*).py$', self.filename) self.file_name = match[0] st = os.stat(self.filename) if (st.st_mode & stat.S_IWOTH): raise PappyException("Refusing to load world-writable macro: %s" % self.filename) module_name = os.path.basename(os.path.splitext(self.filename)[0]) self.source = imp.load_source('%s'%module_name, self.filename) if not hasattr(self.source, 'MACRO_NAME'): raise PappyException('Macro in %s does not define MACRO_NAME' % self.filename) self.name = self.source.MACRO_NAME if self.name == '': raise PappyException('Macro in %s cannot have a blank name' % self.filename) if hasattr(self.source, 'SHORT_NAME'): self.short_name = self.source.SHORT_NAME else: self.short_name = None else: self.source = None
def base64_decode_helper(s): try: return base64.b64decode(s) except TypeError: for i in range(1, 5): try: s_padded = base64.b64decode(s + '=' * i) return s_padded except: pass raise PappyException("Unable to base64 decode string")
def do_load_macros(self, line): global macro_dict global int_macro_dict global loaded_macros global loaded_int_macros if line: load_dir = line else: load_dir = '.' (to_load, int_to_load) = load_macros(load_dir) if not to_load and not int_to_load: raise PappyException('No macros to load.') macro_dict = {} loaded_macros = [] int_macro_dict = {} loaded_int_macros = [] for macro in to_load: if macro.name in macro_dict: print 'Name conflict in %s! "%s" already in use, not loading.' % ( macro.filename, macro.name) elif macro.short_name and macro.short_name in macro_dict: print 'Name conflict in %s! "%s" already in use, not loading.' % ( macro.filename, macro.short_name) elif macro.file_name in macro_dict: print 'Name conflict in %s! "%s" already in use, not loading.' % ( macro.filename, macro.file_name) else: macro_dict[macro.name] = macro macro_dict[macro.file_name] = macro if macro.short_name: macro_dict[macro.short_name] = macro loaded_macros.append(macro) print 'Loaded "%s"' % macro for macro in int_to_load: if macro.name in int_macro_dict: print 'Name conflict in %s! "%s" already in use, not loading.' % ( macro.filename, macro.name) elif macro.short_name and macro.short_name in int_macro_dict: print 'Name conflict in %s! "%s" already in use, not loading.' % ( macro.filename, macro.short_name) elif macro.file_name in int_macro_dict: print 'Name conflict in %s! "%s" already in use, not loading.' % ( macro.filename, macro.file_name) else: int_macro_dict[macro.name] = macro int_macro_dict[macro.file_name] = macro if macro.short_name: int_macro_dict[macro.short_name] = macro loaded_int_macros.append(macro) print 'Loaded "%s"' % macro
def do_generate_macro(self, line): if line == '': raise PappyException('Macro name is required') args = shlex.split(line) name = args[0] reqs = yield load_reqlist(args[1]) script_str = macro_from_requests(reqs) fname = 'macro_%s.py' % name with open(fname, 'wc') as f: f.write(script_str) print 'Wrote script to %s' % fname
def run_int_macro(line): """ Activate an intercepting macro Usage: run_int_macro <macro name or macro short name> Macro can be stopped with stop_int_macro """ global int_macro_dict global loaded_int_macros args = shlex.split(line) if len(args) == 0: raise PappyException('You must give an intercepting macro to run. You can give its short name, or the name in the filename.') if args[0] not in int_macro_dict: raise PappyException('%s not a loaded intercepting macro' % line) macro = int_macro_dict[args[0]] try: macro.init(args[1:]) add_intercepting_macro(macro.name, macro) print '"%s" started' % macro.name except Exception as e: print 'Error initializing macro:' raise e
def filtercmd(line): """ Apply a filter to the current context Usage: filter <filter string> See README.md for information on filter strings """ if not line: raise PappyException("Filter string required") filter_to_add = pappyproxy.context.Filter(line) yield filter_to_add.generate() pappyproxy.pappy.main_context.add_filter(filter_to_add)
def pretty_print_request(line): """ Print the body of the request pretty printed. Usage: pretty_print_request <format> <reqid(s)> """ args = shlex.split(line) if len(args) < 2: raise PappyException("Usage: pretty_print_request <format> <reqid(s)>") reqids = args[1] reqs = yield load_reqlist(reqids) for req in reqs: pretty_print_body(args[0], req.body)
def generate_int_macro(line): """ Generate an intercepting macro script Usage: generate_int_macro <name> """ if line == '': raise PappyException('Macro name is required') args = shlex.split(line) name = args[0] script_str = gen_imacro() fname = 'int_%s.py' % name with open(fname, 'wc') as f: f.write(script_str) print 'Wrote script to %s' % fname
def pretty_print_response(line): """ Print the body of the request pretty printed. Usage: pretty_print_request <format> <reqid(s)> """ args = shlex.split(line) if len(args) < 2: raise PappyException("Usage: pretty_print_request <format> <reqid(s)>") reqids = args[1] reqs = yield load_reqlist(reqids) for req in reqs: if req.response: pretty_print_body(args[0], req.response.body) else: print 'No response associated with request %s' % req.reqid