diff --git a/CHANGELOG.md b/CHANGELOG.md index f181f75c64..d3fcce57c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## HEAD +### Cleanup + + * Remove v2 log list package files. + ## v1.1.4 [Published 2022-10-21](https://github.com/google/certificate-transparency-go/releases/tag/v1.1.4) @@ -12,7 +16,7 @@ ### Migrillian -* #960: Skip consistency check when root is size zero. + * #960: Skip consistency check when root is size zero. ### Misc diff --git a/loglist2/logfilter.go b/loglist2/logfilter.go deleted file mode 100644 index 225e644397..0000000000 --- a/loglist2/logfilter.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2018 Google LLC. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package loglist2 - -import ( - "github.com/google/certificate-transparency-go/x509" - "github.com/google/certificate-transparency-go/x509util" - "k8s.io/klog/v2" -) - -// LogRoots maps Log-URLs (stated at LogList) to the pools of their accepted -// root-certificates. -type LogRoots map[string]*x509util.PEMCertPool - -// Compatible creates a new LogList containing only Logs matching the temporal, -// root-acceptance and Log-status conditions. -func (ll *LogList) Compatible(cert *x509.Certificate, certRoot *x509.Certificate, roots LogRoots) LogList { - active := ll.TemporallyCompatible(cert) - return active.RootCompatible(certRoot, roots) -} - -// SelectByStatus creates a new LogList containing only logs with status -// provided from the original. -func (ll *LogList) SelectByStatus(lstats []LogStatus) LogList { - var active LogList - for _, op := range ll.Operators { - activeOp := *op - activeOp.Logs = []*Log{} - for _, l := range op.Logs { - for _, lstat := range lstats { - if l.State.LogStatus() == lstat { - activeOp.Logs = append(activeOp.Logs, l) - break - } - } - } - if len(activeOp.Logs) > 0 { - active.Operators = append(active.Operators, &activeOp) - } - } - return active -} - -// RootCompatible creates a new LogList containing only the logs of original -// LogList that are compatible with the provided cert, according to -// the passed in collection of per-log roots. Logs that are missing from -// the collection are treated as always compatible and included, even if -// an empty cert root is passed in. -// Cert-root when provided is expected to be CA-cert. -func (ll *LogList) RootCompatible(certRoot *x509.Certificate, roots LogRoots) LogList { - var compatible LogList - - // Check whether root is a CA-cert. - if certRoot != nil && !certRoot.IsCA { - klog.Warningf("Compatible method expects fully rooted chain, while last cert of the chain provided is not root") - return compatible - } - - for _, op := range ll.Operators { - compatibleOp := *op - compatibleOp.Logs = []*Log{} - for _, l := range op.Logs { - // If root set is not defined, we treat Log as compatible assuming no - // knowledge of its roots. - if _, ok := roots[l.URL]; !ok { - compatibleOp.Logs = append(compatibleOp.Logs, l) - continue - } - - if certRoot == nil { - continue - } - - // Check root is accepted. - if roots[l.URL].Included(certRoot) { - compatibleOp.Logs = append(compatibleOp.Logs, l) - } - } - if len(compatibleOp.Logs) > 0 { - compatible.Operators = append(compatible.Operators, &compatibleOp) - } - } - return compatible -} - -// TemporallyCompatible creates a new LogList containing only the logs of -// original LogList that are compatible with the provided cert, according to -// NotAfter and TemporalInterval matching. -// Returns empty LogList if nil-cert is provided. -func (ll *LogList) TemporallyCompatible(cert *x509.Certificate) LogList { - var compatible LogList - if cert == nil { - return compatible - } - - for _, op := range ll.Operators { - compatibleOp := *op - compatibleOp.Logs = []*Log{} - for _, l := range op.Logs { - if l.TemporalInterval == nil { - compatibleOp.Logs = append(compatibleOp.Logs, l) - continue - } - if cert.NotAfter.Before(l.TemporalInterval.EndExclusive) && (cert.NotAfter.After(l.TemporalInterval.StartInclusive) || cert.NotAfter.Equal(l.TemporalInterval.StartInclusive)) { - compatibleOp.Logs = append(compatibleOp.Logs, l) - } - } - if len(compatibleOp.Logs) > 0 { - compatible.Operators = append(compatible.Operators, &compatibleOp) - } - } - return compatible -} diff --git a/loglist2/logfilter_test.go b/loglist2/logfilter_test.go deleted file mode 100644 index fc08f366ce..0000000000 --- a/loglist2/logfilter_test.go +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2018 Google LLC. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package loglist2 - -import ( - "log" - "testing" - "time" - - "github.com/google/certificate-transparency-go/testdata" - "github.com/google/certificate-transparency-go/x509" - "github.com/google/certificate-transparency-go/x509util" - - "github.com/kylelemons/godebug/pretty" -) - -func subLogList(logURLs map[string]bool) LogList { - var ll LogList - for _, op := range sampleLogList.Operators { - opCopy := *op - opCopy.Logs = []*Log{} - for _, l := range op.Logs { - if logURLs[l.URL] { - opCopy.Logs = append(opCopy.Logs, l) - } - } - if len(opCopy.Logs) > 0 { - ll.Operators = append(ll.Operators, &opCopy) - } - } - return ll -} - -func TestSelectUsable(t *testing.T) { - tests := []struct { - name string - in LogList - want LogList - }{ - { - name: "Sample", - in: sampleLogList, - want: subLogList(map[string]bool{"https://ct.googleapis.com/icarus/": true}), - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - usableStat := make([]LogStatus, 1) - usableStat[0] = UsableLogStatus - got := test.in.SelectByStatus(usableStat) - if diff := pretty.Compare(test.want, got); diff != "" { - t.Errorf("Extracting active logs out of %v diff: (-want +got)\n%s", test.in, diff) - } - }) - } -} - -func TestSelectPendingAndQualified(t *testing.T) { - tests := []struct { - name string - in LogList - want LogList - }{ - { - name: "Sample", - in: sampleLogList, - want: subLogList(map[string]bool{"https://ct.googleapis.com/logs/argon2020/": true}), - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - stats := make([]LogStatus, 2) - stats[0] = PendingLogStatus - stats[1] = QualifiedLogStatus - got := test.in.SelectByStatus(stats) - if diff := pretty.Compare(test.want, got); diff != "" { - t.Errorf("Extracting qualified logs out of %v diff: (-want +got)\n%s", test.in, diff) - } - }) - } -} - -func artificialRoots(source string) LogRoots { - roots := LogRoots{ - "https://log.bob.io": x509util.NewPEMCertPool(), - "https://ct.googleapis.com/racketeer/": x509util.NewPEMCertPool(), - "https://ct.googleapis.com/rocketeer/": x509util.NewPEMCertPool(), - "https://ct.googleapis.com/aviator/": x509util.NewPEMCertPool(), - "https://ct.googleapis.com/logs/argon2020/": x509util.NewPEMCertPool(), - } - roots["https://log.bob.io"].AppendCertsFromPEM([]byte(source)) - return roots -} - -func TestRootCompatible(t *testing.T) { - cert, _ := x509util.CertificateFromPEM([]byte(testdata.TestPreCertPEM)) - caCert, _ := x509util.CertificateFromPEM([]byte(testdata.CACertPEM)) - - tests := []struct { - name string - in LogList - cert *x509.Certificate - rootCert *x509.Certificate - roots LogRoots - want LogList - }{ - { - name: "RootedChain", - in: sampleLogList, - cert: cert, - rootCert: caCert, - roots: artificialRoots(testdata.CACertPEM), - want: subLogList(map[string]bool{"https://log.bob.io": true, "https://ct.googleapis.com/icarus/": true}), // icarus has no root info. - }, - { - name: "RootedChainNoRootAccepted", - in: sampleLogList, - cert: cert, - rootCert: caCert, - roots: artificialRoots(testdata.TestPreCertPEM), - want: subLogList(map[string]bool{"https://ct.googleapis.com/icarus/": true}), // icarus has no root info. - }, - { - name: "Non-RootedChain", - in: sampleLogList, - cert: cert, - rootCert: cert, - roots: artificialRoots(testdata.CACertPEM), - want: subLogList(map[string]bool{}), - }, - { - name: "EmptyChain", - in: sampleLogList, - cert: nil, - rootCert: nil, - roots: artificialRoots(testdata.CACertPEM), - want: subLogList(map[string]bool{"https://ct.googleapis.com/icarus/": true}), - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.in.RootCompatible(test.rootCert, test.roots) - if diff := pretty.Compare(test.want, got); diff != "" { - t.Errorf("Getting compatible logs diff: (-want +got)\n%s", diff) - } - }) - } -} - -// stripErr is helper func for wrapping time.Parse -func stripErr(t time.Time, err error) time.Time { - if err != nil { - log.Fatal(err) - } - return t -} - -func TestTemporallyCompatible(t *testing.T) { - cert, _ := x509util.CertificateFromPEM([]byte(testdata.TestPreCertPEM)) - - tests := []struct { - name string - in LogList - cert *x509.Certificate - notAfter time.Time - want LogList - }{ - { - name: "AllLogsFitTemporally", - in: sampleLogList, - cert: cert, - notAfter: stripErr(time.Parse(time.UnixDate, "Sat Nov 8 11:06:00 PST 2014")), - want: subLogList(map[string]bool{"https://ct.googleapis.com/aviator/": true, "https://log.bob.io": true, "https://ct.googleapis.com/icarus/": true, "https://ct.googleapis.com/racketeer/": true, "https://ct.googleapis.com/rocketeer/": true}), - }, - { - name: "OperatorExcludedAllItsLogsMismatch", - in: sampleLogList, - cert: cert, - notAfter: stripErr(time.Parse(time.UnixDate, "Sat Mar 8 11:06:00 PST 2014")), - want: subLogList(map[string]bool{"https://ct.googleapis.com/aviator/": true, "https://ct.googleapis.com/icarus/": true, "https://ct.googleapis.com/racketeer/": true, "https://ct.googleapis.com/rocketeer/": true}), - }, - { - name: "TwoLogsAfterCertTimeExcluded", - in: sampleLogList, - cert: cert, - notAfter: stripErr(time.Parse(time.UnixDate, "Sat Mar 8 11:06:00 PST 2013")), - want: subLogList(map[string]bool{"https://ct.googleapis.com/icarus/": true, "https://ct.googleapis.com/racketeer/": true, "https://ct.googleapis.com/rocketeer/": true}), - }, - { - name: "TwoLogsBeforeCertTimeExcluded", - in: sampleLogList, - cert: cert, - notAfter: stripErr(time.Parse(time.UnixDate, "Sat Mar 8 11:06:00 PST 2016")), - want: subLogList(map[string]bool{"https://ct.googleapis.com/icarus/": true, "https://ct.googleapis.com/racketeer/": true, "https://ct.googleapis.com/rocketeer/": true}), - }, - { - name: "NilCert", - in: sampleLogList, - cert: nil, - notAfter: stripErr(time.Parse(time.UnixDate, "Sat Nov 8 11:06:00 PST 2014")), - want: subLogList(map[string]bool{}), - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if test.cert != nil { - test.cert.NotAfter = test.notAfter - } - got := test.in.TemporallyCompatible(test.cert) - if diff := pretty.Compare(test.want, got); diff != "" { - t.Errorf("Getting NotBefore-compatible logs diff: (-want +got)\n%s", diff) - } - }) - } -} - -func TestCompatible(t *testing.T) { - cert, _ := x509util.CertificateFromPEM([]byte(testdata.TestPreCertPEM)) - caCert, _ := x509util.CertificateFromPEM([]byte(testdata.CACertPEM)) - - tests := []struct { - name string - in LogList - notBefore time.Time - cert *x509.Certificate - rootCert *x509.Certificate - roots LogRoots - want LogList - }{ - { - name: "RootedChain", - in: sampleLogList, - notBefore: stripErr(time.Parse(time.UnixDate, "Sat Mar 8 11:06:00 PST 2016")), - cert: cert, - rootCert: caCert, - roots: artificialRoots(testdata.CACertPEM), - want: subLogList(map[string]bool{"https://ct.googleapis.com/icarus/": true}), // icarus has no root info. - }, - { - name: "RootedChainNoRootAccepted", - in: sampleLogList, - notBefore: stripErr(time.Parse(time.UnixDate, "Sat Mar 8 11:06:00 PST 2016")), - cert: cert, - rootCert: caCert, - roots: artificialRoots(testdata.TestPreCertPEM), - want: subLogList(map[string]bool{"https://ct.googleapis.com/icarus/": true}), // icarus has no root info. - }, - { - name: "Non-RootedChain", - in: sampleLogList, - notBefore: stripErr(time.Parse(time.UnixDate, "Sat Mar 8 11:06:00 PST 2016")), - cert: cert, - rootCert: cert, - roots: artificialRoots(testdata.CACertPEM), - want: subLogList(map[string]bool{}), - }, - { - name: "EmptyChain", - in: sampleLogList, - notBefore: stripErr(time.Parse(time.UnixDate, "Sat Mar 8 11:06:00 PST 2016")), - cert: nil, - rootCert: nil, - roots: artificialRoots(testdata.CACertPEM), - want: subLogList(map[string]bool{}), - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if test.cert != nil { - test.cert.NotBefore = test.notBefore - } - got := test.in.Compatible(test.cert, test.rootCert, test.roots) - if diff := pretty.Compare(test.want, got); diff != "" { - t.Errorf("Getting compatible logs diff: (-want +got)\n%s", diff) - } - }) - } -} diff --git a/loglist2/loglist2.go b/loglist2/loglist2.go deleted file mode 100644 index 861df4daba..0000000000 --- a/loglist2/loglist2.go +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright 2018 Google LLC. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package loglist2 allows parsing and searching of the master CT Log list. -// It expects the log list to conform to the v2 schema. -package loglist2 - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/rsa" - "crypto/sha256" - "encoding/base64" - "encoding/hex" - "encoding/json" - "fmt" - "regexp" - "strings" - "time" - "unicode" - - "github.com/google/certificate-transparency-go/tls" -) - -const ( - // LogListURL has the master URL for Google Chrome's log list. - LogListURL = "https://www.gstatic.com/ct/log_list/v2/log_list.json" - // LogListSignatureURL has the URL for the signature over Google Chrome's log list. - LogListSignatureURL = "https://www.gstatic.com/ct/log_list/v2/log_list.sig" - // AllLogListURL has the URL for the list of all known logs (which isn't signed). - AllLogListURL = "https://www.gstatic.com/ct/log_list/v2/all_logs_list.json" -) - -// Manually mapped from https://www.gstatic.com/ct/log_list/v2/log_list_schema.json - -// LogList holds a collection of CT logs, grouped by operator. -type LogList struct { - // Operators is a list of CT log operators and the logs they operate. - Operators []*Operator `json:"operators"` -} - -// Operator holds a collection of CT logs run by the same organisation. -// It also provides information about that organisation, e.g. contact details. -type Operator struct { - // Name is the name of the CT log operator. - Name string `json:"name"` - // Email lists the email addresses that can be used to contact this log - // operator. - Email []string `json:"email"` - // Logs is a list of CT logs run by this operator. - Logs []*Log `json:"logs"` -} - -// Log describes a single CT log. -type Log struct { - // Description is a human-readable string that describes the log. - Description string `json:"description,omitempty"` - // LogID is the SHA-256 hash of the log's public key. - LogID []byte `json:"log_id"` - // Key is the public key with which signatures can be verified. - Key []byte `json:"key"` - // URL is the address of the HTTPS API. - URL string `json:"url"` - // DNS is the address of the DNS API. - DNS string `json:"dns,omitempty"` - // MMD is the Maximum Merge Delay, in seconds. All submitted - // certificates must be incorporated into the log within this time. - MMD int32 `json:"mmd"` - // State is the current state of the log, from the perspective of the - // log list distributor. - State *LogStates `json:"state,omitempty"` - // TemporalInterval, if set, indicates that this log only accepts - // certificates with a NotAfter date in this time range. - TemporalInterval *TemporalInterval `json:"temporal_interval,omitempty"` - // Type indicates the purpose of this log, e.g. "test" or "prod". - Type string `json:"log_type,omitempty"` -} - -// TemporalInterval is a time range. -type TemporalInterval struct { - // StartInclusive is the beginning of the time range. - StartInclusive time.Time `json:"start_inclusive"` - // EndExclusive is just after the end of the time range. - EndExclusive time.Time `json:"end_exclusive"` -} - -// LogStatus indicates Log status. -type LogStatus int - -// LogStatus values -const ( - UndefinedLogStatus LogStatus = iota - PendingLogStatus - QualifiedLogStatus - UsableLogStatus - ReadOnlyLogStatus - RetiredLogStatus - RejectedLogStatus -) - -//go:generate stringer -type=LogStatus - -// LogStates are the states that a CT log can be in, from the perspective of a -// user agent. Only one should be set - this is the current state. -type LogStates struct { - // Pending indicates that the log is in the "pending" state. - Pending *LogState `json:"pending,omitempty"` - // Qualified indicates that the log is in the "qualified" state. - Qualified *LogState `json:"qualified,omitempty"` - // Usable indicates that the log is in the "usable" state. - Usable *LogState `json:"usable,omitempty"` - // ReadOnly indicates that the log is in the "readonly" state. - ReadOnly *ReadOnlyLogState `json:"readonly,omitempty"` - // Retired indicates that the log is in the "retired" state. - Retired *LogState `json:"retired,omitempty"` - // Rejected indicates that the log is in the "rejected" state. - Rejected *LogState `json:"rejected,omitempty"` -} - -// LogState contains details on the current state of a CT log. -type LogState struct { - // Timestamp is the time when the state began. - Timestamp time.Time `json:"timestamp"` -} - -// ReadOnlyLogState contains details on the current state of a read-only CT log. -type ReadOnlyLogState struct { - LogState - // FinalTreeHead is the root hash and tree size at which the CT log was - // made read-only. This should never change while the log is read-only. - FinalTreeHead TreeHead `json:"final_tree_head"` -} - -// TreeHead is the root hash and tree size of a CT log. -type TreeHead struct { - // SHA256RootHash is the root hash of the CT log's Merkle tree. - SHA256RootHash []byte `json:"sha256_root_hash"` - // TreeSize is the size of the CT log's Merkle tree. - TreeSize int64 `json:"tree_size"` -} - -// LogStatus method returns Log-status enum value for descriptive struct. -func (ls *LogStates) LogStatus() LogStatus { - switch { - case ls == nil: - return UndefinedLogStatus - case ls.Pending != nil: - return PendingLogStatus - case ls.Qualified != nil: - return QualifiedLogStatus - case ls.Usable != nil: - return UsableLogStatus - case ls.ReadOnly != nil: - return ReadOnlyLogStatus - case ls.Retired != nil: - return RetiredLogStatus - case ls.Rejected != nil: - return RejectedLogStatus - default: - return UndefinedLogStatus - } -} - -// String method returns printable name of the state. -func (ls *LogStates) String() string { - return ls.LogStatus().String() -} - -// Active picks the set-up state. If multiple states are set (not expected) picks one of them. -func (ls *LogStates) Active() (*LogState, *ReadOnlyLogState) { - if ls == nil { - return nil, nil - } - switch { - case ls.Pending != nil: - return ls.Pending, nil - case ls.Qualified != nil: - return ls.Qualified, nil - case ls.Usable != nil: - return ls.Usable, nil - case ls.ReadOnly != nil: - return nil, ls.ReadOnly - case ls.Retired != nil: - return ls.Retired, nil - case ls.Rejected != nil: - return ls.Rejected, nil - default: - return nil, nil - } -} - -// GoogleOperated returns whether Operator is considered to be Google. -func (op *Operator) GoogleOperated() bool { - for _, email := range op.Email { - if strings.Contains(email, "google-ct-logs@googlegroups") { - return true - } - } - return false -} - -// NewFromJSON creates a LogList from JSON encoded data. -func NewFromJSON(llData []byte) (*LogList, error) { - var ll LogList - if err := json.Unmarshal(llData, &ll); err != nil { - return nil, fmt.Errorf("failed to parse log list: %v", err) - } - return &ll, nil -} - -// NewFromSignedJSON creates a LogList from JSON encoded data, checking a -// signature along the way. The signature data should be provided as the -// raw signature data. -func NewFromSignedJSON(llData, rawSig []byte, pubKey crypto.PublicKey) (*LogList, error) { - var sigAlgo tls.SignatureAlgorithm - switch pkType := pubKey.(type) { - case *rsa.PublicKey: - sigAlgo = tls.RSA - case *ecdsa.PublicKey: - sigAlgo = tls.ECDSA - default: - return nil, fmt.Errorf("unsupported public key type %v", pkType) - } - tlsSig := tls.DigitallySigned{ - Algorithm: tls.SignatureAndHashAlgorithm{ - Hash: tls.SHA256, - Signature: sigAlgo, - }, - Signature: rawSig, - } - if err := tls.VerifySignature(pubKey, llData, tlsSig); err != nil { - return nil, fmt.Errorf("failed to verify signature: %v", err) - } - return NewFromJSON(llData) -} - -// FindLogByName returns all logs whose names contain the given string. -func (ll *LogList) FindLogByName(name string) []*Log { - name = strings.ToLower(name) - var results []*Log - for _, op := range ll.Operators { - for _, log := range op.Logs { - if strings.Contains(strings.ToLower(log.Description), name) { - results = append(results, log) - } - } - } - return results -} - -// FindLogByURL finds the log with the given URL. -func (ll *LogList) FindLogByURL(url string) *Log { - for _, op := range ll.Operators { - for _, log := range op.Logs { - // Don't count trailing slashes - if strings.TrimRight(log.URL, "/") == strings.TrimRight(url, "/") { - return log - } - } - } - return nil -} - -// FindLogByKeyHash finds the log with the given key hash. -func (ll *LogList) FindLogByKeyHash(keyhash [sha256.Size]byte) *Log { - for _, op := range ll.Operators { - for _, log := range op.Logs { - if bytes.Equal(log.LogID, keyhash[:]) { - return log - } - } - } - return nil -} - -// FindLogByKeyHashPrefix finds all logs whose key hash starts with the prefix. -func (ll *LogList) FindLogByKeyHashPrefix(prefix string) []*Log { - var results []*Log - for _, op := range ll.Operators { - for _, log := range op.Logs { - hh := hex.EncodeToString(log.LogID[:]) - if strings.HasPrefix(hh, prefix) { - results = append(results, log) - } - } - } - return results -} - -// FindLogByKey finds the log with the given DER-encoded key. -func (ll *LogList) FindLogByKey(key []byte) *Log { - for _, op := range ll.Operators { - for _, log := range op.Logs { - if bytes.Equal(log.Key[:], key) { - return log - } - } - } - return nil -} - -var hexDigits = regexp.MustCompile("^[0-9a-fA-F]+$") - -// FuzzyFindLog tries to find logs that match the given unspecified input, -// whose format is unspecified. This generally returns a single log, but -// if text input that matches multiple log descriptions is provided, then -// multiple logs may be returned. -func (ll *LogList) FuzzyFindLog(input string) []*Log { - input = strings.Trim(input, " \t") - if logs := ll.FindLogByName(input); len(logs) > 0 { - return logs - } - if log := ll.FindLogByURL(input); log != nil { - return []*Log{log} - } - // Try assuming the input is binary data of some form. First base64: - if data, err := base64.StdEncoding.DecodeString(input); err == nil { - if len(data) == sha256.Size { - var hash [sha256.Size]byte - copy(hash[:], data) - if log := ll.FindLogByKeyHash(hash); log != nil { - return []*Log{log} - } - } - if log := ll.FindLogByKey(data); log != nil { - return []*Log{log} - } - } - // Now hex, but strip all internal whitespace first. - input = stripInternalSpace(input) - if data, err := hex.DecodeString(input); err == nil { - if len(data) == sha256.Size { - var hash [sha256.Size]byte - copy(hash[:], data) - if log := ll.FindLogByKeyHash(hash); log != nil { - return []*Log{log} - } - } - if log := ll.FindLogByKey(data); log != nil { - return []*Log{log} - } - } - // Finally, allow hex strings with an odd number of digits. - if hexDigits.MatchString(input) { - if logs := ll.FindLogByKeyHashPrefix(input); len(logs) > 0 { - return logs - } - } - - return nil -} - -func stripInternalSpace(input string) string { - return strings.Map(func(r rune) rune { - if !unicode.IsSpace(r) { - return r - } - return -1 - }, input) -} diff --git a/loglist2/loglist2_test.go b/loglist2/loglist2_test.go deleted file mode 100644 index be93c1d9d4..0000000000 --- a/loglist2/loglist2_test.go +++ /dev/null @@ -1,542 +0,0 @@ -// Copyright 2018 Google LLC. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package loglist2 - -import ( - "bytes" - "crypto/sha256" - "encoding/base64" - "encoding/json" - "fmt" - "log" - "reflect" - "sort" - "strings" - "testing" - "time" - - "github.com/sergi/go-diff/diffmatchpatch" -) - -// mustParseTime is helper func -func mustParseTime(format string, sTime string) time.Time { - tm, e := time.Parse(format, sTime) - if e != nil { - log.Fatal(e) - } - return tm.UTC() -} - -var sampleLogList = LogList{ - Operators: []*Operator{ - { - Name: "Google", - Email: []string{"google-ct-logs@googlegroups.com"}, - Logs: []*Log{ - { - Description: "Google 'Aviator' log", - LogID: deb64("aPaY+B9kgr46jO65KB1M/HFRXWeT1ETRCmesu09P+8Q="), - Key: deb64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1/TMabLkDpCjiupacAlP7xNi0I1JYP8bQFAHDG1xhtolSY1l4QgNRzRrvSe8liE+NPWHdjGxfx3JhTsN9x8/6Q=="), - URL: "https://ct.googleapis.com/aviator/", - MMD: 86400, - State: &LogStates{ - ReadOnly: &ReadOnlyLogState{ - LogState: LogState{Timestamp: time.Unix(1480512258, 330000000).UTC()}, - FinalTreeHead: TreeHead{ - TreeSize: 46466472, - SHA256RootHash: deb64("LcGcZRsm+LGYmrlyC5LXhV1T6OD8iH5dNlb0sEJl9bA="), - }, - }, - }, - TemporalInterval: &TemporalInterval{ - StartInclusive: mustParseTime(time.UnixDate, "Fri Mar 7 11:06:00 UTC 2014"), - EndExclusive: mustParseTime(time.UnixDate, "Sat Mar 7 12:00:00 UTC 2015"), - }, - DNS: "aviator.ct.googleapis.com", - }, - { - Description: "Google 'Icarus' log", - LogID: deb64("KTxRllTIOWW6qlD8WAfUt2+/WHopctykwwz05UVH9Hg="), - Key: deb64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETtK8v7MICve56qTHHDhhBOuV4IlUaESxZryCfk9QbG9co/CqPvTsgPDbCpp6oFtyAHwlDhnvr7JijXRD9Cb2FA=="), - URL: "https://ct.googleapis.com/icarus/", - MMD: 86400, - State: &LogStates{ - Usable: &LogState{ - Timestamp: mustParseTime(time.RFC3339, "2018-02-27T00:00:00Z"), - }, - }, - DNS: "icarus.ct.googleapis.com", - }, - { - Description: "Google 'Racketeer' log", - LogID: deb64("7kEv4llINIlh4vPgjGgugT7A/3cLbXUXF2OvMBT/l2g="), - // Key value chosed to have a hash that starts ee4... (specifically ee412fe25948348961e2f3e08c682e813ec0ff770b6d75171763af3014ff9768) - Key: deb64("Hy2TPTZ2yq9ASMmMZiB9SZEUx5WNH5G0Ft5Tm9vKMcPXA+ic/Ap3gg6fXzBJR8zLkt5lQjvKMdbHYMGv7yrsZg=="), - URL: "https://ct.googleapis.com/racketeer/", - MMD: 86400, - DNS: "racketeer.ct.googleapis.com", - }, - { - Description: "Google 'Rocketeer' log", - LogID: deb64("7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo/cs="), - Key: deb64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIFsYyDzBi7MxCAC/oJBXK7dHjG+1aLCOkHjpoHPqTyghLpzA9BYbqvnV16mAw04vUjyYASVGJCUoI3ctBcJAeg=="), - URL: "https://ct.googleapis.com/rocketeer/", - MMD: 86400, - DNS: "rocketeer.ct.googleapis.com", - }, - { - Description: "Google 'Argon2020' log", - LogID: deb64("sh4FzIuizYogTodm+Su5iiUgZ2va+nDnsklTLe+LkF4="), - Key: deb64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6Tx2p1yKY4015NyIYvdrk36es0uAc1zA4PQ+TGRY+3ZjUTIYY9Wyu+3q/147JG4vNVKLtDWarZwVqGkg6lAYzA=="), - URL: "https://ct.googleapis.com/logs/argon2020/", - DNS: "argon2020.ct.googleapis.com", - MMD: 86400, - State: &LogStates{ - Qualified: &LogState{ - Timestamp: mustParseTime(time.RFC3339, "2018-02-27T00:00:00Z"), - }, - }, - TemporalInterval: &TemporalInterval{ - StartInclusive: mustParseTime(time.RFC3339, "2020-01-01T00:00:00Z"), - EndExclusive: mustParseTime(time.RFC3339, "2021-01-01T00:00:00Z"), - }, - }, - }, - }, - { - Name: "Bob's CT Log Shop", - Email: []string{"bob@example.com"}, - Logs: []*Log{ - { - Description: "Bob's Dubious Log", - LogID: deb64("zbUXm3/BwEb+6jETaj+PAC5hgvr4iW/syLL1tatgSQA="), - Key: deb64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECyPLhWKYYUgEc+tUXfPQB4wtGS2MNvXrjwFCCnyYJifBtd2Sk7Cu+Js9DNhMTh35FftHaHu6ZrclnNBKwmbbSA=="), - URL: "https://log.bob.io", - MMD: 86400, - State: &LogStates{ - Retired: &LogState{ - Timestamp: time.Unix(1460678400, 0).UTC(), - }, - }, - TemporalInterval: &TemporalInterval{ - StartInclusive: mustParseTime(time.UnixDate, "Fri Nov 7 12:00:00 UTC 2014"), - EndExclusive: mustParseTime(time.UnixDate, "Sat Mar 7 12:00:00 UTC 2015"), - }, - DNS: "dubious-bob.ct.googleapis.com", - }, - }, - }, - }, -} - -func TestJSONMarshal(t *testing.T) { - var tests = []struct { - name string - in LogList - want, wantErr string - }{ - { - name: "MultiValid", - in: sampleLogList, - want: `{"operators":[` + - `{"name":"Google","email":["google-ct-logs@googlegroups.com"],"logs":[` + - `{"description":"Google 'Aviator' log","log_id":"aPaY+B9kgr46jO65KB1M/HFRXWeT1ETRCmesu09P+8Q=","key":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1/TMabLkDpCjiupacAlP7xNi0I1JYP8bQFAHDG1xhtolSY1l4QgNRzRrvSe8liE+NPWHdjGxfx3JhTsN9x8/6Q==","url":"https://ct.googleapis.com/aviator/","dns":"aviator.ct.googleapis.com","mmd":86400,"state":{"readonly":{"timestamp":"2016-11-30T13:24:18.33Z","final_tree_head":{"sha256_root_hash":"LcGcZRsm+LGYmrlyC5LXhV1T6OD8iH5dNlb0sEJl9bA=","tree_size":46466472}}},"temporal_interval":{"start_inclusive":"2014-03-07T11:06:00Z","end_exclusive":"2015-03-07T12:00:00Z"}},` + - `{"description":"Google 'Icarus' log","log_id":"KTxRllTIOWW6qlD8WAfUt2+/WHopctykwwz05UVH9Hg=","key":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETtK8v7MICve56qTHHDhhBOuV4IlUaESxZryCfk9QbG9co/CqPvTsgPDbCpp6oFtyAHwlDhnvr7JijXRD9Cb2FA==","url":"https://ct.googleapis.com/icarus/","dns":"icarus.ct.googleapis.com","mmd":86400,"state":{"usable":{"timestamp":"2018-02-27T00:00:00Z"}}},` + - `{"description":"Google 'Racketeer' log","log_id":"7kEv4llINIlh4vPgjGgugT7A/3cLbXUXF2OvMBT/l2g=","key":"Hy2TPTZ2yq9ASMmMZiB9SZEUx5WNH5G0Ft5Tm9vKMcPXA+ic/Ap3gg6fXzBJR8zLkt5lQjvKMdbHYMGv7yrsZg==","url":"https://ct.googleapis.com/racketeer/","dns":"racketeer.ct.googleapis.com","mmd":86400},` + - `{"description":"Google 'Rocketeer' log","log_id":"7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo/cs=","key":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIFsYyDzBi7MxCAC/oJBXK7dHjG+1aLCOkHjpoHPqTyghLpzA9BYbqvnV16mAw04vUjyYASVGJCUoI3ctBcJAeg==","url":"https://ct.googleapis.com/rocketeer/","dns":"rocketeer.ct.googleapis.com","mmd":86400},` + - `{"description":"Google 'Argon2020' log","log_id": "sh4FzIuizYogTodm+Su5iiUgZ2va+nDnsklTLe+LkF4=","key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6Tx2p1yKY4015NyIYvdrk36es0uAc1zA4PQ+TGRY+3ZjUTIYY9Wyu+3q/147JG4vNVKLtDWarZwVqGkg6lAYzA==","url":"https://ct.googleapis.com/logs/argon2020/","dns":"argon2020.ct.googleapis.com","mmd":86400,"state":{"qualified":{"timestamp":"2018-02-27T00:00:00Z"}},"temporal_interval":{"start_inclusive":"2020-01-01T00:00:00Z","end_exclusive":"2021-01-01T00:00:00Z"}}]},` + - `{"name":"Bob's CT Log Shop","email":["bob@example.com"],"logs":[` + - `{"description":"Bob's Dubious Log","log_id":"zbUXm3/BwEb+6jETaj+PAC5hgvr4iW/syLL1tatgSQA=","key":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECyPLhWKYYUgEc+tUXfPQB4wtGS2MNvXrjwFCCnyYJifBtd2Sk7Cu+Js9DNhMTh35FftHaHu6ZrclnNBKwmbbSA==","url":"https://log.bob.io","dns":"dubious-bob.ct.googleapis.com","mmd":86400,"state":{"retired":{"timestamp":"2016-04-15T00:00:00Z"}},"temporal_interval":{"start_inclusive":"2014-11-07T12:00:00Z","end_exclusive":"2015-03-07T12:00:00Z"}}]}]}`, - }, - } - - const jsonPrefix = "" - const jsonIndent = " " - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got, err := json.MarshalIndent(&test.in, jsonPrefix, jsonIndent) - if err != nil { - if test.wantErr == "" { - t.Errorf("json.Marshal()=nil,%v; want _,nil", err) - } else if !strings.Contains(err.Error(), test.wantErr) { - t.Errorf("json.Marshal()=nil,%v; want nil,err containing %q", err, test.wantErr) - } - return - } - if test.wantErr != "" { - t.Errorf("json.Marshal()=%q,nil; want nil,err containing %q", got, test.wantErr) - } - // Format `test.want` in the same way as `got` - var wantIndented bytes.Buffer - if err := json.Indent(&wantIndented, []byte(test.want), jsonPrefix, jsonIndent); err != nil { - t.Fatalf("json.Indent(test.want) = %q, want nil", err) - } - dmp := diffmatchpatch.New() - if diffs := dmp.DiffMain(wantIndented.String(), string(got), false); len(diffs) > 1 { - t.Errorf("json.Marshal(): diff \n%s", dmp.DiffPrettyText(diffs)) - } - }) - } -} - -func TestFindLogByName(t *testing.T) { - var tests = []struct { - name, in string - want int - }{ - {name: "Single", in: "Dubious", want: 1}, - {name: "SingleDifferentCase", in: "DUBious", want: 1}, - {name: "Multiple", in: "Google", want: 5}, - {name: "None", in: "Llamalog", want: 0}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := sampleLogList.FindLogByName(test.in) - if len(got) != test.want { - t.Errorf("len(FindLogByName(%q)=%d, want %d", test.in, len(got), test.want) - } - }) - } -} - -func TestFindLogByURL(t *testing.T) { - var tests = []struct { - name, in, want string - }{ - {name: "NotFound", in: "nowhere.com"}, - {name: "Found//", in: "https://ct.googleapis.com/icarus/", want: "Google 'Icarus' log"}, - {name: "Found./", in: "https://ct.googleapis.com/icarus", want: "Google 'Icarus' log"}, - {name: "Found/.", in: "https://log.bob.io/", want: "Bob's Dubious Log"}, - {name: "Found..", in: "https://log.bob.io", want: "Bob's Dubious Log"}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - l := sampleLogList.FindLogByURL(test.in) - got := "" - if l != nil { - got = l.Description - } - if got != test.want { - t.Errorf("FindLogByURL(%q)=%q, want %q", test.in, got, test.want) - } - }) - } -} - -func TestFindLogByKeyhash(t *testing.T) { - var tests = []struct { - name string - in []byte - want string - }{ - { - name: "NotFound", - in: []byte{0xaa, 0xbb, 0xcc}, - }, - { - name: "FoundRocketeer", - in: []byte{ - 0xee, 0x4b, 0xbd, 0xb7, 0x75, 0xce, 0x60, 0xba, 0xe1, 0x42, 0x69, 0x1f, 0xab, 0xe1, 0x9e, 0x66, - 0xa3, 0x0f, 0x7e, 0x5f, 0xb0, 0x72, 0xd8, 0x83, 0x00, 0xc4, 0x7b, 0x89, 0x7a, 0xa8, 0xfd, 0xcb, - }, - want: "Google 'Rocketeer' log", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var hash [sha256.Size]byte - copy(hash[:], test.in) - l := sampleLogList.FindLogByKeyHash(hash) - got := "" - if l != nil { - got = l.Description - } - if got != test.want { - t.Errorf("FindLogByKeyHash(%x)=%q, want %q", test.in, got, test.want) - } - }) - } -} - -func TestFindLogByKeyhashPrefix(t *testing.T) { - var tests = []struct { - name, in string - want []string - }{ - { - name: "NotFound", - in: "aabbcc", - want: []string{}, - }, - { - name: "FoundRocketeer", - in: "ee4b", - want: []string{"Google 'Rocketeer' log"}, - }, - { - name: "FoundRocketeerOdd", - in: "ee4bb", - want: []string{"Google 'Rocketeer' log"}, - }, - { - name: "FoundMultiple", - in: "ee4", - want: []string{"Google 'Racketeer' log", "Google 'Rocketeer' log"}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - logs := sampleLogList.FindLogByKeyHashPrefix(test.in) - got := make([]string, len(logs)) - for i, l := range logs { - got[i] = l.Description - } - sort.Strings(got) - if !reflect.DeepEqual(got, test.want) { - t.Errorf("FindLogByKeyHash(%x)=%q, want %q", test.in, got, test.want) - } - }) - } -} - -func TestFindLogByKey(t *testing.T) { - var tests = []struct { - name string - in []byte - want string - }{ - { - name: "NotFound", - in: []byte{0xaa, 0xbb, 0xcc}, - }, - { - name: "FoundRocketeer", - in: deb64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIFsYyDzBi7MxCAC/oJBXK7dHjG+1aLCOkHjpoHPqTyghLpzA9BYbqvnV16mAw04vUjyYASVGJCUoI3ctBcJAeg=="), - want: "Google 'Rocketeer' log", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - l := sampleLogList.FindLogByKey(test.in) - got := "" - if l != nil { - got = l.Description - } - if got != test.want { - t.Errorf("FindLogByKey(%x)=%q, want %q", test.in, got, test.want) - } - }) - } -} - -func TestFuzzyFindLog(t *testing.T) { - var tests = []struct { - name, in string - want []string - }{ - { - name: "NotFound", - in: "aabbcc", - want: []string{}, - }, - { - name: "FoundByKey64", - in: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIFsYyDzBi7MxCAC/oJBXK7dHjG+1aLCOkHjpoHPqTyghLpzA9BYbqvnV16mAw04vUjyYASVGJCUoI3ctBcJAeg==", - want: []string{"Google 'Rocketeer' log"}, - }, - { - name: "FoundByKeyHex", - in: "3059301306072a8648ce3d020106082a8648ce3d03010703420004205b18c83cc18bb3310800bfa090572bb7478c6fb568b08e9078e9a073ea4f28212e9cc0f4161baaf9d5d7a980c34e2f523c9801254624252823772d05c2407a", - want: []string{"Google 'Rocketeer' log"}, - }, - { - name: "FoundByKeyHashHex", - in: " ee 4b bd b7 75 ce 60 ba e1 42 69 1f ab e1 9e 66 a3 0f 7e 5f b0 72 d8 83 00 c4 7b 89 7a a8 fd cb", - want: []string{"Google 'Rocketeer' log"}, - }, - { - name: "FoundByKeyHashHexPrefix", - in: "ee4bbdb7", - want: []string{"Google 'Rocketeer' log"}, - }, - { - name: "FoundByKeyHash64", - in: "7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo/cs=", - want: []string{"Google 'Rocketeer' log"}, - }, - { - name: "FoundByName", - in: "Rocketeer", - want: []string{"Google 'Rocketeer' log"}, - }, - { - name: "FoundByNameDifferentCase", - in: "rocketeer", - want: []string{"Google 'Rocketeer' log"}, - }, - { - name: "FoundByURL", - in: "https://ct.googleapis.com/rocketeer", - want: []string{"Google 'Rocketeer' log"}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - logs := sampleLogList.FuzzyFindLog(test.in) - got := make([]string, len(logs)) - for i, l := range logs { - got[i] = l.Description - } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("FuzzyFindLog(%q)=%v, want %v", test.in, got, test.want) - } - }) - } -} - -func TestStripInternalSpace(t *testing.T) { - var tests = []struct { - in string - want string - }{ - {in: "xyz", want: "xyz"}, - {in: "x y z", want: "xyz"}, - {in: "x yz ", want: "xyz"}, - {in: " xyz ", want: "xyz"}, - {in: "xy\t\tz", want: "xyz"}, - } - - for _, test := range tests { - got := stripInternalSpace(test.in) - if got != test.want { - t.Errorf("stripInternalSpace(%q)=%q, want %q", test.in, got, test.want) - } - } -} - -func TestLogStatesString(t *testing.T) { - var tests = []struct { - name string - logURL string - want string - }{ - {name: "ReadOnly", logURL: "https://ct.googleapis.com/aviator/", want: "ReadOnlyLogStatus"}, - {name: "Empty", logURL: "https://ct.googleapis.com/racketeer/", want: "UndefinedLogStatus"}, - {name: "Usable", logURL: "https://ct.googleapis.com/icarus/", want: "UsableLogStatus"}, - {name: "Retired", logURL: "https://log.bob.io", want: "RetiredLogStatus"}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - l := sampleLogList.FindLogByURL(test.logURL) - if got := l.State.String(); got != test.want { - t.Errorf("%q: Log.State.String() = %s, want %s", test.logURL, got, test.want) - } - }) - } -} - -func TestLogStatesActive(t *testing.T) { - a := &LogState{Timestamp: time.Unix(1460678400, 0).UTC()} - f := &ReadOnlyLogState{ - LogState: LogState{Timestamp: time.Unix(1480512258, 330000000).UTC()}, - FinalTreeHead: TreeHead{ - TreeSize: 46466472, - SHA256RootHash: []byte{}, - }, - } - var tests = []struct { - name string - in *LogStates - wantState *LogState - wantFState *ReadOnlyLogState - }{ - { - name: "Retired", - in: &LogStates{ - Retired: a, - }, - wantState: a, - wantFState: nil, - }, - { - name: "ReadOnly", - in: &LogStates{ - ReadOnly: f, - }, - wantState: nil, - wantFState: f, - }, - { - name: "Qualified", - in: &LogStates{ - Qualified: a, - }, - wantState: a, - wantFState: nil, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - gotState, gotFState := test.in.Active() - if gotState != test.wantState { - t.Errorf("Log-state from Active() = %q, want %q", gotState, test.wantState) - } - if gotFState != test.wantFState { - t.Errorf("ReadOnly Log-state from Active() = %q, want %q", gotFState, test.wantFState) - } - }) - } -} - -func changeOperatorEmail(op Operator, email string) Operator { - op.Email = make([]string, 1) - op.Email[0] = email - return op -} - -func TestGoogleOperated(t *testing.T) { - var tests = []struct { - in Operator - out bool - }{ - {in: *(sampleLogList.Operators[0]), out: true}, - {in: *sampleLogList.Operators[1], out: false}, - {in: changeOperatorEmail(*sampleLogList.Operators[1], "google-ct-logs@googlegroups"), out: true}, - {in: changeOperatorEmail(*sampleLogList.Operators[0], "operator@googlegroups"), out: false}, - } - for _, test := range tests { - isGoog := test.in.GoogleOperated() - if isGoog != test.out { - t.Errorf("GoogleOperated status for %s is %t, want %t", test.in.Name, isGoog, test.out) - } - } -} - -func deb64(b string) []byte { - data, err := base64.StdEncoding.DecodeString(b) - if err != nil { - panic(fmt.Sprintf("hard-coded test data failed to decode: %v", err)) - } - return data -} diff --git a/loglist2/logstatus_string.go b/loglist2/logstatus_string.go deleted file mode 100644 index 0d1adf60dc..0000000000 --- a/loglist2/logstatus_string.go +++ /dev/null @@ -1,29 +0,0 @@ -// Code generated by "stringer -type=LogStatus"; DO NOT EDIT. - -package loglist2 - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[UndefinedLogStatus-0] - _ = x[PendingLogStatus-1] - _ = x[QualifiedLogStatus-2] - _ = x[UsableLogStatus-3] - _ = x[ReadOnlyLogStatus-4] - _ = x[RetiredLogStatus-5] - _ = x[RejectedLogStatus-6] -} - -const _LogStatus_name = "UndefinedLogStatusPendingLogStatusQualifiedLogStatusUsableLogStatusReadOnlyLogStatusRetiredLogStatusRejectedLogStatus" - -var _LogStatus_index = [...]uint8{0, 18, 34, 52, 67, 84, 100, 117} - -func (i LogStatus) String() string { - if i < 0 || i >= LogStatus(len(_LogStatus_index)-1) { - return "LogStatus(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _LogStatus_name[_LogStatus_index[i]:_LogStatus_index[i+1]] -}