Пример #1
0
    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
Пример #2
0
    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
Пример #3
0
    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
Пример #4
0
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
Пример #5
0
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)
Пример #6
0
 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.")
Пример #7
0
 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.")
Пример #8
0
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")
Пример #9
0
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")
Пример #10
0
 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
Пример #11
0
    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
Пример #12
0
    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))
Пример #13
0
 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
Пример #14
0
    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
Пример #15
0
    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
Пример #16
0
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
Пример #17
0
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
Пример #18
0
 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
Пример #19
0
    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)
Пример #20
0
    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))
Пример #21
0
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
Пример #22
0
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)
Пример #23
0
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)
Пример #24
0
 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))
Пример #25
0
 def __init__(self, val):
     self.val = strutils.escaped_str_to_bytes(val)
Пример #26
0
 def __init__(self, val):
     self.val = strutils.escaped_str_to_bytes(val)