Exemplo n.º 1
0
 def test_SelfSourceExpression_match(self):
     """A 'self' source expression matches if the scheme, host and port of the self and other URI
     are the same (using default ports if absent)."""
     selfURI = URI("http", "seclab.nu", 80, "/other-path")
     srcExpr = SelfSourceExpression.SELF()
     assert srcExpr.matches(SourceExpressionTest.uri_urlFull, selfURI)
     assert srcExpr.matches(SourceExpressionTest.uri_urlFull_longer1,
                            selfURI)
     assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_secure,
                                selfURI)
     assert not srcExpr.matches(SourceExpressionTest.uri_url1Sub, selfURI)
     assert srcExpr.matches(SourceExpressionTest.uri_schemedomain,
                            selfURI)  # using default port in URI
     assert not srcExpr.matches(URI.EMPTY(), selfURI)
     assert not srcExpr.matches(URI.INVALID(), selfURI)
     assert not srcExpr.matches(URI.INLINE(), selfURI)
     assert not srcExpr.matches(URI.EVAL(), selfURI)
     selfURIDefaultPort = URI("https", "seclab.nu", None,
                              "/yet-another-path")
     assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_secure,
                                selfURIDefaultPort)
     assert srcExpr.matches(
         SourceExpressionTest.uri_urlFull_secure_defaultPort,
         selfURIDefaultPort)
     selfURINoPort = SourceExpressionTest.uri_chromeExtension
     assert not srcExpr.matches(
         SourceExpressionTest.uri_chromeExtension,
         selfURINoPort)  # no valid port can be deduced from this scheme
Exemplo n.º 2
0
 def testReport_generatePolicy_wrongDocumentURI(self):
     reportEmptyDocument = Report({
         "blocked-uri": ReportTest.sampleURI1a,
         "violated-directive": ReportTest.sampleDirective1a,
         "document-uri": URI.EMPTY()
     })
     assert reportEmptyDocument.generatePolicy(
         "regular") == Policy.INVALID()
Exemplo n.º 3
0
 def test_regularURI_singletons(self):
     """All the singleton URIs should return False for isRegularURI()."""
     assert URI.EMPTY().isRegularURI() == False
     assert URI.INVALID().isRegularURI() == False
     assert URI.INLINE().isRegularURI() == False
     assert URI.EVAL().isRegularURI() == False
     assert URI("http", "seclab.nu", None, None,
                None).isRegularURI() == True
Exemplo n.º 4
0
 def testReportParser_parse_emptyOrSelfURI(self):
     """This tests that the internal settings of the URI parser are chosen such that empty or self URIs are
     correctly handled."""
     report = """{"empty-uri": "", "self-uri": "self", "document-uri": "http://seclab.nu"}"""
     expected = Report({
         "empty-uri": URI.EMPTY(),
         "self-uri": ReportTest.sampleURI1a,
         "document-uri": ReportTest.sampleURI1a
     })
     assert ReportParser(requiredKeys=[], strict=True, uriKeys=["empty-uri", "self-uri", "document-uri"]) \
                     .parseString(report) == expected
Exemplo n.º 5
0
 def testPolicy_matches_invalid(self):
     """An invalid policy matches nothing."""
     selfURI = PolicyTest.sampleURI2
     assert not Policy.INVALID().matches(PolicyTest.sampleURI1a,
                                         "script-src", selfURI)
     assert not Policy.INVALID().matches(URI.INVALID(), "script-src",
                                         selfURI)
     assert not Policy.INVALID().matches(URI.EMPTY(), "script-src", selfURI)
     assert not Policy.INVALID().matches(URI.INLINE(), "script-src",
                                         selfURI)
     assert not Policy.INVALID().matches(URI.EVAL(), "script-src", selfURI)
Exemplo n.º 6
0
 def testDirective_matches_special(self):
     """An invalid/special directive matches nothing."""
     selfURI = DirectiveTest.sampleURI2
     assert not Directive.INVALID().matches(URI.EMPTY(), selfURI)
     assert not Directive.INVALID().matches(URI.INVALID(), selfURI)
     assert not Directive.INVALID().matches(URI.INLINE(), selfURI)
     assert not Directive.INVALID().matches(URI.EVAL(), selfURI)
     assert not Directive.INVALID().matches(DirectiveTest.sampleURI1,
                                            selfURI)
     assert not Directive.EVAL_SCRIPT_BASE_RESTRICTION().matches(
         URI.EMPTY(), selfURI)
     assert not Directive.EVAL_SCRIPT_BASE_RESTRICTION().matches(
         URI.INVALID(), selfURI)
     assert not Directive.EVAL_SCRIPT_BASE_RESTRICTION().matches(
         URI.INLINE(), selfURI)
     assert not Directive.EVAL_SCRIPT_BASE_RESTRICTION().matches(
         URI.EVAL(), selfURI)
     assert not Directive.EVAL_SCRIPT_BASE_RESTRICTION().matches(
         DirectiveTest.sampleURI1, selfURI)
     assert not Directive.INLINE_SCRIPT_BASE_RESTRICTION().matches(
         URI.EMPTY(), selfURI)
     assert not Directive.INLINE_SCRIPT_BASE_RESTRICTION().matches(
         URI.INVALID(), selfURI)
     assert not Directive.INLINE_SCRIPT_BASE_RESTRICTION().matches(
         URI.INLINE(), selfURI)
     assert not Directive.INLINE_SCRIPT_BASE_RESTRICTION().matches(
         URI.EVAL(), selfURI)
     assert not Directive.INLINE_SCRIPT_BASE_RESTRICTION().matches(
         DirectiveTest.sampleURI1, selfURI)
     assert not Directive.INLINE_STYLE_BASE_RESTRICTION().matches(
         URI.EMPTY(), selfURI)
     assert not Directive.INLINE_STYLE_BASE_RESTRICTION().matches(
         URI.INVALID(), selfURI)
     assert not Directive.INLINE_STYLE_BASE_RESTRICTION().matches(
         URI.INLINE(), selfURI)
     assert not Directive.INLINE_STYLE_BASE_RESTRICTION().matches(
         URI.EVAL(), selfURI)
     assert not Directive.INLINE_STYLE_BASE_RESTRICTION().matches(
         DirectiveTest.sampleURI1, selfURI)
