def _index_for(self, minified_src: str) -> sourcemap.SourceMapDecoder: '''Return the source map index for minified_src, loading it if not already loaded.''' # Prevent path traversal assert ".." not in minified_src and "/" not in minified_src if minified_src not in self._indices: for source_dir in self._dirs: filename = os.path.join(source_dir, minified_src + '.map') if os.path.isfile(filename): # Use 'mark_sanitized' to force Pysa to ignore the fact that # 'filename' is user controlled. While putting user # controlled data into a filesystem operation is bad, in # this case it's benign because 'filename' can't traverse # directories outside of the pre-configured 'sourcemap_dirs' # (due to the above assertions) and will always end in # '.map'. Additionally, the result of this function is used # for error logging and not returned to the user, so # controlling the loaded file would not be useful to an # attacker. with open(mark_sanitized(filename)) as fp: self._indices[minified_src] = sourcemap.load(fp) break return self._indices[minified_src]
def _index_for(self, minified_src): '''Return the source map index for minified_src, loading it if not already loaded.''' if minified_src not in self._indices: with open(os.path.join(self._dir, minified_src + '.map')) as fp: self._indices[minified_src] = sourcemap.load(fp) return self._indices[minified_src]
def _index_for(self, minified_src): """Return the source map index for minified_src, loading it if not already loaded.""" if minified_src not in self._indices: with open(os.path.join(self._dir, minified_src + ".map")) as fp: self._indices[minified_src] = sourcemap.load(fp) return self._indices[minified_src]
def _index_for(self, minified_src): # type: (Text) -> sourcemap.SourceMapDecoder '''Return the source map index for minified_src, loading it if not already loaded.''' if minified_src not in self._indices: with open(os.path.join(self._dir, minified_src + '.map')) as fp: self._indices[minified_src] = sourcemap.load(fp) return self._indices[minified_src]
def _load_js_sourcemap(self): sourcemap_path = "build/pebble-js-app.js.map" if not os.path.exists(sourcemap_path): return None with open(sourcemap_path) as sourcemap_file: try: return sourcemap.load(sourcemap_file) except SourceMapDecodeError as e: logger.warning('Found %s, but failed to parse it: %s', sourcemap_path, str(e)) return None
def _index_for(self, minified_src: str) -> sourcemap.SourceMapDecoder: '''Return the source map index for minified_src, loading it if not already loaded.''' if minified_src not in self._indices: for source_dir in self._dirs: filename = os.path.join(source_dir, minified_src + '.map') if os.path.isfile(filename): with open(filename) as fp: self._indices[minified_src] = sourcemap.load(fp) break return self._indices[minified_src]
def __init__(self, compiled_file_path, sourcemap_file_path): with open(compiled_file_path) as stream: self._compiled_lines = stream.read().splitlines() with open(sourcemap_file_path) as stream: self._sourcemap_index = sourcemap.load(stream)
def expand_javascript_source(data, **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 Mutates the input ``data`` with expanded context if available. """ from sentry.interfaces import Stacktrace try: stacktrace = Stacktrace(**data['sentry.interfaces.Stacktrace']) except KeyError: logger.debug('No stacktrace for event %r', data['event_id']) return # build list of frames that we can actually grab source for frames = [ f for f in stacktrace.frames if f.lineno is not None and f.is_url() ] if not frames: logger.debug('Event %r has no frames with enough context to fetch remote source', data['event_id']) return data file_list = set() sourcemap_capable = set() source_code = {} sourcemap_idxs = {} for f in frames: file_list.add(f.abs_path) if f.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 map_path = discover_sourcemap(result) source_code[filename] = (result.body.splitlines(), map_path) if map_path: logger.debug('Found sourcemap %r for minified script %r', map_path, result.url) if not map_path or map_path not in sourcemap_idxs: continue index = fetch_sourcemap(map_path, logger=logger) if not index: continue sourcemap_idxs[sourcemap] = sourcemap.load(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, map_path = source_code[frame.abs_path] except KeyError: # we must've failed pulling down the source continue # may have had a failure pulling down the sourcemap previously if map_path in sourcemap_idxs and frame.colno is not None: token = sourcemap_idxs[map_path].lookup(frame.lineno, frame.colno) # TODO: is this urljoin right? (is it relative to the sourcemap or the originating file) abs_path = urljoin(map_path, token.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': map_path, } # SourceMap's return zero-indexed lineno's frame.lineno = token.src_line + 1 frame.colno = token.src_col frame.function = token.name frame.abs_path = abs_path frame.filename = token.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=frame.lineno) if has_changes: data['sentry.interfaces.Stacktrace'] = stacktrace.serialize()
def processFile(js_file_path): # Load in the minified file minified = open(js_file_path).read() # Create lexer lexer = get_lexer_for_filename(js_file_path) # Tokenize input and compute mappings between the different # indices used: (line, col), flat, (l,c) in token list indexBuilder = IndexBuilder(lex(minified, lexer)) tokens = indexBuilder.tokens # print 'RUNNING IndexBuilder:', len(tokens)>0 # Compute scoping: name2scope is a dictionary where keys # are (name, start_index) tuples and values are scope identifiers. # Note: start_index is a flat (unidimensional) index, # not a (line_chr_idx, col_chr_idx) index. scopeAnalyst = ScopeAnalyst(js_file_path) name2defScope = scopeAnalyst.resolve_scope() isGlobal = scopeAnalyst.isGlobal name2useScope = scopeAnalyst.name2useScope name2pth = scopeAnalyst.name2pth nameOrigin = scopeAnalyst.nameOrigin scopes = set(name2useScope.values()) print print '=== FOUND %d SCOPES ===' % len(scopes) print for scope in scopes: print 'USE SCOPE:', scope lc_list = [ indexBuilder.revTokMap[indexBuilder.revFlatMat[pos]] for (t, pos) in name2useScope.keys() if name2useScope[(t, pos)] == scope ] highlight(tokens, lc_list) print scopes = set(name2defScope.values()) print print '=== FOUND %d NAME SCOPES ===' % len(scopes) print for scope in scopes: print 'DEF SCOPE:', scope lc_list = [ indexBuilder.revTokMap[indexBuilder.revFlatMat[pos]] for (t, pos) in name2defScope.keys() if name2defScope[(t, pos)] == scope ] highlight(tokens, lc_list) print # Discover the path to the source map map_path = sourcemap.discover(minified) # Read and parse our sourcemap if map_path: sourcemapIndex = sourcemap.load(open(map_path)) # Cluster names by scope nameScope2Positions = {} # Index data by (name,scope) for token, l in indexBuilder.name2CharPositions.iteritems(): for (line, col) in sorted(l, key=lambda (a, b): (a, b)): pos = indexBuilder.flatMap[(line, col)] if name2defScope.has_key((token, pos)): scope = name2defScope[(token, pos)] use_scope = name2useScope[(token, pos)] pth = name2pth[(token, pos)] glb = isGlobal[(token, pos)] nameScope2Positions.setdefault((token, scope, glb), []) nameScope2Positions[(token, scope, glb)].append((line, col)) # print token, pos # print 'def:', scope # print 'use:', use_scope # print 'pth:', pth # highlight(tokens, [indexBuilder.revTokMap[indexBuilder.revFlatMat[pos]]]) # print print print for (token,scope,glb), positions in sorted(nameScope2Positions.iteritems(), \ key=lambda (x,y):x[0]): pos = sorted(positions, key=lambda e: (e[0], e[1])) tt = [] line_tok_idxs = set([]) for (l, c) in pos: (tl, tc) = indexBuilder.revTokMap[(l, c)] line_tok_idxs.add(tl) p = indexBuilder.flatMap[(l, c)] if map_path: orig = sourcemapIndex.lookup(line=l, column=c).name else: orig = token print token, scope, (l, c), orig tt.append(((tl, tc), p, orig)) # t.append(orig) # if token == 'n': print '\nNAME:', token.encode( 'utf-8'), '( isGlobal =', glb, '; original =', orig, ')' # print scope # highlight(tokens, [indexBuilder.revTokMap[indexBuilder.revFlatMat[pos]]]) for ((tli, tci), p, orig) in tt: scope = name2defScope[(token, p)] use_scope = name2useScope[(token, p)] pth = name2pth[(token, p)] origin = nameOrigin[(token, scope)] # print token #, p, origin # print # print 'def:', scope # print 'use:', use_scope # print 'pth:', pth # print for tl in sorted(set([tli for ((tli, tci), p, orig) in tt])): l = list(tokens[tl]) for tc in [tci for ((tli, tci), p, orig) in tt if tli == tl]: l[tc] = (l[tc][0], unichr(0x2588) + token + unichr(0x2588)) # pos = indexBuilder.flatMap[(line,col)] print ' ', '%d:' % (tl + 1), ' '.join( [x[1].encode('utf-8') for x in l]) print return