Пример #1
0
    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]
Пример #2
0
    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]
Пример #3
0
    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]
Пример #4
0
    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]
Пример #5
0
 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
Пример #6
0
 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
Пример #7
0
    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]
Пример #8
0
    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]
Пример #9
0
 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)
Пример #10
0
 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)
Пример #11
0
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()
Пример #12
0
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