Exemplo n.º 7
0
 def testDirective_generateDirective_incompatibleURI(self):
     violatedRegular = Directive("object-src", [])
     violatedInline = Directive("style-src", [])
     violatedEval = Directive("script-src", [])
     assert violatedRegular.generateDirective(
         "regular", URI.EMPTY()) == Directive.INVALID()
     assert violatedRegular.generateDirective(
         "regular", URI.INVALID()) == Directive.INVALID()
     #assert violatedInline.generateDirective("inline", DirectiveTest.sampleURI1) == Directive.INVALID()
     assert violatedInline.generateDirective(
         "inline", URI.INVALID()) == Directive.INVALID()
     #assert violatedEval.generateDirective("eval", DirectiveTest.sampleURI1) == Directive.INVALID()
     assert violatedEval.generateDirective(
         "eval", URI.INVALID()) == Directive.INVALID()
Exemplo n.º 8
0
 def test_str(self):
     """Checks the string serialisation of a few URIs."""
     assert str(URI("data", "", None, None)) == "data:"
     assert str(URI("data", "image/png;base64,iVBORw0KGgoAAAA", None,
                    None)) == "data:image/png;base64,iVBORw0KGgoAAAA"
     assert str(URI("about", "blank", None, None)) == "about:blank"
     assert str(
         URI("http", "www.seclab.org", 80, "/file", "parameter=value")
     ) == "http://www.seclab.org:80/file?parameter=value"
     assert str(URI(None, "www.seclab.nu", None, None)) == "www.seclab.nu"
     assert str(URI.EMPTY()) == ""
     assert str(URI.INVALID()) == "[invalid]"
     assert str(URI.INLINE()) == "[inline]"
     assert str(URI.EVAL()) == "[eval]"
Exemplo n.º 9
0
 def test_URISourceExpression_match_star(self):
     "A source expression that should match everything (except for special URIs)."
     srcExpr = URISourceExpression(None, "*", None, None)
     selfURI = SourceExpressionTest.uri_chromeExtension
     assert srcExpr.matches(SourceExpressionTest.uri_chromeExtension,
                            selfURI)
     assert srcExpr.matches(SourceExpressionTest.uri_urlFull, selfURI)
     assert srcExpr.matches(SourceExpressionTest.uri_urlFull_secure,
                            selfURI)
     assert srcExpr.matches(SourceExpressionTest.uri_urlFull_other, selfURI)
     assert not srcExpr.matches(SourceExpressionTest.uri_empty, selfURI)
     assert srcExpr.matches(SourceExpressionTest.uri_domain, selfURI)
     assert srcExpr.matches(SourceExpressionTest.uri_data, selfURI)
     assert not srcExpr.matches(URI.EMPTY(), selfURI)
     assert not srcExpr.matches(URI.INVALID(), selfURI)
     assert not srcExpr.matches(URI.INLINE(), selfURI)
     assert not srcExpr.matches(URI.EVAL(), selfURI)
Exemplo n.º 10
0
 def testReportParser_parse_inferSelfURI(self):
     """Tests if the self URI is correctly inferred from the "document-uri" field (even
     after renaming)."""
     report = """{"violated-directive": "default-src 'self'", "referrer": "",""" \
             + """ "blocked-uri": "self", "document-URL":""" \
             + """ "http://seclab.nu"}"""
     expected = Report({
         "violated-directive": ReportTest.sampleDirective1a,
         "referrer": URI.EMPTY(),
         "blocked-uri": ReportTest.sampleURI1a,
         "document-uri": ReportTest.sampleURI1a
     })
     parser = ReportParser(
         requiredKeys=["violated-directive", "document-uri", "blocked-uri"],
         strict=True,
         directiveKeys=["violated-directive"],
         uriKeys=["referrer", "blocked-uri", "document-uri"],
         keyNameReplacements={'document-url': 'document-uri'})
     cspReport = parser.parseString(report)
     assert cspReport == expected
