def test_simple(self): indexed_sourcemap = sourcemap_to_index(sourcemap) result = find_source(indexed_sourcemap, 1, 56) assert result == SourceMap(dst_line=0, dst_col=50, src='file2.js', src_line=0, src_col=9, name='multiply')
def fetch_sourcemap(url): result = fetch_url(url) if result == BAD_SOURCE: return body = result.body # According to spec (https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.h7yy76c5il9v) # A SourceMap may be prepended with ")]}'" to cause a Javascript error. # If the file starts with that string, ignore the entire first line. if body.startswith(")]}'"): body = body.split('\n', 1)[1] try: index = sourcemap_to_index(body) except (JSONDecodeError, ValueError): return else: return index
def fetch_sourcemap(url): result = fetch_url(url) if result == BAD_SOURCE: return body = result.body # According to spec (https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.h7yy76c5il9v) # A SourceMap may be prepended with ")]}'" to cause a Javascript error. # If the file starts with that string, ignore the entire first line. if body.startswith(")]}'"): body = body.split("\n", 1)[1] try: index = sourcemap_to_index(body) except (JSONDecodeError, ValueError): return else: return index
def fetch_sourcemap(url, logger=None): result = fetch_url(url, logger=logger) if result == BAD_SOURCE: return body = result.body # According to spec (https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.h7yy76c5il9v) # A SourceMap may be prepended with ")]}'" to cause a Javascript error. # If the file starts with that string, ignore the entire first line. if body.startswith(")]}'"): body = body.split('\n', 1)[1] try: index = sourcemap_to_index(body) except JSONDecodeError: if logger: logger.warning('Failed parsing sourcemap JSON: %r', body[:15], exc_info=True) else: return index
def fetch_javascript_source(event, **kwargs): """ Attempt to fetch source code for javascript frames. Frames must match the following requirements: - lineno >= 0 - colno >= 0 - abs_path is the HTTP URI to the source - context_line is empty """ logger = fetch_javascript_source.get_logger() try: stacktrace = event.data["sentry.interfaces.Stacktrace"] except KeyError: logger.debug("No stacktrace for event %r", event.id) return # build list of frames that we can actually grab source for frames = [ f for f in stacktrace["frames"] if f.get("lineno") is not None and f.get("context_line") is None and f.get("abs_path", "").startswith(("http://", "https://")) ] if not frames: logger.debug("Event %r has no frames with enough context to fetch remote source", event.id) return file_list = set() sourcemap_capable = set() source_code = {} sourcemaps = {} for f in frames: file_list.add(f["abs_path"]) if f.get("colno") is not None: sourcemap_capable.add(f["abs_path"]) while file_list: filename = file_list.pop() # TODO: respect cache-contro/max-age headers to some extent logger.debug("Fetching remote source %r", filename) result = fetch_url(filename) if result == BAD_SOURCE: continue # If we didn't have a colno, a sourcemap wont do us any good if filename not in sourcemap_capable: source_code[filename] = (result.body.splitlines(), None) continue # TODO: we're currently running splitlines twice sourcemap = discover_sourcemap(result, logger=logger) source_code[filename] = (result.body.splitlines(), sourcemap) if sourcemap: logger.debug("Found sourcemap %r for minified script %r", sourcemap, result.url) # pull down sourcemap if sourcemap and sourcemap not in sourcemaps: result = fetch_url(sourcemap, logger=logger) if result == BAD_SOURCE: continue index = sourcemap_to_index(result.body) sourcemaps[sourcemap] = index # queue up additional source files for download for source in index.sources: if source not in source_code: file_list.add(urljoin(result.url, source)) for frame in frames: try: source, sourcemap = source_code[frame["abs_path"]] except KeyError: # we must've failed pulling down the source continue if frame.get("colno") and sourcemap: state = find_source(sourcemaps[sourcemap], frame["lineno"], frame["colno"]) # TODO: is this urljoin right? (is it relative to the sourcemap or the originating file) abs_path = urljoin(sourcemap, state.src) logger.debug("Mapping compressed source %r to mapping in %r", frame["abs_path"], abs_path) try: source, _ = source_code[abs_path] except KeyError: pass else: # Store original data in annotation frame["data"] = { "orig_lineno": frame["lineno"], "orig_colno": frame["colno"], "orig_function": frame["function"], "orig_abs_path": frame["abs_path"], "orig_filename": frame["filename"], "sourcemap": sourcemap, } # SourceMap's return zero-indexed lineno's frame["lineno"] = state.src_line + 1 frame["colno"] = state.src_col frame["function"] = state.name frame["abs_path"] = abs_path frame["filename"] = state.src # TODO: theoretically a minified source could point to another mapped, minified source frame["pre_context"], frame["context_line"], frame["post_context"] = get_source_context( source=source, lineno=int(frame["lineno"]) ) event.update(data=event.data)
def fetch_javascript_source(event, **kwargs): """ Attempt to fetch source code for javascript frames. Frames must match the following requirements: - lineno >= 0 - colno >= 0 - abs_path is the HTTP URI to the source - context_line is empty """ import logging logger = fetch_javascript_source.get_logger() logger.setLevel(logging.INFO) logger.addHandler(logging.StreamHandler()) try: stacktrace = event.data['sentry.interfaces.Stacktrace'] except KeyError: logger.info('No stacktrace for event %r', event.id) return # build list of frames that we can actually grab source for frames = [f for f in stacktrace['frames'] if f.get('lineno') is not None and f.get('colno') is not None and f.get('abs_path', '').startswith(('http://', 'https://')) and f.get('context_line') is None] if not frames: logger.info('Event %r has no frames with enough context to fetch remote source', event.id) return file_list = set((f['abs_path'] for f in frames)) source_code = {} sourcemaps = {} while file_list: filename = file_list.pop() # TODO: respect cache-contro/max-age headers to some extent result = fetch_url(filename) if result == BAD_SOURCE: continue # TODO: we're currently running splitlines twice sourcemap = discover_sourcemap(result, logger=logger) source_code[filename] = (result.body.splitlines(), sourcemap) if sourcemap: logger.info('Found sourcemap %r for minified script %r', sourcemap, result.url) # pull down sourcemap if sourcemap and sourcemap not in sourcemaps: result = fetch_url(sourcemap, logger=logger) if result == BAD_SOURCE: continue index = sourcemap_to_index(result.body) sourcemaps[sourcemap] = index # queue up additional source files for download for source in index.sources: if source not in source_code: file_list.add(urljoin(result.url, source)) for frame in frames: try: source, sourcemap = source_code[frame['abs_path']] except KeyError: # we must've failed pulling down the source continue if sourcemap: state = find_source(sourcemaps[sourcemap], frame['lineno'], frame['colno']) # TODO: is this urljoin right? (is it relative to the sourcemap or the originating file) abs_path = urljoin(sourcemap, state.src) try: source, _ = source_code[abs_path] except KeyError: pass else: # SourceMap's return zero-indexed lineno's frame['lineno'] = state.src_line + 1 frame['colno'] = state.src_col frame['name'] = state.name frame['abs_path'] = abs_path frame['filename'] = state.src # TODO: theoretically a minified source could point to another mapped, minified source frame['pre_context'], frame['context_line'], frame['post_context'] = get_source_context( source=source, lineno=int(frame['lineno'])) event.save()
def fetch_javascript_source(event, **kwargs): """ Attempt to fetch source code for javascript frames. Frames must match the following requirements: - lineno >= 0 - colno >= 0 - abs_path is the HTTP URI to the source - context_line is empty """ logger = fetch_javascript_source.get_logger() try: stacktrace = event.data['sentry.interfaces.Stacktrace'] except KeyError: logger.debug('No stacktrace for event %r', event.id) return # build list of frames that we can actually grab source for frames = [f for f in stacktrace['frames'] if f.get('lineno') is not None and f.get('context_line') is None and f.get('abs_path', '').startswith(('http://', 'https://'))] if not frames: logger.debug('Event %r has no frames with enough context to fetch remote source', event.id) return file_list = set() sourcemap_capable = set() source_code = {} sourcemaps = {} for f in frames: file_list.add(f['abs_path']) if f.get('colno') is not None: sourcemap_capable.add(f['abs_path']) while file_list: filename = file_list.pop() # TODO: respect cache-contro/max-age headers to some extent logger.debug('Fetching remote source %r', filename) result = fetch_url(filename) if result == BAD_SOURCE: continue # If we didn't have a colno, a sourcemap wont do us any good if filename not in sourcemap_capable: source_code[filename] = (result.body.splitlines(), None) continue # TODO: we're currently running splitlines twice sourcemap = discover_sourcemap(result, logger=logger) source_code[filename] = (result.body.splitlines(), sourcemap) if sourcemap: logger.debug('Found sourcemap %r for minified script %r', sourcemap, result.url) # pull down sourcemap if sourcemap and sourcemap not in sourcemaps: result = fetch_url(sourcemap, logger=logger) if result == BAD_SOURCE: continue body = result.body # According to spec (https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.h7yy76c5il9v) # A SouceMap may be prepended with ")]}'" to cause a Javascript error. # If the file starts with that string, ignore the entire first line. if body.startswith(")]}'"): body = body.split('\n', 1)[1] index = sourcemap_to_index(body) sourcemaps[sourcemap] = index # queue up additional source files for download for source in index.sources: if source not in source_code: file_list.add(urljoin(result.url, source)) has_changes = False for frame in frames: try: source, sourcemap = source_code[frame['abs_path']] except KeyError: # we must've failed pulling down the source continue if frame.get('colno') and sourcemap: state = find_source(sourcemaps[sourcemap], frame['lineno'], frame['colno']) # TODO: is this urljoin right? (is it relative to the sourcemap or the originating file) abs_path = urljoin(sourcemap, state.src) logger.debug('Mapping compressed source %r to mapping in %r', frame['abs_path'], abs_path) try: source, _ = source_code[abs_path] except KeyError: pass else: # Store original data in annotation frame['data'] = { 'orig_lineno': frame['lineno'], 'orig_colno': frame['colno'], 'orig_function': frame['function'], 'orig_abs_path': frame['abs_path'], 'orig_filename': frame['filename'], 'sourcemap': sourcemap, } # SourceMap's return zero-indexed lineno's frame['lineno'] = state.src_line + 1 frame['colno'] = state.src_col frame['function'] = state.name frame['abs_path'] = abs_path frame['filename'] = state.src has_changes = True # TODO: theoretically a minified source could point to another mapped, minified source frame['pre_context'], frame['context_line'], frame['post_context'] = get_source_context( source=source, lineno=int(frame['lineno'])) if has_changes: event.update(data=event.data)
def fetch_javascript_source(event, **kwargs): """ Attempt to fetch source code for javascript frames. Frames must match the following requirements: - lineno >= 0 - colno >= 0 - abs_path is the HTTP URI to the source - context_line is empty """ logger = fetch_javascript_source.get_logger() try: stacktrace = event.data['sentry.interfaces.Stacktrace'] except KeyError: logger.debug('No stacktrace for event %r', event.id) return # build list of frames that we can actually grab source for frames = [ f for f in stacktrace['frames'] if f.get('lineno') is not None and f.get('context_line') is None and f.get('abs_path', '').startswith(('http://', 'https://')) ] if not frames: logger.debug( 'Event %r has no frames with enough context to fetch remote source', event.id) return file_list = set() sourcemap_capable = set() source_code = {} sourcemaps = {} for f in frames: file_list.add(f['abs_path']) if f.get('colno') is not None: sourcemap_capable.add(f['abs_path']) while file_list: filename = file_list.pop() # TODO: respect cache-contro/max-age headers to some extent logger.debug('Fetching remote source %r', filename) result = fetch_url(filename) if result == BAD_SOURCE: continue # If we didn't have a colno, a sourcemap wont do us any good if filename not in sourcemap_capable: source_code[filename] = (result.body.splitlines(), None) continue # TODO: we're currently running splitlines twice sourcemap = discover_sourcemap(result, logger=logger) source_code[filename] = (result.body.splitlines(), sourcemap) if sourcemap: logger.debug('Found sourcemap %r for minified script %r', sourcemap, result.url) # pull down sourcemap if sourcemap and sourcemap not in sourcemaps: result = fetch_url(sourcemap, logger=logger) if result == BAD_SOURCE: continue index = sourcemap_to_index(result.body) sourcemaps[sourcemap] = index # queue up additional source files for download for source in index.sources: if source not in source_code: file_list.add(urljoin(result.url, source)) has_changes = False for frame in frames: try: source, sourcemap = source_code[frame['abs_path']] except KeyError: # we must've failed pulling down the source continue if frame.get('colno') and sourcemap: state = find_source(sourcemaps[sourcemap], frame['lineno'], frame['colno']) # TODO: is this urljoin right? (is it relative to the sourcemap or the originating file) abs_path = urljoin(sourcemap, state.src) logger.debug('Mapping compressed source %r to mapping in %r', frame['abs_path'], abs_path) try: source, _ = source_code[abs_path] except KeyError: pass else: # Store original data in annotation frame['data'] = { 'orig_lineno': frame['lineno'], 'orig_colno': frame['colno'], 'orig_function': frame['function'], 'orig_abs_path': frame['abs_path'], 'orig_filename': frame['filename'], 'sourcemap': sourcemap, } # SourceMap's return zero-indexed lineno's frame['lineno'] = state.src_line + 1 frame['colno'] = state.src_col frame['function'] = state.name frame['abs_path'] = abs_path frame['filename'] = state.src has_changes = True # TODO: theoretically a minified source could point to another mapped, minified source frame['pre_context'], frame['context_line'], frame[ 'post_context'] = get_source_context(source=source, lineno=int(frame['lineno'])) if has_changes: event.update(data=event.data)