コード例 #1
0
ファイル: langpack.py プロジェクト: pombredanne/amo-validator
def test_unsafe_html(err, filename, data):
    'Tests for unsafe HTML tags in language pack files.'

    context = ContextGenerator(data)

    unsafe_pttrn = re.compile('<(script|embed|object)', re.I)

    match = unsafe_pttrn.search(data)
    if match:
        line = context.get_line(match.start())
        err.warning(
            ('testcases_langpack', 'test_unsafe_html', 'unsafe_content_html'),
            'Unsafe HTML found in language pack files.',
            'Language packs are not allowed to contain scripts, '
            'embeds, or other executable code in the language '
            'definition files.',
            filename,
            line=line,
            context=context)

    remote_pttrn = re.compile(BAD_LINK, re.I)

    match = remote_pttrn.search(data)
    if match:
        line = context.get_line(match.start())
        err.warning(
            ('testcases_langpack', 'test_unsafe_html', 'unsafe_content_link'),
            'Unsafe remote resource found in language pack.',
            'Language packs are not allowed to contain references to '
            'remote resources.',
            filename,
            line=line,
            context=context)
コード例 #2
0
ファイル: langpack.py プロジェクト: nmaier/amo-validator
def test_unsafe_html(err, filename, data):
    "Tests for unsafe HTML tags in language pack files."
    
    context = ContextGenerator(data)

    unsafe_pttrn = re.compile('<(script|embed|object)', re.I)
    
    match = unsafe_pttrn.search(data)
    if match:
        line = context.get_line(match.start())
        err.warning(("testcases_langpack",
                     "test_unsafe_html",
                     "unsafe_content_html"),
                    "Unsafe HTML found in language pack files.",
                    """Language packs are not allowed to contain scripts,
                    embeds, or other executable code in the language
                    definition files.""",
                    filename,
                    line=line,
                    context=context)
    
    remote_pttrn = re.compile(BAD_LINK, re.I)

    match = remote_pttrn.search(data)
    if match:
        line = context.get_line(match.start())
        err.warning(("testcases_langpack",
                     "test_unsafe_html",
                     "unsafe_content_link"),
                    "Unsafe remote resource found in language pack.",
                    """Language packs are not allowed to contain
                    references to remote resources.""",
                    filename,
                    line=line,
                    context=context)
コード例 #3
0
ファイル: scripting.py プロジェクト: nmaier/amo-validator
def _regex_tests(err, data, filename):

    c = ContextGenerator(data)

    np_warning = "Network preferences may not be modified."

    errors = {"globalStorage\\[.*\\].password":
                  "******",
              "network\\.http": np_warning,
              "extensions\\.blocklist\\.url": np_warning,
              "extensions\\.blocklist\\.level": np_warning,
              "extensions\\.blocklist\\.interval": np_warning,
              "general\\.useragent": np_warning}

    for regex, message in errors.items():
        reg = re.compile(regex)
        match = reg.search(data)

        if match:
            line = c.get_line(match.start())
            err.warning(("testcases_scripting",
                         "regex_tests",
                         "compiled_error"),
                        "Potentially malicious JS",
                        message,
                        filename=filename,
                        line=line,
                        context=c)
コード例 #4
0
def test_get_context():
    """Test that contexts are generated properly."""

    d = open('tests/resources/contextgenerator/data.txt').read()
    c = ContextGenerator(d)
    print c.data

    c_start = c.get_context(line=1, column=0)
    c_end = c.get_context(line=11, column=0)
    print c_start
    print c_end
    # Contexts are always length 3
    assert len(c_start) == 3
    assert c_start[0] == None
    assert len(c_end) == 3
    assert c_end[2] == None

    assert c_start[1] == '0123456789'
    assert c_end[0] == '9012345678'
    assert c_end[1] == ''

    c_mid = c.get_context(line=5)
    assert len(c_mid) == 3
    assert c_mid[0] == '3456789012'
    assert c_mid[2] == '5678901234'
    print c_mid
コード例 #5
0
def test_get_context():
    """Test that contexts are generated properly."""

    d = open('tests/resources/contextgenerator/data.txt').read()
    c = ContextGenerator(d)
    print c.data

    c_start = c.get_context(line=1, column=0)
    c_end = c.get_context(line=11, column=0)
    print c_start
    print c_end
    # Contexts are always length 3
    assert len(c_start) == 3
    assert c_start[0] is None
    assert len(c_end) == 3
    assert c_end[2] is None

    assert c_start[1] == '0123456789'
    assert c_end[0] == '9012345678'
    assert c_end[1] == ''

    c_mid = c.get_context(line=5)
    assert len(c_mid) == 3
    assert c_mid[0] == '3456789012'
    assert c_mid[2] == '5678901234'
    print c_mid
コード例 #6
0
def test_get_context():
    """Test that contexts are generated properly."""

    d = open("tests/resources/contextgenerator/data.txt").read()
    c = ContextGenerator(d)
    print c.data

    c_start = c.get_context(line=1, column=0)
    c_end = c.get_context(line=11, column=0)
    print c_start
    print c_end
    # Contexts are always length 3
    assert len(c_start) == 3
    assert c_start[0] == None
    assert len(c_end) == 3
    assert c_end[2] == None

    assert c_start[1] == "0123456789"
    assert c_end[0] == "9012345678"
    assert c_end[1] == ""

    c_mid = c.get_context(line=5)
    assert len(c_mid) == 3
    assert c_mid[0] == "3456789012"
    assert c_mid[2] == "5678901234"
    print c_mid
コード例 #7
0
def test_get_line():
    """Test that the context generator returns the proper line."""

    d = open('tests/resources/contextgenerator/data.txt').read()
    c = ContextGenerator(d)

    assert c.get_line(30) == 3
    assert c.get_line(11) == 2
    assert c.get_line(10000) is None
