TEST_F(ContentSecurityPolicyTest, NonceInline)
{
    struct TestCase {
        const char* policy;
        const char* nonce;
        bool allowed;
    } cases[] = {
        { "'unsafe-inline'", "", true },
        { "'unsafe-inline'", "yay", true },
        { "'nonce-yay'", "", false },
        { "'nonce-yay'", "yay", true },
        { "'unsafe-inline' 'nonce-yay'", "", false },
        { "'unsafe-inline' 'nonce-yay'", "yay", true },
    };

    String contextURL;
    String content;
    WTF::OrdinalNumber contextLine;
    for (const auto& test : cases) {
        SCOPED_TRACE(testing::Message() << "Policy: `" << test.policy << "`, Nonce: `" << test.nonce << "`");

        unsigned expectedReports = test.allowed ? 0u : 1u;

        // Enforce 'script-src'
        Persistent<ContentSecurityPolicy> policy = ContentSecurityPolicy::create();
        policy->bindToExecutionContext(document.get());
        policy->didReceiveHeader(String("script-src ") + test.policy, ContentSecurityPolicyHeaderTypeEnforce, ContentSecurityPolicyHeaderSourceHTTP);
        EXPECT_EQ(test.allowed, policy->allowInlineScript(contextURL, String(test.nonce), contextLine, content));
        EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());

        // Enforce 'style-src'
        policy = ContentSecurityPolicy::create();
        policy->bindToExecutionContext(document.get());
        policy->didReceiveHeader(String("style-src ") + test.policy, ContentSecurityPolicyHeaderTypeEnforce, ContentSecurityPolicyHeaderSourceHTTP);
        EXPECT_EQ(test.allowed, policy->allowInlineStyle(contextURL, String(test.nonce), contextLine, content));
        EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());

        // Report 'script-src'
        policy = ContentSecurityPolicy::create();
        policy->bindToExecutionContext(document.get());
        policy->didReceiveHeader(String("script-src ") + test.policy, ContentSecurityPolicyHeaderTypeReport, ContentSecurityPolicyHeaderSourceHTTP);
        EXPECT_TRUE(policy->allowInlineScript(contextURL, String(test.nonce), contextLine, content));
        EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());

        // Report 'style-src'
        policy = ContentSecurityPolicy::create();
        policy->bindToExecutionContext(document.get());
        policy->didReceiveHeader(String("style-src ") + test.policy, ContentSecurityPolicyHeaderTypeReport, ContentSecurityPolicyHeaderSourceHTTP);
        EXPECT_TRUE(policy->allowInlineStyle(contextURL, String(test.nonce), contextLine, content));
        EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());
    }
}
TEST_F(ContentSecurityPolicyTest, NonceSinglePolicy)
{
    struct TestCase {
        const char* policy;
        const char* url;
        const char* nonce;
        bool allowed;
    } cases[] = {
        { "script-src 'nonce-yay'", "https://example.com/js", "", false },
        { "script-src 'nonce-yay'", "https://example.com/js", "yay", true },
        { "script-src https://example.com", "https://example.com/js", "", true },
        { "script-src https://example.com", "https://example.com/js", "yay", true },
        { "script-src https://example.com 'nonce-yay'", "https://not.example.com/js", "", false },
        { "script-src https://example.com 'nonce-yay'", "https://not.example.com/js", "yay", true },
    };

    for (const auto& test : cases) {
        SCOPED_TRACE(testing::Message() << "Policy: `" << test.policy << "`, URL: `" << test.url << "`, Nonce: `" << test.nonce << "`");
        KURL resource = KURL(KURL(), test.url);

        unsigned expectedReports = test.allowed ? 0u : 1u;

        // Single enforce-mode policy should match `test.expected`:
        Persistent<ContentSecurityPolicy> policy = ContentSecurityPolicy::create();
        policy->bindToExecutionContext(document.get());
        policy->didReceiveHeader(test.policy, ContentSecurityPolicyHeaderTypeEnforce, ContentSecurityPolicyHeaderSourceHTTP);
        EXPECT_EQ(test.allowed, policy->allowScriptFromSource(resource, String(test.nonce)));
        // If this is expected to generate a violation, we should have sent a report.
        EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());

        // Single report-mode policy should always be `true`:
        policy = ContentSecurityPolicy::create();
        policy->bindToExecutionContext(document.get());
        policy->didReceiveHeader(test.policy, ContentSecurityPolicyHeaderTypeReport, ContentSecurityPolicyHeaderSourceHTTP);
        EXPECT_TRUE(policy->allowScriptFromSource(resource, String(test.nonce)));
        // If this is expected to generate a violation, we should have sent a report, even though
        // we don't deny access in `allowScriptFromSource`:
        EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());
    }
}
TEST_F(ContentSecurityPolicyTest, NonceMultiplePolicy)
{
    struct TestCase {
        const char* policy1;
        const char* policy2;
        const char* url;
        const char* nonce;
        bool allowed1;
        bool allowed2;
    } cases[] = {
        // Passes both:
        { "script-src 'nonce-yay'", "script-src 'nonce-yay'", "https://example.com/js", "yay", true, true },
        { "script-src https://example.com", "script-src 'nonce-yay'", "https://example.com/js", "yay", true, true },
        { "script-src 'nonce-yay'", "script-src https://example.com", "https://example.com/js", "yay", true, true },
        { "script-src https://example.com 'nonce-yay'", "script-src https://example.com 'nonce-yay'", "https://example.com/js", "yay", true, true },
        { "script-src https://example.com 'nonce-yay'", "script-src https://example.com 'nonce-yay'", "https://example.com/js", "", true, true },
        { "script-src https://example.com", "script-src https://example.com 'nonce-yay'", "https://example.com/js", "yay", true, true },
        { "script-src https://example.com 'nonce-yay'", "script-src https://example.com", "https://example.com/js", "yay", true, true },

        // Fails one:
        { "script-src 'nonce-yay'", "script-src https://example.com", "https://example.com/js", "", false, true },
        { "script-src 'nonce-yay'", "script-src 'none'", "https://example.com/js", "yay", true, false },
        { "script-src 'nonce-yay'", "script-src https://not.example.com", "https://example.com/js", "yay", true, false },

        // Fails both:
        { "script-src 'nonce-yay'", "script-src https://example.com", "https://not.example.com/js", "", false, false },
        { "script-src https://example.com", "script-src 'nonce-yay'", "https://not.example.com/js", "", false, false },
        { "script-src 'nonce-yay'", "script-src 'none'", "https://not.example.com/js", "boo", false, false },
        { "script-src 'nonce-yay'", "script-src https://not.example.com", "https://example.com/js", "", false, false },
    };

    for (const auto& test : cases) {
        SCOPED_TRACE(testing::Message() << "Policy: `" << test.policy1 << "`/`" << test.policy2 << "`, URL: `" << test.url << "`, Nonce: `" << test.nonce << "`");
        KURL resource = KURL(KURL(), test.url);

        unsigned expectedReports = test.allowed1 != test.allowed2 ? 1u : (test.allowed1 ? 0u : 2u);

        // Enforce / Report
        Persistent<ContentSecurityPolicy> policy = ContentSecurityPolicy::create();
        policy->bindToExecutionContext(document.get());
        policy->didReceiveHeader(test.policy1, ContentSecurityPolicyHeaderTypeEnforce, ContentSecurityPolicyHeaderSourceHTTP);
        policy->didReceiveHeader(test.policy2, ContentSecurityPolicyHeaderTypeReport, ContentSecurityPolicyHeaderSourceHTTP);
        EXPECT_EQ(test.allowed1, policy->allowScriptFromSource(resource, String(test.nonce)));
        EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());

        // Report / Enforce
        policy = ContentSecurityPolicy::create();
        policy->bindToExecutionContext(document.get());
        policy->didReceiveHeader(test.policy1, ContentSecurityPolicyHeaderTypeReport, ContentSecurityPolicyHeaderSourceHTTP);
        policy->didReceiveHeader(test.policy2, ContentSecurityPolicyHeaderTypeEnforce, ContentSecurityPolicyHeaderSourceHTTP);
        EXPECT_EQ(test.allowed2, policy->allowScriptFromSource(resource, String(test.nonce)));
        EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());

        // Enforce / Enforce
        policy = ContentSecurityPolicy::create();
        policy->bindToExecutionContext(document.get());
        policy->didReceiveHeader(test.policy1, ContentSecurityPolicyHeaderTypeEnforce, ContentSecurityPolicyHeaderSourceHTTP);
        policy->didReceiveHeader(test.policy2, ContentSecurityPolicyHeaderTypeEnforce, ContentSecurityPolicyHeaderSourceHTTP);
        EXPECT_EQ(test.allowed1 && test.allowed2, policy->allowScriptFromSource(resource, String(test.nonce)));
        EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());

        // Report / Report
        policy = ContentSecurityPolicy::create();
        policy->bindToExecutionContext(document.get());
        policy->didReceiveHeader(test.policy1, ContentSecurityPolicyHeaderTypeReport, ContentSecurityPolicyHeaderSourceHTTP);
        policy->didReceiveHeader(test.policy2, ContentSecurityPolicyHeaderTypeReport, ContentSecurityPolicyHeaderSourceHTTP);
        EXPECT_TRUE(policy->allowScriptFromSource(resource, String(test.nonce)));
        EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());
    }
}