Ejemplo n.º 1
0
    def normalize_crumb(cls, crumb):
        ty = crumb.get('type') or 'default'
        ts = parse_timestamp(crumb.get('timestamp'))
        if ts is None:
            raise InterfaceValidationError('Unable to determine timestamp '
                                           'for crumb')

        rv = {
            'type': ty,
            'timestamp': to_timestamp(ts),
        }

        level = crumb.get('level')
        if level not in (None, 'info'):
            rv['level'] = level

        msg = crumb.get('message')
        if msg is not None:
            rv['message'] = trim(six.text_type(msg), 4096)

        category = crumb.get('category')
        if category is not None:
            rv['category'] = trim(six.text_type(category), 256)

        event_id = crumb.get('event_id')
        if event_id is not None:
            rv['event_id'] = event_id

        if crumb.get('data'):
            try:
                for key, value in six.iteritems(crumb['data']):
                    if not isinstance(value, six.string_types):
                        crumb['data'][key] = json.dumps(value)
            except AttributeError:
                # TODO(dcramer): we dont want to discard the the rest of the
                # crumb, but it'd be nice if we could record an error
                # raise InterfaceValidationError(
                #     'The ``data`` on breadcrumbs must be a mapping (received {})'.format(
                #         type(crumb['data']),
                #     )
                # )
                pass
            else:
                rv['data'] = trim(crumb['data'], 4096)

        return rv
Ejemplo n.º 2
0
def process_symbolic_image(image):
    try:
        symbolic_image = {
            'id': normalize_debug_id(image.get('id')),
            'image_addr': _addr(image.get('image_addr')),
            'image_size': parse_addr(image['image_size']),
            'image_vmaddr': _addr(image.get('image_vmaddr') or 0),
            'name': image.get('name'),
        }

        if image.get('arch') is not None:
            symbolic_image['arch'] = image.get('arch')

        return symbolic_image
    except KeyError as e:
        raise InterfaceValidationError('Missing value for symbolic image: %s' %
                                       e.args[0])
Ejemplo n.º 3
0
    def to_python(cls, data):
        is_debug_build = data.get('is_debug_build', None)
        if is_debug_build is not None and not isinstance(is_debug_build, bool):
            raise InterfaceValidationError(
                'Invalid value for "is_debug_build"')

        images = []
        for x in data.get('images', None) or ():
            if x is None:
                continue
            images.append(cls.normalize_image(x))

        return cls(
            images=images,
            sdk_info=cls.normalize_sdk_info(data.get('sdk_info')),
            is_debug_build=is_debug_build,
        )
Ejemplo n.º 4
0
    def normalize_crumb(cls, crumb):
        ty = crumb.get('type') or 'default'
        level = crumb.get('level')
        if not isinstance(level, six.string_types) or \
           (level not in LOG_LEVELS_MAP and level != 'critical'):
            level = 'info'

        ts = parse_timestamp(crumb.get('timestamp'))
        if ts is None:
            raise InterfaceValidationError(
                'Unable to determine timestamp for crumb')
        ts = to_timestamp(ts)

        msg = crumb.get('message')
        if msg is not None:
            msg = trim(six.text_type(msg), 4096)

        category = crumb.get('category')
        if category is not None:
            category = trim(six.text_type(category), 256)

        event_id = crumb.get('event_id')

        data = crumb.get('data')
        if not isinstance(data, dict):
            # TODO(dcramer): we dont want to discard the the rest of the
            # crumb, but it'd be nice if we could record an error
            # raise InterfaceValidationError(
            #     'The ``data`` on breadcrumbs must be a mapping (received {})'.format(
            #         type(crumb['data']),
            #     )
            # )
            data = None
        else:
            data = trim(data, 4096)

        return {
            'type': ty,
            'level': level,
            'timestamp': ts,
            'message': msg,
            'category': category,
            'event_id': event_id,
            'data': data
        }
Ejemplo n.º 5
0
    def normalize_crumb(cls, crumb):
        ty = crumb.get('type') or 'default'
        level = crumb.get('level') or 'info'
        ts = parse_timestamp(crumb.get('timestamp'))
        if ts is None:
            raise InterfaceValidationError('Unable to determine timestamp for crumb')
        ts = to_timestamp(ts)

        msg = crumb.get('message')
        if msg is not None:
            msg = trim(six.text_type(msg), 4096)

        category = crumb.get('category')
        if category is not None:
            category = trim(six.text_type(category), 256)

        event_id = crumb.get('event_id')

        data = crumb.get('data')
        if data:
            try:
                for key, value in six.iteritems(data):
                    if not isinstance(value, six.string_types):
                        data[key] = json.dumps(value)
            except AttributeError:
                # TODO(dcramer): we dont want to discard the the rest of the
                # crumb, but it'd be nice if we could record an error
                # raise InterfaceValidationError(
                #     'The ``data`` on breadcrumbs must be a mapping (received {})'.format(
                #         type(crumb['data']),
                #     )
                # )
                data = None
            else:
                data = trim(data, 4096)

        return {
            'type': ty,
            'level': level,
            'timestamp': ts,
            'message': msg,
            'category': category,
            'event_id': event_id,
            'data': data
        }
