예제 #1
0
def _define_obj(traverser, node):
    "Creates a local context object"

    var = JSObject()
    for prop in node["properties"]:
        if prop["type"] == "PrototypeMutation":
            var_name = "prototype"
        else:
            key = prop["key"]
            if key["type"] == "Literal":
                var_name = key["value"]
            elif isinstance(key["name"], basestring):
                var_name = key["name"]
            else:
                if "property" in key["name"]:
                    name = key["name"]
                else:
                    name = {"property": key["name"]}
                var_name = _get_member_exp_property(traverser, name)

        var_value = traverser._traverse_node(prop["value"])
        var.set(var_name, var_value, traverser)

        # TODO: Observe "kind"

    if not isinstance(var, JSWrapper):
        return JSWrapper(var, lazy=True, traverser=traverser)
    var.lazy = True
    return var
예제 #2
0
    def wrap():
        traverser.function_collection.append([])
        traverser._debug("THIS_PUSH")
        # Allow references to "this"
        traverser.this_stack.append(JSObject(traverser=traverser))

        # inherit contexts from the current scope
        traverser.contexts = current_contexts

        params = {}
        for param in node["params"]:
            if param["type"] == "Identifier":
                params[param["name"]] = lambda: JSObject(traverser=traverser)
            else:
                # TODO: Support array and object destructuring.
                pass
        context = JSContext(data=params, traverser=traverser)
        traverser.contexts.append(context)

        traverser.traverse_node(node["body"])

        # Call all of the function collection's members to traverse all of the
        # child functions.
        func_coll = traverser.function_collection.pop()
        for func in func_coll:
            func()

        traverser.contexts.pop()
        # Since we need to manually manage the "this" stack, pop off that
        # context.
        traverser._debug("THIS_POP")
        traverser.this_stack.pop()
예제 #3
0
def _define_obj(traverser, node):
    'Creates a local context object'

    var = JSObject()
    for prop in node['properties']:
        if prop['type'] == 'PrototypeMutation':
            var_name = 'prototype'
        else:
            key = prop['key']
            if key['type'] == 'Literal':
                var_name = key['value']
            elif isinstance(key['name'], basestring):
                var_name = key['name']
            else:
                if 'property' in key['name']:
                    name = key['name']
                else:
                    name = {'property': key['name']}
                var_name = _get_member_exp_property(traverser, name)

        var_value = traverser._traverse_node(prop['value'])
        var.set(var_name, var_value, traverser)

        # TODO: Observe "kind"

    if not isinstance(var, JSWrapper):
        return JSWrapper(var, lazy=True, traverser=traverser)
    var.lazy = True
    return var
예제 #4
0
def _define_obj(traverser, node):
    'Creates a local context object'

    var = JSObject()
    for prop in node['properties']:
        if prop['type'] == 'PrototypeMutation':
            var_name = 'prototype'
        else:
            key = prop['key']
            if key['type'] == 'Literal':
                var_name = key['value']
            elif isinstance(key['name'], basestring):
                var_name = key['name']
            else:
                if 'property' in key['name']:
                    name = key['name']
                else:
                    name = {'property': key['name']}
                var_name = _get_member_exp_property(traverser, name)

        var_value = traverser._traverse_node(prop['value'])
        var.set(var_name, var_value, traverser)

        # TODO: Observe "kind"

    if not isinstance(var, JSWrapper):
        return JSWrapper(var, lazy=True, traverser=traverser)
    var.lazy = True
    return var
def ObjectExpression(traverser, node):
    var = JSObject(traverser=traverser)
    for prop in node["properties"]:
        key = prop["key"]
        var.set(key["value" if key["type"] == "Literal" else "name"],
                traverser.traverse_node(prop["value"]),
                traverser=traverser, ignore_setters=True)
        # TODO: Observe "kind"

    return var
예제 #6
0
def ObjectExpression(traverser, node):
    var = JSObject(traverser=traverser)
    for prop in node["properties"]:
        key = prop["key"]
        var.set(key["value" if key["type"] == "Literal" else "name"],
                traverser.traverse_node(prop["value"]),
                traverser=traverser,
                ignore_setters=True)
        # TODO: Observe "kind"

    return var
