Chromium Code Reviews| Index: net/tools/transport_security_state_generator/input_file_parsers_unittest.cc |
| diff --git a/net/tools/transport_security_state_generator/input_file_parsers_unittest.cc b/net/tools/transport_security_state_generator/input_file_parsers_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..0e3cff0a1f95821f871cc2d36ea0f136117ffc13 |
| --- /dev/null |
| +++ b/net/tools/transport_security_state_generator/input_file_parsers_unittest.cc |
| @@ -0,0 +1,316 @@ |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include <string> |
| + |
| +#include "net/tools/transport_security_state_generator/input_file_parsers.h" |
| +#include "net/tools/transport_security_state_generator/pinsets.h" |
| +#include "net/tools/transport_security_state_generator/transport_security_state_entry.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace net { |
| + |
| +namespace transport_security_state { |
| + |
| +namespace { |
| + |
| +// Test that all values are correctly parsed from a valid JSON input. |
| +TEST(InputFileParsersTest, ParseJSON) { |
| + std::string valid = |
| + "{" |
| + " \"pinsets\": [{" |
| + " \"name\": \"test\"," |
| + " \"static_spki_hashes\": [\"TestSPKI\"]," |
| + " \"bad_static_spki_hashes\": [\"BadTestSPKI\"]," |
| + " \"report_uri\": \"https://hpkp-log.example.com\"" |
| + " }]," |
| + " \"entries\": [" |
| + " {" |
| + " \"name\": \"hsts.example.com\"," |
| + " \"mode\": \"force-https\", " |
| + " \"include_subdomains\": true" |
| + " }, {" |
| + " \"name\": \"hpkp.example.com\"," |
| + " \"pins\": \"thepinset\"," |
| + " \"include_subdomains_for_pinning\": true" |
| + " }, {" |
| + " \"name\": \"expect-ct.example.com\"," |
| + " \"expect_ct\": true," |
| + " \"expect_ct_report_uri\": \"https://expect-ct-log.example.com\"" |
| + " }, {" |
| + " \"name\": \"expect-staple.example.com\"," |
| + " \"expect_staple\": true," |
| + " \"expect_staple_report_uri\": " |
| + "\"https://expect-staple-log.example.com\"," |
| + " \"include_subdomains_for_expect_staple\": true" |
|
davidben
2017/04/03 20:53:02
Think it's worth including non-include_subdomains
martijnc
2017/04/04 18:31:10
Added.
|
| + " }" |
| + " ]," |
| + " \"domain_ids\": [" |
| + " \"NOT_PINNED\"," |
| + " \"EXAMPLE_COM\"" |
| + " ]" |
| + "}"; |
| + |
| + TransportSecurityStateEntries entries; |
| + Pinsets pinsets; |
| + DomainIDList domain_ids; |
| + |
| + EXPECT_TRUE(ParseJSON(valid, &entries, &pinsets, &domain_ids)); |
| + |
| + ASSERT_EQ(1U, pinsets.size()); |
| + PinsetMap::const_iterator pinset = pinsets.pinsets().find("test"); |
| + ASSERT_NE(pinset, pinsets.pinsets().cend()); |
| + EXPECT_EQ("test", pinset->second->name()); |
| + EXPECT_EQ("https://hpkp-log.example.com", pinset->second->report_uri()); |
| + |
| + ASSERT_EQ(1U, pinset->second->static_spki_hashes().size()); |
| + EXPECT_EQ("TestSPKI", pinset->second->static_spki_hashes()[0]); |
| + |
| + ASSERT_EQ(1U, pinset->second->bad_static_spki_hashes().size()); |
| + EXPECT_EQ("BadTestSPKI", pinset->second->bad_static_spki_hashes()[0]); |
| + |
| + ASSERT_EQ(4U, entries.size()); |
| + TransportSecurityStateEntry* entry = entries[0].get(); |
| + EXPECT_EQ("hsts.example.com", entry->hostname); |
|
davidben
2017/04/03 20:53:02
EXPECT_TRUE(entry->include_subdomains)?
martijnc
2017/04/04 18:31:10
Added.
|
| + EXPECT_TRUE(entry->force_https); |
| + EXPECT_FALSE(entry->hpkp_include_subdomains); |
| + EXPECT_EQ("", entry->pinset); |
| + EXPECT_FALSE(entry->expect_ct); |
| + EXPECT_EQ("", entry->expect_ct_report_uri); |
| + EXPECT_FALSE(entry->expect_staple); |
| + EXPECT_FALSE(entry->expect_staple_include_subdomains); |
| + EXPECT_EQ("", entry->expect_staple_report_uri); |
| + |
| + entry = entries[1].get(); |
| + EXPECT_EQ("hpkp.example.com", entry->hostname); |
|
davidben
2017/04/03 20:53:02
EXPECT_FALSE(entry->include_subdomains)? (Ditto fo
martijnc
2017/04/04 18:31:10
Added.
|
| + EXPECT_FALSE(entry->force_https); |
| + EXPECT_TRUE(entry->hpkp_include_subdomains); |
| + EXPECT_EQ("thepinset", entry->pinset); |
| + EXPECT_FALSE(entry->expect_ct); |
| + EXPECT_EQ("", entry->expect_ct_report_uri); |
| + EXPECT_FALSE(entry->expect_staple); |
| + EXPECT_FALSE(entry->expect_staple_include_subdomains); |
| + EXPECT_EQ("", entry->expect_staple_report_uri); |
| + |
| + entry = entries[2].get(); |
| + EXPECT_EQ("expect-ct.example.com", entry->hostname); |
| + EXPECT_FALSE(entry->force_https); |
| + EXPECT_FALSE(entry->hpkp_include_subdomains); |
| + EXPECT_EQ("", entry->pinset); |
| + EXPECT_TRUE(entry->expect_ct); |
| + EXPECT_EQ("https://expect-ct-log.example.com", entry->expect_ct_report_uri); |
| + EXPECT_FALSE(entry->expect_staple); |
| + EXPECT_FALSE(entry->expect_staple_include_subdomains); |
| + EXPECT_EQ("", entry->expect_staple_report_uri); |
| + |
| + entry = entries[3].get(); |
| + EXPECT_EQ("expect-staple.example.com", entry->hostname); |
| + EXPECT_FALSE(entry->force_https); |
| + EXPECT_FALSE(entry->hpkp_include_subdomains); |
| + EXPECT_EQ("", entry->pinset); |
| + EXPECT_FALSE(entry->expect_ct); |
| + EXPECT_EQ("", entry->expect_ct_report_uri); |
| + EXPECT_TRUE(entry->expect_staple); |
| + EXPECT_TRUE(entry->expect_staple_include_subdomains); |
| + EXPECT_EQ("https://expect-staple-log.example.com", |
| + entry->expect_staple_report_uri); |
| + |
| + ASSERT_EQ(2U, domain_ids.size()); |
| + EXPECT_EQ("NOT_PINNED", domain_ids[0]); |
| + EXPECT_EQ("EXAMPLE_COM", domain_ids[1]); |
| +} |
| + |
| +// Test that parsing valid JSON with missing keys fails. |
| +TEST(InputFileParsersTest, ParseJSONInvalid) { |
| + TransportSecurityStateEntries entries; |
| + Pinsets pinsets; |
| + DomainIDList domain_ids; |
| + |
| + std::string no_pinsets = |
| + "{" |
| + " \"entries\": []," |
| + " \"domain_ids\": []" |
| + "}"; |
| + |
| + EXPECT_FALSE(ParseJSON(no_pinsets, &entries, &pinsets, &domain_ids)); |
| + |
| + std::string no_entries = |
| + "{" |
| + " \"pinsets\": []," |
| + " \"domain_ids\": []" |
| + "}"; |
| + |
| + EXPECT_FALSE(ParseJSON(no_entries, &entries, &pinsets, &domain_ids)); |
| + |
| + std::string no_domain_ids = |
| + "{" |
| + " \"pinsets\": []," |
| + " \"entries\": []" |
| + "}\n"; |
| + |
| + EXPECT_FALSE(ParseJSON(no_domain_ids, &entries, &pinsets, &domain_ids)); |
| + |
| + std::string missing_hostname = |
| + "{" |
| + " \"pinsets\": []," |
| + " \"entries\": [" |
| + " {" |
| + " \"mode\": \"force-https\"" |
| + " }" |
| + " ]," |
| + " \"domain_ids\": []" |
| + "}"; |
| + |
| + EXPECT_FALSE(ParseJSON(missing_hostname, &entries, &pinsets, &domain_ids)); |
| +} |
| + |
| +// Test that parsing valid JSON with an invalid (HPKP) pinset fails. |
| +TEST(InputFileParsersTest, ParseJSONInvalidPinset) { |
| + TransportSecurityStateEntries entries; |
| + Pinsets pinsets; |
| + DomainIDList domain_ids; |
| + |
| + std::string missing_pinset_name = |
| + "{" |
| + " \"pinsets\": [{" |
| + " \"static_spki_hashes\": [\"TestSPKI\"]," |
| + " \"bad_static_spki_hashes\": [\"BadTestSPKI\"]," |
| + " \"report_uri\": \"https://hpkp-log.example.com\"" |
| + " }]," |
| + " \"entries\": []," |
| + " \"domain_ids\": []" |
| + "}"; |
| + |
| + EXPECT_FALSE(ParseJSON(missing_pinset_name, &entries, &pinsets, &domain_ids)); |
| +} |
| + |
| +// Test parsing of all 3 SPKI formats. |
| +TEST(InputFileParsersTest, ParseCertificatesFile) { |
| + std::string valid = |
| + "# This line should ignored. The rest should result in 3 pins.\n" |
| + "TestPublicKey1\n" |
| + "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" |
| + "\n" |
| + "TestPublicKey2\n" |
| + "-----BEGIN PUBLIC KEY-----\n" |
| + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAujzwcb5bJuC/A/Y9izGl\n" |
| + "LlA3fnKGbeyn53BdVznJN4fQwU82WKVYdqt8d/1ZDRdYyhGrTgXJeCURe9VSJyX1\n" |
| + "X2a5EApSFsopP8Yjy0Rl6dNOLO84KCW9dPmfHC3uP0ac4hnHT5dUr05YvhJmHCkf\n" |
| + "as6v/aEgpPLDhRF6UruSUh+gIpUg/F3+vlD99HLfbloukoDtQyxW+86s9sO7RQ00\n" |
| + "pd79VOoa/v09FvoS7MFgnBBOtvBQLOXjEH7/qBsnrXFtHBeOtxSLar/FL3OhVXuh\n" |
| + "dUTRyc1Mg0ECtz8zHZugW+LleIm5Bf5Yr0bN1O/HfDPCkDaCldcm6xohEHn9pBaW\n" |
| + "+wIDAQAB\n" |
| + "-----END PUBLIC KEY-----\n" |
| + "\n" |
| + "# The 'Chromium' prefix is required here.\n" |
| + "ChromiumTestCertificate3\n" |
| + "-----BEGIN CERTIFICATE-----\n" |
| + "MIIDeTCCAmGgAwIBAgIJAMRHXuiAgufAMA0GCSqGSIb3DQEBCwUAMFMxETAPBgNV\n" |
| + "BAMMCENocm9taXVtMR4wHAYDVQQKDBVUaGUgQ2hyb21pdW0gUHJvamVjdHMxETAP\n" |
| + "BgNVBAsMCFNlY3VyaXR5MQswCQYDVQQGEwJVUzAeFw0xNzAyMDExOTAyMzFaFw0x\n" |
| + "ODAyMDExOTAyMzFaMFMxETAPBgNVBAMMCENocm9taXVtMR4wHAYDVQQKDBVUaGUg\n" |
| + "Q2hyb21pdW0gUHJvamVjdHMxETAPBgNVBAsMCFNlY3VyaXR5MQswCQYDVQQGEwJV\n" |
| + "UzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALtggpf5vNVsmQrJKTQe\n" |
| + "ynTeOzVOyROGDugGtR+Cri8WlNg1UAlIyYIS8txZ4oCknsT8gs3TFfu0wxmWNxx5\n" |
| + "4oLGy2BQOHH00dgBAsKgqX//mY4mH5AZ85UFYni1hj9aszIJMIBWtgbNGVkppW65\n" |
| + "8maF1KVdHmxXMvtKxn/9UsusH/A0ng5UJDYBPISQMv0XqIlv0wdVTIVWIcQhOjWz\n" |
| + "MGwFDSjxS1WgEnPgd4Qi7MYaDbUTsXGtWba83vZJ8CQzjLumSJJCnz2aquGmraX0\n" |
| + "J0joUjB4fuYL8xrbDqnFmADvozMMVkZ4843w8ikvJkM8nWoIXexVvirfXDoqtdUo\n" |
| + "YOcCAwEAAaNQME4wHQYDVR0OBBYEFGJ6O/oLtzpb4OWvrEFxieYb1JbsMB8GA1Ud\n" |
| + "IwQYMBaAFGJ6O/oLtzpb4OWvrEFxieYb1JbsMAwGA1UdEwQFMAMBAf8wDQYJKoZI\n" |
| + "hvcNAQELBQADggEBAFpt9jlBT6OsfKFAJZnmExbW8JlsqXOJAaR+nD1XOnp6o+DM\n" |
| + "NIguj9+wJOW34OM+2Om0n+KMYbDER0p4g3gxoaDblu7otgnC0OTOnx3DPUYab0jr\n" |
| + "uT6O4C3/nfWW5sl3Ni3Y99dmdcqKcmYkHsr7uADLPWsjb+sfUrQQfHHnPwzyUz/A\n" |
| + "w4rSJ0wxnLOmjk5F5YHMLkNpPrzFA1mFyGIau7THsRIr3B632MLNcOlNR21nOc7i\n" |
| + "eB4u+OzpcZXuiQg3bqrNp6Xb70OIW1rfNEiCpps4UZyRnZ/nrzByxeHH5zPWWZk9\n" |
| + "nZtxI+65PFOekOjBpbnRC8v1CfOmUSVKIqWaPys=\n" |
| + "-----END CERTIFICATE-----"; |
| + |
| + Pinsets pinsets; |
| + EXPECT_TRUE(ParseCertificatesFile(valid, &pinsets)); |
| + EXPECT_EQ(3U, pinsets.spki_size()); |
| + |
| + const SPKIHashMap& hashes = pinsets.spki_hashes(); |
| + EXPECT_NE(hashes.cend(), hashes.find("TestPublicKey1")); |
| + EXPECT_NE(hashes.cend(), hashes.find("TestPublicKey2")); |
| + EXPECT_NE(hashes.cend(), hashes.find("ChromiumTestCertificate3")); |
| +} |
| + |
| +TEST(InputFileParsersTest, ParseCertificatesFileInvalid) { |
| + Pinsets pinsets; |
| + |
| + std::string invalid = |
| + "TestName\n" |
| + "unexpected"; |
| + EXPECT_FALSE(ParseCertificatesFile(invalid, &pinsets)); |
| +} |
| + |
| +// Test that parsing invalid certificate names fails. |
| +TEST(InputFileParsersTest, ParseCertificatesFileInvalidName) { |
| + Pinsets pinsets; |
| + |
| + std::string invalid_name_small_character = |
| + "startsWithSmallLetter\n" |
| + "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n"; |
| + EXPECT_FALSE(ParseCertificatesFile(invalid_name_small_character, &pinsets)); |
| + |
| + std::string invalid_name_invalid_characters = |
| + "Invalid-Characters-In-Name\n" |
| + "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n"; |
| + EXPECT_FALSE( |
| + ParseCertificatesFile(invalid_name_invalid_characters, &pinsets)); |
| + |
| + std::string invalid_name_number = |
| + "1InvalidName\n" |
| + "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n"; |
| + EXPECT_FALSE(ParseCertificatesFile(invalid_name_number, &pinsets)); |
| + |
| + std::string invalid_name_space = |
| + "Invalid Name\n" |
| + "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n"; |
| + EXPECT_FALSE(ParseCertificatesFile(invalid_name_space, &pinsets)); |
| +} |
| + |
| +// Test that parsing of a certificate with an incomplete or incorrect name |
| +// fails. |
| +TEST(InputFileParsersTest, ParseCertificatesFileInvalidCertificateName) { |
| + Pinsets pinsets; |
| + std::string certificate = |
| + "-----BEGIN CERTIFICATE-----\n" |
| + "MIIDIzCCAgugAwIBAgIJALs84KlxWh4GMA0GCSqGSIb3DQEBCwUAMCgxGTAXBgNV\n" |
| + "BAoMEENocm9taXVtIENsYXNzIDMxCzAJBgNVBAsMAkcxMB4XDTE3MDIwMTE5NTUw\n" |
| + "NVoXDTE4MDIwMTE5NTUwNVowKDEZMBcGA1UECgwQQ2hyb21pdW0gQ2xhc3MgMzEL\n" |
| + "MAkGA1UECwwCRzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkolrR\n" |
| + "7gCPm22Cc9psS2Jh1mksVneee5ntEezZ2gEU20y9Z9URBReo8SFvaZcgKkAkca1v\n" |
| + "552YIG+FBO/u8njxzlHXvuVJ5x2geciqqR4TRhA4jO1ndrNW6nlJfOoYueWbdym3\n" |
| + "8zwugoULoCtyLyzdiMI5g8iVBQHDh8+K3TZIHar3HS49TjX5u5nv4igO4RfDcFUa\n" |
| + "h8g+6x5nWoFF8oa3FG0YTN+q6iI1i2JHmj/q03fVPv3WLPGJ3JADau9gO1Lw1/qf\n" |
| + "R/N3l4MVtjDFFGYzclfqW2UmL6zRirEV0GF2gwSBAGVX3WWhpOcM8rFIWYkZCsI5\n" |
| + "iUdtwFNBfcKS9sNpAgMBAAGjUDBOMB0GA1UdDgQWBBTm4VJfibducqwb9h4XELn3\n" |
| + "p6zLVzAfBgNVHSMEGDAWgBTm4VJfibducqwb9h4XELn3p6zLVzAMBgNVHRMEBTAD\n" |
| + "AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQApTm40RfsZG20IIgWJ62pZ2end/lvaneTh\n" |
| + "MZSgFnoTRjKkd/5dh22YyKPw9PnpIuiyi85L36COreqZUvbxqRQnpL1oSCRlLBJQ\n" |
| + "2LcGlF0j0Opa+SY2VWup4XjnYF8CvwMl4obNpSuywTFmkXCRxzN23tn8whNHvWHM\n" |
| + "BQ7abw8X1KY02uPbHucrpou6KXkKkhyhfML8OD8IRkSM56K6YyedqV97cmEdW0Ie\n" |
| + "LlpFJQVX13bmojtSNI1zaiCiEenn5xLa/dAlyFT18Mq6y8plioBinVWFYd0qcRoA\n" |
| + "E2j3m+jTVIv3CZ+ivGxggZQ8ZYN8FJ/iTW3pXGojogHh0NRJJ8dM\n" |
| + "-----END CERTIFICATE-----"; |
| + |
| + std::string missing_prefix = "Class3_G1_Test\n" + certificate; |
| + EXPECT_FALSE(ParseCertificatesFile(missing_prefix, &pinsets)); |
| + |
| + std::string missing_class = "Chromium_G1_Test\n" + certificate; |
| + EXPECT_FALSE(ParseCertificatesFile(missing_class, &pinsets)); |
| + |
| + std::string missing_number = "Chromium_Class3_Test\n" + certificate; |
| + EXPECT_FALSE(ParseCertificatesFile(missing_number, &pinsets)); |
| + |
| + std::string valid = "Chromium_Class3_G1_Test\n" + certificate; |
| + EXPECT_TRUE(ParseCertificatesFile(valid, &pinsets)); |
| +} |
| +} |
|
davidben
2017/04/03 20:53:02
Nit: newline above this and a "// namespace" comme
martijnc
2017/04/04 18:31:10
Done.
|
| + |
| +} // namespace transport_security_state |
| + |
| +} // namespace net |