def replace(self, pattern, repl, flags=0, count=0): """ Replaces a regular expression pattern with repl in both the headers and the body of the message. Encoded body will be decoded before replacement, and re-encoded afterwards. Returns: The number of replacements made. """ if isinstance(pattern, str): pattern = strutils.escaped_str_to_bytes(pattern) if isinstance(repl, str): repl = strutils.escaped_str_to_bytes(repl) replacements = 0 if self.content: self.content, replacements = re.subn(pattern, repl, self.content, flags=flags, count=count) replacements += self.headers.replace(pattern, repl, flags=flags, count=count) return replacements
def replace(self, pattern, repl, flags=0, count=0): """ Replaces a regular expression pattern with repl in each "name: value" header line. Returns: The number of replacements made. """ if isinstance(pattern, str): pattern = strutils.escaped_str_to_bytes(pattern) if isinstance(repl, str): repl = strutils.escaped_str_to_bytes(repl) pattern = re.compile(pattern, flags) replacements = 0 flag_count = count > 0 fields = [] for name, value in self.fields: line, n = pattern.subn(repl, name + b": " + value, count=count) try: name, value = line.split(b": ", 1) except ValueError: # We get a ValueError if the replacement removed the ": " # There's not much we can do about this, so we just keep the header as-is. pass else: replacements += n if flag_count: count -= n if count == 0: break fields.append((name, value)) self.fields = tuple(fields) return replacements
def _extract_modifier_config(modifier: bytes, ctx: dict) -> tuple: modifier_obj = json.loads(modifier) regex = _replace_modifier_values(modifier_obj['regex'], ctx) if regex is None: return None, None replacement = _replace_modifier_values(modifier_obj['replacement'], ctx) if replacement is None: return None, None regex = re.compile(strutils.escaped_str_to_bytes(regex)) replacement = strutils.escaped_str_to_bytes(replacement) return regex, replacement
def parse_modify_spec(option) -> ModifySpec: """ The form for the modify_* options is as follows: * modify_headers: [/flow-filter]/header-name/[@]header-value * modify_body: [/flow-filter]/search-regex/[@]replace The @ allows to provide a file path that is used to read the respective option. Both ModifyHeaders and ModifyBody use ModifySpec to represent a single rule. The first character specifies the separator. Example: :~q:foo:bar If only two clauses are specified, the flow filter is set to match universally (i.e. ".*"). Example: /foo/bar Clauses are parsed from left to right. Extra separators are taken to be part of the final clause. For instance, the last parameter (header-value or replace) below is "foo/bar/": /one/two/foo/bar/ """ sep, rem = option[0], option[1:] parts = rem.split(sep, 2) if len(parts) == 2: flow_filter_pattern = ".*" subject, replacement = parts elif len(parts) == 3: flow_filter_pattern, subject, replacement = parts else: raise ValueError("Invalid number of parameters (2 or 3 are expected)") flow_filter = flowfilter.parse(flow_filter_pattern) if not flow_filter: raise ValueError(f"Invalid filter pattern: {flow_filter_pattern}") subject = strutils.escaped_str_to_bytes(subject) replacement = strutils.escaped_str_to_bytes(replacement) if replacement.startswith(b"@") and not os.path.isfile( os.path.expanduser(replacement[1:])): raise ValueError(f"Invalid file path: {replacement[1:]}") return ModifySpec(flow_filter_pattern, flow_filter, subject, replacement)
def __init__(self, expr): self.expr = expr if self.is_binary: expr = strutils.escaped_str_to_bytes(expr) try: self.re = re.compile(expr, self.flags) except: raise ValueError("Cannot compile expression.")
def test_escaped_str_to_bytes(): assert strutils.escaped_str_to_bytes("foo") == b"foo" assert strutils.escaped_str_to_bytes("\x08") == b"\b" assert strutils.escaped_str_to_bytes("&!?=\\\\)") == br"&!?=\)" assert strutils.escaped_str_to_bytes(u"\\x08") == b"\b" assert strutils.escaped_str_to_bytes(u"&!?=\\\\)") == br"&!?=\)" assert strutils.escaped_str_to_bytes(u"\u00fc") == b'\xc3\xbc' with tutils.raises(ValueError): strutils.escaped_str_to_bytes(b"very byte")
def test_escaped_str_to_bytes(): assert strutils.escaped_str_to_bytes("foo") == b"foo" assert strutils.escaped_str_to_bytes("\x08") == b"\b" assert strutils.escaped_str_to_bytes("&!?=\\\\)") == br"&!?=\)" assert strutils.escaped_str_to_bytes(u"\\x08") == b"\b" assert strutils.escaped_str_to_bytes(u"&!?=\\\\)") == br"&!?=\)" assert strutils.escaped_str_to_bytes(u"\u00fc") == b'\xc3\xbc' with pytest.raises(ValueError): strutils.escaped_str_to_bytes(b"very byte")
def get_data(self) -> bytes: txt = self._w.get_text()[0].strip() try: return strutils.escaped_str_to_bytes(txt) except ValueError: signals.status_message.send(self, message="Invalid data.", expire=1000) raise
def inject_tcp(self, flow: Flow, to_client: bool, message: str): if not isinstance(flow, tcp.TCPFlow): ctx.log.warn("Cannot inject TCP messages into non-TCP flows.") message_bytes = strutils.escaped_str_to_bytes(message) event = TcpMessageInjected(flow, tcp.TCPMessage(not to_client, message_bytes)) try: self.inject_event(event) except ValueError as e: ctx.log.warn(str(e))
def get_data(self) -> bytes: txt = self._w.get_text()[0].strip() try: return strutils.escaped_str_to_bytes(txt) except ValueError: signals.status_message.send( self, message="Invalid Python-style string encoding.", expire=1000 ) raise
def replace(self, pattern, repl, flags=0, count=0): """ Replaces a regular expression pattern with repl in the headers, the request path and the body of the request. Encoded content will be decoded before replacement, and re-encoded afterwards. Returns: The number of replacements made. """ if isinstance(pattern, str): pattern = strutils.escaped_str_to_bytes(pattern) if isinstance(repl, str): repl = strutils.escaped_str_to_bytes(repl) c = super().replace(pattern, repl, flags, count) self.path, pc = re.subn( pattern, repl, self.data.path, flags=flags, count=count ) c += pc return c
def parse_modify_spec(option, subject_is_regex: bool) -> ModifySpec: """ The form for the modify_* options is as follows: * modify_headers: [/flow-filter]/header-name/[@]header-value * modify_body: [/flow-filter]/search-regex/[@]replace The @ allows to provide a file path that is used to read the respective option. Both ModifyHeaders and ModifyBody use ModifySpec to represent a single rule. The first character specifies the separator. Example: :~q:foo:bar If only two clauses are specified, the flow filter is set to match universally (i.e. ".*"). Example: /foo/bar Clauses are parsed from left to right. Extra separators are taken to be part of the final clause. For instance, the last parameter (header-value or replace) below is "foo/bar/": /one/two/foo/bar/ """ sep, rem = option[0], option[1:] parts = rem.split(sep, 2) if len(parts) == 2: flow_filter = _match_all subject, replacement = parts elif len(parts) == 3: flow_filter_pattern, subject, replacement = parts flow_filter = flowfilter.parse(flow_filter_pattern) # type: ignore if not flow_filter: raise ValueError(f"Invalid filter pattern: {flow_filter_pattern}") else: raise ValueError("Invalid number of parameters (2 or 3 are expected)") subject = strutils.escaped_str_to_bytes(subject) if subject_is_regex: try: re.compile(subject) except re.error as e: raise ValueError(f"Invalid regular expression {subject!r} ({e})") spec = ModifySpec(flow_filter, subject, replacement) try: spec.read_replacement() except IOError as e: raise ValueError(f"Invalid file path: {replacement[1:]} ({e})") return spec
def read_file(filename: str, escaped: bool) -> typing.AnyStr: filename = os.path.expanduser(filename) try: with open(filename, "r" if escaped else "rb") as f: d = f.read() except IOError as v: raise exceptions.CommandError(v) if escaped: try: d = strutils.escaped_str_to_bytes(d) except ValueError: raise exceptions.CommandError("Invalid Python-style string encoding.") return d
def get_data(self) -> strbytes: txt = self._w.get_text()[0].strip() try: if self.type == bytes: return strutils.escaped_str_to_bytes(txt) else: return txt.decode() except ValueError: signals.status_message.send( self, message="Invalid Python-style string encoding.", expire=1000) raise
def read_replacement(self) -> bytes: """ Process the replacement str. This usually just involves converting it to bytes. However, if it starts with `@`, we interpret the rest as a file path to read from. Raises: - IOError if the file cannot be read. """ if self.replacement_str.startswith("@"): return Path(self.replacement_str[1:]).expanduser().read_bytes() else: # We could cache this at some point, but unlikely to be a problem. return strutils.escaped_str_to_bytes(self.replacement_str)
def inject_websocket(self, flow: Flow, to_client: bool, message: str, is_text: bool = True): if not isinstance(flow, http.HTTPFlow) or not flow.websocket: ctx.log.warn("Cannot inject WebSocket messages into non-WebSocket flows.") message_bytes = strutils.escaped_str_to_bytes(message) msg = websocket.WebSocketMessage( Opcode.TEXT if is_text else Opcode.BINARY, not to_client, message_bytes ) event = WebSocketMessageInjected(flow, msg) try: self.inject_event(event) except ValueError as e: ctx.log.warn(str(e))
def parse_modify_spec(option: str, subject_is_regex: bool) -> ModifySpec: flow_filter, subject_str, replacement = parse_spec(option) subject = strutils.escaped_str_to_bytes(subject_str) if subject_is_regex: try: re.compile(subject) except re.error as e: raise ValueError(f"Invalid regular expression {subject!r} ({e})") spec = ModifySpec(flow_filter, subject, replacement) try: spec.read_replacement() except OSError as e: raise ValueError(f"Invalid file path: {replacement[1:]} ({e})") return spec
def read_file(filename: str, callback: Callable[..., None], escaped: bool) -> Optional[str]: if not filename: return filename = os.path.expanduser(filename) try: with open(filename, "r" if escaped else "rb") as f: d = f.read() except IOError as v: return str(v) if escaped: try: d = strutils.escaped_str_to_bytes(d) except ValueError: return "Invalid Python-style string encoding." # TODO: Refactor the status_prompt_path signal so that we # can raise exceptions here and return the content instead. callback(d)
def parse(self, manager: "CommandManager", t: type, s: str) -> bytes: try: return strutils.escaped_str_to_bytes(s) except ValueError as e: raise exceptions.TypeError(str(e))
def __init__(self, val): self.val = strutils.escaped_str_to_bytes(val)