예제 #7
0
def _function(traverser, node):
    """
    A helper function that traverses and instantiates function declarations and
    function expressions.
    """

    current_contexts = traverser.contexts[:]

    def wrap():
        traverser.function_collection.append([])
        traverser._debug("THIS_PUSH")
        # Allow references to "this"
        traverser.this_stack.append(JSObject(traverser=traverser))

        # inherit contexts from the current scope
        traverser.contexts = current_contexts

        params = {}
        for param in node["params"]:
            if param["type"] == "Identifier":
                params[param["name"]] = lambda: JSObject(traverser=traverser)
            else:
                # TODO: Support array and object destructuring.
                pass
        context = JSContext(data=params, traverser=traverser)
        traverser.contexts.append(context)

        traverser.traverse_node(node["body"])

        # Call all of the function collection's members to traverse all of the
        # child functions.
        func_coll = traverser.function_collection.pop()
        for func in func_coll:
            func()

        traverser.contexts.pop()
        # Since we need to manually manage the "this" stack, pop off that
        # context.
        traverser._debug("THIS_POP")
        traverser.this_stack.pop()

    # Put the function off for traversal at the end of the current block scope.
    if traverser.function_collection:
        traverser.function_collection[-1].append(wrap)
    else:
        wrap(traverser, node)

    output = JSObject(traverser=traverser, callable_=True)
    output.TYPEOF = "function"
    return output
예제 #8
0
def _function(traverser, node):
    """
    A helper function that traverses and instantiates function declarations and
    function expressions.
    """

    current_contexts = traverser.contexts[:]

    def wrap():
        traverser.function_collection.append([])
        traverser._debug("THIS_PUSH")
        # Allow references to "this"
        traverser.this_stack.append(JSObject(traverser=traverser))

        # inherit contexts from the current scope
        traverser.contexts = current_contexts

        params = {}
        for param in node["params"]:
            if param["type"] == "Identifier":
                params[param["name"]] = lambda: JSObject(traverser=traverser)
            else:
                # TODO: Support array and object destructuring.
                pass
        context = JSContext(data=params, traverser=traverser)
        traverser.contexts.append(context)

        traverser.traverse_node(node["body"])

        # Call all of the function collection's members to traverse all of the
        # child functions.
        func_coll = traverser.function_collection.pop()
        for func in func_coll:
            func()

        traverser.contexts.pop()
        # Since we need to manually manage the "this" stack, pop off that
        # context.
        traverser._debug("THIS_POP")
        traverser.this_stack.pop()

    # Put the function off for traversal at the end of the current block scope.
    if traverser.function_collection:
        traverser.function_collection[-1].append(wrap)
    else:
        wrap(traverser, node)

    output = JSObject(traverser=traverser, callable_=True)
    output.TYPEOF = "function"
    return output
예제 #9
0
def MemberExpression(traverser, node, instantiate=False):
    "Traces a MemberExpression and returns the appropriate object"

    traverser._debug("TESTING>>%s" % node["type"])
    if node["type"] == "MemberExpression":
        # x.y or x[y]
        # x = base
        base = MemberExpression(traverser, node["object"], instantiate)
        identifier = _get_member_exp_property(traverser, node)

        traverser._debug("MEMBER_EXP>>PROPERTY (%s)" % identifier)
        return base.get(traverser, identifier, instantiate=instantiate)

    elif node["type"] == "Identifier":
        traverser._debug("MEMBER_EXP>>ROOT:IDENTIFIER (%s)" % node["name"])

        # If we're supposed to instantiate the object and it doesn't already
        # exist, instantitate the object.
        if instantiate and not traverser._is_defined(node["name"]):
            output = JSObject(traverser=traverser)
            traverser.contexts[0].set(node["name"], output)
        else:
            output = traverser._seek_variable(node["name"])

        return output
    else:
        traverser._debug("MEMBER_EXP>>ROOT:EXPRESSION")
        # It's an expression, so just try your damndest.
        return traverser.traverse_node(node)
예제 #10
0
def CallExpression(traverser, node):
    args = [traverser.traverse_node(a) for a in node["arguments"]]

    member = traverser.traverse_node(node["callee"])

    if (node["callee"]["type"] == "MemberExpression"
            and node["callee"]["property"]["type"] == "Identifier"):

        # If we can identify the function being called on any member of any
        # instance, we can use that to either generate an output value or test
        # for additional conditions.
        identifier_name = node["callee"]["property"]["name"]
        if identifier_name in instanceactions.INSTANCE_DEFINITIONS:
            traverser._debug('Calling instance action...')
            result = instanceactions.INSTANCE_DEFINITIONS[identifier_name](
                args, traverser,
                traverser.traverse_node(node["callee"]["object"]))
            if result is not None:
                return result

    if isinstance(member, JSGlobal) and "return" in member.global_data:
        traverser._debug("EVALUATING RETURN...")
        output = member.global_data["return"](wrapper=member,
                                              arguments=args,
                                              traverser=traverser)
        if output is not None:
            return output
    return JSObject(traverser=traverser)
