def _test(script): err = ErrorBundle() err.supported_versions = {} err.save_resource("em:bootstrap", "true") scripting.test_js_file(err, "foo", script) return err
def _test(script): err = ErrorBundle() err.supported_versions = {} err.save_resource('em:bootstrap', 'true') scripting.test_js_file(err, 'foo', script) return err
def set_contentScript(value, traverser): """Warns when values are assigned to the `contentScript` properties, which are essentially the same as calling `eval`.""" if value.is_literal(): content_script = actions._get_as_str(value) # Avoid import loop. from validator.testcases.scripting import test_js_file test_js_file(traverser.err, traverser.filename, content_script, line=traverser.line, context=traverser.context) else: traverser.warning( err_id=('testcases_javascript_instanceproperties', 'contentScript', 'set_non_literal'), warning='`contentScript` properties should not be used', description='Creating content scripts from dynamic values ' 'is dangerous and error-prone. Please use a separate ' 'JavaScript file, along with the ' '`contentScriptFile` property instead.', signing_help='Please do not use the `contentScript` property ' 'in any add-ons submitted for automated signing.', signing_severity='high')
def PageMod(args, traverser, node, wrapper): """ This is the function that is called in Jetpack to modify the contents of a page with a "content script". This function needs to analyze he first parameter. If it is an object and if that object contains a "contentScript" string, that string needs to be passed to the validator.testcases.scripting library for testing as its own JS script file. """ if not args: return pm_properties = traverser._traverse_node(args[0]) if not pm_properties.has_property("contentScript"): return content_script = pm_properties.get(traverser, "contentScript") if not content_script.is_literal(): return content_script = actions._get_as_str(content_script.get_literal_value()) if not content_script.strip(): return import validator.testcases.scripting as sub_scripting sub_scripting.test_js_file( traverser.err, traverser.filename, content_script, line=traverser.line, context=traverser.context)
def test_packed_scripts(err, xpi_package): """ Scripts must be tested separately from normal files to allow for markup files to mark scripts as being potentially polluting. """ # This test doesn't apply to subpackages. We keep references to the # subpackage bundles so we can process everything at once in an unpushed # state. if err.is_nested_package(): return scripts = err.get_resource("scripts") if not scripts: return # Get the chrome manifest in case there's information about pollution # exemptions. chrome = err.get_resource("chrome.manifest_nopush") marked_scripts = err.get_resource("marked_scripts") if not marked_scripts: marked_scripts = set() # Process all of the scripts that were found seperately from the rest of # the package contents. for script_bundle in scripts: package = script_bundle["package"] # Set the error bundle's package state to what it was when we first # encountered the script file during the content tests. err.package_stack = script_bundle["state"] for script in script_bundle["scripts"]: file_data = unicodehelper.decode(package.read(script)) if marked_scripts: reversed_script = chrome.reverse_lookup(script_bundle["state"], script) # Run the standard script tests on the script, but mark the # script as pollutable if its chrome URL is marked as being so. testendpoint_js.test_js_file( err, script, file_data, pollutable=reversed_script in marked_scripts) else: # Run the standard script tests on the scripts. testendpoint_js.test_js_file(err, script, file_data) run_regex_tests(file_data, err, script, is_js=True) # We only run this testcase if the package stack is empty, return it to its # original state. err.package_stack = []
def test_scripting_disabled(): "Ensures that Spidermonkey is not run if it is set to be disabled" err = ErrorBundle() err.save_resource("SPIDERMONKEY", None) assert scripting.test_js_file(err, "abc def", "foo bar") is None err = ErrorBundle() si = scripting.SPIDERMONKEY_INSTALLATION scripting.SPIDERMONKEY_INSTALLATION = None assert scripting.test_js_file(err, "abc def", "foo bar") is None scripting.SPIDERMONKEY_INSTALLATION = si
def set_contentScript(value, traverser): """Warns when values are assigned to the `contentScript` properties, which are essentially the same as calling `eval`.""" if value.is_literal(): content_script = actions._get_as_str(value) # Avoid import loop. from validator.testcases.scripting import test_js_file test_js_file( traverser.err, traverser.filename, content_script, line=traverser.line, context=traverser.context) else: traverser.warning( err_id=("testcases_javascript_instanceproperties", "contentScript", "set_non_literal"), warning="`contentScript` properties should not be used", description="Creating content scripts from dynamic values " "is dangerous and error-prone. Please use a separate " "JavaScript file, along with the " "`contentScriptFile` property instead.", signing_severity="high")
def set_contentScript(value, traverser): """Warns when values are assigned to the `contentScript` properties, which are essentially the same as calling `eval`.""" if value.is_literal(): content_script = actions._get_as_str(value) # Avoid import loop. from validator.testcases.scripting import test_js_file test_js_file( traverser.err, traverser.filename, content_script, line=traverser.line, context=traverser.context) else: traverser.warning( err_id=('testcases_javascript_instanceproperties', 'contentScript', 'set_non_literal'), warning='`contentScript` properties should not be used', description='Creating content scripts from dynamic values ' 'is dangerous and error-prone. Please use a separate ' 'JavaScript file, along with the ' '`contentScriptFile` property instead.', signing_help='Please do not use the `contentScript` property ' 'in any add-ons submitted for automated signing.', signing_severity='high')
def _process_file(err, xpi_package, name, file_data, name_lower, pollutable=False): """Process a single file's content tests.""" extension = os.path.splitext(name_lower)[1] # If that item is a container file, unzip it and scan it. if extension == '.jar': # This is either a subpackage or a nested theme. is_subpackage = not err.get_resource('is_multipackage') # Unpack the package and load it up. package = StringIO(file_data) try: sub_xpi = XPIManager(package, mode='r', name=name, subpackage=is_subpackage) except BadZipfile: err.error(('testcases_content', 'test_packed_packages', 'jar_subpackage_corrupt'), 'Subpackage corrupt.', 'The subpackage appears to be corrupt, and could not ' 'be opened.', name) return # Let the error bunder know we're in a sub-package. err.push_state(name) err.detected_type = (PACKAGE_SUBPACKAGE if is_subpackage else PACKAGE_THEME) err.set_tier(1) supported_versions = (err.supported_versions.copy() if err.supported_versions else err.supported_versions) if is_subpackage: testendpoint_validator.test_inner_package(err, sub_xpi) else: testendpoint_validator.test_package(err, package, name) err.pop_state() err.set_tier(2) err.supported_versions = supported_versions elif extension == '.xpi': # It's not a subpackage, it's a nested extension. These are # found in multi-extension packages. # Unpack! package = StringIO(file_data) err.push_state(name_lower) err.set_tier(1) # There are no expected types for packages within a multi- # item package. testendpoint_validator.test_package(err, package, name) err.pop_state() err.set_tier(2) # Reset to the current tier else: if not file_data: return # Convert the file data to unicode. file_data = unicodehelper.decode(file_data) if extension in ('.js', '.jsm'): testendpoint_js.test_js_file(err, name, file_data, pollutable=pollutable) elif extension == '.css': testendpoint_css.test_css_file(err, name, file_data) run_regex_tests(file_data, err, filename=name)
def test_packed_scripts(err, xpi_package): """ Scripts must be tested separately from normal files to allow for markup files to mark scripts as being potentially polluting. """ # This test doesn't apply to subpackages. We keep references to the # subpackage bundles so we can process everything at once in an unpushed # state. if err.is_nested_package: return scripts = err.get_resource('scripts') if not scripts: return total_scripts = sum(len(bundle['scripts']) for bundle in scripts) exhaustive = True if total_scripts > MAX_JS_THRESHOLD: err.warning( err_id=('testcases_content', 'packed_js', 'too_much_js'), warning='TOO MUCH JS FOR EXHAUSTIVE VALIDATION', description='There are too many JS files for the validator to ' 'process sequentially. An editor must manually ' 'review the JS in this add-on.') exhaustive = False # Get the chrome manifest in case there's information about pollution # exemptions. chrome = err.get_resource('chrome.manifest_nopush') marked_scripts = err.get_resource('marked_scripts') if not marked_scripts: marked_scripts = set() # Process all of the scripts that were found seperately from the rest of # the package contents. for script_bundle in scripts: package = script_bundle['package'] # Set the error bundle's package state to what it was when we first # encountered the script file during the content tests. for archive in script_bundle['state']: err.push_state(archive) for script in script_bundle['scripts']: file_data = unicodehelper.decode(package.read(script)) run_regex_tests(file_data, err, script) # If we're not running an exhaustive set of tests, skip the full JS # parse and traversal. if not exhaustive: continue if marked_scripts: reversed_script = chrome.reverse_lookup( script_bundle['state'], script) # Run the standard script tests on the script, but mark the # script as pollutable if its chrome URL is marked as being so. testendpoint_js.test_js_file(err, script, file_data, pollutable=reversed_script in marked_scripts) else: # Run the standard script tests on the scripts. testendpoint_js.test_js_file(err, script, file_data) for i in range(len(script_bundle['state'])): err.pop_state()
def _process_file(err, xpi_package, name, file_data, name_lower, pollutable=False): """Process a single file's content tests.""" # If that item is a container file, unzip it and scan it. if name_lower.endswith(".jar"): # This is either a subpackage or a nested theme. is_subpackage = not err.get_resource("is_multipackage") # Unpack the package and load it up. package = StringIO(file_data) try: sub_xpi = XPIManager(package, mode="r", name=name, subpackage=is_subpackage) except Exception: err.error(("testcases_content", "test_packed_packages", "jar_subpackage_corrupt"), "Subpackage corrupt.", "The subpackage could not be opened due to issues " "with corruption. Ensure that the file is valid.", name) return None # Let the error bunder know we're in a sub-package. err.push_state(name) err.detected_type = (PACKAGE_SUBPACKAGE if is_subpackage else PACKAGE_THEME) err.set_tier(1) supported_versions = (err.supported_versions.copy() if err.supported_versions else err.supported_versions) if is_subpackage: testendpoint_validator.test_inner_package(err, sub_xpi) else: testendpoint_validator.test_package(err, package, name) err.pop_state() err.set_tier(2) err.supported_versions = supported_versions elif name_lower.endswith(".xpi"): # It's not a subpackage, it's a nested extension. These are # found in multi-extension packages. # Unpack! package = StringIO(file_data) err.push_state(name_lower) err.set_tier(1) # There are no expected types for packages within a multi- # item package. testendpoint_validator.test_package(err, package, name) err.pop_state() err.set_tier(2) # Reset to the current tier elif name_lower.endswith((".css", ".js", ".jsm")): if not file_data: return None # Convert the file data to unicode file_data = unicodehelper.decode(file_data) is_js = False if name_lower.endswith(".css"): testendpoint_css.test_css_file(err, name, file_data) elif name_lower.endswith((".js", ".jsm")): is_js = True testendpoint_js.test_js_file(err, name, file_data, pollutable=pollutable) run_regex_tests(file_data, err, name, is_js=is_js) return True return False
def test_packed_scripts(err, xpi_package): """ Scripts must be tested separately from normal files to allow for markup files to mark scripts as being potentially polluting. """ # This test doesn't apply to subpackages. We keep references to the # subpackage bundles so we can process everything at once in an unpushed # state. if err.is_nested_package: return scripts = err.get_resource("scripts") if not scripts: return total_scripts = sum(len(bundle["scripts"]) for bundle in scripts) exhaustive = True if total_scripts > MAX_JS_THRESHOLD: err.warning( err_id=("testcases_content", "packed_js", "too_much_js"), warning="TOO MUCH JS FOR EXHAUSTIVE VALIDATION", description="There are too many JS files for the validator to " "process sequentially. An editor must manually " "review the JS in this add-on.") exhaustive = False # Get the chrome manifest in case there's information about pollution # exemptions. chrome = err.get_resource("chrome.manifest_nopush") marked_scripts = err.get_resource("marked_scripts") if not marked_scripts: marked_scripts = set() # Process all of the scripts that were found seperately from the rest of # the package contents. for script_bundle in scripts: package = script_bundle["package"] # Set the error bundle's package state to what it was when we first # encountered the script file during the content tests. for archive in script_bundle["state"]: err.push_state(archive) for script in script_bundle["scripts"]: file_data = unicodehelper.decode(package.read(script)) run_regex_tests(file_data, err, script, is_js=True) # If we're not running an exhaustive set of tests, skip the full JS # parse and traversal. if not exhaustive: continue if marked_scripts: reversed_script = chrome.reverse_lookup(script_bundle["state"], script) # Run the standard script tests on the script, but mark the # script as pollutable if its chrome URL is marked as being so. testendpoint_js.test_js_file( err, script, file_data, pollutable=reversed_script in marked_scripts) else: # Run the standard script tests on the scripts. testendpoint_js.test_js_file(err, script, file_data) for i in range(len(script_bundle["state"])): err.pop_state()
def _process_file(err, xpi_package, name, file_data, name_lower, pollutable=False): """Process a single file's content tests.""" # If that item is a container file, unzip it and scan it. if name_lower.endswith('.jar'): # This is either a subpackage or a nested theme. is_subpackage = not err.get_resource('is_multipackage') # Unpack the package and load it up. package = StringIO(file_data) try: sub_xpi = XPIManager(package, mode='r', name=name, subpackage=is_subpackage) except BadZipfile: err.error(('testcases_content', 'test_packed_packages', 'jar_subpackage_corrupt'), 'Subpackage corrupt.', 'The subpackage appears to be corrupt, and could not ' 'be opened.', name) return None # Let the error bunder know we're in a sub-package. err.push_state(name) err.detected_type = (PACKAGE_SUBPACKAGE if is_subpackage else PACKAGE_THEME) err.set_tier(1) supported_versions = (err.supported_versions.copy() if err.supported_versions else err.supported_versions) if is_subpackage: testendpoint_validator.test_inner_package(err, sub_xpi) else: testendpoint_validator.test_package(err, package, name) err.pop_state() err.set_tier(2) err.supported_versions = supported_versions elif name_lower.endswith('.xpi'): # It's not a subpackage, it's a nested extension. These are # found in multi-extension packages. # Unpack! package = StringIO(file_data) err.push_state(name_lower) err.set_tier(1) # There are no expected types for packages within a multi- # item package. testendpoint_validator.test_package(err, package, name) err.pop_state() err.set_tier(2) # Reset to the current tier elif name_lower.endswith(('.css', '.js', '.jsm')): if not file_data: return None # Convert the file data to unicode file_data = unicodehelper.decode(file_data) is_js = name_lower.endswith(('.js', '.jsm')) if name_lower.endswith('.css'): testendpoint_css.test_css_file(err, name, file_data) elif is_js: testendpoint_js.test_js_file(err, name, file_data, pollutable=pollutable) run_regex_tests(file_data, err, name, is_js=is_js) return True else: if file_data: file_data = unicodehelper.decode(file_data) run_regex_tests(file_data, err, name, explicit=True) return False
def test_packed_packages(err, package_contents=None, xpi_package=None): "Tests XPI and JAR files for naughty content." processed_files = 0 hash_whitelist = [x[:-1] for x in open(os.path.join(os.path.dirname(__file__), 'whitelist_hashes.txt')).readlines()] # Iterate each item in the package. for name, data in package_contents.items(): if name.startswith("__MACOSX") or \ name.startswith(".DS_Store"): continue if name.split("/")[-1].startswith("._"): err.notice(("testcases_content", "test_packed_packages", "macintosh_junk"), "Garbage file found.", ["""A junk file has been detected. It may cause problems with proper operation of the add-on down the road.""", "It is recommended that you delete the file"], name) try: file_data = xpi_package.read(name) except KeyError: # pragma: no cover _read_error(err, name) # Skip over whitelisted hashes hash = hashlib.sha1(file_data).hexdigest() if hash in hash_whitelist: continue processed = False # If that item is a container file, unzip it and scan it. if data["extension"] == "jar": # This is either a subpackage or a nested theme. # Whether this is a subpackage or a nested theme is # determined by whether it is in the root folder or not. # Subpackages are always found in a directory such as # /chrome or /content. is_subpackage = name.count("/") > 0 # Unpack the package and load it up. package = StringIO(file_data) sub_xpi = XPIManager(package, name, is_subpackage) if not sub_xpi.zf: err.error(("testcases_content", "test_packed_packages", "jar_subpackage_corrupt"), "Subpackage corrupt.", """The subpackage could not be opened due to issues with corruption. Ensure that the file is valid.""", name) continue temp_contents = sub_xpi.get_file_data() # Let the error bunder know we're in a sub-package. err.push_state(data["name_lower"]) err.set_type(PACKAGE_SUBPACKAGE) # Subpackage testendpoint_validator.test_inner_package(err, temp_contents, sub_xpi) err.tier = 2 package.close() err.pop_state() elif data["extension"] == "xpi": # It's not a subpackage, it's a nested extension. These are # found in multi-extension packages. # Unpack! package = StringIO(file_data) err.push_state(data["name_lower"]) # There are no expected types for packages within a multi- # item package. testendpoint_validator.test_package(err, package, name) err.tier = 2 # Reset to the current tier package.close() err.pop_state() elif data["extension"] in ("xul", "xml", "html", "xhtml"): parser = testendpoint_markup.MarkupParser(err) parser.process(name, charsethelper.decode(file_data), data["extension"]) processed = True elif data["extension"] in ("css", "js", "jsm"): if not file_data: continue file_data = charsethelper.decode(file_data) if data["extension"] == "css": testendpoint_css.test_css_file(err, name, file_data) elif data["extension"] in ("js", "jsm"): testendpoint_js.test_js_file(err, name, file_data) # This is tested in test_langpack.py if err.detected_type == PACKAGE_LANGPACK and not processed: testendpoint_langpack.test_unsafe_html(err, name, file_data) # This aids in creating unit tests. processed_files += 1 return processed_files
from validator.outputhandlers.shellcolors import OutputHandler import validator.testcases.scripting as scripting import validator.testcases.javascript.traverser from validator.testcases.javascript.predefinedentities import GLOBAL_ENTITIES import validator.testcases.javascript.spidermonkey as spidermonkey validator.testcases.javascript.traverser.DEBUG = True if __name__ == '__main__': err = ErrorBundle(instant=True) err.handler = OutputHandler(sys.stdout, False) err.supported_versions = {} if len(sys.argv) > 1: path = sys.argv[1] script = open(path).read() scripting.test_js_file(err=err, filename=path, data=script) else: trav = validator.testcases.javascript.traverser.Traverser(err, "stdin") trav._push_context() def do_inspect(wrapper, arguments, traverser): print "~" * 50 for arg in arguments: if arg["type"] == "Identifier": print 'Identifier: "%s"' % arg["name"] else: print arg["type"] a = traverser._traverse_node(arg) print a.output()
def test_valid(): err = ErrorBundle(None, True) data = open("tests/resources/bug_590992_valid.js").read() scripting.test_js_file(err=err, filename="test", data=data) assert not err.failed()
def handle_endtag(self, tag): tag = tag.lower() if tag == 'xul:script': tag = 'script' if tag == 'script' and len(self.xml_buffer[-1]) > 1000: self.err.warning(('markup', 'complex_script'), 'Long inline script', 'Please store complex scripts in .js files ' 'rather than inline script nodes.', self.filename, line=self.line, context=self.context, tier=2) if DEBUG: # pragma: no cover print 'E: ', tag, self.xml_state if not self.xml_state: if 'closing_tags' in self.reported or not self.strict: if DEBUG: print 'Unstrict; extra closing tags ------' return self.err.warning(('markup', 'endtag', 'extra_closing_tags'), 'Markup parsing error', 'The markup file has more closing tags than it ' 'has opening tags.', self.filename, line=self.line, context=self.context, tier=2) self.reported.add('closing_tags') if DEBUG: # pragma: no cover print 'Too many closing tags ------' return elif 'script' in self.xml_state[:-1]: # If we're in a script tag, nothing else matters. Just rush # everything possible into the xml buffer. self._save_to_buffer('</' + tag + '>') if DEBUG: print 'Markup as text in script ------' return elif tag not in self.xml_state: # If the tag we're processing isn't on the stack, then # something is wrong. self.err.warning(('markup', 'endtag', 'extra_closing_tags'), 'Parse error: tag closed before opened', ['Markup tags cannot be closed before they are ' 'opened. Perhaps you were just a little ' 'overzealous with forward-slashes?', 'Tag "%s" closed before it was opened' % tag], self.filename, line=self.line, context=self.context, tier=2) if DEBUG: # pragma: no cover print 'Tag closed before opened ------' return data_buffer = self.xml_buffer.pop() old_state = self.xml_state.pop() old_line = self.xml_line_stack.pop() old_position = self.xml_position_stack.pop() or (old_line, 0) script_type = True if old_state == 'script': script_type = self.xml_state_scripts.pop() # If the tag on the stack isn't what's being closed and it also # classifies as a self-closing tag, we just recursively close # down to the level of the tag we're actualy closing. if old_state != tag and old_state in SELF_CLOSING_TAGS: if DEBUG: print 'Self closing tag cascading down ------' return self.handle_endtag(tag) # If this is an XML-derived language, everything must nest # properly. No overlapping tags. # # ^ Oh, basta, you really have no idea, do you... if (old_state != tag and self.extension[0] == 'x' and not self.strict): self.err.warning(('markup', 'endtag', 'invalid_nesting'), 'Markup invalidly nested', 'It has been determined that the document ' 'invalidly nests its tags. This is not permitted ' 'in the specified document type.', self.filename, line=self.line, context=self.context, tier=2) if DEBUG: # pragma: no cover print 'Invalid markup nesting ------' # Perform analysis on collected data. if data_buffer: if tag == 'script' and not script_type: self.err.add_script_load(self.filename, self.filename) scripting.test_js_file(err=self.err, data=data_buffer, filename=self.filename, line=old_position[0], column=old_position[1] + 1, context=self.context) elif tag == 'style': csstester.test_css_file(self.err, self.filename, data_buffer, old_line)
def handle_endtag(self, tag): tag = tag.lower() if tag == 'xul:script': tag = 'script' if tag == 'script' and len(self.xml_buffer[-1]) > 1000: self.err.warning(('markup', 'complex_script'), 'Long inline script', 'Please store complex scripts in .js files ' 'rather than inline script nodes.', self.filename, line=self.line, context=self.context, tier=2) if DEBUG: # pragma: no cover print 'E: ', tag, self.xml_state if not self.xml_state: if 'closing_tags' in self.reported or not self.strict: if DEBUG: print 'Unstrict; extra closing tags ------' return self.err.warning(('markup', 'endtag', 'extra_closing_tags'), 'Markup parsing error', 'The markup file has more closing tags than it ' 'has opening tags.', self.filename, line=self.line, context=self.context, tier=2) self.reported.add('closing_tags') if DEBUG: # pragma: no cover print 'Too many closing tags ------' return elif 'script' in self.xml_state[:-1]: # If we're in a script tag, nothing else matters. Just rush # everything possible into the xml buffer. self._save_to_buffer('</' + tag + '>') if DEBUG: print 'Markup as text in script ------' return elif tag not in self.xml_state: # If the tag we're processing isn't on the stack, then # something is wrong. self.err.warning( ('markup', 'endtag', 'extra_closing_tags'), 'Parse error: tag closed before opened', [ 'Markup tags cannot be closed before they are ' 'opened. Perhaps you were just a little ' 'overzealous with forward-slashes?', 'Tag "%s" closed before it was opened' % tag ], self.filename, line=self.line, context=self.context, tier=2) if DEBUG: # pragma: no cover print 'Tag closed before opened ------' return data_buffer = self.xml_buffer.pop() old_state = self.xml_state.pop() old_line = self.xml_line_stack.pop() old_position = self.xml_position_stack.pop() or (old_line, 0) script_type = True if old_state == 'script': script_type = self.xml_state_scripts.pop() # If the tag on the stack isn't what's being closed and it also # classifies as a self-closing tag, we just recursively close # down to the level of the tag we're actualy closing. if old_state != tag and old_state in SELF_CLOSING_TAGS: if DEBUG: print 'Self closing tag cascading down ------' return self.handle_endtag(tag) # If this is an XML-derived language, everything must nest # properly. No overlapping tags. # # ^ Oh, basta, you really have no idea, do you... if (old_state != tag and self.extension[0] == 'x' and not self.strict): self.err.warning(('markup', 'endtag', 'invalid_nesting'), 'Markup invalidly nested', 'It has been determined that the document ' 'invalidly nests its tags. This is not permitted ' 'in the specified document type.', self.filename, line=self.line, context=self.context, tier=2) if DEBUG: # pragma: no cover print 'Invalid markup nesting ------' # Perform analysis on collected data. if data_buffer: if tag == 'script' and not script_type: self.err.add_script_load(self.filename, self.filename) scripting.test_js_file(err=self.err, data=data_buffer, filename=self.filename, line=old_position[0], column=old_position[1] + 1, context=self.context) elif tag == 'style': csstester.test_css_file(self.err, self.filename, data_buffer, old_line)