Ejemplo n.º 6
0
    def to_python(cls, data):
        if not data.get('message'):
            raise InterfaceValidationError("No 'message' present")

        # TODO(dcramer): some day we should stop people from sending arbitrary
        # crap to the server
        if not isinstance(data['message'], basestring):
            data['message'] = json.dumps(data['message'])

        kwargs = {
            'message': trim(data['message'], settings.SENTRY_MAX_MESSAGE_LENGTH),
            'formatted': None,
        }

        if data.get('params'):
            kwargs['params'] = trim(data['params'], 1024)
        else:
            kwargs['params'] = ()

        if kwargs['formatted']:
            if not isinstance(kwargs['formatted'], basestring):
                data['formatted'] = json.dumps(data['formatted'])
        # support python-esque formatting (e.g. %s)
        elif '%' in kwargs['message'] and kwargs['params']:
            if isinstance(kwargs['params'], list):
                kwargs['params'] = tuple(kwargs['params'])

            try:
                kwargs['formatted'] = trim(
                    kwargs['message'] % kwargs['params'],
                    settings.SENTRY_MAX_MESSAGE_LENGTH,
                )
            except Exception:
                pass
        # support very basic placeholder formatters (non-typed)
        elif '{}' in kwargs['message'] and kwargs['params']:
            try:
                kwargs['formatted'] = trim(
                    kwargs['message'].format(kwargs['params']),
                    settings.SENTRY_MAX_MESSAGE_LENGTH,
                )
            except Exception:
                pass

        return cls(**kwargs)
Ejemplo n.º 7
0
 def to_python(cls, data):
     values = []
     for crumb in data.get('values') or ():
         ty = crumb.get('type') or 'message'
         ts = parse_new_timestamp(crumb.get('timestamp'))
         if ts is None:
             raise InterfaceValidationError(
                 'Unable to determine timestamp for crumb')
         values.append({
             'type':
             ty,
             # We need to store timestamps here as this will go into
             # the node store which does not support datetime objects.
             'timestamp':
             to_timestamp(ts),
             'data':
             validate_payload_for_type(crumb.get('data'), ty),
         })
     return cls(values=values)
    def to_python(cls, data):
        is_valid, errors = validate_and_default_interface(data, cls.path)
        if not is_valid:
            raise InterfaceValidationError("Invalid device")

        data = data.copy()

        extra_data = data.pop('data', data)
        name = trim(data.pop('name'), 64)
        version = trim(data.pop('version'), 64)
        build = trim(data.pop('build', None), 64)

        kwargs = {
            'name': name,
            'version': version,
            'build': build,
            'data': trim_dict(extra_data),
        }
        return cls(**kwargs)
Ejemplo n.º 9
0
    def to_python(cls, data, has_system_frames=None, slim_frames=True):
        if not (data.get('type') or data.get('value')):
            raise InterfaceValidationError("No 'type' or 'value' present")

        if data.get('stacktrace') and data['stacktrace'].get('frames'):
            stacktrace = Stacktrace.to_python(
                data['stacktrace'],
                has_system_frames=has_system_frames,
                slim_frames=slim_frames,
            )
        else:
            stacktrace = None

        if data.get('raw_stacktrace') and data['raw_stacktrace'].get('frames'):
            raw_stacktrace = Stacktrace.to_python(
                data['raw_stacktrace'],
                has_system_frames=has_system_frames,
                slim_frames=slim_frames,
            )
        else:
            raw_stacktrace = None

        type = data.get('type')
        value = data.get('value')
        if not type and ':' in value.split(' ', 1)[0]:
            type, value = value.split(':', 1)
            # in case of TypeError: foo (no space)
            value = value.strip()

        if value is not None and not isinstance(value, basestring):
            value = json.dumps(value)
        value = trim(value, 4096)

        kwargs = {
            'type': trim(type, 128),
            'value': value,
            'module': trim(data.get('module'), 128),
            'stacktrace': stacktrace,
            'raw_stacktrace': raw_stacktrace,
        }

        return cls(**kwargs)