예제 #11
0
    def set_lvalue(value):
        node_left = node["left"]
        traverser._debug("ASSIGNING:DIRECT(%s)" % node_left["type"])

        global_overwrite = False
        readonly_value = True
        if node_left["type"] == "Identifier":
            # Identifiers just need the ID name and a value to push.
            # Raise a global overwrite issue if the identifier is global.
            global_overwrite = traverser._is_global(node_left["name"])

            # Get the readonly attribute and store its value if is_global
            if global_overwrite:
                from predefinedentities import GLOBAL_ENTITIES
                global_dict = GLOBAL_ENTITIES[node_left["name"]]
                readonly_value = global_dict.get("readonly", False)

            traverser._declare_variable(node_left["name"], value, type_="glob")

        elif node_left["type"] == "MemberExpression":
            member_object = MemberExpression(traverser,
                                             node_left["object"],
                                             instantiate=True)
            member_property = _get_member_exp_property(traverser, node_left)
            traverser._debug("ASSIGNMENT:MEMBER_PROPERTY(%s)" %
                             member_property)

            if member_object is None:
                member_object = JSObject()

            member_object.set(member_property, value, traverser)
예제 #12
0
    def wrap(traverser, node):
        me = JSObject()

        traverser.function_collection.append([])

        # Replace the current context with a prototypeable JS object.
        traverser._pop_context()
        me.type_ = "default"  # Treat the function as a normal object.
        traverser._push_context(me)
        traverser._debug("THIS_PUSH")
        traverser.this_stack.append(me)  # Allow references to "this"

        # Declare parameters in the local scope
        params = []
        for param in node["params"]:
            if param["type"] == "Identifier":
                params.append(param["name"])
            elif param["type"] == "ArrayPattern":
                for element in param["elements"]:
                    # Array destructuring in function prototypes? LOL!
                    if element is None or element["type"] != "Identifier":
                        continue
                    params.append(element["name"])

        local_context = traverser._peek_context(1)
        for param in params:
            var = JSWrapper(lazy=True, traverser=traverser)

            # We can assume that the params are static because we don't care
            # about what calls the function. We want to know whether the
            # function solely returns static values. If so, it is a static
            # function.
            local_context.set(param, var)

        traverser._traverse_node(node["body"])

        # Since we need to manually manage the "this" stack, pop off that
        # context.
        traverser._debug("THIS_POP")
        traverser.this_stack.pop()

        # Call all of the function collection's members to traverse all of the
        # child functions.
        func_coll = traverser.function_collection.pop()
        for func in func_coll:
            func()
예제 #13
0
    def wrap(traverser, node):
        me = JSObject()

        traverser.function_collection.append([])

        # Replace the current context with a prototypeable JS object.
        traverser._pop_context()
        me.type_ = 'default'  # Treat the function as a normal object.
        traverser._push_context(me)
        traverser._debug('THIS_PUSH')
        traverser.this_stack.append(me)  # Allow references to "this"

        # Declare parameters in the local scope
        params = []
        for param in node['params']:
            if param['type'] == 'Identifier':
                params.append(param['name'])
            elif param['type'] == 'ArrayPattern':
                for element in param['elements']:
                    # Array destructuring in function prototypes? LOL!
                    if element is None or element['type'] != 'Identifier':
                        continue
                    params.append(element['name'])

        local_context = traverser._peek_context(1)
        for param in params:
            var = JSWrapper(lazy=True, traverser=traverser)

            # We can assume that the params are static because we don't care
            # about what calls the function. We want to know whether the
            # function solely returns static values. If so, it is a static
            # function.
            local_context.set(param, var)

        traverser._traverse_node(node['body'])

        # Since we need to manually manage the "this" stack, pop off that
        # context.
        traverser._debug('THIS_POP')
        traverser.this_stack.pop()

        # Call all of the function collection's members to traverse all of the
        # child functions.
        func_coll = traverser.function_collection.pop()
        for func in func_coll:
            func()
예제 #14
0
def Literal(traverser, node):
    """
    Convert a literal node in the parse tree to its corresponding
    interpreted value.
    """
    value = node["value"]
    if isinstance(value, dict):
        return JSObject(traverser=traverser)
    return JSLiteral(value, traverser=traverser)
예제 #15
0
def _ident(traverser, node):
    'Initiates an object lookup on the traverser based on an identifier token'

    name = node['name']

    # Ban bits like "newThread"
    test_identifier(traverser, name)

    if traverser._is_defined(name):
        return traverser._seek_variable(name)

    return JSWrapper(JSObject(), traverser=traverser, dirty=True)
예제 #16
0
def nsIJSON_deprec(wrapper, arguments, traverser):
    """Throw a compatibility error about removed XPCOM methods."""
    traverser.notice(
        err_id=('testcases_javascript_calldefinitions', 'nsIJSON', 'deprec'),
        notice='Deprecated nsIJSON methods in use.',
        description=('The `encode` and `decode` methods in nsIJSON have been '
                     'deprecated in Gecko 7. You can use the methods in the '
                     'global JSON object instead. See %s for more '
                     'information.') %
        'https://developer.mozilla.org/En/Using_native_JSON')

    return JSWrapper(JSObject(), traverser=traverser, dirty=True)
