def to_python(cls, data): name = data.get('name') if not name: raise InterfaceValidationError("No 'name' value") version = data.get('version') if not version: raise InterfaceValidationError("No 'version' value") integrations = data.get('integrations') if integrations and not isinstance(integrations, list): raise InterfaceValidationError("'integrations' must be a list") packages = data.get('packages') if packages and not isinstance(packages, list): raise InterfaceValidationError("'packages' must be a list") kwargs = { 'name': trim(name, 128), 'version': trim(version, 128), 'integrations': integrations, 'packages': packages, } return cls(**kwargs)
def merge_symbolicated_frame(new_frame, sfrm, platform=None): if sfrm.get('function'): raw_func = trim(sfrm['function'], 256) func = trim(trim_function_name(sfrm['function'], platform), 256) # if function and raw function match, we can get away without # storing a raw function if func == raw_func: new_frame['function'] = raw_func # otherwise we store both else: new_frame['raw_function'] = raw_func new_frame['function'] = func if sfrm.get('instruction_addr'): new_frame['instruction_addr'] = sfrm['instruction_addr'] if sfrm.get('symbol'): new_frame['symbol'] = sfrm['symbol'] if sfrm.get('abs_path'): new_frame['abs_path'] = sfrm['abs_path'] new_frame['filename'] = posixpath.basename(sfrm['abs_path']) if sfrm.get('filename'): new_frame['filename'] = sfrm['filename'] if sfrm.get('lineno'): new_frame['lineno'] = sfrm['lineno'] if sfrm.get('colno'): new_frame['colno'] = sfrm['colno'] if sfrm.get('package'): new_frame['package'] = sfrm['package'] if sfrm.get('trust'): new_frame['trust'] = sfrm['trust'] if sfrm.get('status'): frame_meta = new_frame.setdefault('data', {}) frame_meta['symbolicator_status'] = sfrm['status']
def to_python(cls, data): data = data.copy() extra_data = data.pop('data', data) if not isinstance(extra_data, dict): extra_data = {} try: name = trim(data.pop('name'), 64) except KeyError: raise InterfaceValidationError("Missing or invalid value for 'name'") try: version = trim(data.pop('version'), 64) except KeyError: raise InterfaceValidationError("Missing or invalid value for 'version'") build = trim(data.pop('build', None), 64) kwargs = { 'name': name, 'version': version, 'build': build, 'data': trim_dict(data), } kwargs['data'] = trim_dict(data) return cls(**kwargs)
def to_python(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 normalize_crumb(cls, crumb): ty = crumb.get('type') or 'default' ts = parse_new_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(unicode(msg), 4096) category = crumb.get('category') if category is not None: rv['category'] = trim(unicode(category), 256) event_id = crumb.get('event_id') if event_id is not None: rv['event_id'] = event_id if 'data' in crumb: rv['data'] = trim(crumb['data'], 4096) return rv
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'): for key, value in six.iteritems(crumb['data']): if not isinstance(value, six.string_types): crumb['data'][key] = json.dumps(value) rv['data'] = trim(crumb['data'], 4096) return rv
def to_python(cls, data, rust_renormalized=RUST_RENORMALIZED_DEFAULT): if rust_renormalized: for key in ( 'abs_path', 'filename', 'context_line', 'lineno', 'pre_context', 'post_context', ): data.setdefault(key, None) return 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 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 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, } return cls(**kwargs)
def to_python(cls, data, rust_renormalized=RUST_RENORMALIZED_DEFAULT): if rust_renormalized: for key in ( 'name', 'version', 'integrations', 'packages', ): data.setdefault(key, None) return cls(**data) name = data.get('name') version = data.get('version') integrations = data.get('integrations') if integrations and not isinstance(integrations, list): raise InterfaceValidationError("'integrations' must be a list") packages = data.get('packages') if packages and not isinstance(packages, list): raise InterfaceValidationError("'packages' must be a list") kwargs = { 'name': trim(name, 128), 'version': trim(version, 128), 'integrations': integrations, 'packages': packages, } return cls(**kwargs)
def to_python(cls, data): data = data.copy() extra_data = data.pop("data", data) if not isinstance(extra_data, dict): extra_data = {} ident = trim(data.pop("id", None), 128) if ident: ident = unicode(ident) try: email = trim(validate_email(data.pop("email", None), False), 128) except ValueError: raise InterfaceValidationError("Invalid value for 'email'") username = trim(data.pop("username", None), 128) if username: username = unicode(username) try: ip_address = validate_ip(data.pop("ip_address", None), False) except ValueError: raise InterfaceValidationError("Invalid value for 'ip_address'") # TODO(dcramer): patch in fix to deal w/ old data but not allow new # if not (ident or email or username or ip_address): # raise ValueError('No identifying value') kwargs = {"id": ident, "email": email, "username": username, "ip_address": ip_address} kwargs["data"] = trim_dict(extra_data) return cls(**kwargs)
def to_python(cls, data): abs_path = data.get('abs_path') filename = data.get('filename') if not abs_path: abs_path = filename if not filename: filename = abs_path if abs_path and is_url(abs_path): urlparts = urlparse(abs_path) if urlparts.path: filename = urlparts.path assert filename or data.get('function') or data.get('module') 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) # 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)) kwargs = { 'abs_path': trim(abs_path, 256), 'filename': trim(filename, 256), 'module': trim(data.get('module'), 256), 'function': trim(data.get('function'), 256), 'in_app': validate_bool(data.get('in_app'), False), 'context_line': trim(data.get('context_line'), 256), # TODO(dcramer): trim pre/post_context 'pre_context': data.get('pre_context'), 'post_context': data.get('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): if not data.get('url'): raise InterfaceValidationError("No value for 'url'") kwargs = {} if data.get('method'): method = data['method'].upper() if method not in HTTP_METHODS: 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'] = body kwargs['url'] = urlunsplit((scheme, netloc, path, '', '')) kwargs['fragment'] = trim(fragment, 1024) return cls(**kwargs)
def to_python(cls, data): assert data.get('query') kwargs = { 'query': trim(data['query'], 1024), 'engine': trim(data.get('engine'), 128), } return cls(**kwargs)
def get_metadata(self): exception = self.data['sentry.interfaces.Exception']['values'][0] # in some situations clients are submitting non-string data for these return { 'type': trim(exception.get('type', 'Error'), 128), 'value': trim(exception.get('value', ''), 1024), }
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): if not data.get('query'): raise InterfaceValidationError("No 'query' value") kwargs = { 'query': trim(data['query'], 1024), 'engine': trim(data.get('engine'), 128), } return cls(**kwargs)
def to_python(cls, data): abs_path = data.get("abs_path") filename = data.get("filename") if not abs_path: abs_path = filename if not filename: filename = abs_path if abs_path and is_url(abs_path): urlparts = urlparse(abs_path) if urlparts.path: filename = urlparts.path assert filename or data.get("function") or data.get("module") 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) # 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)) kwargs = { "abs_path": trim(abs_path, 256), "filename": trim(filename, 256), "module": trim(data.get("module"), 256), "function": trim(data.get("function"), 256), "in_app": validate_bool(data.get("in_app"), False), "context_line": trim(data.get("context_line"), 256), # TODO(dcramer): trim pre/post_context "pre_context": data.get("pre_context"), "post_context": data.get("post_context"), "vars": context_locals, "data": extra_data, } if data.get("lineno") is not None: kwargs["lineno"] = int(data["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): name = data.get("name") if not name: raise InterfaceValidationError("No 'name' value") version = data.get("version") if not version: raise InterfaceValidationError("No 'version' value") kwargs = {"name": trim(name, 128), "version": trim(version, 128)} return cls(**kwargs)
def validate_query(payload): rv = {} for key in 'params', 'duration', 'classifier': value = payload.get(key) if value is not None: rv[key] = trim(value, 1024) if 'query' not in payload: raise InterfaceValidationError("Query not provided for 'query' " "breadcrumb.") rv['query'] = trim(payload['query'], 4096) return rv
def to_python(cls, data): data = data.copy() kwargs = { 'id': trim(data.pop('id', None), 128), 'email': trim(data.pop('email', None), 128), 'username': trim(data.pop('username', None), 128), 'ip_address': validate_ip(data.pop('ip_address', None), False), } kwargs['data'] = trim_dict(data.pop('data', 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, raw=True ) 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, six.string_types): value = json.dumps(value) value = trim(value, 4096) mechanism = data.get('mechanism') if mechanism is not None: if not isinstance(mechanism, dict): raise InterfaceValidationError('Bad value for mechanism') mechanism = trim(data.get('mechanism'), 4096) mechanism.setdefault('type', 'generic') 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): assert data.get('message') kwargs = { 'message': trim(data['message'], 2048) } if data.get('params'): kwargs['params'] = trim(data['params'], 1024) else: kwargs['params'] = () return cls(**kwargs)
def to_python(cls, data): data = data.copy() ident = data.pop('id', None) if ident is not None: ident = trim(six.text_type(ident), 128) try: email = trim(validate_email(data.pop('email', None), False), MAX_EMAIL_FIELD_LENGTH) except ValueError: raise InterfaceValidationError("Invalid value for 'email'") username = data.pop('username', None) if username is not None: username = trim(six.text_type(username), 128) name = data.pop('name', None) if name is not None: name = trim(six.text_type(name), 128) try: ip_address = validate_ip(data.pop('ip_address', None), False) except ValueError: raise InterfaceValidationError("Invalid value for 'ip_address'") geo = data.pop('geo', None) if not geo and ip_address: geo = Geo.from_ip_address(ip_address) elif geo: geo = Geo.to_python(geo) extra_data = data.pop('data', None) if not isinstance(extra_data, dict): extra_data = {} extra_data.update(data) # TODO(dcramer): patch in fix to deal w/ old data but not allow new # if not (ident or email or username or ip_address): # raise ValueError('No identifying value') kwargs = { 'id': ident, 'email': email, 'username': username, 'ip_address': ip_address, 'name': name, 'geo': geo, 'data': trim_dict(extra_data) } return cls(**kwargs)
def to_python(cls, data): if not data.get('message'): raise InterfaceValidationError("No 'message' present") kwargs = { 'message': trim(data['message'], 2048) } if data.get('params'): kwargs['params'] = trim(data['params'], 1024) else: kwargs['params'] = () return cls(**kwargs)
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'], six.string_types): data['message'] = json.dumps(data['message']) kwargs = { 'message': trim(data['message'], settings.SENTRY_MAX_MESSAGE_LENGTH), 'formatted': data.get('formatted'), } if data.get('params'): kwargs['params'] = trim(data['params'], 1024) else: kwargs['params'] = () if kwargs['formatted']: if not isinstance(kwargs['formatted'], six.string_types): 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 # don't wastefully store formatted message twice if kwargs['formatted'] == kwargs['message']: kwargs['formatted'] = None return cls(**kwargs)
def to_python(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") meta = data.get('meta', {}) mach_exception = 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 = 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 = 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), '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): assert data.get("filename") assert data.get("context_line") assert data.get("lineno") kwargs = { "abs_path": trim(data.get("abs_path", None), 256), "filename": trim(data["filename"], 256), "context_line": trim(data.get("context_line", None), 256), "lineno": int(data["lineno"]), # TODO(dcramer): trim pre/post_context "pre_context": data.get("pre_context"), "post_context": data.get("post_context"), } 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 to_python(cls, data): threads = [] for thread in data.get('values') or (): threads.append({ 'stacktrace': get_stacktrace(thread.get('stacktrace')), 'raw_stacktrace': get_stacktrace(thread.get('raw_stacktrace'), raw=True), 'id': trim(thread.get('id'), 40), 'crashed': bool(thread.get('crashed')), 'current': bool(thread.get('current')), 'name': trim(thread.get('name'), 200), }) return cls(values=threads)
def to_python(cls, data): assert data.get('filename') assert data.get('context_line') assert data.get('lineno') kwargs = { 'abs_path': trim(data.get('abs_path', None), 256), 'filename': trim(data['filename'], 256), 'context_line': trim(data.get('context_line', None), 256), 'lineno': int(data['lineno']), # TODO(dcramer): trim pre/post_context 'pre_context': data.get('pre_context'), 'post_context': data.get('post_context'), } return cls(**kwargs)
def to_python(cls, data): assert data.get('type') or data.get('value') if data.get('stacktrace'): stacktrace = Stacktrace.to_python(data['stacktrace']) else: stacktrace = None kwargs = { 'type': trim(data.get('type'), 128), 'value': trim(data.get('value'), 256), 'module': trim(data.get('module'), 128), 'stacktrace': stacktrace, } return cls(**kwargs)
def __init__(self, alias, data): self.alias = alias ctx_data = {} for key, value in trim(data).iteritems(): if value not in EMPTY_VALUES: ctx_data[force_text(key)] = force_text(value) self.data = ctx_data
def to_python(cls, data): kwargs = {k: trim(data.get(k, None), 1024) for k in REPORT_KEYS} if kwargs['effective_directive'] not in DIRECTIVES: raise InterfaceValidationError( "Invalid value for 'effective-directive'") # Some reports from Chrome report blocked-uri as just 'about'. # In this case, this is not actionable and is just noisy. # Observed in Chrome 45 and 46. if kwargs['blocked_uri'] == 'about': raise InterfaceValidationError("Invalid value for 'blocked-uri'") # Here, we want to block reports that are coming from browser extensions # and other sources that are meaningless if kwargs['source_file'] is not None: if kwargs['source_file'].startswith(DISALLOWED_SOURCES): raise InterfaceValidationError( "Invalid value for 'source-file'") # Anything resulting from an "inline" whatever violation is either sent # as 'self', or left off. In the case if it missing, we want to noramalize. if not kwargs['blocked_uri']: kwargs['blocked_uri'] = 'self' return cls(**kwargs)
def from_raw(cls, raw): # Firefox doesn't send effective-directive, so parse it from # violated-directive but prefer effective-directive when present # # refs: https://bugzil.la/1192684#c8 try: report = raw['csp-report'] report['effective-directive'] = report.get( 'effective-directive', report['violated-directive'].split(None, 1)[0]) except (KeyError, IndexError): pass # Validate the raw data against the input schema (raises on failure) schema = INPUT_SCHEMAS[cls.path] jsonschema.validate(raw, schema) # For CSP, the values we want are nested under the 'csp-report' key. raw = raw['csp-report'] # Trim values and convert keys to use underscores kwargs = { k.replace('-', '_'): trim(v, 1024) for k, v in six.iteritems(raw) } return cls.to_python(kwargs)
def validate_event(payload): rv = {} for key in 'type', 'target', 'classifier': value = payload.get(key) if value is not None: rv[key] = trim(value, 1024) return rv
def validate_error(payload): rv = {} for key in 'type', 'message', 'event_id': value = payload.get(key) if value is not None: rv[key] = trim(value, 1024) return rv
def search_message(self): """ The internal search_message attribute is only used for search purposes. It adds a bunch of data from the metadata and the culprit. """ data = self.data culprit = self.culprit event_metadata = self.get_event_metadata() if event_metadata is None: event_metadata = eventtypes.get( self.get_event_type())().get_metadata(self.data) message = "" if data.get("logentry"): message += data["logentry"].get( "formatted") or data["logentry"].get("message") or "" if event_metadata: for value in event_metadata.values(): value_u = force_text(value, errors="replace") if value_u not in message: message = f"{message} {value_u}" if culprit and culprit not in message: culprit_u = force_text(culprit, errors="replace") message = f"{message} {culprit_u}" return trim(message.strip(), settings.SENTRY_MAX_MESSAGE_LENGTH)
def to_python(cls, data): data = data.copy() extra_data = data.pop('data', data) if not isinstance(extra_data, dict): extra_data = {} kwargs = { 'id': trim(data.pop('id', None), 128), 'email': trim(data.pop('email', None), 128), 'username': trim(data.pop('username', None), 128), 'ip_address': validate_ip(data.pop('ip_address', None), False), } kwargs['data'] = trim_dict(extra_data) return cls(**kwargs)
def get_search_message(self, event_metadata=None, culprit=None): """This generates the internal event.message attribute which is used for search purposes. It adds a bunch of data from the metadata and the culprit. """ if event_metadata is None: event_metadata = self.get_event_type().get_metadata(self._data) if culprit is None: culprit = self.get_culprit() data = self._data message = "" if data.get("logentry"): message += data["logentry"].get( "formatted") or data["logentry"].get("message") or "" if event_metadata: for value in six.itervalues(event_metadata): value_u = force_text(value, errors="replace") if value_u not in message: message = u"{} {}".format(message, value_u) if culprit and culprit not in message: culprit_u = force_text(culprit, errors="replace") message = u"{} {}".format(message, culprit_u) return trim(message.strip(), settings.SENTRY_MAX_MESSAGE_LENGTH)
def to_python(cls, data): threads = [] for thread in data.get('values') or (): threads.append( { 'stacktrace': get_stacktrace(thread.get('stacktrace')), 'raw_stacktrace': get_stacktrace(thread.get('raw_stacktrace'), raw=True), 'id': trim(thread.get('id'), 40), 'crashed': bool(thread.get('crashed')), 'current': bool(thread.get('current')), 'name': trim(thread.get('name'), 200), } ) return cls(values=threads)
def _merge_frame(new_frame, symbolicated): if symbolicated.get("function"): raw_func = trim(symbolicated["function"], 256) func = trim(trim_function_name(symbolicated["function"], "native"), 256) # if function and raw function match, we can get away without # storing a raw function if func == raw_func: new_frame["function"] = raw_func # otherwise we store both else: new_frame["raw_function"] = raw_func new_frame["function"] = func if symbolicated.get("instruction_addr"): new_frame["instruction_addr"] = symbolicated["instruction_addr"] if symbolicated.get("symbol"): new_frame["symbol"] = symbolicated["symbol"] if symbolicated.get("abs_path"): new_frame["abs_path"] = symbolicated["abs_path"] new_frame["filename"] = posixpath.basename(symbolicated["abs_path"]) if symbolicated.get("filename"): new_frame["filename"] = symbolicated["filename"] if symbolicated.get("lineno"): new_frame["lineno"] = symbolicated["lineno"] if symbolicated.get("colno"): new_frame["colno"] = symbolicated["colno"] if symbolicated.get("package"): new_frame["package"] = symbolicated["package"] if symbolicated.get("trust"): new_frame["trust"] = symbolicated["trust"] if symbolicated.get("pre_context"): new_frame["pre_context"] = symbolicated["pre_context"] if symbolicated.get("context_line") is not None: new_frame["context_line"] = symbolicated["context_line"] if symbolicated.get("post_context"): new_frame["post_context"] = symbolicated["post_context"] addr_mode = symbolicated.get("addr_mode") if addr_mode is None: new_frame.pop("addr_mode", None) else: new_frame["addr_mode"] = addr_mode if symbolicated.get("status"): frame_meta = new_frame.setdefault("data", {}) frame_meta["symbolicator_status"] = symbolicated["status"]
def get_metadata(self): exception = get_path(self.data, 'exception', 'values', -1) # in some situations clients are submitting non-string data for these rv = { 'type': trim(get_path(exception, 'type', default='Error'), 128), 'value': trim(get_path(exception, 'value', default=''), 1024), } # Attach crash location stacktrace = exception.get('stacktrace') if stacktrace: fn = get_crash_file(stacktrace) if fn is not None: rv['filename'] = fn return rv
def get_metadata(self): exception = self.data['sentry.interfaces.Exception']['values'][-1] # in some situations clients are submitting non-string data for these rv = { 'type': trim(exception.get('type', 'Error'), 128), 'value': trim(exception.get('value', ''), 1024), } # Attach crash location stacktrace = exception.get('stacktrace') if stacktrace: fn = get_crash_file(stacktrace) if fn is not None: rv['filename'] = fn return rv
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 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 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): 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 from_raw(cls, raw): # Validate the raw data against the input schema (raises on failure) schema = INPUT_SCHEMAS[cls.path] jsonschema.validate(raw, schema) # Trim values and convert keys to use underscores kwargs = {k.replace('-', '_'): trim(v, 1024) for k, v in six.iteritems(raw)} return cls.to_python(kwargs)
def to_python(cls, data): if not data.get('filename'): raise InterfaceValidationError("Missing 'filename'") if not data.get('context_line'): raise InterfaceValidationError("Missing 'context_line'") if not data.get('lineno'): raise InterfaceValidationError("Missing 'lineno'") kwargs = { 'abs_path': trim(data.get('abs_path', None), 256), 'filename': trim(data['filename'], 256), 'context_line': trim(data.get('context_line', None), 256), 'lineno': int(data['lineno']), # TODO(dcramer): trim pre/post_context 'pre_context': data.get('pre_context'), 'post_context': data.get('post_context'), } return cls(**kwargs)
def __init__(self, alias, data): self.alias = alias ctx_data = {} for key, value in six.iteritems(trim(data)): # we use simple checks here, rathern than ' in set()' to avoid # issues with maps/lists if value is not None and value != '': ctx_data[force_text(key)] = value self.data = ctx_data
def to_python(cls, data): kwargs = {k: trim(data.get(k, None), 1024) for k in REPORT_KEYS} # Anything resulting from an "inline" whatever violation is either sent # as 'self', or left off. In the case if it missing, we want to noramalize. if not kwargs['blocked_uri']: kwargs['blocked_uri'] = 'self' 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 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): name = data.get('name') if not name: raise InterfaceValidationError("No 'name' value") version = data.get('version') if not version: raise InterfaceValidationError("No 'version' value") integrations = data.get('integrations') if integrations and not isinstance(integrations, list): raise InterfaceValidationError("'integrations' must be a list") kwargs = { 'name': trim(name, 128), 'version': trim(version, 128), 'integrations': integrations, } return cls(**kwargs)
def to_python(cls, data): data = data.copy() extra_data = data.pop('data', data) if not isinstance(extra_data, dict): extra_data = {} ident = trim(data.pop('id', None), 128) if ident: ident = six.text_type(ident) try: email = trim(validate_email(data.pop('email', None), False), MAX_EMAIL_FIELD_LENGTH) except ValueError: raise InterfaceValidationError("Invalid value for 'email'") username = trim(data.pop('username', None), 128) if username: username = six.text_type(username) name = trim(data.pop('name', None), 128) if name: name = six.text_type(name) try: ip_address = validate_ip(data.pop('ip_address', None), False) except ValueError: raise InterfaceValidationError("Invalid value for 'ip_address'") # TODO(dcramer): patch in fix to deal w/ old data but not allow new # if not (ident or email or username or ip_address): # raise ValueError('No identifying value') kwargs = { 'id': ident, 'email': email, 'username': username, 'ip_address': ip_address, 'name': name, } kwargs['data'] = trim_dict(extra_data) return cls(**kwargs)
def validate_rpc(payload): rv = {} for key in 'endpoint', 'params', 'classifier': value = payload.get(key) if value is not None: rv[key] = trim(value, 1024) if not rv.get('endpoint'): raise InterfaceValidationError("No endpoint provided for " "'rpc' breadcrumb.") return rv
def validate_query(payload): rv = {} for key in 'query', 'params', 'duration', 'classifier': value = payload.get(key) if value is not None: rv[key] = trim(value, 1024) if 'query' not in rv: raise InterfaceValidationError("Query not provided for 'query' " "breadcrumb.") return rv
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, 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): threads = [] for thread in data.get("values") or (): if thread is None: # XXX(markus): We should handle this in the UI and other # consumers of this interface continue threads.append( { "stacktrace": get_stacktrace(thread.get("stacktrace")), "raw_stacktrace": get_stacktrace(thread.get("raw_stacktrace"), raw=True), "id": trim(thread.get("id"), 40), "crashed": bool(thread.get("crashed")), "current": bool(thread.get("current")), "name": trim(thread.get("name"), 200), } ) return cls(values=threads)