blob: 557b36da38c01cf9bbafaa51697f03c9716c080d [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/network/public/cpp/integrity_policy_parser.h"
#include <optional>
#include <string>
#include <vector>
#include "base/test/scoped_feature_list.h"
#include "net/http/http_response_headers.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/integrity_policy.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network {
namespace {
struct TestCase {
std::optional<std::string> integrity_policy_header;
std::vector<mojom::IntegrityPolicy_Destination> blocked_destinations;
std::vector<mojom::IntegrityPolicy_Source> sources;
std::vector<std::string> endpoints;
std::vector<std::string> parsing_errors;
IntegrityPolicyHeaderType type = IntegrityPolicyHeaderType::kEnforce;
};
} // namespace
TEST(IntegrityPolicyParserTest, Parse) {
base::test::ScopedFeatureList scoped_feature;
scoped_feature.InitAndEnableFeature(features::kIntegrityPolicyScript);
const auto kNoHeader = std::optional<std::string>();
const auto kEmptyHeader = std::string();
const auto kEmptyDestination =
std::vector<mojom::IntegrityPolicy_Destination>();
const auto kScriptDestination =
std::vector<mojom::IntegrityPolicy_Destination>(
{mojom::IntegrityPolicy_Destination::kScript});
const auto kEmptySource = std::vector<mojom::IntegrityPolicy_Source>();
const auto kInlineSource = std::vector<mojom::IntegrityPolicy_Source>(
{mojom::IntegrityPolicy_Source::kInline});
const auto kEmptyVectorString = std::vector<std::string>();
TestCase test_cases[] = {
{kNoHeader, kEmptyDestination, kEmptySource, kEmptyVectorString,
kEmptyVectorString},
{kEmptyHeader, kEmptyDestination, kEmptySource, kEmptyVectorString,
kEmptyVectorString},
{"not a dictionary",
kEmptyDestination,
kEmptySource,
kEmptyVectorString,
{"The Integrity-Policy value \"not a dictionary\" is not a "
"dictionary."}},
{"not a dictionary",
kEmptyDestination,
kEmptySource,
kEmptyVectorString,
{"The Integrity-Policy-Report-Only value \"not a dictionary\" is not a "
"dictionary."},
IntegrityPolicyHeaderType::kReportOnly},
{"wrongkey=(something)",
kEmptyDestination,
kInlineSource,
kEmptyVectorString,
{"Unrecognized wrongkey in Integrity-Policy header."}},
{"wrongkey=(something), blocked-destinations=(script)",
kScriptDestination,
kInlineSource,
kEmptyVectorString,
{"Unrecognized wrongkey in Integrity-Policy header."}},
{"wrongkey=(something), blocked-destinations=(wrongdestination script)",
kScriptDestination,
kInlineSource,
kEmptyVectorString,
{"Unrecognized wrongkey in Integrity-Policy header.",
"The Integrity-Policy destination 'wrongdestination' is not "
"supported."}},
{"blocked-destinations=(unsupported)",
kEmptyDestination,
kInlineSource,
kEmptyVectorString,
{"The Integrity-Policy destination 'unsupported' is not supported."}},
{"sources=(inline)", kEmptyDestination, kInlineSource, kEmptyVectorString,
kEmptyVectorString},
{"blocked-destinations=(script)", kScriptDestination, kInlineSource,
kEmptyVectorString, kEmptyVectorString},
{"blocked-destinations=(script), sources=(inline)", kScriptDestination,
kInlineSource, kEmptyVectorString, kEmptyVectorString},
{"blocked-destinations=(script), sources=(inline other)",
kScriptDestination,
kInlineSource,
kEmptyVectorString,
{"The Integrity-Policy source 'other' is not supported."}},
{"blocked-destinations=(script), sources=(other)",
kScriptDestination,
kEmptySource,
kEmptyVectorString,
{"The Integrity-Policy source 'other' is not supported."}},
{"endpoints=(https://example.com/api)",
kEmptyDestination,
kInlineSource,
{"https://example.com/api"},
kEmptyVectorString},
{"sources=(inline), blocked-destinations=(script), "
"endpoints=(https://example.com/api)",
kScriptDestination,
kInlineSource,
{"https://example.com/api"},
kEmptyVectorString},
{"blocked-destinations=(script), endpoints=(endpoint1 endpoint2)",
kScriptDestination,
kInlineSource,
{"endpoint1", "endpoint2"},
kEmptyVectorString},
{"blocked-destinations=(script), endpoints=(invalid1, invalid2)",
kEmptyDestination,
kEmptySource,
kEmptyVectorString,
{"The Integrity-Policy value \"blocked-destinations=(script), "
"endpoints=(invalid1, invalid2)\" is not a dictionary."}},
{"sources=(other), blocked-destinations=(script)",
kScriptDestination,
kEmptySource,
kEmptyVectorString,
{"The Integrity-Policy source 'other' is not supported."}},
{"sources=(inline), blocked-destinations=[], endpoints=[]",
kEmptyDestination,
kEmptySource,
kEmptyVectorString,
{"The Integrity-Policy value \"sources=(inline), "
"blocked-destinations=[], endpoints=[]\" is not a dictionary."}},
};
for (const TestCase& test_case : test_cases) {
auto headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK\n");
std::string header_value;
if (test_case.integrity_policy_header) {
if (test_case.type == IntegrityPolicyHeaderType::kEnforce) {
headers->AddHeader("Integrity-Policy",
*test_case.integrity_policy_header);
} else {
headers->AddHeader("Integrity-Policy-Report-Only",
*test_case.integrity_policy_header);
}
header_value += *test_case.integrity_policy_header;
}
auto message = testing::Message() << "header: " << header_value;
SCOPED_TRACE(message);
const auto policy =
ParseIntegrityPolicyFromHeaders(*headers, test_case.type);
EXPECT_EQ(policy.blocked_destinations, test_case.blocked_destinations);
EXPECT_EQ(policy.sources, test_case.sources);
EXPECT_EQ(policy.endpoints, test_case.endpoints);
EXPECT_EQ(policy.parsing_errors, test_case.parsing_errors);
}
}
} // namespace network