예제 #17
0
def ObjectExpression(traverser, node):
    var = JSObject(traverser=traverser)
    for prop in node["properties"]:
        if prop["type"] == "PrototypeMutation":
            var_name = "prototype"
        else:
            key = prop["key"]
            if key["type"] == "Literal":
                var_name = key["value"]
            elif isinstance(key["name"], basestring):
                var_name = key["name"]
            else:
                if "property" in key["name"]:
                    name = key["name"]
                else:
                    name = {"property": key["name"]}
                var_name = _get_member_exp_property(traverser, name)

        var_value = traverser.traverse_node(prop["value"])
        var.set(var_name, var_value, traverser)

    return var
예제 #18
0
def _define_literal(traverser, node):
    """
    Convert a literal node in the parse tree to its corresponding
    interpreted value.
    """
    value = node['value']
    if isinstance(value, dict):
        return JSWrapper(JSObject(), traverser=traverser, dirty=True)

    wrapper = JSWrapper(value if value is not None else JSLiteral(None),
                        traverser=traverser)
    test_literal(traverser, wrapper)
    return wrapper
예제 #19
0
def js_unwrap(wrapper, arguments, traverser):
    """Return the unwrapped variant of an unwrapped JSObject."""
    if not arguments:
        traverser._debug('UNWRAP:NO ARGS')
        return

    traverser._debug('UNWRAPPING OBJECT')
    obj = traverser._traverse_node(arguments[0])
    if obj.value is None:
        traverser._debug('UNWRAPPING OBJECT>>NOTHING TO UNWRAP')
        return JSWrapper(JSObject(unwrapped=True), traverser=traverser)

    if obj.is_global:
        obj.value['is_unwrapped'] = True
    else:
        obj.value.is_unwrapped = True

    return obj
예제 #20
0
def UnaryExpression(traverser, node):
    operator = node["operator"]
    arg = traverser.traverse_node(node["argument"])
    if (isinstance(arg, JSGlobal) and "literal" in arg.global_data
            and not (operator == "typeof" and "undefined" in arg.global_data)):
        arg = JSLiteral(utils.evaluate_lambdas(traverser,
                                               arg.global_data["literal"]),
                        traverser=traverser)
    if operator in UNARY_OPERATORS:
        traverser._debug("Defined unary operator (%s)" % operator)
        return JSLiteral(UNARY_OPERATORS[node["operator"]](arg, traverser),
                         traverser=traverser)
    elif operator == "void":
        traverser._debug("Void unary operator")
        from predefinedentities import resolve_entity
        return JSGlobal(resolve_entity(traverser, "undefined"),
                        traverser=traverser)
    elif operator == "typeof":
        traverser._debug("Typeof unary operator")
        return JSLiteral(_expr_unary_typeof(arg))
    else:
        traverser._debug("Undefined unary operator")
        return JSObject(traverser=traverser)
예제 #21
0
def js_wrap(wrapper, arguments, traverser):
    """Return the wrapped variant of an unwrapped JSObject."""
    if not arguments:
        traverser._debug('WRAP:NO ARGS')
        return

    traverser._debug('WRAPPING OBJECT')
    obj = traverser._traverse_node(arguments[0])
    if obj.value is None:
        traverser._debug('WRAPPING OBJECT>>NOTHING TO WRAP')
        return JSWrapper(JSObject(), traverser=traverser)

    if len(arguments) > 1:
        traverser.warning(
            err_id=('testcases_js_xpcom', 'xpcnativewrapper', 'shallow'),
            warning='Shallow XPCOM wrappers should not be used',
            description='Shallow XPCOM wrappers are seldom necessary and '
            'should not be used. Please use deep wrappers '
            'instead.',
            signing_help='Extensions making use of shallow wrappers will not '
            'be accepted for automated signing. Please remove '
            'the second and subsequent arguments of any calls '
            'to `XPCNativeWrapper`, as well as any code which '
            'applies `XPCNativeWrapper` to properties obtained '
            'from these shallowly wrapped objects.',
            signing_severity='high')
        # Do not mark shallow wrappers as not unwrapped.
        return obj

    if obj.is_global:
        # Why are we changing the original object? XPCNativeWrapper
        # does not alter its arguments.
        obj.value['is_unwrapped'] = False
    else:
        obj.value.is_unwrapped = False

    return obj