コード例 #8
0
def test_get_context_trimming():
    "Tests that contexts are generated properly when lines are >140 characters"

    d = open("tests/resources/contextgenerator/longdata.txt").read()
    c = ContextGenerator(d)
    print c.data

    trimmed = c.get_context(line=2, column=89)
    proper_lengths = (140, 148, 140)
    print trimmed
    print [len(x) for x in trimmed]
コード例 #9
0
def test_get_line():
    """Test that the context generator returns the proper line."""

    d = open("tests/resources/contextgenerator/data.txt").read()
    c = ContextGenerator(d)
    print c.data

    print c.get_line(30)
    assert c.get_line(30) == 3
    print c.get_line(11)
    assert c.get_line(11) == 2
    print c.get_line(10000)
    assert c.get_line(10000) == 11
コード例 #10
0
def test_get_context_trimming_inverse():
    """Tests that surrounding lines are trimmed properly; the error line is
    ignored if it is less than 140 characters."""

    d = open("tests/resources/contextgenerator/longdata.txt").read()
    c = ContextGenerator(d)
    print c.data

    trimmed = c.get_context(line=6, column=0)
    print trimmed

    assert trimmed[1] == "This line should be entirely visible."
    assert trimmed[0][0] != "X"
    assert trimmed[2][-1] != "X"
コード例 #11
0
    def __init__(self, data, path):
        self.context = ContextGenerator(data)
        self.lines = data.split('\n')

        # Extract the data from the triples in the manifest
        triples = []
        counter = 0

        for line in self.lines:
            line = line.strip()

            counter += 1

            # Skip weird lines.
            if line.startswith('#'):
                continue

            triple = line.split(None, 2)
            if not triple:
                continue
            elif len(triple) == 2:
                triple.append('')
            if len(triple) < 3:
                continue

            triples.append({
                'subject': triple[0],
                'predicate': triple[1],
                'object': triple[2],
                'line': counter,
                'filename': path,
                'context': self.context
            })

        self.triples = triples
コード例 #12
0
def test_get_context_trimming():
    """
    Test that contexts are generated properly when lines are >140 characters.
    """

    d = open('tests/resources/contextgenerator/longdata.txt').read()
    c = ContextGenerator(d)
    print c.data

    trimmed = c.get_context(line=2, column=89)
    proper_lengths = (140, 148, 140)
    print trimmed
    print [len(x) for x in trimmed]

    for i in range(3):
        eq_(len(trimmed[i]), proper_lengths[i])
コード例 #13
0
    def __init__(self, data, path):
        self.context = ContextGenerator(data)
        self.lines = data.split("\n")

        # Extract the data from the triples in the manifest
        triples = []
        counter = 0

        for line in self.lines:
            line = line.strip()

            counter += 1

            # Skip weird lines.
            if line.startswith("#"):
                continue

            triple = line.split(None, 2)
            if not triple:
                continue
            elif len(triple) == 2:
                triple.append("")
            if len(triple) < 3:
                continue

            triples.append({
                "subject": triple[0],
                "predicate": triple[1],
                "object": triple[2],
                "line": counter,
                "filename": path,
                "context": self.context
            })

        self.triples = triples
コード例 #14
0
ファイル: regex.py プロジェクト: pombredanne/amo-validator
def run_regex_tests(document, err, filename, context=None, is_js=False,
                    explicit=False):
    """Run all of the regex-based JS tests."""

    # When `explicit` is true, only run tests which explicitly
    # specify which files they're applicable to.

    if context is None:
        context = ContextGenerator(document)

    # Run all of the registered tests.
    for cls in registered_regex_tests:
        if not hasattr(cls, 'applicable'):
            if explicit:
                continue
        else:
            if not cls.applicable(err, filename, document):
                continue

        t = cls(err, document, filename, context)
        # Run standard tests.
        for test in t.tests():
            test()
        # Run tests that would otherwise be guarded by `is_js`.
        if is_js:
            for test in t.js_tests():
                test()
コード例 #15
0
def test_get_context_trimming_inverse():
    """
    Tests that surrounding lines are trimmed properly; the error line is
    ignored if it is less than 140 characters.
    """

    d = open('tests/resources/contextgenerator/longdata.txt').read()
    c = ContextGenerator(d)
    print c.data

    trimmed = c.get_context(line=6, column=0)
    print trimmed

    assert trimmed[1] == 'This line should be entirely visible.'
    assert trimmed[0][0] != 'X'
    assert trimmed[2][-1] != 'X'
コード例 #16
0
ファイル: jsshell.py プロジェクト: pombredanne/amo-validator
def get_tree(code, err=None, filename=None, shell=None):
    """Retrieve the parse tree for a JS snippet."""

    try:
        return JSShell.get_shell().get_tree(code)
    except JSReflectException as exc:
        str_exc = str(exc)
        if 'SyntaxError' in str_exc or 'ReferenceError' in str_exc:
            err.warning(('testcases_scripting', 'test_js_file',
                         'syntax_error'),
                        'JavaScript Compile-Time Error',
                        ['A compile-time error in the JavaScript halted '
                         'validation of that file.',
                         'Message: %s' % str_exc.split(':', 1)[-1].strip()],
                        filename=filename,
                        line=exc.line,
                        context=ContextGenerator(code))
        elif 'InternalError: too much recursion' in str_exc:
            err.notice(('testcases_scripting', 'test_js_file',
                        'recursion_error'),
                       'JS too deeply nested for validation',
                       'A JS file was encountered that could not be valiated '
                       'due to limitations with Spidermonkey. It should be '
                       'manually inspected.',
                       filename=filename)
        else:
            err.error(('testcases_scripting', 'test_js_file',
                       'retrieving_tree'),
                      'JS reflection error prevented validation',
                      ['An error in the JavaScript file prevented it from '
                       'being properly read by the Spidermonkey JS engine.',
                       str(exc)],
                      filename=filename)