Exemplo n.º 11
0
 def test_eq(self):
     """Checks that the eq and hash methods are consistent for a few URIs."""
     uri1a = URI("http", "www.seclab.org", 80, "/", "query")
     uri1b = URI("http", "www.seclab.org", 80, "/", "query")
     uri2 = URI("https", "www.seclab.org", 80, "/")
     uri3 = URI("http", "www.seclab.org", 80, None)
     assert uri1a == uri1b
     assert hash(uri1a) == hash(uri1b)
     assert uri1a != uri2
     assert uri1a != uri3
     assert uri2 != uri3
     assert URI.EMPTY() == URI.EMPTY()
     assert URI.INVALID() == URI.INVALID()
     assert URI.EMPTY() not in (uri1a, uri1b, uri2, uri3, URI.INVALID(),
                                URI.INLINE(), URI.EVAL())
     assert URI.INVALID() not in (uri1a, uri1b, uri2, uri3, URI.EMPTY(),
                                  URI.INLINE(), URI.EVAL())
     assert URI.INLINE() not in (uri1a, uri1b, uri2, uri3, URI.EMPTY(),
                                 URI.INVALID(), URI.EVAL())
     assert URI.EVAL() not in (uri1a, uri1b, uri2, uri3, URI.EMPTY(),
                               URI.INVALID(), URI.INLINE())
Exemplo n.º 12
0
 def testDirective_generateDirective_invalidDirective(self):
     assert Directive.INVALID().generateDirective(
         "eval", URI.EMPTY()) == Directive.INVALID()
Exemplo n.º 13
0
 def testDirective_generateDirective_invalidType(self):
     violated = Directive("script-src", [DirectiveTest.sampleSrcExpr1a])
     assert violated.generateDirective("evaluate",
                                       URI.EMPTY()) == Directive.INVALID()
Exemplo n.º 14
0
 def testDirective_generateDirective_eval(self):
     violated = Directive("script-src", [DirectiveTest.sampleSrcExpr1a])
     generated = violated.generateDirective("eval", URI.EMPTY())
     assert generated == Directive("script-src",
                                   [SourceExpression.UNSAFE_EVAL()])
Exemplo n.º 15
0
class LogEntryTest(unittest.TestCase):

    starSourceExpr = URISourceExpression(None, "*", None, None)
    strLogEntry = """{"csp-report": {"blocked-uri": "", "document-uri": "http://seclab.nu/csp-test.html", """ \
                        + """"original-policy": "default-src *; script-src 'unsafe-eval' *; style-src *", """ \
                        + """"referrer": "", "status-code": 200, "violated-directive": "style-src *"}, """ \
                        + """"header-type": "webkit", "http-user-agent": """ \
                        + """"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, """ \
                        + """like Gecko) Chrome/31.0.1650.63 Safari/537.36", "policy-type": "inline", """ \
                        + """"remote-addr": "1.2.3.4", "timestamp-utc": "2013-12-14 01:02:03.456789"}"""
    logEntryData = {u"csp-report": Report({u"document-uri": URI("http", "seclab.nu", None, u"/csp-test.html"),
                                                  u"referrer": URI.EMPTY(),
                                                  u"violated-directive": Directive("style-src",
                                                                                  (starSourceExpr,)),
                                                  u"original-policy": Policy((Directive("default-src",
                                                                                       (starSourceExpr,)),
                                                                             Directive("script-src",
                                                                                       (starSourceExpr,
                                                                                        SourceExpression.UNSAFE_EVAL())),
                                                                             Directive("style-src",
                                                                                       (starSourceExpr,)))),
                                                  u"blocked-uri": URI.EMPTY(),
                                                  u"status-code": 200
                                                  }),
                            u"remote-addr": u"1.2.3.4",
                            u"http-user-agent": u"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) " \
                                + u"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36",
                            u"timestamp-utc": u"2013-12-14 01:02:03.456789",
                            u"policy-type": u"inline",
                            u"header-type": u"webkit"
                            }
    cspLogEntry = LogEntry(logEntryData)

    def testLogEntry_str_invalid(self):
        assert str(LogEntry.INVALID()) == "[invalid]"

    def testLogEntry_str_regular(self):
        assert str(LogEntryTest.cspLogEntry) == LogEntryTest.strLogEntry

    def testLogEntry_dict_iterateAndImmutable(self):
        assert len(LogEntryTest.cspLogEntry) == 6
        assert "http-user-agent" in LogEntryTest.cspLogEntry
        assert LogEntryTest.cspLogEntry["remote-addr"] == "1.2.3.4"
        for key in LogEntryTest.cspLogEntry.keys():
            assert key in ("csp-report", "remote-addr", "http-user-agent",
                           "timestamp-utc", "policy-type", "header-type")
        with pytest.raises(TypeError):
            LogEntryTest.cspLogEntry["csp-report"] = None
        with pytest.raises(TypeError):
            LogEntryTest.cspLogEntry["something-else"] = 123

    def testLogEntry_eq(self):
        assert LogEntryTest.cspLogEntry != LogEntry.INVALID()

    def testLogEntry_generatePolicy_standard(self):
        assert LogEntryTest.cspLogEntry.generatePolicy() == Policy(
            [Directive("style-src", [SourceExpression.UNSAFE_INLINE()])])

    def testLogEntry_generatePolicy_invalid(self):
        assert LogEntry.INVALID().generatePolicy() == Policy.INVALID()

    def testLogEntry_generatePolicy_incomplete(self):
        logEntryNoReport = LogEntryTest.logEntryData.copy()
        del logEntryNoReport['csp-report']
        logEntryNoPolicyType = LogEntryTest.logEntryData.copy()
        del logEntryNoPolicyType['policy-type']
        assert LogEntry(logEntryNoReport).generatePolicy() == Policy.INVALID()
        assert LogEntry(
            logEntryNoPolicyType).generatePolicy() == Policy.INVALID()

    def testLogEntryParser_parse(self):
        parser = LogEntryParser(strict=True)
        parsed = parser.parseString(LogEntryTest.strLogEntry)
        print parsed._entryData
        print LogEntryTest.cspLogEntry._entryData
        print parsed._entryData[
            'csp-report'] == LogEntryTest.cspLogEntry._entryData['csp-report']
        assert parsed == LogEntryTest.cspLogEntry