Ejemplo n.º 10
0
    def to_python(cls, data, rust_renormalized=RUST_RENORMALIZED_DEFAULT):
        if rust_renormalized:
            for key in (
                'message',
                'formatted',
                'params',
            ):
                data.setdefault(key, None)
            return cls(**data)

        formatted = stringify(data.get('formatted'))
        message = stringify(data.get('message'))
        if formatted is None and message is None:
            raise InterfaceValidationError("No message present")

        params = data.get('params')
        if isinstance(params, (list, tuple)):
            params = tuple(p for p in params)
        elif isinstance(params, dict):
            params = {k: v for k, v in six.iteritems(params)}
        else:
            params = ()

        if formatted is None and params:
            try:
                if '%' in message:
                    formatted = message % params
                elif '{}' in message and isinstance(params, tuple):
                    formatted = message.format(*params)
                # NB: Named newstyle arguments were never supported
            except Exception:
                pass

        if formatted is None or message == formatted:
            formatted = message
            message = None

        return cls(
            formatted=trim(formatted, settings.SENTRY_MAX_MESSAGE_LENGTH),
            message=trim(message, settings.SENTRY_MAX_MESSAGE_LENGTH),
            params=trim(params, 1024),
        )
Ejemplo n.º 11
0
    def to_python(cls, data, has_system_frames=None):
        if not (data.get('type') or data.get('value')):
            raise InterfaceValidationError("No 'type' or 'value' present")

        if data.get('stacktrace') and data['stacktrace'].get('frames'):
            stacktrace = Stacktrace.to_python(
                data['stacktrace'],
                has_system_frames=has_system_frames,
            )
        else:
            stacktrace = None

        kwargs = {
            'type': trim(data.get('type'), 128),
            'value': trim(data.get('value'), 4096),
            'module': trim(data.get('module'), 128),
            'stacktrace': stacktrace,
        }

        return cls(**kwargs)
Ejemplo n.º 12
0
    def to_python(cls, data):
        is_valid, errors = validate_and_default_interface(data, cls.path)
        if not is_valid:
            raise InterfaceValidationError("Invalid template")

        kwargs = {
            'abs_path':
            trim(data.get('abs_path', None), 256),
            'filename':
            trim(data.get('filename', None), 256),
            'context_line':
            trim(data.get('context_line', None), 256),
            'lineno':
            int(data['lineno'])
            if data.get('lineno', None) is not None else None,
            # TODO(dcramer): trim pre/post_context
            'pre_context':
            data.get('pre_context'),
            'post_context':
            data.get('post_context'),
        }
        return cls(**kwargs)
Ejemplo n.º 13
0
def process_apple_image(image):
    def _addr(x):
        return '0x%x' % parse_addr(x)
    try:
        apple_image = {
            'cpu_type': image['cpu_type'],
            'cpu_subtype': image['cpu_subtype'],
            'image_addr': _addr(image['image_addr']),
            'image_size': image['image_size'],
            'image_vmaddr': _addr(image.get('image_vmaddr') or 0),
            'name': image['name'],
            'uuid': image['uuid']
        }
        if image.get('major_version') is not None:
            apple_image['major_version'] = image['major_version']
        if image.get('minor_version') is not None:
            apple_image['minor_version'] = image['minor_version']
        if image.get('revision_version') is not None:
            apple_image['revision_version'] = image['revision_version']
        return apple_image
    except KeyError as e:
        raise InterfaceValidationError('Missing value for apple image: %s'
                                       % e.args[0])
Ejemplo n.º 14
0
    def to_python(cls, data, slim_frames=True, raw=False):
        is_valid, errors = validate_and_default_interface(data, cls.path)
        if not is_valid:
            raise InterfaceValidationError("Invalid stack frame data.")

        # Trim down the frame list to a hard limit. Leave the last frame in place in case
        # it's useful for debugging.
        frameiter = data.get('frames') or []
        if len(frameiter) > settings.SENTRY_STACKTRACE_FRAMES_HARD_LIMIT:
            frameiter = chain(
                islice(data['frames'],
                       settings.SENTRY_STACKTRACE_FRAMES_HARD_LIMIT - 1),
                (data['frames'][-1], ))

        frame_list = []

        for f in frameiter:
            if f is None:
                continue
            # XXX(dcramer): handle PHP sending an empty array for a frame
            frame_list.append(Frame.to_python(f or {}, raw=raw))

        kwargs = {
            'frames': frame_list,
        }

        kwargs['registers'] = None
        if data.get('registers') and isinstance(data['registers'], dict):
            kwargs['registers'] = data.get('registers')

        kwargs['frames_omitted'] = data.get('frames_omitted') or None

        instance = cls(**kwargs)
        if slim_frames:
            slim_frame_data(instance)
        return instance
