// Copyright 2015 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 "components/feedback/redaction_tool.h" #include #include #include #include "base/strings/string_util.h" #include "build/chromeos_buildflags.h" #include "components/feedback/pii_types.h" namespace feedback { const char kFakeFirstPartyID[] = "nkoccljplnhpfnfiajclkommnmllphnl"; const char* const kFakeFirstPartyExtensionIDs[] = {kFakeFirstPartyID, nullptr}; struct StringWithRedaction { // The raw version of the string before redaction. May contain PII sensitive // data. std::string pre_redaction; // The string that's redacted of PII sensitive data. std::string post_redaction; // The PII type that string contains. PIIType::kNone if the string doesn't // contain any PII sensitive data. PIIType pii_type; }; // For better readability, put all the pre/post redaction strings in an array of // StringWithRedaction struct, and then convert that to two strings which become // the input and output of the redactor. const StringWithRedaction kStringsWithRedactions[] = { {"aaaaaaaa [SSID=123aaaaaa]aaaaa", // SSID. "aaaaaaaa [SSID=]aaaaa", PIIType::kSSID}, {"aaaaaaaahttp://tets.comaaaaaaa", // URL. "aaaaaaaa", PIIType::kURL}, {"u:object_r:system_data_file:s0:c512,c768", // No PII, it is an SELinux // context. "u:object_r:system_data_file:s0:c512,c768", PIIType::kNone}, {"aaaaaemail@example.comaaa", // Email address. "", PIIType::kEmail}, {"example@@1234", // No PII, it is not a valid email address. "example@@1234", PIIType::kNone}, {"255.255.155.2", // IP address. "", PIIType::kIPAddress}, {"255.255.155.255", // IP address. "", PIIType::kIPAddress}, {"127.0.0.1", // IPv4 loopback. "<127.0.0.0/8: 3>", PIIType::kIPAddress}, {"127.255.0.1", // IPv4 loopback. "<127.0.0.0/8: 4>", PIIType::kIPAddress}, {"0.0.0.0", // Any IPv4. "<0.0.0.0/8: 5>", PIIType::kIPAddress}, {"0.255.255.255", // Any IPv4. "<0.0.0.0/8: 6>", PIIType::kIPAddress}, {"10.10.10.100", // IPv4 private class A. "<10.0.0.0/8: 7>", PIIType::kIPAddress}, {"10.10.10.100", // Intentional duplicate. "<10.0.0.0/8: 7>", PIIType::kIPAddress}, {"10.10.10.101", // IPv4 private class A. "<10.0.0.0/8: 8>", PIIType::kIPAddress}, {"10.255.255.255", // IPv4 private class A. "<10.0.0.0/8: 9>", PIIType::kIPAddress}, {"172.16.0.0", // IPv4 private class B. "<172.16.0.0/12: 10>", PIIType::kIPAddress}, {"172.31.255.255", // IPv4 private class B. "<172.16.0.0/12: 11>", PIIType::kIPAddress}, {"172.11.5.5", // IP address. "", PIIType::kIPAddress}, {"172.111.5.5", // IP address. "", PIIType::kIPAddress}, {"192.168.0.0", // IPv4 private class C. "<192.168.0.0/16: 14>", PIIType::kIPAddress}, {"192.168.255.255", // IPv4 private class C. "<192.168.0.0/16: 15>", PIIType::kIPAddress}, {"192.169.2.120", // IP address. "", PIIType::kIPAddress}, {"169.254.0.1", // Link local. "<169.254.0.0/16: 17>", PIIType::kIPAddress}, {"169.200.0.1", // IP address. "", PIIType::kIPAddress}, {"fe80::", // Link local. "", PIIType::kIPAddress}, {"fe80::ffff", // Link local. "", PIIType::kIPAddress}, {"febf:ffff::ffff", // Link local. "", PIIType::kIPAddress}, {"fecc::1111", // IP address. "", PIIType::kIPAddress}, {"224.0.0.24", // Multicast. "<224.0.0.0/4: 19>", PIIType::kIPAddress}, {"240.0.0.0", // IP address. "", PIIType::kIPAddress}, {"255.255.255.255", // Broadcast. "255.255.255.255", PIIType::kNone}, {"100.115.92.92", // ChromeOS. "100.115.92.92", PIIType::kNone}, {"100.115.91.92", // IP address. "", PIIType::kIPAddress}, {"1.1.1.1", // DNS "1.1.1.1", PIIType::kNone}, {"8.8.8.8", // DNS "8.8.8.8", PIIType::kNone}, {"8.8.4.4", // DNS "8.8.4.4", PIIType::kNone}, {"8.8.8.4", // IP address. "", PIIType::kIPAddress}, {"255.255.259.255", // Not an IP address. "255.255.259.255", PIIType::kNone}, {"255.300.255.255", // Not an IP address. "255.300.255.255", PIIType::kNone}, {"3-1.2.3.4", // USB path, not an IP address. "3-1.2.3.4", PIIType::kNone}, {"Revision: 81600.0000.00.29.19.16_DO", // Modem firmware "Revision: 81600.0000.00.29.19.16_DO", PIIType::kNone}, {"aaaa123.123.45.4aaa", // IP address. "aaaaaaa", PIIType::kIPAddress}, {"11:11;11::11", // IP address. "11:11;", PIIType::kIPAddress}, {"11::11", // IP address. "", PIIType::kIPAddress}, {"11:11:abcdef:0:0:0:0:0", // No PII. "11:11:abcdef:0:0:0:0:0", PIIType::kNone}, {"::", // Unspecified. "::", PIIType::kNone}, {"::1", // Local host. "::1", PIIType::kNone}, {"Instance::Set", // Ignore match, no PII. "Instance::Set", PIIType::kNone}, {"Instant::ff", // Ignore match, no PII. "Instant::ff", PIIType::kNone}, {"net::ERR_CONN_TIMEOUT", // Ignore match, no PII. "net::ERR_CONN_TIMEOUT", PIIType::kNone}, {"ff01::1", // All nodes address (interface local). "ff01::1", PIIType::kNone}, {"ff01::2", // All routers (interface local). "ff01::2", PIIType::kNone}, {"ff01::3", // Multicast (interface local). "", PIIType::kIPAddress}, {"ff02::1", // All nodes address (link local). "ff02::1", PIIType::kNone}, {"ff02::2", // All routers (link local). "ff02::2", PIIType::kNone}, {"ff02::3", // Multicast (link local). "", PIIType::kIPAddress}, {"ff02::fb", // mDNSv6 (link local). "", PIIType::kIPAddress}, {"ff08::fb", // mDNSv6. "", PIIType::kIPAddress}, {"ff0f::101", // All NTP servers. "", PIIType::kIPAddress}, {"::ffff:cb0c:10ea", // IPv4-mapped IPV6 (IP address). "", PIIType::kIPAddress}, {"::ffff:a0a:a0a", // IPv4-mapped IPV6 (private class A). "", PIIType::kIPAddress}, {"::ffff:a0a:a0a", // Intentional duplicate. "", PIIType::kIPAddress}, {"::ffff:ac1e:1e1e", // IPv4-mapped IPV6 (private class B). "", PIIType::kIPAddress}, {"::ffff:c0a8:640a", // IPv4-mapped IPV6 (private class C). "", PIIType::kIPAddress}, {"::ffff:6473:5c01", // IPv4-mapped IPV6 (Chrome). "", PIIType::kIPAddress}, {"64:ff9b::a0a:a0a", // IPv4-translated 6to4 IPV6 (private class A). "", PIIType::kIPAddress}, {"64:ff9b::6473:5c01", // IPv4-translated 6to4 IPV6 (Chrome). "", PIIType::kIPAddress}, {"::0101:ffff:c0a8:640a", // IP address. "", PIIType::kIPAddress}, {"aa:aa:aa:aa:aa:aa", // MAC address (BSSID). "[MAC OUI=aa:aa:aa IFACE=1]", PIIType::kMACAddress}, {"chrome://resources/foo", // Secure chrome resource, exempt. "chrome://resources/foo", PIIType::kNone}, {"chrome://settings/crisper.js", // Exempt settings URLs. "chrome://settings/crisper.js", PIIType::kNone}, // Exempt first party extension. {"chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js", "chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js", PIIType::kNone}, {"chrome://resources/f?user=bar", // Potentially PII in parameter. "", PIIType::kURL}, {"chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js?bar=x", "", PIIType::kURL}, // Potentially PII in parameter. {"/root/27540283740a0897ab7c8de0f809add2bacde78f/foo", "/root//foo", PIIType::kStableIdentifier}, // Hash string. {"B3mcFTkQAHofv94DDTUuVJGGEI/BbzsyDncplMCR2P4=", "", PIIType::kStableIdentifier}, #if BUILDFLAG(IS_CHROMEOS_ASH) // We only redact Android paths on Chrome OS. // Allowed android storage path. {"112K\t/home/root/deadbeef1234/android-data/data/system_de", "112K\t/home/root/deadbeef1234/android-data/data/system_de", PIIType::kNone}, // Redacted app-specific storage path. {"8.0K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/de", "8.0K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/d_", PIIType::kAndroidAppStoragePath}, #endif // BUILDFLAG(IS_CHROMEOS_ASH) }; class RedactionToolTest : public testing::Test { protected: std::string RedactMACAddresses(const std::string& input) { return redactor_.RedactMACAddresses(input, nullptr); } std::string RedactHashes(const std::string& input) { return redactor_.RedactHashes(input, nullptr); } std::string RedactAndroidAppStoragePaths(const std::string& input) { return redactor_.RedactAndroidAppStoragePaths(input, nullptr); } std::string RedactCustomPatterns(const std::string& input) { return redactor_.RedactAndKeepSelectedCustomPatterns( input, /*pii_types_to_keep=*/{}); } std::string RedactCustomPatternWithContext( const std::string& input, const CustomPatternWithAlias& pattern) { return redactor_.RedactCustomPatternWithContext(input, pattern, nullptr); } std::string RedactCustomPatternWithoutContext( const std::string& input, const CustomPatternWithAlias& pattern) { return redactor_.RedactCustomPatternWithoutContext(input, pattern, nullptr); } RedactionTool redactor_{kFakeFirstPartyExtensionIDs}; }; TEST_F(RedactionToolTest, Redact) { EXPECT_EQ("", redactor_.Redact("")); EXPECT_EQ("foo\nbar\n", redactor_.Redact("foo\nbar\n")); // Make sure MAC address redaction is invoked. EXPECT_EQ("[MAC OUI=02:46:8a IFACE=1]", redactor_.Redact("02:46:8a:ce:13:57")); // Make sure hash redaction is invoked. EXPECT_EQ("", redactor_.Redact("11223344556677889900AABBCCDDEEFF")); // Make sure custom pattern redaction is invoked. EXPECT_EQ("Cell ID: ''", RedactCustomPatterns("Cell ID: 'A1B2'")); // Make sure UUIDs are redacted. EXPECT_EQ( "REQUEST localhost - - \"POST /printers/ HTTP/1.1\" 200 291 " "Create-Job successful-ok", redactor_.Redact( "REQUEST localhost - - \"POST /printers/" "cb738a9f-6433-4d95-a81e-94e4ae0ed30b HTTP/1.1\" 200 291 Create-Job " "successful-ok")); EXPECT_EQ( "REQUEST localhost - - \"POST /printers/ HTTP/1.1\" 200 286 " "Create-Job successful-ok", redactor_.Redact( "REQUEST localhost - - \"POST /printers/" "d17188da-9cd3-44f4-b148-3e1d748a3b0f HTTP/1.1\" 200 286 Create-Job " "successful-ok")); } TEST_F(RedactionToolTest, RedactMACAddresses) { EXPECT_EQ("", RedactMACAddresses("")); EXPECT_EQ("foo\nbar\n", RedactMACAddresses("foo\nbar\n")); EXPECT_EQ("11:22:33:44:55", RedactMACAddresses("11:22:33:44:55")); EXPECT_EQ("[MAC OUI=aa:bb:cc IFACE=1]", RedactMACAddresses("aa:bb:cc:dd:ee:ff")); EXPECT_EQ("[MAC OUI=aa:bb:cc IFACE=1]", RedactMACAddresses("aa_bb_cc_dd_ee_ff")); EXPECT_EQ("[MAC OUI=aa:bb:cc IFACE=1]", RedactMACAddresses("aa-bb-cc-dd-ee-ff")); EXPECT_EQ("00:00:00:00:00:00", RedactMACAddresses("00:00:00:00:00:00")); EXPECT_EQ("ff:ff:ff:ff:ff:ff", RedactMACAddresses("ff:ff:ff:ff:ff:ff")); EXPECT_EQ( "BSSID: [MAC OUI=aa:bb:cc IFACE=1] in the middle\n" "[MAC OUI=bb:cc:dd IFACE=2] start of line\n" "end of line [MAC OUI=aa:bb:cc IFACE=1]\n" "no match across lines aa:bb:cc:\n" "dd:ee:ff two on the same line:\n" "x [MAC OUI=bb:cc:dd IFACE=2] [MAC OUI=cc:dd:ee IFACE=3] x\n", RedactMACAddresses("BSSID: aa:bb:cc:dd:ee:ff in the middle\n" "bb:cc:dd:ee:ff:00 start of line\n" "end of line aa:bb:cc:dd:ee:ff\n" "no match across lines aa:bb:cc:\n" "dd:ee:ff two on the same line:\n" "x bb:cc:dd:ee:ff:00 cc:dd:ee:ff:00:11 x\n")); EXPECT_EQ("Remember [MAC OUI=bb:cc:dd IFACE=2]?", RedactMACAddresses("Remember bB:Cc:DD:ee:ff:00?")); } TEST_F(RedactionToolTest, RedactHashes) { EXPECT_EQ("", RedactHashes("")); EXPECT_EQ("foo\nbar\n", RedactHashes("foo\nbar\n")); // Too short. EXPECT_EQ("11223344556677889900aabbccddee", RedactHashes("11223344556677889900aabbccddee")); // Not the right length. EXPECT_EQ("11223344556677889900aabbccddeeff1122", RedactHashes("11223344556677889900aabbccddeeff1122")); // Too long. EXPECT_EQ( "11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff11", RedactHashes("11223344556677889900aabbccddeeff11223344556677889900aabb" "ccddeeff11")); // Test all 3 valid lengths. EXPECT_EQ("", RedactHashes("aabbccddeeff00112233445566778899")); EXPECT_EQ("", RedactHashes("aabbccddeeff00112233445566778899aabbccdd")); EXPECT_EQ( "", RedactHashes( "99887766554433221100ffeeddccbbaaaabbccddeeff00112233445566778899")); // Skip 32 byte hashes that have a at least 3 whitespace chars before it. EXPECT_EQ(" ", RedactHashes(" aabbccddeeff00112233445566778899")); EXPECT_EQ(" aabbccddeeff00112233445566778899", RedactHashes(" aabbccddeeff00112233445566778899")); // Multiline test. EXPECT_EQ( "Hash value=, should be replaced as\n" "well as // and mixed case of\n" " but we don't go across lines\n" "aabbccddeeff\n00112233445566778899 but allow multiple on a line " "-" "\n", RedactHashes( "Hash value=aabbccddeeff00112233445566778899, should be replaced as\n" "well as /aabbccddeeff00112233445566778899/ and mixed case of\n" "AaBbCCddEeFf00112233445566778899 but we don't go across lines\n" "aabbccddeeff\n00112233445566778899 but allow multiple on a line " "aabbccddeeffaabbccddeeffaabbccddeeffaabb-" "00112233445566778899aabbccddeeff\n")); } TEST_F(RedactionToolTest, RedactCustomPatterns) { EXPECT_EQ("", RedactCustomPatterns("")); EXPECT_EQ("Cell ID: ''", RedactCustomPatterns("Cell ID: 'A1B2'")); EXPECT_EQ("Cell ID: ''", RedactCustomPatterns("Cell ID: 'C1D2'")); EXPECT_EQ("foo Cell ID: '' bar", RedactCustomPatterns("foo Cell ID: 'A1B2' bar")); EXPECT_EQ("foo Location area code: '' bar", RedactCustomPatterns("foo Location area code: 'A1B2' bar")); EXPECT_EQ("foo\na SSID='' b\n'", RedactCustomPatterns("foo\na SSID='Joe's' b\n'")); EXPECT_EQ("ssid ''", RedactCustomPatterns("ssid 'My AP'")); EXPECT_EQ("bssid 'aa:bb'", RedactCustomPatterns("bssid 'aa:bb'")); EXPECT_EQ("Scan SSID - hexdump(len=6): \nfoo", RedactCustomPatterns( "Scan SSID - hexdump(len=6): 47 6f 6f 67 6c 65\nfoo")); EXPECT_EQ( "a\nb [SSID=] [SSID=] [SSID=foo\nbar] b", RedactCustomPatterns("a\nb [SSID=foo] [SSID=Joe's] [SSID=foo\nbar] b")); EXPECT_EQ("ssid=\"\"", RedactCustomPatterns("ssid=\"LittleTsunami\"")); EXPECT_EQ("* SSID=", RedactCustomPatterns("* SSID=agnagna")); EXPECT_EQ("Specifier: SSID: ", RedactCustomPatterns( "Specifier: SSID: \"GoogleGuest\"")); EXPECT_EQ("Specifier: SSID: ", RedactCustomPatterns( "Specifier: SSID: 'GoogleGuest'")); EXPECT_EQ("Specifier: SSID: ", RedactCustomPatterns( "Specifier: SSID: GoogleGuest")); EXPECT_EQ("SerialNumber: ", RedactCustomPatterns("SerialNumber: 1217D7EF")); EXPECT_EQ("serial number: ", RedactCustomPatterns("serial number: 50C971FEE7F3x010900")); EXPECT_EQ("SerialNumber: ", RedactCustomPatterns("SerialNumber: EVT23-17BA01-004")); EXPECT_EQ("serial=\"\"", RedactCustomPatterns("serial=\"1234AA5678\"")); EXPECT_EQ("\"serial_number\"=\"\"", RedactCustomPatterns("\"serial_number\"=\"1217D7EF\"")); EXPECT_EQ("SerialNumber: ", RedactCustomPatterns("SerialNumber: 5:00:14.0")); EXPECT_EQ("Serial: ", RedactCustomPatterns("Serial: ABCEFG\x01kjmn-as:342/234\\432")); // Don't overly redact serial numbers, we only do this for a specific // formatting case for edid-decode. EXPECT_EQ("Foo serial number 123", RedactCustomPatterns("Foo serial number 123")); EXPECT_EQ("Foo Serial Number ", RedactCustomPatterns("Foo Serial Number 123")); // redact serial number separated by a | with the label "serial" EXPECT_EQ("serial | ", RedactCustomPatterns("serial | 0x1cc04416")); EXPECT_EQ("serial |", RedactCustomPatterns("serial |0x1cc04417")); EXPECT_EQ("serial|", RedactCustomPatterns("serial|0x1cc04418")); EXPECT_EQ("serial|", RedactCustomPatterns("serial|agnagna")); // redact attested device id that is also a serial number EXPECT_EQ("\"attested_device_id\"=\"\"", RedactCustomPatterns("\"attested_device_id\"=\"5CD045B0DZ\"")); EXPECT_EQ("\"attested_device_id\"=\"\"", RedactCustomPatterns("\"attested_device_id\"=\"5CD04-5B0DZ\"")); // The dash cannot appear first or last. EXPECT_EQ("\"attested_device_id\"=\"-5CD045B0DZ\"", RedactCustomPatterns("\"attested_device_id\"=\"-5CD045B0DZ\"")); EXPECT_EQ("\"attested_device_id\"=\"5CD045B0DZ-\"", RedactCustomPatterns("\"attested_device_id\"=\"5CD045B0DZ-\"")); EXPECT_EQ("\"gaia_id\":\"\"", RedactCustomPatterns("\"gaia_id\":\"1234567890\"")); EXPECT_EQ("gaia_id=''", RedactCustomPatterns("gaia_id='987654321'")); EXPECT_EQ("{id: , email:", RedactCustomPatterns("{id: 1234567890, email:")); EXPECT_EQ("", RedactCustomPatterns("foo@bar.com")); EXPECT_EQ("Email: .", RedactCustomPatterns("Email: foo@bar.com.")); EXPECT_EQ("Email:\n\n", RedactCustomPatterns("Email:\nfooooo@bar.com\n")); EXPECT_EQ("[]", RedactCustomPatterns("[2001:0db8:0000:0000:0000:ff00:0042:8329]")); EXPECT_EQ("[]", RedactCustomPatterns("[2001:db8:0:0:0:ff00:42:8329]")); EXPECT_EQ("[]", RedactCustomPatterns("[2001:db8::ff00:42:8329]")); EXPECT_EQ("[]", RedactCustomPatterns("[aa::bb]")); EXPECT_EQ("State::Abort", RedactCustomPatterns("State::Abort")); // Real IPv4 address EXPECT_EQ("", RedactCustomPatterns("192.160.0.1")); // Non-PII IPv4 address (see MaybeScrubIPAddress) EXPECT_EQ("255.255.255.255", RedactCustomPatterns("255.255.255.255")); // Not an actual IPv4 address EXPECT_EQ("75.748.86.91", RedactCustomPatterns("75.748.86.91")); // USB Path - not an actual IPv4 Address EXPECT_EQ("4-3.3.3.3", RedactCustomPatterns("4-3.3.3.3")); // ModemManager modem firmware revisions - not actual IPv4 Addresses EXPECT_EQ("Revision: 81600.0000.00.29.19.16_DO", RedactCustomPatterns("Revision: 81600.0000.00.29.19.16_DO")); EXPECT_EQ("Revision: 11.608.09.01.21", RedactCustomPatterns("Revision: 11.608.09.01.21")); EXPECT_EQ("Revision: 11.208.09.01.21", RedactCustomPatterns("Revision: 11.208.09.01.21")); EXPECT_EQ("Revision: BD_3GHAP673A4V1.0.0B02", RedactCustomPatterns("Revision: BD_3GHAP673A4V1.0.0B02")); EXPECT_EQ("Revision: 2.5.21Hd (Date: Jun 17 2008, Time: 12:30:47)", RedactCustomPatterns( "Revision: 2.5.21Hd (Date: Jun 17 2008, Time: 12:30:47)")); EXPECT_EQ( "Revision: 9.5.05.01-02 [2006-10-20 17:19:09]", RedactCustomPatterns("Revision: 9.5.05.01-02 [2006-10-20 17:19:09]")); EXPECT_EQ("Revision: LQA0021.1.1_M573A", RedactCustomPatterns("Revision: LQA0021.1.1_M573A")); EXPECT_EQ("Revision: 10.10.10.10", RedactCustomPatterns("Revision: 10.10.10.10")); EXPECT_EQ("", RedactCustomPatterns("http://example.com/foo?test=1")); EXPECT_EQ("Foo Bar", RedactCustomPatterns("Foo http://192.168.0.1/foo?test=1#123 Bar")); const char* kURLs[] = { "http://example.com/foo?test=1", "http://userid:password@example.com:8080", "http://userid:password@example.com:8080/", "http://@example.com", "http://192.168.0.1", "http://192.168.0.1/", "http://اختبار.com", "http://test.com/foo(bar)baz.html", "http://test.com/foo%20bar", "ftp://test:tester@test.com", "chrome://extensions/", "chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/options.html", "http://example.com/foo?email=foo@bar.com", "rtsp://root@example.com/", "https://aaaaaaaaaaaaaaaa.com", "file:///var/log/messages", "file:///usr/local/home/iby/web%20page%20test.html", }; for (size_t i = 0; i < std::size(kURLs); ++i) { SCOPED_TRACE(kURLs[i]); std::string got = RedactCustomPatterns(kURLs[i]); EXPECT_TRUE( base::StartsWith(got, "", base::CompareCase::INSENSITIVE_ASCII)); } // Test that "Android:" is not considered a schema with empty hier part. EXPECT_EQ("The following applies to Android:", RedactCustomPatterns("The following applies to Android:")); } TEST_F(RedactionToolTest, RedactCustomPatternWithContext) { // The PIIType for the CustomPatternWithAlias is not relevant, only for // testing. const CustomPatternWithAlias kPattern1 = {"ID", "(\\b(?i)id:? ')(\\d+)(')", PIIType::kStableIdentifier}; const CustomPatternWithAlias kPattern2 = {"ID", "(\\b(?i)id=')(\\d+)(')", PIIType::kStableIdentifier}; const CustomPatternWithAlias kPattern3 = {"IDG", "(\\b(?i)idg=')(\\d+)(')", PIIType::kLocationInfo}; EXPECT_EQ("", RedactCustomPatternWithContext("", kPattern1)); EXPECT_EQ("foo\nbar\n", RedactCustomPatternWithContext("foo\nbar\n", kPattern1)); EXPECT_EQ("id ''", RedactCustomPatternWithContext("id '2345'", kPattern1)); EXPECT_EQ("id ''", RedactCustomPatternWithContext("id '1234'", kPattern1)); EXPECT_EQ("id: ''", RedactCustomPatternWithContext("id: '1234'", kPattern1)); EXPECT_EQ("ID: ''", RedactCustomPatternWithContext("ID: '2345'", kPattern1)); EXPECT_EQ("x1 id '' 1x id ''\nid ''\n", RedactCustomPatternWithContext( "x1 id '2345' 1x id '1234'\nid '2345'\n", kPattern1)); // Different pattern with same alias should reuse the replacements. EXPECT_EQ("id=''", RedactCustomPatternWithContext("id='1234'", kPattern2)); // Different alias should not reuse replacement from another pattern. EXPECT_EQ("idg=''", RedactCustomPatternWithContext("idg='1234'", kPattern3)); EXPECT_EQ("xz", RedactCustomPatternWithContext("xyz", {"FOO", "()(y+)()"})); } TEST_F(RedactionToolTest, RedactCustomPatternWithoutContext) { // The PIIType for the CustomPatternWithAlias here is not relevant, only for // testing. CustomPatternWithAlias kPattern = {"pattern", "(o+)", PIIType::kEmail}; EXPECT_EQ("", RedactCustomPatternWithoutContext("", kPattern)); EXPECT_EQ("f\nfz\nfl\n", RedactCustomPatternWithoutContext("fo\nfooz\nfol\n", kPattern)); } TEST_F(RedactionToolTest, RedactChunk) { std::string redaction_input; std::string redaction_output; for (const auto& s : kStringsWithRedactions) { redaction_input.append(s.pre_redaction).append("\n"); redaction_output.append(s.post_redaction).append("\n"); } EXPECT_EQ(redaction_output, redactor_.Redact(redaction_input)); } TEST_F(RedactionToolTest, RedactAndKeepSelected) { std::string redaction_input; std::string redaction_output; for (const auto& s : kStringsWithRedactions) { redaction_input.append(s.pre_redaction).append("\n"); redaction_output.append(s.post_redaction).append("\n"); } // Test RedactAndKeepSelected() with no PII type to keep. EXPECT_EQ(redaction_output, redactor_.RedactAndKeepSelected(redaction_input, {})); // Test RedactAndKeepSelected() by only keeping IP addresses in the redacted // output. std::string redaction_output_ip; for (const auto& s : kStringsWithRedactions) { if (s.pii_type == PIIType::kIPAddress) { redaction_output_ip.append(s.pre_redaction).append("\n"); } else { redaction_output_ip.append(s.post_redaction).append("\n"); } } EXPECT_EQ(redaction_output_ip, redactor_.RedactAndKeepSelected( redaction_input, {PIIType::kIPAddress})); // Test RedactAndKeepSelected() by keeping MAC addresses and hashes in the // redacted output. The hashes that URLs and Android storage paths contain // will be redacted with the URL or Android storage path that they're part of. std::string redaction_output_mac_and_hashes; for (const auto& s : kStringsWithRedactions) { if (s.pii_type == PIIType::kMACAddress || s.pii_type == PIIType::kStableIdentifier) { redaction_output_mac_and_hashes.append(s.pre_redaction).append("\n"); } else { redaction_output_mac_and_hashes.append(s.post_redaction).append("\n"); } } EXPECT_EQ( redaction_output_mac_and_hashes, redactor_.RedactAndKeepSelected( redaction_input, {PIIType::kMACAddress, PIIType::kStableIdentifier})); } TEST_F(RedactionToolTest, RedactUid) { EXPECT_EQ("", redactor_.RedactAndKeepSelected( "B3mcFTkQAHofv94DDTUuVJGGEI/BbzsyDncplMCR2P4=", {})); } TEST_F(RedactionToolTest, RedactAndKeepSelectedHashes) { // Array of pairs containing pre/post redaction versions of the same string. // Will be appended to create input and expected output for the test. Keep // URLs and Android app storage paths but redact hashes. URLs and Android app // storage paths that contain hashes will be partially redacted. const std::pair redaction_strings_with_hashes[] = { {"chrome://resources/" "f?user=" "99887766554433221100ffeeddccbbaaaabbccddeeff00112233445566778899", "chrome://resources/f?user="}, // URL that contains a hash. {"/root/27540283740a0897ab7c8de0f809add2bacde78f/foo", "/root//foo"}, // String that contains a hash. {"this is the user hash that we need to redact " "aabbccddeeff00112233445566778899", "this is the user hash that we need to redact "}, // String // that // contains // a hash. #if BUILDFLAG(IS_CHROMEOS_ASH) {"8.0K\t/home/root/aabbccddeeff00112233445566778899/" "android-data/data/data/pa.ckage2/de", // Android app storage // path that contains a // hash. "8.0K\t/home/root//android-data/data/data/pa.ckage2/de"} #endif // BUILDFLAG(IS_CHROMEOS_ASH) }; std::string redaction_input; std::string redaction_output; for (const auto& s : redaction_strings_with_hashes) { redaction_input.append(s.first).append("\n"); redaction_output.append(s.second).append("\n"); } EXPECT_EQ( redaction_output, redactor_.RedactAndKeepSelected( redaction_input, {PIIType::kAndroidAppStoragePath, PIIType::kURL})); } TEST_F(RedactionToolTest, DetectPII) { std::string redaction_input; for (const auto& s : kStringsWithRedactions) { redaction_input.append(s.pre_redaction).append("\n"); } std::map> pii_in_data { #if BUILDFLAG(IS_CHROMEOS_ASH) // We only detect Android paths on Chrome OS. {PIIType::kAndroidAppStoragePath, {"/de"}}, #endif // BUILDFLAG(IS_CHROMEOS_ASH) {PIIType::kSSID, {"123aaaaaa"}}, {PIIType::kURL, {"http://tets.comaaaaaaa", "chrome://resources/f?user=bar", "chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/" "foobar.js?bar=x"}}, {PIIType::kEmail, {"aaaaaemail@example.comaaa"}}, {PIIType::kIPAddress, { "255.255.155.2", "255.255.155.255", "127.0.0.1", "127.255.0.1", "0.0.0.0", "0.255.255.255", "10.10.10.100", "10.10.10.101", "10.255.255.255", "172.16.0.0", "172.31.255.255", "172.11.5.5", "172.111.5.5", "192.168.0.0", "192.168.255.255", "192.169.2.120", "169.254.0.1", "169.200.0.1", "224.0.0.24", "240.0.0.0", "100.115.91.92", "8.8.8.4", "123.123.45.4", "fe80::", "fe80::ffff", "febf:ffff::ffff", "fecc::1111", "11::11", "ff01::3", "ff02::3", "ff02::fb", "ff08::fb", "ff0f::101", "::ffff:cb0c:10ea", "::ffff:a0a:a0a", "::ffff:ac1e:1e1e", "::ffff:c0a8:640a", "::ffff:6473:5c01", "64:ff9b::a0a:a0a", "64:ff9b::6473:5c01", "::0101:ffff:c0a8:640a", }}, {PIIType::kMACAddress, {"aa:aa:aa:aa:aa:aa"}}, { PIIType::kStableIdentifier, { "27540283740a0897ab7c8de0f809add2bacde78f", "B3mcFTkQAHofv94DDTUuVJGGEI/BbzsyDncplMCR2P4=", } } }; EXPECT_EQ(pii_in_data, redactor_.Detect(redaction_input)); } #if BUILDFLAG(IS_CHROMEOS_ASH) // We only redact Android paths on Chrome OS. TEST_F(RedactionToolTest, RedactAndroidAppStoragePaths) { EXPECT_EQ("", RedactAndroidAppStoragePaths("")); EXPECT_EQ("foo\nbar\n", RedactAndroidAppStoragePaths("foo\nbar\n")); constexpr char kDuOutput[] = "112K\t/home/root/deadbeef1234/android-data/data/system_de\n" // /data/data will be modified by the redactor. "8.0K\t/home/root/deadbeef1234/android-data/data/data/pack.age1/a\n" "8.0K\t/home/root/deadbeef1234/android-data/data/data/pack.age1/bc\n" "24K\t/home/root/deadbeef1234/android-data/data/data/pack.age1\n" "8.0K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/de\n" "8.0K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/de/" "\xe3\x81\x82\n" "8.1K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/de/" "\xe3\x81\x82\xe3\x81\x83\n" "8.0K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/ef\n" "24K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2\n" "8.0K\t/home/root/deadbeef1234/android-data/data/app/pack.age1/a\n" "8.0K\t/home/root/deadbeef1234/android-data/data/app/pack.age1/bc\n" "24K\t/home/root/deadbeef1234/android-data/data/app/pack.age1\n" "8.0K\t/home/root/deadbeef1234/android-data/data/user_de/0/pack.age1/a\n" "8.0K\t/home/root/deadbeef1234/android-data/data/user_de/0/pack.age1/bc\n" "24K\t/home/root/deadbeef1234/android-data/data/user_de/0/pack.age1\n" "78M\t/home/root/deadbeef1234/android-data/data/data\n" "key=value path=/data/data/pack.age1/bc key=value\n" "key=value path=/data/user_de/0/pack.age1/bc key=value\n" "key=value exe=/data/app/pack.age1/bc key=value\n"; constexpr char kDuOutputRedacted[] = "112K\t/home/root/deadbeef1234/android-data/data/system_de\n" "8.0K\t/home/root/deadbeef1234/android-data/data/data/pack.age1/a\n" "8.0K\t/home/root/deadbeef1234/android-data/data/data/pack.age1/b_\n" "24K\t/home/root/deadbeef1234/android-data/data/data/pack.age1\n" "8.0K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/d_\n" // The non-ASCII directory names will become '*_'. "8.0K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/d_/*_\n" "8.1K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/d_/*_\n" "8.0K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2/e_\n" "24K\t/home/root/deadbeef1234/android-data/data/data/pa.ckage2\n" "8.0K\t/home/root/deadbeef1234/android-data/data/app/pack.age1/a\n" "8.0K\t/home/root/deadbeef1234/android-data/data/app/pack.age1/b_\n" "24K\t/home/root/deadbeef1234/android-data/data/app/pack.age1\n" "8.0K\t/home/root/deadbeef1234/android-data/data/user_de/0/pack.age1/a\n" "8.0K\t/home/root/deadbeef1234/android-data/data/user_de/0/pack.age1/b_\n" "24K\t/home/root/deadbeef1234/android-data/data/user_de/0/pack.age1\n" "78M\t/home/root/deadbeef1234/android-data/data/data\n" "key=value path=/data/data/pack.age1/b_ key=value\n" "key=value path=/data/user_de/0/pack.age1/b_ key=value\n" "key=value exe=/data/app/pack.age1/b_ key=value\n"; EXPECT_EQ(kDuOutputRedacted, RedactAndroidAppStoragePaths(kDuOutput)); } #endif // BUILDFLAG(IS_CHROMEOS_ASH) TEST_F(RedactionToolTest, RedactBlockDevices) { // Test cases in the form {input, output}. std::pair test_cases[] = { // UUIDs that come from the 'blkid' tool. {"PTUUID=\"985dff64-9c0f-3f49-945b-2d8c2e0238ec\"", "PTUUID=\"\""}, {"UUID=\"E064-868C\"", "UUID=\"\""}, {"PARTUUID=\"7D242B2B1C751832\"", "PARTUUID=\"\""}, // Volume labels. {"LABEL=\"ntfs\"", "LABEL=\"\""}, {"PARTLABEL=\"SD Card\"", "PARTLABEL=\"\""}, // LVM UUIDd. {"{\"pv_fmt\":\"lvm2\", " "\"pv_uuid\":\"duD18x-P7QE-sTya-SaeO-aq07-YgEq-xj8UEz\", " "\"dev_size\":\"230.33g\"}", "{\"pv_fmt\":\"lvm2\", \"pv_uuid\":\"\", " "\"dev_size\":\"230.33g\"}"}, {"{\"lv_uuid\":\"lKYORl-TWDP-OFLT-yDnB-jlQ7-aQrE-AwA8Oa\", " "\"lv_name\":\"[thinpool_tdata]\"", "{\"lv_uuid\":\"\", \"lv_name\":\"[thinpool_tdata]\""}, // Removable media paths. {"/media/removable/SD Card/", "/media/removable//"}, {"'/media/removable/My Secret Volume Name' don't redact this", "'/media/removable/' don't redact this"}, {"0 part /media/removable/My Secret Volume Name With Spaces ", "0 part /media/removable/"}, }; for (const auto& p : test_cases) { EXPECT_EQ(redactor_.Redact(p.first), p.second); } } } // namespace feedback