예제 #22
0
def _expr_assignment(traverser, node):
    """Evaluate an AssignmentExpression node."""

    traverser._debug('ASSIGNMENT_EXPRESSION')
    traverser.debug_level += 1

    traverser._debug('ASSIGNMENT>>PARSING RIGHT')
    right = traverser._traverse_node(node['right'])
    right = JSWrapper(right, traverser=traverser)

    # Treat direct assignment different than augmented assignment.
    if node['operator'] == '=':
        from predefinedentities import GLOBAL_ENTITIES, is_shared_scope

        global_overwrite = False
        readonly_value = is_shared_scope(traverser)

        node_left = node['left']
        traverser._debug('ASSIGNMENT:DIRECT(%s)' % node_left['type'])

        if node_left['type'] == 'Identifier':
            # Identifiers just need the ID name and a value to push.
            # Raise a global overwrite issue if the identifier is global.
            global_overwrite = traverser._is_global(node_left['name'])

            # Get the readonly attribute and store its value if is_global
            if global_overwrite:
                global_dict = GLOBAL_ENTITIES[node_left['name']]
                if 'readonly' in global_dict:
                    readonly_value = global_dict['readonly']

            traverser._declare_variable(node_left['name'], right, type_='glob')
        elif node_left['type'] == 'MemberExpression':
            member_object = trace_member(traverser,
                                         node_left['object'],
                                         instantiate=True)
            global_overwrite = (member_object.is_global and
                                not ('overwritable' in member_object.value
                                     and member_object.value['overwritable']))
            member_property = _get_member_exp_property(traverser, node_left)
            traverser._debug('ASSIGNMENT:MEMBER_PROPERTY(%s)' %
                             member_property)
            traverser._debug('ASSIGNMENT:GLOB_OV::%s' % global_overwrite)

            # Don't do the assignment if we're facing a global.
            if not member_object.is_global:
                if member_object.value is None:
                    member_object.value = JSObject()

                if not member_object.is_global:
                    member_object.value.set(member_property, right, traverser)
                else:
                    # It's probably better to do nothing.
                    pass

            elif 'value' in member_object.value:
                member_object_value = _expand_globals(traverser,
                                                      member_object).value
                if member_property in member_object_value['value']:

                    # If it's a global and the actual member exists, test
                    # whether it can be safely overwritten.
                    member = member_object_value['value'][member_property]
                    if 'readonly' in member:
                        global_overwrite = True
                        readonly_value = member['readonly']

        traverser._debug('ASSIGNMENT:DIRECT:GLOB_OVERWRITE %s' %
                         global_overwrite)
        traverser._debug('ASSIGNMENT:DIRECT:READONLY %r' % readonly_value)

        if callable(readonly_value):
            readonly_value = readonly_value(traverser, right, node['right'])

        if readonly_value and global_overwrite:

            kwargs = dict(
                err_id=('testcases_javascript_actions', '_expr_assignment',
                        'global_overwrite'),
                warning='Global variable overwrite',
                description='An attempt was made to overwrite a global '
                'variable in some JavaScript code.')

            if isinstance(readonly_value, DESCRIPTION_TYPES):
                kwargs['description'] = readonly_value
            elif isinstance(readonly_value, dict):
                kwargs.update(readonly_value)

            traverser.warning(**kwargs)

        return right

    lit_right = right.get_literal_value()

    traverser._debug('ASSIGNMENT>>PARSING LEFT')
    left = traverser._traverse_node(node['left'])
    traverser._debug('ASSIGNMENT>>DONE PARSING LEFT')
    traverser.debug_level -= 1

    if isinstance(left, JSWrapper):
        if left.dirty:
            return left

        lit_left = left.get_literal_value()
        token = node['operator']

        # Don't perform an operation on None. Python freaks out
        if lit_left is None:
            lit_left = 0
        if lit_right is None:
            lit_right = 0

        # Give them default values so we have them in scope.
        gleft, gright = 0, 0

        # All of the assignment operators
        operators = {
            '=': lambda: right,
            '+=': lambda: lit_left + lit_right,
            '-=': lambda: gleft - gright,
            '*=': lambda: gleft * gright,
            '/=': lambda: 0 if gright == 0 else (gleft / gright),
            '%=': lambda: 0 if gright == 0 else (gleft % gright),
            '<<=': lambda: int(gleft) << int(gright),
            '>>=': lambda: int(gleft) >> int(gright),
            '>>>=': lambda: float(abs(int(gleft)) >> gright),
            '|=': lambda: int(gleft) | int(gright),
            '^=': lambda: int(gleft) ^ int(gright),
            '&=': lambda: int(gleft) & int(gright)
        }

        # If we're modifying a non-numeric type with a numeric operator, return
        # NaN.
        if (not isinstance(lit_left, NUMERIC_TYPES)
                and token in NUMERIC_OPERATORS):
            left.set_value(get_NaN(traverser), traverser=traverser)
            return left

        # If either side of the assignment operator is a string, both sides
        # need to be casted to strings first.
        if (isinstance(lit_left, types.StringTypes)
                or isinstance(lit_right, types.StringTypes)):
            lit_left = _get_as_str(lit_left)
            lit_right = _get_as_str(lit_right)

        gleft, gright = _get_as_num(left), _get_as_num(right)

        traverser._debug('ASSIGNMENT>>OPERATION:%s' % token)
        if token not in operators:
            # We don't support that operator. (yet?)
            traverser._debug('ASSIGNMENT>>OPERATOR NOT FOUND', 1)
            return left
        elif token in ('<<=', '>>=', '>>>=') and gright < 0:
            # The user is doing weird bitshifting that will return 0 in JS but
            # not in Python.
            left.set_value(0, traverser=traverser)
            return left
        elif (token in ('<<=', '>>=', '>>>=', '|=', '^=', '&=')
              and (abs(gleft) == float('inf') or abs(gright) == float('inf'))):
            # Don't bother handling infinity for integer-converted operations.
            left.set_value(get_NaN(traverser), traverser=traverser)
            return left

        traverser._debug(
            'ASSIGNMENT::L-value global? (%s)' %
            ('Y' if left.is_global else 'N'), 1)
        try:
            new_value = operators[token]()
        except Exception:
            traverser.system_error(exc_info=sys.exc_info())
            new_value = None

        # Cap the length of analyzed strings.
        if (isinstance(new_value, types.StringTypes)
                and len(new_value) > MAX_STR_SIZE):
            new_value = new_value[:MAX_STR_SIZE]

        traverser._debug('ASSIGNMENT::New value >> %s' % new_value, 1)
        left.set_value(new_value, traverser=traverser)
        return left

    # Though it would otherwise be a syntax error, we say that 4=5 should
    # evaluate out to 5.
    return right