Ejemplo n.º 15
0
    def to_python(cls, data, rust_renormalized=RUST_RENORMALIZED_DEFAULT):
        if rust_renormalized:
            for key in (
                'type',
                'synthetic',
                'description',
                'help_link',
                'handled',
                'data',
                'meta',
            ):
                data.setdefault(key, None)

            return cls(**data)

        data = upgrade_legacy_mechanism(data)
        is_valid, errors = validate_and_default_interface(data, cls.path)
        if not is_valid:
            raise InterfaceValidationError("Invalid mechanism")

        if not data.get('type'):
            raise InterfaceValidationError("No 'type' present")

        mechanism_meta = data.get('meta') or {}
        mach_exception = mechanism_meta.get('mach_exception')
        if mach_exception is not None:
            mach_exception = prune_empty_keys({
                'exception': mach_exception['exception'],
                'code': mach_exception['code'],
                'subcode': mach_exception['subcode'],
                'name': mach_exception.get('name'),
            })

        signal = mechanism_meta.get('signal')
        if signal is not None:
            signal = prune_empty_keys({
                'number': signal['number'],
                'code': signal.get('code'),
                'name': signal.get('name'),
                'code_name': signal.get('code_name'),
            })

        errno = mechanism_meta.get('errno')
        if errno is not None:
            errno = prune_empty_keys({
                'number': errno['number'],
                'name': errno.get('name'),
            })

        kwargs = {
            'type': trim(data['type'], 128),
            'synthetic': data.get('synthetic'),
            'description': trim(data.get('description'), 1024),
            'help_link': trim(data.get('help_link'), 1024),
            'handled': data.get('handled'),
            'data': trim(data.get('data'), 4096),
            'meta': {
                'errno': errno,
                'mach_exception': mach_exception,
                'signal': signal,
            },
        }

        return cls(**kwargs)
Ejemplo n.º 16
0
    def to_python(cls, data):
        abs_path = data.get('abs_path')
        filename = data.get('filename')
        function = data.get('function')
        module = data.get('module')

        for name in ('abs_path', 'filename', 'function', 'module'):
            if not isinstance(data.get(name), (string_types, NoneType)):
                raise InterfaceValidationError("Invalid value for '%s'" % name)

        # absolute path takes priority over filename
        # (in the end both will get set)
        if not abs_path:
            abs_path = filename
            filename = None

        if not filename and abs_path:
            if is_url(abs_path):
                urlparts = urlparse(abs_path)
                if urlparts.path:
                    filename = urlparts.path
                else:
                    filename = abs_path
            else:
                filename = abs_path

        if not (filename or function or module):
            raise InterfaceValidationError(
                "No 'filename' or 'function' or 'module'")

        if function == '?':
            function = None

        context_locals = data.get('vars') or {}
        if isinstance(context_locals, (list, tuple)):
            context_locals = dict(enumerate(context_locals))
        elif not isinstance(context_locals, dict):
            context_locals = {}
        context_locals = trim_dict(context_locals, object_hook=handle_nan)

        # extra data is used purely by internal systems,
        # so we dont trim it
        extra_data = data.get('data') or {}
        if isinstance(extra_data, (list, tuple)):
            extra_data = dict(enumerate(extra_data))

        # XXX: handle lines which were sent as 'null'
        context_line = trim(data.get('context_line'), 256)
        if context_line is not None:
            pre_context = data.get('pre_context', None)
            if pre_context:
                pre_context = [c or '' for c in pre_context]

            post_context = data.get('post_context', None)
            if post_context:
                post_context = [c or '' for c in post_context]
        else:
            pre_context, post_context = None, None

        try:
            in_app = validate_bool(data.get('in_app'), False)
        except AssertionError:
            raise InterfaceValidationError("Invalid value for 'in_app'")

        instruction_offset = data.get('instruction_offset')
        if instruction_offset is not None and \
           not isinstance(instruction_offset, (int, long)):
            raise InterfaceValidationError(
                "Invalid value for 'instruction_offset'")

        kwargs = {
            'abs_path': trim(abs_path, 256),
            'filename': trim(filename, 256),
            'module': trim(module, 256),
            'function': trim(function, 256),
            'package': trim(data.get('package'), 256),
            'symbol_addr': trim(data.get('symbol_addr'), 16),
            'instruction_addr': trim(data.get('instruction_addr'), 16),
            'instruction_offset': instruction_offset,
            'in_app': in_app,
            'context_line': context_line,
            # TODO(dcramer): trim pre/post_context
            'pre_context': pre_context,
            'post_context': post_context,
            'vars': context_locals,
            'data': extra_data,
            'errors': data.get('errors'),
        }

        if data.get('lineno') is not None:
            lineno = int(data['lineno'])
            if lineno < 0:
                lineno = None
            kwargs['lineno'] = lineno
        else:
            kwargs['lineno'] = None

        if data.get('colno') is not None:
            kwargs['colno'] = int(data['colno'])
        else:
            kwargs['colno'] = None

        return cls(**kwargs)