コード例 #17
0
ファイル: csstester.py プロジェクト: pombreda/amo-validator
def test_css_file(err, filename, data, line_start=1):
    "Parse and test a whole CSS file."

    tokenizer = cssutils.tokenize2.Tokenizer()
    context = ContextGenerator(data)

    if data:
        data = "".join(c for c in data if 8 < ord(c) < 127)

    token_generator = tokenizer.tokenize(data)

    try:
        _run_css_tests(err,
                       tokens=token_generator,
                       filename=filename,
                       line_start=line_start - 1,
                       context=context)
    except Exception:  # pragma: no cover
        # This happens because tokenize is a generator.
        # Bravo, Mr. Bond, Bravo.
        err.warning(
            ("testcases_markup_csstester", "test_css_file", "could_not_parse"),
            "Could not parse CSS file",
            "CSS file could not be parsed by the tokenizer.", filename)
        #raise
        return
コード例 #18
0
    def __init__(self, dtd):
        """
        Properties parsers can initialized based on a file path
        (provided as a string to the path), or directly (in memory as a
        StringIO object).
        """

        self.entities = {}
        self.items = []

        if isinstance(dtd, types.StringTypes):
            data = open(dtd).read()
        elif isinstance(dtd, StringIO):
            data = dtd.getvalue()
        elif isinstance(dtd, file):
            data = dtd.read()

        # Create a context!
        self.context = ContextGenerator(data)

        split_data = data.split('\n')
        line_buffer = None
        line_number = 0
        for line in split_data:

            # Increment the line number
            line_number += 1

            # Clean things up
            clean_line = line.strip()
            if not clean_line:
                continue
            if clean_line.startswith('#'):
                continue

            # It's a line that wraps
            if clean_line.count('=') == 0:
                if line_buffer:
                    line_buffer[-1] += clean_line
                else:
                    continue
            else:

                if line_buffer:
                    # This line terminates a wrapped line
                    self.entities[line_buffer[0].strip()] = \
                        line_buffer[1].strip()
                    self.items.append((line_buffer[0].strip(),
                                       line_buffer[1].strip(), line_number))

                line_buffer = clean_line.split('=', 1)

        # Handle any left-over wrapped line data
        if line_buffer:
            self.entities[line_buffer[0].strip()] = \
                line_buffer[1].strip()
            self.items.append(
                (line_buffer[0].strip(), line_buffer[1].strip(), line_number))
コード例 #19
0
def test_json_constructs():
    """This tests some of the internal JSON stuff so we don't break zamboni."""

    e = ErrorBundle()
    e.detected_type = 1
    e.error(("a", "b", "c"), "Test")
    e.error(("a", "b", "foo"), "Test")
    e.error(("a", "foo", "c"), "Test")
    e.error(("a", "foo", "c"), "Test")
    e.error(("b", "foo", "bar"), "Test")
    e.warning((), "Context test", context=("x", "y", "z"))
    e.warning((),
              "Context test",
              context=ContextGenerator("x\ny\nz\n"),
              line=2,
              column=0)
    e.notice((), "none")
    e.notice((), "line", line=1)
    e.notice((), "column", column=0)
    e.notice((), "line column", line=1, column=1)

    results = e.render_json()
    print results
    j = json.loads(results)

    assert "detected_type" in j
    assert j["detected_type"] == "extension"

    assert "message_tree" in j
    tree = j["message_tree"]

    assert "__errors" not in tree
    assert not tree["a"]["__messages"]
    assert tree["a"]["__errors"] == 4
    assert not tree["a"]["b"]["__messages"]
    assert tree["a"]["b"]["__errors"] == 2
    assert not tree["a"]["b"]["__messages"]
    assert tree["a"]["b"]["c"]["__errors"] == 1
    assert tree["a"]["b"]["c"]["__messages"]

    assert "messages" in j
    for m in (m for m in j["messages"] if m["type"] == "warning"):
        assert m["context"] == ["x", "y", "z"]

    for m in (m for m in j["messages"] if m["type"] == "notice"):
        if "line" in m["message"]:
            assert m["line"] is not None
            assert isinstance(m["line"], int)
            assert m["line"] > 0
        else:
            assert m["line"] is None

        if "column" in m["message"]:
            assert m["column"] is not None
            assert isinstance(m["column"], int)
            assert m["column"] > -1
        else:
            assert m["column"] is None
コード例 #20
0
def test_json_constructs():
    """This tests some of the internal JSON stuff so we don't break zamboni."""

    e = ErrorBundle()
    e.detected_type = 1
    e.error(('a', 'b', 'c'), 'Test')
    e.error(('a', 'b', 'foo'), 'Test')
    e.error(('a', 'foo', 'c'), 'Test')
    e.error(('a', 'foo', 'c'), 'Test')
    e.error(('b', 'foo', 'bar'), 'Test')
    e.warning((), 'Context test', context=('x', 'y', 'z'))
    e.warning((),
              'Context test',
              context=ContextGenerator('x\ny\nz\n'),
              line=2,
              column=0)
    e.notice((), 'none')
    e.notice((), 'line', line=1)
    e.notice((), 'column', column=0)
    e.notice((), 'line column', line=1, column=1)

    results = e.render_json()
    print results
    j = json.loads(results)

    assert 'detected_type' in j
    assert j['detected_type'] == 'extension'

    assert 'message_tree' in j
    tree = j['message_tree']

    assert '__errors' not in tree
    assert not tree['a']['__messages']
    assert tree['a']['__errors'] == 4
    assert not tree['a']['b']['__messages']
    assert tree['a']['b']['__errors'] == 2
    assert not tree['a']['b']['__messages']
    assert tree['a']['b']['c']['__errors'] == 1
    assert tree['a']['b']['c']['__messages']

    assert 'messages' in j
    for m in (m for m in j['messages'] if m['type'] == 'warning'):
        assert m['context'] == ['x', 'y', 'z']

    for m in (m for m in j['messages'] if m['type'] == 'notice'):
        if 'line' in m['message']:
            assert m['line'] is not None
            assert isinstance(m['line'], int)
            assert m['line'] > 0
        else:
            assert m['line'] is None

        if 'column' in m['message']:
            assert m['column'] is not None
            assert isinstance(m['column'], int)
            assert m['column'] > -1
        else:
            assert m['column'] is None