예제 #23
0
def trace_member(traverser, node, instantiate=False):
    'Traces a MemberExpression and returns the appropriate object'

    traverser._debug('TESTING>>%s' % node['type'])
    if node['type'] == 'MemberExpression':
        # x.y or x[y]
        # x = base
        base = trace_member(traverser, node['object'], instantiate)
        base = _expand_globals(traverser, base)

        identifier = _get_member_exp_property(traverser, node)

        # Handle the various global entity properties.
        if base.is_global:
            # If we've got an XPCOM wildcard, return a copy of the entity.
            if 'xpcom_wildcard' in base.value:
                traverser._debug('MEMBER_EXP>>XPCOM_WILDCARD')

                from predefinedentities import CONTRACT_ENTITIES
                if identifier in CONTRACT_ENTITIES:
                    kw = dict(err_id=('js', 'actions', 'dangerous_contract'),
                              warning='Dangerous XPCOM contract ID')
                    kw.update(CONTRACT_ENTITIES[identifier])

                    traverser.warning(**kw)

                base.value = base.value.copy()
                del base.value['xpcom_wildcard']
                return base

        test_identifier(traverser, identifier)

        traverser._debug('MEMBER_EXP>>PROPERTY: %s' % identifier)
        output = base.get(traverser=traverser,
                          instantiate=instantiate,
                          name=identifier)
        output.context = base.context

        if base.is_global:
            # In the cases of XPCOM objects, methods generally
            # remain bound to their parent objects, even when called
            # indirectly.
            output.parent = base
        return output

    elif node['type'] == 'Identifier':
        traverser._debug('MEMBER_EXP>>ROOT:IDENTIFIER')
        test_identifier(traverser, node['name'])

        # If we're supposed to instantiate the object and it doesn't already
        # exist, instantitate the object.
        if instantiate and not traverser._is_defined(node['name']):
            output = JSWrapper(JSObject(), traverser=traverser)
            traverser.contexts[0].set(node['name'], output)
        else:
            output = traverser._seek_variable(node['name'])

        return _expand_globals(traverser, output)
    else:
        traverser._debug('MEMBER_EXP>>ROOT:EXPRESSION')
        # It's an expression, so just try your damndest.
        return traverser._traverse_node(node)