Ejemplo n.º 17
0
    def to_python(cls, data, raw=False):
        is_valid, errors = validate_and_default_interface(data, cls.path)
        if not is_valid:
            raise InterfaceValidationError("Invalid stack frame data.")

        abs_path = data.get('abs_path')
        filename = data.get('filename')
        symbol = data.get('symbol')
        function = data.get('function')
        module = data.get('module')
        package = data.get('package')

        # For legacy reasons
        if function in ('?', ''):
            function = None

        # For consistency reasons
        if symbol in ('?', ''):
            symbol = None

        # Some of this processing should only be done for non raw frames
        if not raw:
            # absolute path takes priority over filename
            # (in the end both will get set)
            if not abs_path:
                abs_path = filename
                filename = None

            if not filename and abs_path:
                if is_url(abs_path):
                    urlparts = urlparse(abs_path)
                    if urlparts.path:
                        filename = urlparts.path
                    else:
                        filename = abs_path
                else:
                    filename = abs_path

        platform = data.get('platform')

        context_locals = data.get('vars') or {}
        if isinstance(context_locals, (list, tuple)):
            context_locals = dict(enumerate(context_locals))
        elif not isinstance(context_locals, dict):
            context_locals = {}
        context_locals = trim_dict(context_locals, object_hook=handle_nan)

        # extra data is used purely by internal systems,
        # so we dont trim it
        extra_data = data.get('data') or {}
        if isinstance(extra_data, (list, tuple)):
            extra_data = dict(enumerate(extra_data))

        # XXX: handle lines which were sent as 'null'
        context_line = trim(data.get('context_line'), 256)
        pre_context = data.get('pre_context', None)
        if isinstance(pre_context, list) and pre_context:
            pre_context = [c or '' for c in pre_context]
        else:
            pre_context = None

        post_context = data.get('post_context', None)
        if isinstance(post_context, list) and post_context:
            post_context = [c or '' for c in post_context]
        else:
            post_context = None

        if not context_line and (pre_context or post_context):
            context_line = ''

        in_app = validate_bool(data.get('in_app'), False)

        kwargs = {
            'abs_path': trim(abs_path, 2048),
            'filename': trim(filename, 256),
            'platform': platform,
            'module': trim(module, 256),
            'function': trim(function, 256),
            'package': package,
            'image_addr': to_hex_addr(data.get('image_addr')),
            'symbol': trim(symbol, 256),
            'symbol_addr': to_hex_addr(data.get('symbol_addr')),
            'instruction_addr': to_hex_addr(data.get('instruction_addr')),
            'trust': trim(data.get('trust'), 16),
            'in_app': in_app,
            'context_line': context_line,
            # TODO(dcramer): trim pre/post_context
            'pre_context': pre_context,
            'post_context': post_context,
            'vars': context_locals or None,
            'data': extra_data or None,
            'errors': data.get('errors'),
        }

        if data.get('lineno') is not None:
            lineno = int(data['lineno'])
            if lineno < 0:
                lineno = None
            kwargs['lineno'] = lineno
        else:
            kwargs['lineno'] = None

        if data.get('colno') is not None:
            colno = int(data['colno'])
            if colno < 0:
                colno = None
            kwargs['colno'] = colno
        else:
            kwargs['colno'] = None

        return cls(**kwargs)