コード例 #21
0
    def process(self, filename, data, extension="xul"):
        """Processes data by splitting it into individual lines, then
        incrementally feeding each line into the parser, increasing the
        value of the line number with each line."""

        self.line = 0
        self.filename = filename
        self.extension = extension.lower()

        self.reported = set()

        self.context = ContextGenerator(data)

        lines = data.split("\n")

        buffering = False
        pline = 0
        for line in lines:
            self.line += 1

            search_line = line
            while True:
                # If a CDATA element is found, push it and its contents to the
                # buffer. Push everything previous to it to the parser.
                if "<![CDATA[" in search_line and not buffering:
                    # Find the CDATA element.
                    cdatapos = search_line.find("<![CDATA[")

                    # If the element isn't at the start of the line, pass
                    # everything before it to the parser.
                    if cdatapos:
                        self._feed_parser(search_line[:cdatapos])
                    # Collect the rest of the line to send it to the buffer.
                    search_line = search_line[cdatapos + 9:]
                    buffering = True
                    continue

                elif "]]>" in search_line and buffering:
                    # If we find the end element on the line being scanned,
                    # buffer everything up to the end of it, and let the rest
                    # of the line pass through for further processing.
                    end_cdatapos = search_line.find("]]>")
                    self._save_to_buffer(search_line[:end_cdatapos])
                    search_line = search_line[end_cdatapos + 3:]
                    buffering = False
                break

            if buffering:
                self._save_to_buffer(search_line + "\n")
            else:
                self._feed_parser(search_line)
コード例 #22
0
def test_load_data():
    """Test that data is loaded properly into the CG."""

    d = """abc
    def
    ghi"""
    c = ContextGenerator(d)
    print c.data
    assert len(c.data) == 3

    # Through inductive reasoning, we can assert that every other line
    # is imported properly
    assert c.data[0].strip() == 'abc'
    assert c.data[1].strip() == 'def'
コード例 #23
0
def test_css_file(err, filename, data, line_start=1):
    'Parse and test a whole CSS file.'

    tokenizer = cssutils.tokenize2.Tokenizer()
    context = ContextGenerator(data)

    if data:
        # Remove any characters which aren't printable, 7-bit ASCII.
        data = NON_ASCII_FILTER.sub('', data)

    token_generator = tokenizer.tokenize(data)

    _run_css_tests(err,
                   tokens=token_generator,
                   filename=filename,
                   line_start=line_start - 1,
                   context=context)
コード例 #24
0
ファイル: scripting.py プロジェクト: kmaglione/amo-validator
def test_js_file(err,
                 filename,
                 data,
                 line=1,
                 column=0,
                 context=None,
                 pollutable=False):
    'Test a JS file by parsing and analyzing its tokens.'

    if err.detected_type == PACKAGE_THEME:
        err.warning(err_id=('testcases_scripting', 'test_js_file', 'theme_js'),
                    warning='JS run from full theme',
                    description='Themes should not contain executable code.',
                    filename=filename,
                    line=line)

    before_tier = None
    # Set the tier to 4 (Security Tests)
    if err is not None:
        before_tier = err.tier
        err.set_tier(3)

    tree = get_tree(data, filename=filename, err=err)
    if not tree:
        if before_tier:
            err.set_tier(before_tier)
        return

    # Generate a context if one is not available.
    if context is None:
        context = ContextGenerator(data)

    t = traverser.Traverser(err,
                            filename,
                            start_line=line,
                            start_column=column,
                            context=context,
                            pollutable=pollutable,
                            is_jsm=(filename.endswith('.jsm')
                                    or 'EXPORTED_SYMBOLS' in data))
    t.run(tree)

    # Reset the tier so we don't break the world
    if err is not None:
        err.set_tier(before_tier)
コード例 #25
0
ファイル: scripting.py プロジェクト: pombreda/amo-validator
def test_js_file(err, filename, data, line=0, context=None, pollutable=False):
    "Tests a JS file by parsing and analyzing its tokens"

    spidermonkey = err.get_resource("SPIDERMONKEY")

    if spidermonkey is None:  # Default value is False
        return
    elif not spidermonkey:
        spidermonkey = SPIDERMONKEY_INSTALLATION

    if err.detected_type == PACKAGE_THEME:
        err.warning(err_id=("testcases_scripting", "test_js_file", "theme_js"),
                    warning="JS run from full theme",
                    description="Themes should not contain executable code.",
                    filename=filename,
                    line=line)

    before_tier = None
    # Set the tier to 4 (Security Tests)
    if err is not None:
        before_tier = err.tier
        err.set_tier(3)

    tree = get_tree(data, filename=filename, shell=spidermonkey, err=err)
    if not tree:
        if before_tier:
            err.set_tier(before_tier)
        return

    # Generate a context if one is not available.
    if context is None:
        context = ContextGenerator(data)

    t = traverser.Traverser(err,
                            filename,
                            line,
                            context=context,
                            is_jsm=filename.endswith(".jsm")
                            or "EXPORTED_SYMBOLS" in data)
    t.pollutable = pollutable
    t.run(tree)

    # Reset the tier so we don't break the world
    if err is not None:
        err.set_tier(before_tier)
