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
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])
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, )
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 }
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 }
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)
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)
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)
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), )
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)
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)
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])
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
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)
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)
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)
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)
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)
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)
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)
def iverror(message="Invalid data"): raise InterfaceValidationError(message)
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)