Ejemplo n.º 18
0
    def to_python(cls, data):
        is_valid, errors = validate_and_default_interface(data, cls.path)
        if not is_valid:
            raise InterfaceValidationError("Invalid interface data")

        return cls(**data)
    def to_python(cls, data):
        is_valid, errors = validate_and_default_interface(data, cls.path)
        if not is_valid:
            raise InterfaceValidationError("Invalid interface data")

        kwargs = {}

        if data.get('method'):
            method = data['method'].upper()
            # Optimize for the common path here, where it's a GET/POST, falling
            # back to a regular expresion test
            if method not in ('GET',
                              'POST') and not http_method_re.match(method):
                raise InterfaceValidationError("Invalid value for 'method'")
            kwargs['method'] = method
        else:
            kwargs['method'] = None

        scheme, netloc, path, query_bit, fragment_bit = urlsplit(data['url'])

        query_string = data.get('query_string') or query_bit
        if query_string:
            # if querystring was a dict, convert it to a string
            if isinstance(query_string, dict):
                query_string = urlencode([(to_bytes(k), to_bytes(v))
                                          for k, v in query_string.items()])
            else:
                if query_string[0] == '?':
                    # remove '?' prefix
                    query_string = query_string[1:]
            kwargs['query_string'] = trim(query_string, 4096)
        else:
            kwargs['query_string'] = ''

        fragment = data.get('fragment') or fragment_bit

        cookies = data.get('cookies')
        # if cookies were [also] included in headers we
        # strip them out
        headers = data.get('headers')
        if headers:
            headers, cookie_header = format_headers(headers)
            if not cookies and cookie_header:
                cookies = cookie_header
        else:
            headers = ()

        # We prefer the body to be a string, since we can then attempt to parse it
        # as JSON OR decode it as a URL encoded query string, without relying on
        # the correct content type header being passed.
        body = data.get('data')

        content_type = next((v for k, v in headers if k == 'Content-Type'),
                            None)

        # Remove content type parameters
        if content_type is not None:
            content_type = content_type.partition(';')[0].rstrip()

        # We process request data once during ingestion and again when
        # requesting the http interface over the API. Avoid overwriting
        # decoding the body again.
        inferred_content_type = data.get('inferred_content_type', content_type)

        if 'inferred_content_type' not in data and not isinstance(body, dict):
            body, inferred_content_type = heuristic_decode(body, content_type)

        if body:
            body = trim(body, settings.SENTRY_MAX_HTTP_BODY_SIZE)

        env = data.get('env', {})
        # TODO (alex) This could also be accomplished with schema (with formats)
        if 'REMOTE_ADDR' in env:
            try:
                validate_ip(env['REMOTE_ADDR'], required=False)
            except ValueError:
                del env['REMOTE_ADDR']

        kwargs['inferred_content_type'] = inferred_content_type
        kwargs['cookies'] = trim_pairs(format_cookies(cookies))
        kwargs['env'] = trim_dict(env)
        kwargs['headers'] = trim_pairs(headers)
        kwargs['data'] = fix_broken_encoding(body)
        kwargs['url'] = urlunsplit((scheme, netloc, path, '', ''))
        kwargs['fragment'] = trim(fragment, 1024)

        return cls(**kwargs)
Ejemplo n.º 20
0
    def to_python(cls,
                  data,
                  slim_frames=True,
                  rust_renormalized=RUST_RENORMALIZED_DEFAULT):
        if not rust_renormalized:
            is_valid, errors = validate_and_default_interface(data, cls.path)
            if not is_valid:
                raise InterfaceValidationError("Invalid exception")

            if not (data.get('type') or data.get('value')):
                raise InterfaceValidationError("No 'type' or 'value' present")

        if get_path(data, 'stacktrace', 'frames', filter=True):
            stacktrace = Stacktrace.to_python(
                data['stacktrace'],
                slim_frames=slim_frames,
                rust_renormalized=rust_renormalized)
        else:
            stacktrace = None

        if get_path(data, 'raw_stacktrace', 'frames', filter=True):
            raw_stacktrace = Stacktrace.to_python(
                data['raw_stacktrace'],
                slim_frames=slim_frames,
                raw=True,
                rust_renormalized=rust_renormalized)
        else:
            raw_stacktrace = None

        type = data.get('type')
        value = data.get('value')

        if not rust_renormalized:
            if isinstance(value, six.string_types):
                if type is None:
                    m = _type_value_re.match(value)
                    if m:
                        type = m.group(1)
                        value = m.group(2).strip()
            elif value is not None:
                value = json.dumps(value)

            value = trim(value, 4096)

        if data.get('mechanism'):
            mechanism = Mechanism.to_python(
                data['mechanism'], rust_renormalized=rust_renormalized)
        else:
            mechanism = None

        kwargs = {
            'type': trim(type, 128),
            'value': value,
            'module': trim(data.get('module'), 128),
            'mechanism': mechanism,
            'stacktrace': stacktrace,
            'thread_id': trim(data.get('thread_id'), 40),
            'raw_stacktrace': raw_stacktrace,
        }

        return cls(**kwargs)