예제 #24
0
def VariableDeclaration(traverser, node):
    traverser._debug("VARIABLE_DECLARATION")
    traverser.debug_level += 1

    for declaration in node["declarations"]:

        # It could be deconstruction of variables :(
        if declaration["id"]["type"] == "ArrayPattern":

            vars = []
            for element in declaration["id"]["elements"]:
                # NOTE : Multi-level array destructuring sucks. Maybe implement
                # it someday if you're bored, but it's so rarely used and it's
                # so utterly complex, there's probably no need to ever code it
                # up.
                if element is None or element["type"] != "Identifier":
                    vars.append(None)
                    continue
                vars.append(element["name"])

            # The variables are not initialized
            if declaration["init"] is None:
                # Simple instantiation; no initialization
                for var in filter(None, vars):
                    traverser._declare_variable(var,
                                                JSObject(traverser=traverser))

            # The variables are declared inline
            elif declaration["init"]["type"] == "ArrayPattern":
                # TODO : Test to make sure len(values) == len(vars)
                for value in declaration["init"]["elements"]:
                    if vars[0]:
                        traverser._declare_variable(
                            vars[0], traverser.traverse_node(value))
                    vars = vars[1:]  # Pop off the first value

            # It's being assigned by a JSArray (presumably)
            elif declaration["init"]["type"] == "ArrayExpression":
                assigner = traverser.traverse_node(declaration["init"])
                for value, var in zip(assigner.elements, vars):
                    traverser._declare_variable(var, value)

        elif declaration["id"]["type"] == "ObjectPattern":

            init = traverser.traverse_node(declaration["init"])

            def _proc_objpattern(init_obj, properties):
                for prop in properties:
                    # Get the name of the init obj's member
                    if prop["key"]["type"] == "Literal":
                        prop_name = prop["key"]["value"]
                    elif prop["key"]["type"] == "Identifier":
                        prop_name = prop["key"]["name"]
                    else:
                        continue

                    if prop["value"]["type"] == "Identifier":
                        traverser._declare_variable(
                            prop["value"]["name"],
                            init_obj.get(traverser, prop_name))
                    elif prop["value"]["type"] == "ObjectPattern":
                        _proc_objpattern(init_obj.get(traverser, prop_name),
                                         prop["value"]["properties"])

            if init is not None:
                _proc_objpattern(init_obj=init,
                                 properties=declaration["id"]["properties"])

        else:
            var_name = declaration["id"]["name"]
            traverser._debug("NAME>>%s" % var_name)
            traverser._debug("TYPE>>%s" % node["kind"])

            var = traverser.traverse_node(declaration["init"])
            var.const = node["kind"] == "const"
            traverser._declare_variable(var_name, var, type_=node["kind"])

    traverser.debug_level -= 1

    # The "Declarations" branch contains custom elements.
    return True
예제 #25
0
def _call_expression(traverser, node):
    args = node['arguments']
    for arg in args:
        traverser._traverse_node(arg, source='arguments')

    member = traverser._traverse_node(node['callee'])

    if (traverser.filename.startswith('defaults/preferences/')
            and ('name' not in node['callee']
                 or node['callee']['name'] not in (u'pref', u'user_pref'))):

        traverser.err.warning(
            err_id=('testcases_javascript_actions', '_call_expression',
                    'complex_prefs_defaults_code'),
            warning='Complex code should not appear in preference defaults '
            'files',
            description="Calls to functions other than 'pref' and 'user_pref' "
            'should not appear in defaults/preferences/ files.',
            filename=traverser.filename,
            line=traverser.line,
            column=traverser.position,
            context=traverser.context)

    if member.is_global:
        if callable(member.value.get('dangerous', None)):
            result = member.value['dangerous'](a=args,
                                               t=traverser._traverse_node,
                                               e=traverser.err)
            name = member.value.get('name', '')

            if result and name:
                kwargs = {
                    'err_id': ('testcases_javascript_actions',
                               '_call_expression', 'called_dangerous_global'),
                    'warning':
                    '`%s` called in potentially dangerous manner' %
                    member.value['name'],
                    'description':
                    'The global `%s` function was called using a set '
                    'of dangerous parameters. Calls of this nature '
                    'are deprecated.' % member.value['name']
                }

                if isinstance(result, DESCRIPTION_TYPES):
                    kwargs['description'] = result
                elif isinstance(result, dict):
                    kwargs.update(result)

                traverser.warning(**kwargs)

        if callable(member.value.get('forbidden', None)):
            result = member.value['forbidden'](a=args,
                                               t=traverser._traverse_node,
                                               e=traverser.err)
            name = member.value.get('name', '')

            if result and name:
                kwargs = {
                    'err_id': ('testcases_javascript_actions',
                               '_call_expression', 'called_forbidden_global'),
                    'warning':
                    ' Forbidden `%s` called' % member.value['name'],
                    'description':
                    'The forbidden global `%s` function was called.' %
                    member.value['name']
                }

                if isinstance(result, DESCRIPTION_TYPES):
                    kwargs['description'] = result
                elif isinstance(result, dict):
                    kwargs.update(result)

                traverser.error(**kwargs)

    elif (node['callee']['type'] == 'MemberExpression'
          and node['callee']['property']['type'] == 'Identifier'):

        # If we can identify the function being called on any member of any
        # instance, we can use that to either generate an output value or test
        # for additional conditions.
        identifier_name = node['callee']['property']['name']
        if identifier_name in instanceactions.INSTANCE_DEFINITIONS:
            result = instanceactions.INSTANCE_DEFINITIONS[identifier_name](
                args, traverser, node, wrapper=member)
            return result

    if member.is_global and 'return' in member.value:
        if 'object' in node['callee']:
            member.parent = trace_member(traverser, node['callee']['object'])

        return member.value['return'](wrapper=member,
                                      arguments=args,
                                      traverser=traverser)
    return JSWrapper(JSObject(), dirty=True, traverser=traverser)
