blob: 183ccaa4a2908c1d73f835517d0976f05badb669 [file] [log] [blame] [edit]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package feedback
import (
"context"
"io/ioutil"
"os"
"path"
"strings"
"time"
"chromiumos/tast/fsutil"
"chromiumos/tast/local/chrome"
"chromiumos/tast/testing"
)
// Define test types.
const (
localFileTest int = iota
thirdPartySiteTest
)
const (
testPageFilename = "sys_info_pii_test_page.html"
)
func init() {
testing.AddTest(&testing.Test{
Func: SysInfoPII,
Desc: "Verify that known-sensitive data doesn't show up in feedback reports",
Contacts: []string{"xiangdongkong@google.com", "cros-feedback-app@google.com"},
Attr: []string{"group:mainline"},
SoftwareDeps: []string{"chrome"},
Timeout: 5 * time.Minute,
Pre: chrome.LoggedIn(),
Params: []testing.Param{{
Name: "",
Val: localFileTest,
ExtraData: []string{testPageFilename},
ExtraAttr: []string{"informational"},
}, {
Name: "third_party_site",
Val: thirdPartySiteTest,
ExtraAttr: []string{"informational"},
}},
})
}
// saveLog attempts to save the given log to the test's output directory.
func saveLog(outDir, key, value string) error {
return ioutil.WriteFile(path.Join(outDir, key+".log"), []byte(value), 0664)
}
// systemInformation corresponds to the "SystemInformation" defined in autotest_private.idl.
type systemInformation struct {
Key string `json:"key"`
Value string `json:"value"`
}
type contentsDescriptor struct {
content string
desc string
}
// SysInfoPII loads an external website URL to verify that the logs that the
// feedback system sends do not contain sensitive and hard-to-redact
// information, such as tab names.
func SysInfoPII(ctx context.Context, s *testing.State) {
const (
localPageTitle = "feedback.SysInfoPII test page title"
localPageContents = "feedback.SysInfoPII test page contents"
)
testType := s.Param().(int)
sensitiveURL := ""
sensitiveURLWithoutScheme := ""
if testType == localFileTest {
dataFile := s.DataPath(testPageFilename)
targetPath := path.Join("/tmp", testPageFilename)
if err := fsutil.CopyFile(dataFile, targetPath); err != nil {
s.Fatal("Failed to put dataFile in tmp dir: ", err)
}
if err := os.Chmod(targetPath, 0666); err != nil {
s.Fatal("Failed to make dataFile readable: ", err)
}
sensitiveURL = "file://" + targetPath
sensitiveURLWithoutScheme = dataFile
} else {
// Arbitrary third-party website, which shouldn't be logged.
// We use a third-party website to reduce the risk of false positives:
// some utilities hard-code "www.google.com" and log that string,
// which is acceptable (as it's not in response to any user actions).
sensitiveURL = "https://www.facebook.com"
sensitiveURLWithoutScheme = "www.facebook.com"
}
cr := s.PreValue().(*chrome.Chrome)
conn, err := cr.NewConn(ctx, sensitiveURL)
if err != nil {
s.Fatal("Failed to establish a chrome renderer connection: ", err)
}
defer conn.Close()
tconn, err := cr.TestAPIConn(ctx)
if err != nil {
s.Fatal("Could not create test API conn: ", err)
}
s.Log("Calling getSystemInformation")
var ret []*systemInformation
if err := tconn.Eval(ctx, "tast.promisify(chrome.feedbackPrivate.getSystemInformation)()", &ret); err != nil {
s.Fatal("Could not call getSystemInformation: ", err)
}
var actualTitle string
if err := conn.Eval(ctx, "document.title", &actualTitle); err != nil {
s.Fatal("Failed to get the tab title: ", err)
}
// Check that page actually loaded as expected.
if testType == localFileTest && actualTitle != localPageTitle {
// Don't log the expected page title so we don't make later
// runs of this test fail.
s.Error("Unexpected title: ", actualTitle)
}
for _, info := range ret {
if info.Key != "mem_usage_with_title" {
// mem_usage_with_title is only included if the user
// explicitly opts to send tab titles, so it's
// acceptable for it to contain titles or possibly URLs.
badContents := []contentsDescriptor{
{actualTitle, "tabTitle"},
{sensitiveURL, "URL"},
{sensitiveURLWithoutScheme, "URLWithoutScheme"},
}
if testType == localFileTest {
badContents = append(
badContents,
contentsDescriptor{localPageContents, "pageContents"},
)
}
for _, entry := range badContents {
if strings.Contains(info.Value, entry.content) {
// Don't log actual contents so that we don't make later runs
// of this test fail.
s.Errorf("Log %q unexpectedly contained %s", info.Key, entry.desc)
if err := saveLog(s.OutDir(), info.Key, info.Value); err != nil {
s.Error("Also, failed to save log contents: ", err)
}
if err := saveLog(s.OutDir(), entry.desc, entry.content); err != nil {
s.Errorf("Also, failed to save %s: %v", entry.desc, err)
}
}
}
}
// Trim "@gmail.com" to look for both username and full email.
// (The PII redaction should eliminate the email, so looking
// for the full email would not be as useful as looking for the
// username.)
user := strings.TrimSuffix(chrome.DefaultUser, "@gmail.com")
if strings.Contains(info.Value, user) {
// DO NOT actually log the username here -- if we do, and the test fails,
// then the username will be in the syslog and all future runs of the test
// on that device will also fail.
s.Errorf("Log %q unexpectedly contained username", info.Key)
if err := saveLog(s.OutDir(), info.Key, info.Value); err != nil {
s.Error("Also, failed to save log contents: ", err)
}
}
}
}