Ejemplo n.º 21
0
    def to_python(cls, data, rust_renormalized=RUST_RENORMALIZED_DEFAULT):
        if rust_renormalized:
            data.setdefault('query_string', [])
            for key in (
                    "method",
                    "url",
                    "fragment",
                    "cookies",
                    "headers",
                    "data",
                    "env",
                    "inferred_content_type",
            ):
                data.setdefault(key, None)
            return cls(**data)

        is_valid, errors = validate_and_default_interface(data, cls.path)
        if not is_valid:
            raise InterfaceValidationError("Invalid interface data")

        kwargs = {}

        if data.get('method'):
            method = data['method'].upper()
            # Optimize for the common path here, where it's a GET/POST, falling
            # back to a regular expresion test
            if method not in ('GET',
                              'POST') and not http_method_re.match(method):
                raise InterfaceValidationError("Invalid value for 'method'")
            kwargs['method'] = method
        else:
            kwargs['method'] = None

        if data.get('url', None):
            url = to_unicode(data['url'])
            # The JavaScript SDK used to send an ellipsis character for
            # truncated URLs. Canonical URLs do not contain UTF-8 characters in
            # either the path, query string or fragment, so we replace it with
            # three dots (which is the behavior of other SDKs). This effectively
            # makes the string two characters longer, but it will be trimmed
            # again down below.
            if url.endswith(u"\u2026"):
                url = url[:-1] + "..."
            scheme, netloc, path, query_bit, fragment_bit = urlsplit(url)
        else:
            scheme = netloc = path = query_bit = fragment_bit = None

        query_string = data.get('query_string') or query_bit
        if query_string:
            if isinstance(query_string, six.string_types):
                if query_string[0] == '?':
                    query_string = query_string[1:]
                if query_string.endswith(u"\u2026"):
                    query_string = query_string[:-1] + "..."
                query_string = [
                    (to_unicode(k), jsonify(v))
                    for k, v in parse_qsl(query_string, keep_blank_values=True)
                ]
            elif isinstance(query_string, dict):
                query_string = [(to_unicode(k), jsonify(v))
                                for k, v in six.iteritems(query_string)]
            elif isinstance(query_string, list):
                query_string = [
                    tuple(tup) for tup in query_string
                    if isinstance(tup, (tuple, list)) and len(tup) == 2
                ]
            else:
                query_string = []
            kwargs['query_string'] = trim(query_string, 4096)
        else:
            kwargs['query_string'] = []

        fragment = data.get('fragment') or fragment_bit

        cookies = data.get('cookies')
        # if cookies were [also] included in headers we
        # strip them out
        if data.get("headers"):
            headers, cookie_header = format_headers(
                get_path(data, "headers", filter=True))
            if not cookies and cookie_header:
                cookies = cookie_header
        else:
            headers = ()

        # We prefer the body to be a string, since we can then attempt to parse it
        # as JSON OR decode it as a URL encoded query string, without relying on
        # the correct content type header being passed.
        body = data.get('data')

        content_type = next((v for k, v in headers if k == 'Content-Type'),
                            None)

        # Remove content type parameters
        if content_type is not None:
            content_type = content_type.partition(';')[0].rstrip()

        # We process request data once during ingestion and again when
        # requesting the http interface over the API. Avoid overwriting
        # decoding the body again.
        inferred_content_type = data.get('inferred_content_type', content_type)

        if 'inferred_content_type' not in data and not isinstance(body, dict):
            body, inferred_content_type = heuristic_decode(body, content_type)

        if body:
            body = trim(body, settings.SENTRY_MAX_HTTP_BODY_SIZE)

        env = data.get('env', {})
        # TODO (alex) This could also be accomplished with schema (with formats)
        if 'REMOTE_ADDR' in env:
            try:
                validate_ip(env['REMOTE_ADDR'], required=False)
            except ValueError:
                del env['REMOTE_ADDR']

        kwargs['inferred_content_type'] = inferred_content_type
        kwargs['cookies'] = trim_pairs(format_cookies(cookies))
        kwargs['env'] = trim_dict(env)
        kwargs['headers'] = trim_pairs(headers)
        kwargs['data'] = fix_broken_encoding(body)
        kwargs['url'] = urlunsplit((scheme, netloc, path, '', ''))
        kwargs['fragment'] = trim(fragment, 1024)

        return cls(**kwargs)
