blob: 9fb87ed55441d0273a5f030c009956b31f91388c [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// 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 <gtest/gtest.h>
#include <set>
#include <utility>
#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=<SSID: 1>]aaaaa", PIIType::kSSID},
{"aaaaaaaahttp://tets.comaaaaaaa", // URL.
"aaaaaaaa<URL: 1>", 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.
"<email: 1>", PIIType::kEmail},
{"example@@1234", // No PII, it is not a valid email address.
"example@@1234", PIIType::kNone},
{"255.255.155.2", // IP address.
"<IPv4: 1>", PIIType::kIPAddress},
{"255.255.155.255", // IP address.
"<IPv4: 2>", 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.
"<IPv4: 12>", PIIType::kIPAddress},
{"172.111.5.5", // IP address.
"<IPv4: 13>", 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.
"<IPv4: 16>", PIIType::kIPAddress},
{"169.254.0.1", // Link local.
"<169.254.0.0/16: 17>", PIIType::kIPAddress},
{"169.200.0.1", // IP address.
"<IPv4: 18>", PIIType::kIPAddress},
{"fe80::", // Link local.
"<fe80::/10: 1>", PIIType::kIPAddress},
{"fe80::ffff", // Link local.
"<fe80::/10: 2>", PIIType::kIPAddress},
{"febf:ffff::ffff", // Link local.
"<fe80::/10: 3>", PIIType::kIPAddress},
{"fecc::1111", // IP address.
"<IPv6: 4>", PIIType::kIPAddress},
{"224.0.0.24", // Multicast.
"<224.0.0.0/4: 19>", PIIType::kIPAddress},
{"240.0.0.0", // IP address.
"<IPv4: 20>", 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.
"<IPv4: 21>", 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.
"<IPv4: 22>", 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.
"aaaa<IPv4: 23>aaa", PIIType::kIPAddress},
{"11:11;11::11", // IP address.
"11:11;<IPv6: 5>", PIIType::kIPAddress},
{"11::11", // IP address.
"<IPv6: 5>", 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).
"<ff01::/16: 6>", 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).
"<ff02::/16: 7>", PIIType::kIPAddress},
{"ff02::fb", // mDNSv6 (link local).
"<ff02::/16: 8>", PIIType::kIPAddress},
{"ff08::fb", // mDNSv6.
"<IPv6: 9>", PIIType::kIPAddress},
{"ff0f::101", // All NTP servers.
"<IPv6: 10>", PIIType::kIPAddress},
{"::ffff:cb0c:10ea", // IPv4-mapped IPV6 (IP address).
"<IPv6: 11>", PIIType::kIPAddress},
{"::ffff:a0a:a0a", // IPv4-mapped IPV6 (private class A).
"<M 10.0.0.0/8: 12>", PIIType::kIPAddress},
{"::ffff:a0a:a0a", // Intentional duplicate.
"<M 10.0.0.0/8: 12>", PIIType::kIPAddress},
{"::ffff:ac1e:1e1e", // IPv4-mapped IPV6 (private class B).
"<M 172.16.0.0/12: 13>", PIIType::kIPAddress},
{"::ffff:c0a8:640a", // IPv4-mapped IPV6 (private class C).
"<M 192.168.0.0/16: 14>", PIIType::kIPAddress},
{"::ffff:6473:5c01", // IPv4-mapped IPV6 (Chrome).
"<M 100.115.92.1: 15>", PIIType::kIPAddress},
{"64:ff9b::a0a:a0a", // IPv4-translated 6to4 IPV6 (private class A).
"<T 10.0.0.0/8: 16>", PIIType::kIPAddress},
{"64:ff9b::6473:5c01", // IPv4-translated 6to4 IPV6 (Chrome).
"<T 100.115.92.1: 17>", PIIType::kIPAddress},
{"::0101:ffff:c0a8:640a", // IP address.
"<IPv6: 18>", 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.
"<URL: 2>", PIIType::kURL},
{"chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js?bar=x",
"<URL: 3>", PIIType::kURL}, // Potentially PII in parameter.
{"isolated-app://airugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaac/",
"<URL: 4>", PIIType::kURL}, // URL
{"/root/27540283740a0897ab7c8de0f809add2bacde78f/foo",
"/root/<HASH:2754 1>/foo", PIIType::kStableIdentifier}, // Hash string.
{"B3mcFTkQAHofv94DDTUuVJGGEI/BbzsyDncplMCR2P4=", "<UID: 1>",
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("<HASH:1122 1>",
redactor_.Redact("11223344556677889900AABBCCDDEEFF"));
// Make sure custom pattern redaction is invoked.
EXPECT_EQ("Cell ID: '<CellID: 1>'", RedactCustomPatterns("Cell ID: 'A1B2'"));
// Make sure UUIDs are redacted.
EXPECT_EQ(
"REQUEST localhost - - \"POST /printers/<UUID: 1> 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/<UUID: 2> 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("<HASH:aabb 1>", RedactHashes("aabbccddeeff00112233445566778899"));
EXPECT_EQ("<HASH:aabb 2>",
RedactHashes("aabbccddeeff00112233445566778899aabbccdd"));
EXPECT_EQ(
"<HASH:9988 3>",
RedactHashes(
"99887766554433221100ffeeddccbbaaaabbccddeeff00112233445566778899"));
// Skip 32 byte hashes that have a at least 3 whitespace chars before it.
EXPECT_EQ(" <HASH:aabb 1>",
RedactHashes(" aabbccddeeff00112233445566778899"));
EXPECT_EQ(" aabbccddeeff00112233445566778899",
RedactHashes(" aabbccddeeff00112233445566778899"));
// Multiline test.
EXPECT_EQ(
"Hash value=<HASH:aabb 1>, should be replaced as\n"
"well as /<HASH:aabb 1>/ and mixed case of\n"
"<HASH:aabb 1> but we don't go across lines\n"
"aabbccddeeff\n00112233445566778899 but allow multiple on a line "
"<HASH:aabb 4>-"
"<HASH:0011 5>\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: '<CellID: 1>'", RedactCustomPatterns("Cell ID: 'A1B2'"));
EXPECT_EQ("Cell ID: '<CellID: 2>'", RedactCustomPatterns("Cell ID: 'C1D2'"));
EXPECT_EQ("foo Cell ID: '<CellID: 1>' bar",
RedactCustomPatterns("foo Cell ID: 'A1B2' bar"));
EXPECT_EQ("foo Location area code: '<LocAC: 1>' bar",
RedactCustomPatterns("foo Location area code: 'A1B2' bar"));
EXPECT_EQ("foo\na SSID='<SSID: 1>' b\n'",
RedactCustomPatterns("foo\na SSID='Joe's' b\n'"));
EXPECT_EQ("ssid '<SSID: 2>'", RedactCustomPatterns("ssid 'My AP'"));
EXPECT_EQ("bssid 'aa:bb'", RedactCustomPatterns("bssid 'aa:bb'"));
EXPECT_EQ("Scan SSID - hexdump(len=6): <SSIDHex: 1>\nfoo",
RedactCustomPatterns(
"Scan SSID - hexdump(len=6): 47 6f 6f 67 6c 65\nfoo"));
EXPECT_EQ(
"a\nb [SSID=<SSID: 3>] [SSID=<SSID: 1>] [SSID=foo\nbar] b",
RedactCustomPatterns("a\nb [SSID=foo] [SSID=Joe's] [SSID=foo\nbar] b"));
EXPECT_EQ("ssid=\"<SSID: 4>\"",
RedactCustomPatterns("ssid=\"LittleTsunami\""));
EXPECT_EQ("* SSID=<SSID: 5>", RedactCustomPatterns("* SSID=agnagna"));
EXPECT_EQ("Specifier: <ArcNetworkFactory#1> SSID: \"<SSID: 6>\" foo",
RedactCustomPatterns(
"Specifier: <ArcNetworkFactory#1> SSID: \"GoogleGuest1\" foo"));
EXPECT_EQ("Specifier: <ArcNetworkFactory#1> SSID: '<SSID: 7>' foo",
RedactCustomPatterns(
"Specifier: <ArcNetworkFactory#1> SSID: 'GoogleGuest2' foo"));
EXPECT_EQ("Specifier: <ArcNetworkFactory#1> SSID: <SSID: 8>",
RedactCustomPatterns(
"Specifier: <ArcNetworkFactory#1> SSID: GoogleGuest3"));
EXPECT_EQ(
"Specifier: <ArcNetworkFactory#1> SSID: <SSID: 9>",
RedactCustomPatterns(
"Specifier: <ArcNetworkFactory#1> SSID: less than 32 characters"));
EXPECT_EQ("Specifier: <ArcNetworkFactory#1> SSID: <SSID: 10>foo",
RedactCustomPatterns("Specifier: <ArcNetworkFactory#1> SSID: this "
"line is 32 characters long!foo"));
EXPECT_EQ(
"<WifiNetworkSpecifier [, SSID Match pattern=PatternMatcher{LITERAL: "
"<SSID: 11>}, ...]",
RedactCustomPatterns("<WifiNetworkSpecifier [, SSID Match "
"pattern=PatternMatcher{LITERAL: Google-A}, ...]"));
EXPECT_EQ("SerialNumber: <Serial: 1>",
RedactCustomPatterns("SerialNumber: 1217D7EF"));
EXPECT_EQ("serial number: <Serial: 2>",
RedactCustomPatterns("serial number: 50C971FEE7F3x010900"));
EXPECT_EQ("SerialNumber: <Serial: 3>",
RedactCustomPatterns("SerialNumber: EVT23-17BA01-004"));
EXPECT_EQ("serial=\"<Serial: 4>\"",
RedactCustomPatterns("serial=\"1234AA5678\""));
EXPECT_EQ("\"serial_number\"=\"<Serial: 1>\"",
RedactCustomPatterns("\"serial_number\"=\"1217D7EF\""));
EXPECT_EQ("SerialNumber: <Serial: 5>",
RedactCustomPatterns("SerialNumber: 5:00:14.0"));
EXPECT_EQ("Serial: <Serial: 6>",
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 <Serial: 7>",
RedactCustomPatterns("Foo Serial Number 123"));
// redact serial number separated by a | with the label "serial"
EXPECT_EQ("serial | <Serial: 8>",
RedactCustomPatterns("serial | 0x1cc04416"));
EXPECT_EQ("serial |<Serial: 9>",
RedactCustomPatterns("serial |0x1cc04417"));
EXPECT_EQ("serial|<Serial: 10>", RedactCustomPatterns("serial|0x1cc04418"));
EXPECT_EQ("serial|<Serial: 11>", RedactCustomPatterns("serial|agnagna"));
// redact attested device id that is also a serial number
EXPECT_EQ("\"attested_device_id\"=\"<Serial: 12>\"",
RedactCustomPatterns("\"attested_device_id\"=\"5CD045B0DZ\""));
EXPECT_EQ("\"attested_device_id\"=\"<Serial: 13>\"",
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\":\"<GAIA: 1>\"",
RedactCustomPatterns("\"gaia_id\":\"1234567890\""));
EXPECT_EQ("gaia_id='<GAIA: 2>'", RedactCustomPatterns("gaia_id='987654321'"));
EXPECT_EQ("{id: <GAIA: 1>, email:",
RedactCustomPatterns("{id: 1234567890, email:"));
EXPECT_EQ("<email: 1>", RedactCustomPatterns("foo@bar.com"));
EXPECT_EQ("Email: <email: 1>.", RedactCustomPatterns("Email: foo@bar.com."));
EXPECT_EQ("Email:\n<email: 2>\n",
RedactCustomPatterns("Email:\nfooooo@bar.com\n"));
EXPECT_EQ("[<IPv6: 1>]",
RedactCustomPatterns("[2001:0db8:0000:0000:0000:ff00:0042:8329]"));
EXPECT_EQ("[<IPv6: 2>]",
RedactCustomPatterns("[2001:db8:0:0:0:ff00:42:8329]"));
EXPECT_EQ("[<IPv6: 3>]", RedactCustomPatterns("[2001:db8::ff00:42:8329]"));
EXPECT_EQ("[<IPv6: 4>]", RedactCustomPatterns("[aa::bb]"));
EXPECT_EQ("State::Abort", RedactCustomPatterns("State::Abort"));
// Real IPv4 address
EXPECT_EQ("<IPv4: 1>", 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("<URL: 1>", RedactCustomPatterns("http://example.com/foo?test=1"));
EXPECT_EQ("Foo <URL: 2> 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, "<URL: ", base::CompareCase::INSENSITIVE_ASCII));
EXPECT_TRUE(base::EndsWith(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 '<ID: 1>'",
RedactCustomPatternWithContext("id '2345'", kPattern1));
EXPECT_EQ("id '<ID: 2>'",
RedactCustomPatternWithContext("id '1234'", kPattern1));
EXPECT_EQ("id: '<ID: 2>'",
RedactCustomPatternWithContext("id: '1234'", kPattern1));
EXPECT_EQ("ID: '<ID: 1>'",
RedactCustomPatternWithContext("ID: '2345'", kPattern1));
EXPECT_EQ("x1 id '<ID: 1>' 1x id '<ID: 2>'\nid '<ID: 1>'\n",
RedactCustomPatternWithContext(
"x1 id '2345' 1x id '1234'\nid '2345'\n", kPattern1));
// Different pattern with same alias should reuse the replacements.
EXPECT_EQ("id='<ID: 2>'",
RedactCustomPatternWithContext("id='1234'", kPattern2));
// Different alias should not reuse replacement from another pattern.
EXPECT_EQ("idg='<IDG: 1>'",
RedactCustomPatternWithContext("idg='1234'", kPattern3));
EXPECT_EQ("x<FOO: 1>z",
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<pattern: 1>\nf<pattern: 2>z\nf<pattern: 1>l\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("<UID: 1>",
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<std::string, std::string> redaction_strings_with_hashes[] = {
{"chrome://resources/"
"f?user="
"99887766554433221100ffeeddccbbaaaabbccddeeff00112233445566778899",
"chrome://resources/f?user=<HASH:9988 1>"}, // URL that contains a hash.
{"/root/27540283740a0897ab7c8de0f809add2bacde78f/foo",
"/root/<HASH:2754 2>/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 <HASH:aabb 3>"}, // 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/<HASH:aabb 3>/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<PIIType, std::set<std::string>> 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",
"isolated-app://"
"airugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaac/",
"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<std::string, std::string> test_cases[] = {
// UUIDs that come from the 'blkid' tool.
{"PTUUID=\"985dff64-9c0f-3f49-945b-2d8c2e0238ec\"",
"PTUUID=\"<UUID: 1>\""},
{"UUID=\"E064-868C\"", "UUID=\"<UUID: 2>\""},
{"PARTUUID=\"7D242B2B1C751832\"", "PARTUUID=\"<UUID: 3>\""},
// Volume labels.
{"LABEL=\"ntfs\"", "LABEL=\"<Volume Label: 1>\""},
{"PARTLABEL=\"SD Card\"", "PARTLABEL=\"<Volume Label: 2>\""},
// LVM UUIDd.
{"{\"pv_fmt\":\"lvm2\", "
"\"pv_uuid\":\"duD18x-P7QE-sTya-SaeO-aq07-YgEq-xj8UEz\", "
"\"dev_size\":\"230.33g\"}",
"{\"pv_fmt\":\"lvm2\", \"pv_uuid\":\"<UUID: 4>\", "
"\"dev_size\":\"230.33g\"}"},
{"{\"lv_uuid\":\"lKYORl-TWDP-OFLT-yDnB-jlQ7-aQrE-AwA8Oa\", "
"\"lv_name\":\"[thinpool_tdata]\"",
"{\"lv_uuid\":\"<UUID: 5>\", \"lv_name\":\"[thinpool_tdata]\""},
// Removable media paths.
{"/media/removable/SD Card/", "/media/removable/<Volume Label: 2>/"},
{"'/media/removable/My Secret Volume Name' don't redact this",
"'/media/removable/<Volume Label: 3>' don't redact this"},
{"0 part /media/removable/My Secret Volume Name With Spaces ",
"0 part /media/removable/<Volume Label: 4>"},
};
for (const auto& p : test_cases) {
EXPECT_EQ(redactor_.Redact(p.first), p.second);
}
}
} // namespace feedback