コード例 #26
0
def get_tree(code, err=None, filename=None, shell=None):
    "Retrieves the parse tree for a JS snippet"

    if not code or not shell:
        return None

    try:
        return _get_tree(code, shell)
    except JSReflectException as exc:
        str_exc = str(exc).strip("'\"")
        if ("SyntaxError" in str_exc or
            "ReferenceError" in str_exc):
            err.warning(("testcases_scripting",
                         "test_js_file",
                         "syntax_error"),
                         "JavaScript Compile-Time Error",
                         ["A compile-time error in the JavaScript halted "
                          "validation of that file.",
                          "Message: %s" % str_exc.split(":", 1)[-1].strip()],
                         filename=filename,
                         line=exc.line,
                         context=ContextGenerator(code))
        elif "InternalError: too much recursion" in str_exc:
            err.notice(("testcases_scripting",
                        "test_js_file",
                        "recursion_error"),
                       "JS too deeply nested for validation",
                       "A JS file was encountered that could not be valiated "
                       "due to limitations with Spidermonkey. It should be "
                       "manually inspected.",
                       filename=filename)
        else:
            err.error(("testcases_scripting",
                       "test_js_file",
                       "retrieving_tree"),
                      "JS reflection error prevented validation",
                      ["An error in the JavaScript file prevented it from "
                       "being properly read by the Spidermonkey JS engine.",
                       str(exc)],
                      filename=filename)
コード例 #27
0
ファイル: generic.py プロジェクト: kmaglione/amo-validator
    def test(self, string, err, filename, context=None):
        extension = os.path.splitext(filename)[1]
        filters = {
            'filename': filename,
            'extension': extension,
            'is_javascript': extension in self.JAVASCRIPT_EXTENSIONS,
            'document': string
        }

        # Don't bother running tests unless some of our tests match the file.
        if any(
                self.check_filter(test, filters)
                for test in self.tests.itervalues()):

            if context is None:
                context = ContextGenerator(string)

            super(FileRegexTest, self).test(string,
                                            err=err,
                                            filters=filters,
                                            filename=filename,
                                            context=context)
コード例 #28
0
    def __init__(self, dtd):
        """
        Creation of DTD parsers can be done based on a local file
        (provided as a string to the path), or directly (in memory as a
        StringIO object).
        """

        self.entities = {}
        self.items = []

        data = ""
        if isinstance(dtd, types.StringTypes):
            with open(dtd) as dtd_instance:
                data = dtd_instance.read()
        elif isinstance(dtd, file):
            data = dtd.read()
        elif isinstance(dtd, StringIO):
            data = dtd.getvalue()

        self._parse(data)

        # Create a context for the file
        self.context = ContextGenerator(data)