Ejemplo n.º 22
0
    def to_python(cls, data, raw=False):
        abs_path = data.get('abs_path')
        filename = data.get('filename')
        symbol = data.get('symbol')
        function = data.get('function')
        module = data.get('module')
        package = data.get('package')

        # For legacy reasons
        if function == '?':
            function = None

        # For consistency reasons
        if symbol == '?':
            symbol = None

        for name in ('abs_path', 'filename', 'symbol', 'function', 'module',
                     'package'):
            v = data.get(name)
            if v is not None and not isinstance(v, six.string_types):
                raise InterfaceValidationError("Invalid value for '%s'" % name)

        # Some of this processing should only be done for non raw frames
        if not raw:
            # absolute path takes priority over filename
            # (in the end both will get set)
            if not abs_path:
                abs_path = filename
                filename = None

            if not filename and abs_path:
                if is_url(abs_path):
                    urlparts = urlparse(abs_path)
                    if urlparts.path:
                        filename = urlparts.path
                    else:
                        filename = abs_path
                else:
                    filename = abs_path

            if not (filename or function or module or package):
                raise InterfaceValidationError(
                    "No 'filename' or 'function' or "
                    "'module' or 'package'")

        platform = data.get('platform')
        if platform not in VALID_PLATFORMS:
            platform = None

        context_locals = data.get('vars') or {}
        if isinstance(context_locals, (list, tuple)):
            context_locals = dict(enumerate(context_locals))
        elif not isinstance(context_locals, dict):
            context_locals = {}
        context_locals = trim_dict(context_locals, object_hook=handle_nan)

        # extra data is used purely by internal systems,
        # so we dont trim it
        extra_data = data.get('data') or {}
        if isinstance(extra_data, (list, tuple)):
            extra_data = dict(enumerate(extra_data))

        # XXX: handle lines which were sent as 'null'
        context_line = trim(data.get('context_line'), 256)
        if context_line is not None:
            pre_context = data.get('pre_context', None)
            if pre_context:
                pre_context = [c or '' for c in pre_context]

            post_context = data.get('post_context', None)
            if post_context:
                post_context = [c or '' for c in post_context]
        else:
            pre_context, post_context = None, None

        try:
            in_app = validate_bool(data.get('in_app'), False)
        except AssertionError:
            raise InterfaceValidationError("Invalid value for 'in_app'")

        kwargs = {
            'abs_path': trim(abs_path, 2048),
            'filename': trim(filename, 256),
            'platform': platform,
            'module': trim(module, 256),
            'function': trim(function, 256),
            'package': package,
            'image_addr': to_hex_addr(data.get('image_addr')),
            'symbol': trim(symbol, 256),
            'symbol_addr': to_hex_addr(data.get('symbol_addr')),
            'instruction_addr': to_hex_addr(data.get('instruction_addr')),
            'in_app': in_app,
            'context_line': context_line,
            # TODO(dcramer): trim pre/post_context
            'pre_context': pre_context,
            'post_context': post_context,
            'vars': context_locals,
            'data': extra_data,
            'errors': data.get('errors'),
        }

        if data.get('lineno') is not None:
            lineno = int(data['lineno'])
            if lineno < 0:
                lineno = None
            kwargs['lineno'] = lineno
        else:
            kwargs['lineno'] = None

        if data.get('colno') is not None:
            kwargs['colno'] = int(data['colno'])
        else:
            kwargs['colno'] = None

        return cls(**kwargs)
Ejemplo n.º 23
0
def iverror(message="Invalid data"):
    raise InterfaceValidationError(message)
Ejemplo n.º 24
0
    def to_python(cls, data):
        if not data.get('url'):
            raise InterfaceValidationError("No value for 'url'")

        kwargs = {}

        if data.get('method'):
            method = data['method'].upper()
            # Optimize for the common path here, where it's a GET/POST, falling
            # back to a regular expresion test
            if method not in ('GET',
                              'POST') and not http_method_re.match(method):
                raise InterfaceValidationError("Invalid value for 'method'")
            kwargs['method'] = method
        else:
            kwargs['method'] = None

        scheme, netloc, path, query_bit, fragment_bit = urlsplit(data['url'])

        query_string = data.get('query_string') or query_bit
        if query_string:
            # if querystring was a dict, convert it to a string
            if isinstance(query_string, dict):
                query_string = urlencode([(to_bytes(k), to_bytes(v))
                                          for k, v in query_string.items()])
            else:
                query_string = query_string
                if query_string[0] == '?':
                    # remove '?' prefix
                    query_string = query_string[1:]
            kwargs['query_string'] = trim(query_string, 4096)
        else:
            kwargs['query_string'] = ''

        fragment = data.get('fragment') or fragment_bit

        cookies = data.get('cookies')
        # if cookies were [also] included in headers we
        # strip them out
        headers = data.get('headers')
        if headers:
            headers, cookie_header = format_headers(headers)
            if not cookies and cookie_header:
                cookies = cookie_header
        else:
            headers = ()

        body = data.get('data')
        if isinstance(body, dict):
            body = json.dumps(body)

        if body:
            body = trim(body, settings.SENTRY_MAX_HTTP_BODY_SIZE)

        kwargs['cookies'] = trim_pairs(format_cookies(cookies))
        kwargs['env'] = trim_dict(data.get('env') or {})
        kwargs['headers'] = trim_pairs(headers)
        kwargs['data'] = fix_broken_encoding(body)
        kwargs['url'] = urlunsplit((scheme, netloc, path, '', ''))
        kwargs['fragment'] = trim(fragment, 1024)

        return cls(**kwargs)