Exemplo n.º 16
0
 def test_empty_URI(self):
     """Ensure that URI.EMPTY is returned when a string with only whitespace is parsed."""
     uriString = " "
     uriExpected = URI.EMPTY()
     assert URIParser().parse(uriString) == uriExpected
Exemplo n.º 17
0
 def testDirective_generateDirective_inline(self):
     violated = Directive("style-src", [DirectiveTest.sampleSrcExpr2])
     generated = violated.generateDirective("inline", URI.EMPTY())
     assert generated == Directive("style-src",
                                   [SourceExpression.UNSAFE_INLINE()])
Exemplo n.º 18
0
class SourceExpressionTest(unittest.TestCase):

    uri_chromeExtension = URI("chrome-extension",
                              "mkfokfffehpeedafpekjeddnmnjhmcmk", None, None,
                              None)
    uri_urlFull = URI("http", "seclab.nu", 80, "/path", "query")
    uri_urlFull_longer1 = URI("http", "seclab.nu", 80, "/path/1", "param=val1")
    uri_urlFull_longer2 = URI("http", "seclab.nu", 80, "/path/2", "param=val2")
    uri_urlFull_secure = URI("https", "seclab.nu", 80, "/path", "param")
    uri_urlFull_secure_defaultPort = URI("https", "seclab.nu", 443, "/path",
                                         "param")
    uri_urlFull_other = URI("other", "seclab.nu", 80, "/path", "param")
    uri_urlSubstring = URI("http", "iseclab.nu", 80, "/path", None)
    uri_url1Sub = URI("http", "blog.SecLab.nu", 80, "/path", None)
    uri_url2Sub = URI("http", "www.blog.SecLab.nu", 80, "/path", None)
    uri_empty = URI.EMPTY()
    uri_domain = URI(None, "www.seclab.nu", None, None, None)
    uri_schemedomain = URI("http", "seclab.nu", None, None, None)
    uri_schemedomain_secure = URI("https", "seclab.nu", None, None, None)
    uri_data = URI("data", "image/png;base64,iVBORw0KGgoAAAA", None, None,
                   None)

    def test_URISourceExpression_match_star(self):
        "A source expression that should match everything (except for special URIs)."
        srcExpr = URISourceExpression(None, "*", None, None)
        selfURI = SourceExpressionTest.uri_chromeExtension
        assert srcExpr.matches(SourceExpressionTest.uri_chromeExtension,
                               selfURI)
        assert srcExpr.matches(SourceExpressionTest.uri_urlFull, selfURI)
        assert srcExpr.matches(SourceExpressionTest.uri_urlFull_secure,
                               selfURI)
        assert srcExpr.matches(SourceExpressionTest.uri_urlFull_other, selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_empty, selfURI)
        assert srcExpr.matches(SourceExpressionTest.uri_domain, selfURI)
        assert srcExpr.matches(SourceExpressionTest.uri_data, selfURI)
        assert not srcExpr.matches(URI.EMPTY(), selfURI)
        assert not srcExpr.matches(URI.INVALID(), selfURI)
        assert not srcExpr.matches(URI.INLINE(), selfURI)
        assert not srcExpr.matches(URI.EVAL(), selfURI)

    def test_URISourceExpression_match_scheme_only(self):
        "A source expression where only the scheme matters"
        srcExpr = URISourceExpression("chrome-extension", None, None, None)
        selfURI = SourceExpressionTest.uri_chromeExtension
        assert srcExpr.matches(SourceExpressionTest.uri_chromeExtension,
                               selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull, selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_secure,
                                   selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_other,
                                   selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_empty, selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_domain, selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_data, selfURI)

    def test_URISourceExpression_match_nohost(self):
        "An URI with no host does not match any source expression that is not *"
        srcExpr = URISourceExpression("http", "seclab.nu", 80, None)
        selfURI = SourceExpressionTest.uri_chromeExtension
        assert not srcExpr.matches(SourceExpressionTest.uri_empty, selfURI)

    def test_URISourceExpression_match_scheme_different(self):
        "An URI with a different (case-insensitive) scheme than the source expression does not match." ""
        srcExpr = URISourceExpression("https", "seclab.nu", 80, "/path")
        srcExprUpper = URISourceExpression("HTTPS", "seclab.nu", 80, "/path")
        selfURI = SourceExpressionTest.uri_chromeExtension
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull, selfURI)
        assert srcExpr.matches(SourceExpressionTest.uri_urlFull_secure,
                               selfURI)
        assert not srcExprUpper.matches(SourceExpressionTest.uri_urlFull,
                                        selfURI)
        assert srcExprUpper.matches(SourceExpressionTest.uri_urlFull_secure,
                                    selfURI)

    def test_URISourceExpression_match_noscheme_http(self):
        """A source expression without a scheme and http as the protected resource's scheme match only
        if the URI has the scheme http or https"""
        srcExpr = URISourceExpression(None, "seclab.nu", None, None)
        selfURI = SourceExpressionTest.uri_urlFull_longer1
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_other,
                                   selfURI)
        assert srcExpr.matches(SourceExpressionTest.uri_urlFull, selfURI)
        assert srcExpr.matches(
            SourceExpressionTest.uri_urlFull_secure_defaultPort, selfURI)

    def test_URISourceExpression_match_noscheme_same(self):
        """A source expression without a scheme and the protected resource's scheme being different from http
        match only if the URI has the same scheme as the protected resource"""
        srcExpr = URISourceExpression(None, "seclab.nu", None, None)
        selfURI = SourceExpressionTest.uri_urlFull_secure
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_other,
                                   selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull, selfURI)
        assert srcExpr.matches(
            SourceExpressionTest.uri_urlFull_secure_defaultPort, selfURI)

    def test_URISourceExpression_match_scheme_star(self):
        """A source expression with a scheme and a host name including a star matches only if the domain name
        of the URI includes a subdomain at the level of the star (or below)."""
        srcExpr = URISourceExpression("http", "*.seclab.nu", None, None)
        selfURI = SourceExpressionTest.uri_urlFull_secure
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull, selfURI)
        assert srcExpr.matches(SourceExpressionTest.uri_url1Sub, selfURI)
        assert srcExpr.matches(SourceExpressionTest.uri_url2Sub, selfURI)
        assert not srcExpr.matches(
            SourceExpressionTest.uri_urlSubstring,
            selfURI)  # must be a true subdomain, not just substring

    def test_URISourceExpression_match_scheme_host_different(self):
        """A source expression with a scheme and a host name without a star matches only if the host name
        is the same (case insensitive)."""
        srcExpr = URISourceExpression("http", "blog.seclab.nu", None, None)
        selfURI = SourceExpressionTest.uri_urlFull_secure
        assert srcExpr.matches(SourceExpressionTest.uri_url1Sub, selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_url2Sub, selfURI)

    def test_URISourceExpression_match_noport(self):
        """A source expression without a port matches only if the URI uses the default port for the scheme."""
        srcExpr = URISourceExpression("https", "seclab.nu", None, "/path")
        selfURI = SourceExpressionTest.uri_urlFull_secure
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_secure,
                                   selfURI)
        assert srcExpr.matches(
            SourceExpressionTest.uri_urlFull_secure_defaultPort, selfURI)

    def test_URISourceExpression_match_port_star(self):
        """A source expression matches if the port is * (and everything else matches)."""
        srcExpr = URISourceExpression("https", "seclab.nu", "*", None)
        selfURI = SourceExpressionTest.uri_urlFull_secure
        assert srcExpr.matches(SourceExpressionTest.uri_urlFull_secure,
                               selfURI)
        assert srcExpr.matches(
            SourceExpressionTest.uri_urlFull_secure_defaultPort, selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_schemedomain,
                                   selfURI)
        assert srcExpr.matches(SourceExpressionTest.uri_schemedomain_secure,
                               selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull, selfURI)

    def test_URISourceExpression_match_port_same(self):
        """A source expression matches if the port is the same (and everything else matches), or
        if the port is not given in the URI, it matches the default port for the scheme."""
        srcExpr = URISourceExpression("https", "seclab.nu", 443, None)
        selfURI = SourceExpressionTest.uri_urlFull_secure
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_secure,
                                   selfURI)
        assert srcExpr.matches(
            SourceExpressionTest.uri_urlFull_secure_defaultPort, selfURI)
        assert srcExpr.matches(SourceExpressionTest.uri_schemedomain_secure,
                               selfURI)
        srcExpr80 = URISourceExpression("https", "seclab.nu", 80, None)
        assert srcExpr80.matches(SourceExpressionTest.uri_urlFull_secure,
                                 selfURI)
        assert not srcExpr80.matches(
            SourceExpressionTest.uri_urlFull_secure_defaultPort, selfURI)
        assert not srcExpr80.matches(
            SourceExpressionTest.uri_schemedomain_secure, selfURI)

    def test_URISourceExpression_match_empty_path(self):
        """If the path component of the source expression or the URI is the empty string, it
        should be treated the same as being None."""
        srcExprEmpty = URISourceExpression("http", "seclab.nu", 80, "")
        srcExprNone = URISourceExpression("http", "seclab.nu", 80, None)
        selfURI = SourceExpressionTest.uri_urlFull_secure
        assert srcExprEmpty.matches(SourceExpressionTest.uri_urlFull, selfURI)
        assert srcExprEmpty.matches(URI("http", "seclab.nu", 80, ""), selfURI)
        assert srcExprEmpty.matches(URI("http", "seclab.nu", 80, None),
                                    selfURI)
        assert srcExprNone.matches(SourceExpressionTest.uri_urlFull, selfURI)
        assert srcExprNone.matches(URI("http", "seclab.nu", 80, ""), selfURI)
        assert srcExprNone.matches(URI("http", "seclab.nu", 80, None), selfURI)

    def test_URISourceExpression_match_path_slash(self):
        """If the final character of the path in the source expression is /, then the path in
        the URI must be a prefix of the path."""
        srcExprShort = URISourceExpression("http", "seclab.nu", 80, "/")
        srcExprLong = URISourceExpression("http", "seclab.nu", 80, "/path/")
        selfURI = SourceExpressionTest.uri_urlFull_secure
        assert srcExprShort.matches(SourceExpressionTest.uri_urlFull, selfURI)
        assert srcExprShort.matches(SourceExpressionTest.uri_urlFull_longer1,
                                    selfURI)
        assert srcExprShort.matches(SourceExpressionTest.uri_urlFull_longer2,
                                    selfURI)
        assert not srcExprLong.matches(SourceExpressionTest.uri_urlFull,
                                       selfURI)  # this is a file
        assert srcExprLong.matches(SourceExpressionTest.uri_urlFull_longer1,
                                   selfURI)
        assert srcExprLong.matches(SourceExpressionTest.uri_urlFull_longer2,
                                   selfURI)

    def test_URISourceExpression_match_path_exact(self):
        """If the final character of the path in the source expression is not /, then the path
        in the URI must be an exact match (excluding the query component)."""
        srcExpr = URISourceExpression("http", "seclab.nu", 80, "/path")
        selfURI = SourceExpressionTest.uri_urlFull_secure
        assert srcExpr.matches(SourceExpressionTest.uri_urlFull, selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_longer1,
                                   selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_longer2,
                                   selfURI)

    def test_URISourceExpression_match_starhost(self):
        """Checks the behaviour of a fully specified URI with * as the hostname, that should match
        any host as long as the scheme, port, and path match (is this in line with the CSP 1.1 specification??)"""
        srcExpr = URISourceExpression("http", "*", 80, "/path")
        selfURI = SourceExpressionTest.uri_urlFull_secure
        assert srcExpr.matches(SourceExpressionTest.uri_urlFull, selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_longer1,
                                   selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_secure,
                                   selfURI)

    def test_URISourceExpression_match_query(self):
        """The query component in an URI should not matter when matching."""
        srcExpr = URISourceExpression("http", "seclab.nu", 80, "/path")
        selfURI = SourceExpressionTest.uri_urlFull_secure
        assert srcExpr.matches(URI("http", "seclab.nu", 80, "/path", None),
                               selfURI)
        assert srcExpr.matches(URI("http", "seclab.nu", 80, "/path", "query"),
                               selfURI)

    def test_SelfSourceExpression_match(self):
        """A 'self' source expression matches if the scheme, host and port of the self and other URI
        are the same (using default ports if absent)."""
        selfURI = URI("http", "seclab.nu", 80, "/other-path")
        srcExpr = SelfSourceExpression.SELF()
        assert srcExpr.matches(SourceExpressionTest.uri_urlFull, selfURI)
        assert srcExpr.matches(SourceExpressionTest.uri_urlFull_longer1,
                               selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_secure,
                                   selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_url1Sub, selfURI)
        assert srcExpr.matches(SourceExpressionTest.uri_schemedomain,
                               selfURI)  # using default port in URI
        assert not srcExpr.matches(URI.EMPTY(), selfURI)
        assert not srcExpr.matches(URI.INVALID(), selfURI)
        assert not srcExpr.matches(URI.INLINE(), selfURI)
        assert not srcExpr.matches(URI.EVAL(), selfURI)
        selfURIDefaultPort = URI("https", "seclab.nu", None,
                                 "/yet-another-path")
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull_secure,
                                   selfURIDefaultPort)
        assert srcExpr.matches(
            SourceExpressionTest.uri_urlFull_secure_defaultPort,
            selfURIDefaultPort)
        selfURINoPort = SourceExpressionTest.uri_chromeExtension
        assert not srcExpr.matches(
            SourceExpressionTest.uri_chromeExtension,
            selfURINoPort)  # no valid port can be deduced from this scheme

    def test_EvalInlineSourceExpression_match(self):
        """The source expressions 'unsafe-inline' and 'unsafe-eval' do not match any URI."""
        srcExprEval = SourceExpression.UNSAFE_EVAL()
        srcExprInline = SourceExpression.UNSAFE_INLINE()
        selfURI = SourceExpressionTest.uri_chromeExtension
        assert not srcExprEval.matches(SourceExpressionTest.uri_empty, selfURI)
        assert not srcExprInline.matches(SourceExpressionTest.uri_empty,
                                         selfURI)
        assert not srcExprEval.matches(URI.INVALID(), selfURI)
        assert not srcExprInline.matches(URI.INVALID(), selfURI)
        assert srcExprInline.matches(URI.INLINE(), selfURI)
        assert not srcExprInline.matches(URI.EVAL(), selfURI)
        assert not srcExprEval.matches(URI.INLINE(), selfURI)
        assert srcExprEval.matches(URI.EVAL(), selfURI)

    def test_InvalidSourceExpression_match(self):
        """The invalid source expression does not match anything."""
        srcExpr = SourceExpression.INVALID()
        selfURI = SourceExpressionTest.uri_chromeExtension
        assert not srcExpr.matches(SourceExpressionTest.uri_empty, selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_urlFull, selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_urlSubstring,
                                   selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_domain, selfURI)
        assert not srcExpr.matches(SourceExpressionTest.uri_data, selfURI)
        assert not srcExpr.matches(URI.INVALID(), selfURI)
        assert not srcExpr.matches(URI.EVAL(), selfURI)
        assert not srcExpr.matches(URI.INLINE(), selfURI)

    def test_URISourceExpression_str(self):
        srcExprFull = URISourceExpression("http", "seclab.nu", 80, "/")
        assert str(srcExprFull) == "http://seclab.nu:80/"
        srcExprStar = URISourceExpression("http", "*.seclab.nu", None, None)
        assert str(srcExprStar) == "http://*.seclab.nu"
        srcExprScheme = URISourceExpression("chrome-extension", None, None,
                                            None)
        assert str(srcExprScheme) == "chrome-extension:"
        srcExprAll = URISourceExpression(None, "*", None, None)
        assert str(srcExprAll) == "*"

    def test_URISourceExpression_str_unicode(self):
        """Test URISourceExpression serialisation with unicode characters."""
        url = u'http://handbook5.com/a/a-security-analysis-of-amazon%E2%80%99s-elastic-compute-cloud-service-w14847.html'
        unicodePath = u"/a/a-security-analysis-of-amazon’s-elastic-compute-cloud-service-w14847.html"
        parsed = SourceExpressionParser().parse(url)
        assert parsed == URISourceExpression("http", "handbook5.com", None,
                                             unicodePath)
        assert str(parsed) == url  # must use quoted version

    def test_SelfSourceExpression_str(self):
        srcExpr = SelfSourceExpression.SELF()
        assert str(srcExpr) == "'self'"

    def test_EvalSourceExpression_str(self):
        srcExprEval = SourceExpression.UNSAFE_EVAL()
        assert str(srcExprEval) == "'unsafe-eval'"

    def test_InlineSourceExpression_str(self):
        srcExprInline = SourceExpression.UNSAFE_INLINE()
        assert str(srcExprInline) == "'unsafe-inline'"

    def test_URISourceExpression_eq(self):
        srcExprFull1 = URISourceExpression("http", "seclab.nu", 80, "/")
        srcExprFull2 = URISourceExpression("http", "seclab.nu", 80, "/")
        assert srcExprFull1 == srcExprFull2
        assert hash(srcExprFull1) == hash(srcExprFull2)
        srcExprStar = URISourceExpression("http", "*.seclab.nu", None, None)
        assert srcExprFull1 != srcExprStar
        assert srcExprFull1 != SourceExpression.UNSAFE_EVAL()

    def test_URISourceExpression_removePath(self):
        srcExprFull = URISourceExpression("http", "seclab.nu", 80, "/path")
        assert srcExprFull.removePath() == URISourceExpression(
            "http", "seclab.nu", 80, None)

    def test_URISourceExpression_schemeOnly(self):
        srcExprFull = URISourceExpression("chrome-extension",
                                          "mkfokfffehpeedafpekjeddnmnjhmcmk",
                                          None, None)
        assert srcExprFull.schemeOnly() == URISourceExpression(
            "chrome-extension", None, None, None)
        srcExprIncomplete = URISourceExpression(None, "seclab.nu", None, None)
        assert srcExprIncomplete.schemeOnly() == SourceExpression.INVALID()

    def test_SelfSourceExpression_eq(self):
        srcExpr1 = SelfSourceExpression()
        srcExpr2 = SelfSourceExpression.SELF()
        assert srcExpr1 == srcExpr2
        assert hash(srcExpr1) == hash(srcExpr2)
        assert srcExpr1 != SourceExpression.UNSAFE_EVAL()

    def test_EvalInlineSourceExpression_eq(self):
        assert SourceExpression.UNSAFE_EVAL() == SourceExpression.UNSAFE_EVAL()
        assert hash(SourceExpression.UNSAFE_EVAL()) \
            == hash(SourceExpression.UNSAFE_EVAL())
        assert SourceExpression.UNSAFE_INLINE(
        ) == SourceExpression.UNSAFE_INLINE()
        assert hash(SourceExpression.UNSAFE_INLINE()) \
            == hash(SourceExpression.UNSAFE_INLINE())
        assert SourceExpression.UNSAFE_INLINE(
        ) != SourceExpression.UNSAFE_EVAL()

    def test_parse_eval(self):
        srcExpr = SourceExpressionParser().parse("'unsafe-eval'")
        assert srcExpr == SourceExpression.UNSAFE_EVAL()
        assert srcExpr.getType() == "unsafe-eval"
        assert srcExpr == SourceExpressionParser().parse("'UNSAFE-EVAL'")

    def test_parse_inline(self):
        srcExpr = SourceExpressionParser().parse("'unsafe-inline'")
        assert srcExpr == SourceExpression.UNSAFE_INLINE()
        assert srcExpr.getType() == "unsafe-inline"
        assert srcExpr == SourceExpressionParser().parse("'UNSAFE-INLINE'")

    def test_parse_self(self):
        srcExpr = SourceExpressionParser().parse("'self'")
        assert srcExpr == SelfSourceExpression.SELF()
        assert srcExpr.getType() == "self"
        assert srcExpr == SourceExpressionParser().parse("'SELF'")

    def test_parse_uri_full(self):
        exprStr = "http://seclab.nu:80/path"
        srcExpr = SourceExpressionParser().parse(exprStr)
        assert srcExpr == URISourceExpression("http", "seclab.nu", 80, "/path")
        assert srcExpr.getType() == "uri"
        assert srcExpr.getScheme() == "http"
        assert srcExpr.getHost() == "seclab.nu"
        assert srcExpr.getPort() == 80
        assert srcExpr.getPath() == "/path"
        assert str(srcExpr) == exprStr

    def test_parse_uri_scheme(self):
        exprStr = "chrome-extension:"
        srcExpr = SourceExpressionParser().parse(exprStr)
        assert srcExpr == URISourceExpression("chrome-extension", None, None,
                                              None)
        assert srcExpr.getType() == "uri"
        assert srcExpr.getScheme() == "chrome-extension"
        assert srcExpr.getHost() == None
        assert srcExpr.getPort() == None
        assert srcExpr.getPath() == None
        assert str(srcExpr) == exprStr

    def test_parse_uri_star(self):
        exprStr = "*"
        srcExpr = SourceExpressionParser().parse(exprStr)
        assert srcExpr == URISourceExpression(None, "*", None, None)

    def test_parse_uri_noscheme(self):
        exprStr = "seclab.nu"
        srcExpr = SourceExpressionParser().parse(exprStr)
        assert srcExpr == URISourceExpression(None, "seclab.nu", None, None)

    def test_parse_uri_noscheme_path(self):
        exprStr = "seclab.nu/blah"
        srcExpr = SourceExpressionParser().parse(exprStr)
        assert srcExpr == URISourceExpression(None, "seclab.nu", None, "/blah")

    def test_parse_uri_noscheme_port(self):
        exprStr = "seclab.nu:443"
        srcExpr = SourceExpressionParser().parse(exprStr)
        assert srcExpr == URISourceExpression(None, "seclab.nu", 443, None)

    def test_parse_uri_noscheme_port_path(self):
        exprStr = "seclab.nu:443/blah"
        srcExpr = SourceExpressionParser().parse(exprStr)
        assert srcExpr == URISourceExpression(None, "seclab.nu", 443, "/blah")

    def test_parse_uri_starsubdomain(self):
        exprStr = "http://*.seclab.nu"
        srcExpr = SourceExpressionParser().parse(exprStr)
        assert srcExpr == URISourceExpression("http", "*.seclab.nu", None,
                                              None)

    def test_parse_uri_nopath(self):
        exprStr = "http://localhost:8080"
        srcExpr = SourceExpressionParser().parse(exprStr)
        assert srcExpr == URISourceExpression("http", "localhost", 8080, None)

    def test_parse_uri_noport(self):
        exprStr = "https://seclab.nu/"
        srcExpr = SourceExpressionParser().parse(exprStr)
        assert srcExpr == URISourceExpression("https", "seclab.nu", None, "/")

    def test_parse_uri_scheme_star_port_path(self):
        exprStr = "http://*:80/path"
        srcExpr = SourceExpressionParser(knownSchemes=('blubb',
                                                       'http')).parse(exprStr)
        assert srcExpr == URISourceExpression("http", "*", 80, "/path")

    def test_parse_uri_scheme_starport(self):
        exprStr = "http://host:*/path"
        srcExpr = SourceExpressionParser().parse(exprStr)
        assert srcExpr == URISourceExpression("http", "host", "*", "/path")

    def test_parse_uri_decoding(self):
        exprStr = "http://domain/path%20space"
        srcExpr = SourceExpressionParser().parse(exprStr)
        assert srcExpr == URISourceExpression("http", "domain", None,
                                              "/path space")

    def test_parse_unsupported_scheme_fails(self):
        exprStr = "my-scheme://domain/path"
        srcExpr = SourceExpressionParser(knownSchemes=('http',
                                                       'https')).parse(exprStr)
        assert srcExpr == SourceExpression.INVALID()