コード例 #29
0
ファイル: regex.py プロジェクト: andymckay/amo-validator
def run_regex_tests(document, err, filename, context=None, is_js=False):
    """Run all of the regex-based JS tests."""

    if context is None:
        context = ContextGenerator(document)

    def _generic_test(pattern, title, message, metadata={}):
        """Run a single regex test."""
        match = pattern.search(document)
        if match:
            line = context.get_line(match.start())
            err.warning(
                err_id=("testcases_javascript_regex", "generic",
                        "_generic_test"),
                warning=title,
                description=message,
                filename=filename,
                line=line,
                context=context)
            if metadata:
                err.metadata.update(metadata)

    def _substring_test(pattern, title, message):
        """Run a single substringest."""
        match = re.compile(pattern).search(document)
        if match:
            line = context.get_line(match.start())
            err.warning(
                err_id=("testcases_javascript_regex", "generic",
                        "_generic_test"),
                warning=title,
                description=message,
                filename=filename,
                line=line,
                context=context)

    def _compat_test(pattern, title, message, compatibility_type,
                     appversions=None, logFunc=err.notice):
        """Run a single regex test and return a compatibility message."""
        match = pattern.search(document)
        if match:
            line = context.get_line(match.start())
            logFunc(
                ("testcases_javascript_regex", "generic", "_compat_test"),
                title,
                description=message,
                filename=filename,
                line=line,
                context=context,
                compatibility_type=compatibility_type,
                for_appversions=appversions,
                tier=5)

    if not filename.startswith("defaults/preferences/"):
        from javascript.predefinedentities import (BANNED_PREF_BRANCHES,
                                                   BANNED_PREF_REGEXPS)
        for pattern in BANNED_PREF_REGEXPS:
            _generic_test(
                re.compile("[\"']" + pattern),
                "Potentially unsafe preference branch referenced",
                "Extensions should not alter preferences matching /%s/"
                    % pattern)

        for branch in BANNED_PREF_BRANCHES:
            _substring_test(
                branch.replace(r".", r"\."),
                "Potentially unsafe preference branch referenced",
                "Extensions should not alter preferences in the '%s' "
                "preference branch" % branch)

    for pattern, message in GENERIC_PATTERNS.items():
        _generic_test(
                re.compile(pattern),
                "Potentially unsafe JS in use.",
                message)

    for pattern, title, message in CHROME_PATTERNS:
        _generic_test(re.compile(pattern), title, message,
                      {'requires_chrome': True})

    if is_js:
        for pattern in CATEGORY_REGEXES:
            _generic_test(
                    pattern,
                    "Potential JavaScript category registration",
                    "Add-ons should not register JavaScript categories. It "
                    "appears that a JavaScript category was registered via a "
                    "script to attach properties to JavaScript globals. This "
                    "is not allowed.")

        if fnmatch.fnmatch(filename, "defaults/preferences/*.js"):
            _generic_test(
                PASSWORD_REGEX,
                "Passwords may be stored in /defaults/preferences JS files.",
                "Storing passwords in the preferences is insecure and the "
                "Login Manager should be used instead.")

        is_jsm = filename.endswith(".jsm") or "EXPORTED_SYMBOLS" in document

        if not is_jsm:
            # Have a non-static/dynamic test for prototype extension.
            _generic_test(
                    PROTOTYPE_REGEX,
                    "JS Prototype extension",
                    "It appears that an extension of a built-in JS type was "
                    "made. This is not allowed for security and compatibility "
                    "reasons.")

    for pattern in DOM_MUTATION_REGEXES:
        _generic_test(
                pattern,
                "DOM Mutation Events Prohibited",
                "DOM mutation events are flagged because of their "
                "deprecated status, as well as their extreme "
                "inefficiency. Consider using a different event.")

    # Firefox 5 Compatibility
    if err.supports_version(FX5_DEFINITION):
        _compat_test(
                re.compile(r"navigator\.language"),
                "navigator.language may not behave as expected",
                ("JavaScript code was found that references "
                 "navigator.language, which will no longer indicate "
                 "the language of Firefox's UI. To maintain existing "
                 "functionality, general.useragent.locale should be "
                 "used in place of `navigator.language`."),
                compatibility_type="error",
                appversions=FX5_DEFINITION)

    # Firefox 6 Compatibility
    if err.supports_version(FX6_DEFINITION):
        for pattern, bug in FX6_INTERFACES.items():
            _compat_test(
                    re.compile(pattern),
                    "Unsupported interface in use",
                    ("Your add-on uses interface %s, which has been removed "
                     "from Firefox 6. Please refer to %s for possible "
                     "alternatives.") % (pattern, BUGZILLA_BUG % bug),
                    compatibility_type="error",
                    appversions=FX6_DEFINITION,
                    logFunc=err.warning)

        # app.update.timer
        _compat_test(
                re.compile(r"app\.update\.timer"),
                "app.update.timer is incompatible with Firefox 6",
                ("The 'app.update.timer' preference is being replaced by the "
                 "'app.update.timerMinimumDelay' preference in Firefox 6. "
                 "Please refer to %s for more details.") %
                     (BUGZILLA_BUG % 614181),
                compatibility_type="error",
                appversions=FX6_DEFINITION)
        if is_js:
            # javascript/data: URI usage in the address bar
            _compat_test(
                    re.compile(r"['\"](javascript|data):"),
                    "javascript:/data: URIs may be incompatible with Firefox "
                    "6.",
                    ("Loading 'javascript:' and 'data:' URIs through the "
                     "location bar may no longer work as expected in Firefox "
                     "6. If you load these types of URIs, please test your "
                     "add-on on the latest Firefox 6 builds, or refer to %s "
                     "for more information.") %
                         (BUGZILLA_BUG % 656433),
                    compatibility_type="warning",
                    appversions=FX6_DEFINITION)

    # Firefox 7 Compatibility
    if err.supports_version(FX7_DEFINITION):
        for pattern, bug in FX7_INTERFACES.items():
            _compat_test(
                    re.compile(pattern),
                    "Unsupported interface in use",
                    ("Your add-on uses interface %s, which has been removed "
                     "from Firefox 7. Please refer to %s for possible "
                     "alternatives.") % (pattern, BUGZILLA_BUG % bug),
                    compatibility_type="error",
                    appversions=FX7_DEFINITION,
                    logFunc=err.warning)

        # nsINavHistoryObserver
        _compat_test(
                re.compile(r"nsINavHistoryObserver"),
                "nsINavHistoryObserver interface has changed in Firefox 7",
                ("The nsINavHistoryObserver interface has changed in Firefox "
                 "7. Most function calls now required a GUID parameter, "
                 "please refer to %s and %s for more information.") %
                    (NSINHS_LINK, BUGZILLA_BUG % 633266),
                compatibility_type="error",
                appversions=FX7_DEFINITION)
        # nsIMarkupDocumentViewer_MOZILLA_2_0_BRANCH
        _compat_test(
                re.compile(r"nsIMarkupDocumentViewer_MOZILLA_2_0_BRANCH"),
                "MOZILLA_2_0 Namespace has been merged in Firefox 7",
                ("The '_MOZILLA_2_0_BRANCH' interfaces have been merged out. "
                 "You should now use the namespace without the "
                 "'_MOZILLA_2_0_BRANCH' suffix. Please refer to %s for more "
                 "details.") %
                     (BUGZILLA_BUG % 617539),
                compatibility_type="warning",
                appversions=FX7_DEFINITION)

    # Firefox 8 Compatibility
    if err.supports_version(FX8_DEFINITION):
        for pattern, bug in FX8_INTERFACES.items():
            _compat_test(
                    re.compile(pattern),
                    "Removed, deprecated, or unsupported interface in use.",
                    ("The nsISelection2 and nsISelection3 interfaces have "
                     "been removed in Firefox 8. You can use the nsISelection "
                     "interface instead. See %s for more details.") %
                        (BUGZILLA_BUG % bug),
                    compatibility_type="error",
                    appversions=FX8_DEFINITION,
                    logFunc=err.warning)

        # nsIDOMWindowInternal
        NSIDWI_MDN = ("https://developer.mozilla.org/en/"
                          "XPCOM_Interface_Reference/nsIDOMWindow")
        _compat_test(
                re.compile(r"nsIDOMWindowInternal"),
                "nsIDOMWindowInternal has been deprecated in Firefox 8.",
                ("The nsIDOMWindowInternal interface has been deprecated in "
                 "Firefox 8. You can use the nsIDOMWindow interface instead. "
                 "See %s for more information.") % NSIDWI_MDN,
                compatibility_type="warning",
                appversions=FX8_DEFINITION)

        # ISO8601DateUtils
        # TODO(basta): Make this a string test instead once they're invented.
        ISO8601_MDC = ("https://developer.mozilla.org/en/JavaScript/Reference/"
                           "Global_Objects/Date")
        _compat_test(
                re.compile(r"ISO8601DateUtils"),
                "ISO8601DateUtils.jsm was removed in Firefox 8.",
                ("The ISO8601DateUtils object is no longer available in "
                 "Firefox 8. You can use the normal Date object instead. See "
                 "%s for more information.") % ISO8601_MDC,
                compatibility_type="error",
                appversions=FX8_DEFINITION,
                logFunc=err.warning)

    # Firefox 9 Compatibility
    if err.supports_version(FX9_DEFINITION):
        TAINTENABLED_BUG = BUGZILLA_BUG % 679971
        _compat_test(
                re.compile(r"navigator\.taintEnabled"),
                "navigator.taintEnabled was removed in Firefox 9.",
                ("The taintEnabled function is no longer available in"
                 " Firefox 9. Since this function was only used for "
                 "browser detection and this doesn't belong in extension"
                 " code, you should remove it if possible. For more "
                 "information, please see %s.") % TAINTENABLED_BUG,
                compatibility_type="warning",
                appversions=FX9_DEFINITION,
                logFunc=err.warning)
        XRAYPROPS_BUG = BUGZILLA_BUG % 660233
        _compat_test(
            re.compile(r"\.nodePrincipal"),
            ("nodePrincipal only available in chrome context"),
            ("The nodePrincipal property is no longer accessible from "
             "untrusted scripts. For more information, please see %s."
             ) % XRAYPROPS_BUG,
            compatibility_type="warning",
            appversions=FX9_DEFINITION)
        _compat_test(
            re.compile(r"\.documentURIObject"),
            ("documentURIObject only available in chrome context"),
            ("The documentURIObject property is no longer accessible from "
             "untrusted scripts. For more information, please see %s."
             ) % XRAYPROPS_BUG,
            compatibility_type="warning",
            appversions=FX9_DEFINITION)
        _compat_test(
            re.compile(r"\.baseURIObject"),
            ("baseURIObject only available in chrome context"),
            ("The baseURIObject property is no longer accessible from "
             "untrusted scripts. For more information, please see %s."
             ) % XRAYPROPS_BUG,
            compatibility_type="warning",
            appversions=FX9_DEFINITION)
        _compat_test(
            re.compile(r"nsIGlobalHistory3"),
            "nsIGlobalHistory3 was removed in Firefox 9",
            ("The nsIGlobalHistory3 interface has been removed from Firefox."
             " For more information, please see %s."
             ) % (BUGZILLA_BUG % 568971),
            compatibility_type="warning",
            appversions=FX9_DEFINITION,
            logFunc=err.warning)

        # geo.wifi.* warnings
        geo_wifi_description = (
                "The geo.wifi.* preferences are no longer created by default "
                "in Gecko 9. Reading them without testing for their presence "
                "can result in unexpected errors. See %s for more "
                "information." % BUGZILLA_BUG % 689252)
        _compat_test(
            re.compile(r"geo\.wifi\.uri"),
            "The preference 'geo.wifi.uri' was removed in Firefox 9",
            geo_wifi_description,
            compatibility_type="error",
            appversions=FX9_DEFINITION,
            logFunc=err.warning)
        _compat_test(
            re.compile(r"geo\.wifi\.protocol"),
            "The preference 'geo.wifi.protocol' was removed in Firefox 9",
            geo_wifi_description,
            compatibility_type="error",
            appversions=FX9_DEFINITION,
            logFunc=err.warning)

    # Firefox 11 Compatibility
    if err.supports_version(FX11_DEFINITION):
        for pattern, bug in FX11_INTERFACES.items():
            _compat_test(
                    re.compile(pattern),
                    "Unsupported interface in use",
                    "Your add-on uses interface %s, which has been removed "
                    "from Firefox 11. Please refer to %s for possible "
                    "alternatives." % (pattern, BUGZILLA_BUG % bug),
                    compatibility_type="error",
                    appversions=FX11_DEFINITION,
                    logFunc=err.warning)

        # omni.jar renamed
        for instance in re.finditer(r"omni\.jar", document):
            err.warning(
                err_id=("testcases_regex", "regex_regex_tests", "omni.jar"),
                warning="'omni.jar' renamed to 'omni.ja'",
                description="This add-on references omni.jar, which was "
                            "renamed to omni.ja. You should avoid referencing "
                            "this file directly, and at least update this "
                            "reference for any versions that support Firefox "
                            "11 and above. See %s for more information." %
                                BUGZILLA_BUG % 701875,
                filename=filename,
                line=context.get_line(instance.start()),
                context=context,
                for_appversions=FX11_DEFINITION,
                compatibility_type="error",
                tier=5)

    # Firefox 12 Compatibility
    if err.supports_version(FX12_DEFINITION):
        for pattern, bug in FX12_INTERFACES.items():
            if isinstance(bug, tuple):
                bug, message = bug
            else:
                message = ("Your add-on uses interface %s, which has been "
                           "removed from Gecko 12.") % pattern

            message = "%s See %s for more infomration." % (message,
                                                           BUGZILLA_BUG % bug)
            _compat_test(
                    re.compile(pattern),
                    "Unsupported interface in use",
                    message,
                    compatibility_type="error",
                    appversions=FX12_DEFINITION,
                    logFunc=err.warning)

        # Test for `chromemargin` (bug 735876)
        for instance in re.finditer(r"chromemargin", document):
            err.notice(
                err_id=("testcases_regex", "regex_regex_tests", "chromemargin"),
                notice="`chromemargin` attribute changed in Gecko 12",
                description="This add-on uses the chromemargin attribute, "
                            "which after Gecko 12 will not work in the same "
                            "way  with values other than 0 or -1. Please see "
                            "%s for more information." % BUGZILLA_BUG % 735876,
                filename=filename,
                line=context.get_line(instance.start()),
                context=context,
                for_appversions=FX12_DEFINITION,
                compatibility_type="error",
                tier=5)

    # Thunderbird 7 Compatibility rdf:addressdirectory
    if err.supports_version(TB7_DEFINITION):
        # dictUtils.js removal
        _compat_test(
                re.compile(r"resource:///modules/dictUtils.js"),
                "dictUtils.js was removed in Thunderbird 7.",
                "The dictUtils.js file is no longer available in "
                "Thunderbird 7. You can use Dict.jsm instead. See"
                "%s for more information." % BUGZILLA_BUG % 621213,
                compatibility_type="error",
                appversions=TB7_DEFINITION,
                logFunc=err.warning)
        # de-RDF the addressbook
        _compat_test(
                re.compile(r"rdf:addressdirectory"),
                "The address book does not use RDF in Thunderbird 7.",
                "The address book was changed to use a look up table in "
                "Thunderbird 7. See %s and %s for more information." %
                    (TB7_LINK, BUGZILLA_BUG % 621213),
                compatibility_type="error",
                appversions=TB7_DEFINITION)
        # Second test for de-RDFing the addressbook
        # r"GetResource(.*?)\s*\.\s*QueryInterface(.*?nsIAbDirectory);"
        _compat_test(
                re.compile(r"GetResource\(.*?\)\s*\.\s*"
                           r"QueryInterface\(.*?nsIAbDirectory\)"),
                "The address book does not use RDF in Thunderbird 7.",
                "The address book was changed to use a look up table in "
                "Thunderbird 7. See %s and %s for more information." %
                    (TB7_LINK, BUGZILLA_BUG % 621213),
                compatibility_type="error",
                appversions=TB7_DEFINITION)

    # Thunderbird 10 Compatibility
    if err.supports_version(TB10_DEFINITION):
        # gDownloadManagerStrings removal
        _compat_test(
                re.compile(r"gDownloadManagerStrings"),
                "gDownloadManagerStrings was removed in Thunderbird 10.",
                "This global is no longer available in "
                "Thunderbird 10. See %s for more information." %
                    BUGZILLA_BUG % 700220,
                compatibility_type="error",
                appversions=TB10_DEFINITION,
                logFunc=err.warning)
        # nsTryToClose.js removal
        _compat_test(
                re.compile(r"nsTryToClose.js"),
                "nsTryToClose.js was removed in Thunderbird 10.",
                "The nsTryToClose.js file is no longer available in "
                "Thunderbird 10. See %s for more information." %
                    BUGZILLA_BUG % 539997,
                compatibility_type="error",
                appversions=TB10_DEFINITION,
                logFunc=err.warning)

    # Thunderbird 11 Compatibility
    if err.supports_version(TB11_DEFINITION):
        # specialFoldersDeletionAllowed removal
        _compat_test(
            re.compile(r"specialFoldersDeletionAllowed"),
            "specialFoldersDeletionAllowed was removed in Thunderbird 11.",
            "This global is no longer available in "
            "Thunderbird 11. See %s for more information." %
            BUGZILLA_BUG % 39121,
            compatibility_type="error",
            appversions=TB11_DEFINITION,
            logFunc=err.notice)
        for pattern, bug in TB11_STRINGS.items():
            _compat_test(
                re.compile(pattern),
                "Removed, renamed, or changed strings in use",
                "Your add-on uses string %s, which has been changed or "
                "removed from Thunderbird 11. Please refer to %s for "
                "possible alternatives." % (pattern, BUGZILLA_BUG % bug),
                compatibility_type="error",
                appversions=TB11_DEFINITION,
                logFunc=err.warning)
        for pattern, bug in TB11_JS.items():
            _compat_test(
                re.compile(pattern),
                "Removed, renamed, or changed javascript in use",
                "Your add-on uses the javascript method or class %s, which "
                "has been changed or removed from Thunderbird 11. Please "
                "refer to %s for possible alternatives." %
                (pattern, BUGZILLA_BUG % bug),
                compatibility_type="error",
                appversions=TB11_DEFINITION,
                logFunc=err.notice)

    # Thunderbird 12 Compatibility
    if err.supports_version(TB12_DEFINITION):
        _compat_test(
            re.compile(r"EdImage(Map|MapHotSpot|MapShapes|Overlay)\.js"),
            "Removed javascript file EdImage*.js in use ",
            "EdImageMap.js, EdImageMapHotSpot.js, "
            "EdImageMapShapes.js, and EdImageMapOverlay.js "
            "were removed in Thunderbird 12. "
            "See %s for more information." % BUGZILLA_BUG % 717240,
            compatibility_type="error",
            appversions=TB12_DEFINITION,
            logFunc=err.notice)
        for pattern, bug in TB12_STRINGS.items():
            _compat_test(
                re.compile(pattern),
                "Removed, renamed, or changed strings in use",
                "Your add-on uses string %s, which has been changed or "
                "removed from Thunderbird 11. Please refer to %s for "
                "possible alternatives." % (pattern, BUGZILLA_BUG % bug),
                compatibility_type="error",
                appversions=TB12_DEFINITION,
                logFunc=err.warning)
        for pattern, bug in TB12_JS.items():
            _compat_test(
                re.compile(pattern),
                "Removed, renamed, or changed javascript in use",
                "Your add-on uses the javascript method or class %s, which "
                "has been changed or removed from Thunderbird 11. Please "
                "refer to %s for possible alternatives." % 
                (pattern, BUGZILLA_BUG % bug),
                compatibility_type="error",
                appversions=TB12_DEFINITION,
                logFunc=err.notice)
コード例 #30
0
 def run(data, expectation, line=2):
     # Strip blank lines.
     data = '\n'.join(filter(None, data.split('\n')))
     # Get the context and assert its equality.
     c = ContextGenerator(data)
     assert c.get_context(line) == expectation
コード例 #31
0
def test_get_line():
    """Test that the context generator returns the proper line."""

    d = open("tests/resources/contextgenerator/data.txt").read()
    c = ContextGenerator(d)
    print c.data

    print c.get_line(30)
    assert c.get_line(30) == 3
    print c.get_line(11)
    assert c.get_line(11) == 2
    print c.get_line(10000)
    assert c.get_line(10000) == 11
コード例 #32
0
 def run(data, expectation, line=2):
     # Strip blank lines.
     data = '\n'.join(filter(None, data.split('\n')))
     # Get the context and assert its equality.
     c = ContextGenerator(data)
     eq_(c.get_context(line), expectation)