예제 #26
0
def AssignmentExpression(traverser, node):
    traverser._debug("ASSIGNMENT_EXPRESSION")
    traverser.debug_level += 1

    traverser._debug("ASSIGNMENT>>PARSING RIGHT")
    right = traverser.traverse_node(node["right"])

    traverser._debug("ASSIGNMENT>>PARSING LEFT")
    orig_left = left = traverser.traverse_node(node["left"])

    operator = node["operator"]

    def set_lvalue(value):
        node_left = node["left"]
        traverser._debug("ASSIGNING:DIRECT(%s)" % node_left["type"])

        global_overwrite = False
        readonly_value = True
        if node_left["type"] == "Identifier":
            # Identifiers just need the ID name and a value to push.
            # Raise a global overwrite issue if the identifier is global.
            global_overwrite = traverser._is_global(node_left["name"])

            # Get the readonly attribute and store its value if is_global
            if global_overwrite:
                from predefinedentities import GLOBAL_ENTITIES
                global_dict = GLOBAL_ENTITIES[node_left["name"]]
                readonly_value = global_dict.get("readonly", False)

            traverser._declare_variable(node_left["name"], value, type_="glob")

        elif node_left["type"] == "MemberExpression":
            member_object = MemberExpression(traverser,
                                             node_left["object"],
                                             instantiate=True)
            member_property = _get_member_exp_property(traverser, node_left)
            traverser._debug("ASSIGNMENT:MEMBER_PROPERTY(%s)" %
                             member_property)

            if member_object is None:
                member_object = JSObject()

            member_object.set(member_property, value, traverser)

    # Treat direct assignment different than augmented assignment.
    if operator == "=":
        set_lvalue(right)
        return right

    elif operator not in ASSIGNMENT_OPERATORS:
        # We don't support that operator. (yet?)
        traverser._debug("ASSIGNMENT>>OPERATOR NOT FOUND", 1)
        return left

    if left.const:
        traverser.err.warning(
            err_id=("js", "AssignmentExpression", "aug_const_overwrite"),
            warning="Overwritten constant value",
            description="A variable declared as constant has been "
            "overwritten in some JS code by an augmented "
            "assignment.",
            filename=traverser.filename,
            line=traverser.line,
            column=traverser.position,
            context=traverser.context)
        return JSObject(traverser=traverser)

    traverser._debug("ASSIGNMENT>>DONE PARSING LEFT")
    traverser.debug_level -= 1

    # If we're modifying a non-numeric type with a numeric operator, return
    # NaN.
    if (operator in NUMERIC_OPERATORS and not isinstance(
            left.get_literal_value(traverser) or 0, NUMERIC_TYPES)):
        set_lvalue(utils.get_NaN(traverser))
        return left

    gleft, gright = utils.get_as_num(left), utils.get_as_num(right)

    traverser._debug("ASSIGNMENT>>OPERATION:%s" % operator)
    if operator in ("<<=", ">>=", ">>>=") and gright < 0:
        # The user is doing weird bitshifting that will return 0 in JS but
        # not in Python.
        set_lvalue(JSLiteral(0, traverser=traverser))
        return left
    elif (operator in ("<<=", ">>=", ">>>=", "|=", "^=", "&=")
          and (abs(gleft) == float('inf') or abs(gright) == float('inf'))):
        # Don't bother handling infinity for integer-converted operations.
        set_lvalue(utils.get_NaN(traverser))
        return left

    if operator == '+=':
        lit_left = left.get_literal_value(traverser)
        lit_right = right.get_literal_value(traverser)
        # Don't perform an operation on None. Python freaks out.
        if lit_left is None:
            lit_left = 0
        if lit_right is None:
            lit_right = 0

        # If either side of the assignment operator is a string, both sides
        # need to be cast to strings first.
        if (isinstance(lit_left, types.StringTypes)
                or isinstance(lit_right, types.StringTypes)):
            left = utils.get_as_str(lit_left)
            right = utils.get_as_str(lit_right)
        else:
            left, right = lit_left, lit_right

    output = ASSIGNMENT_OPERATORS[operator](left, right, gleft, gright)

    # Cap the length of analyzed strings.
    if isinstance(output, types.StringTypes) and len(output) > MAX_STR_SIZE:
        output = output[:MAX_STR_SIZE]

    traverser._debug("ASSIGNMENT::New value >> %s" % output, 1)
    set_lvalue(JSLiteral(output, traverser=traverser))
    return orig_left