From 0f0f94f2fd3ca54e73716553423d9e967574f1aa Mon Sep 17 00:00:00 2001 From: Amir Omidi Date: Tue, 29 Nov 2022 19:14:01 +0000 Subject: [PATCH 01/12] Add the skeleton around linting CRLs --- v3/lint/base.go | 191 +++++++++++++++++++++-- v3/lint/registration.go | 324 ++++++++++++++++++++++++++++++++-------- 2 files changed, 446 insertions(+), 69 deletions(-) diff --git a/v3/lint/base.go b/v3/lint/base.go index c5afe8a31..1b89142d6 100644 --- a/v3/lint/base.go +++ b/v3/lint/base.go @@ -15,6 +15,7 @@ package lint */ import ( + ox509 "crypto/x509" "fmt" "time" @@ -22,8 +23,26 @@ import ( "github.com/zmap/zlint/v3/util" ) -// LintInterface is implemented by each Lint. -type LintInterface interface { //nolint:revive +// LintInterface is implemented by each certificate linter. +// +// @deprecated - use CertificateLintInterface instead. +type LintInterface = CertificateLintInterface + +// RevocationListLintInterface is implemented by each revocation list linter. +type RevocationListLintInterface interface { + // CheckApplies runs once per revocation list. It returns true if the + // Lint should run on the given certificate. If CheckApplies returns + // false, the Lint result is automatically set to NA without calling + // CheckEffective() or Run(). + CheckApplies(r *ox509.RevocationList) bool + + // Execute() is the body of the lint. It is called for every revocation list + // for which CheckApplies() returns true. + Execute(r *ox509.RevocationList) *LintResult +} + +// CertificateLintInterface is implemented by each certificate linter. +type CertificateLintInterface interface { // CheckApplies runs once per certificate. It returns true if the Lint should // run on the given certificate. If CheckApplies returns false, the Lint // result is automatically set to NA without calling CheckEffective() or @@ -40,10 +59,39 @@ type Configurable interface { Configure() interface{} } +// LintMeta struct represents the metadata associated with a single lint. +type LintMeta struct { + // Name is a lowercase underscore-separated string describing what a given + // Lint checks. If Name beings with "w", the lint MUST NOT return Error, only + // Warn. If Name beings with "e", the Lint MUST NOT return Warn, only Error. + Name string `json:"name,omitempty"` + + // A human-readable description of what the Lint checks. Usually copied + // directly from the CA/B Baseline Requirements or RFC 5280. + Description string `json:"description,omitempty"` + + // The source of the check, e.g. "BRs: 6.1.6" or "RFC 5280: 4.1.2.6". + Citation string `json:"citation,omitempty"` + + // Programmatic source of the check, BRs, RFC5280, or ZLint + Source LintSource `json:"source"` + + // Lints automatically returns NE for all certificates where CheckApplies() is + // true but with NotBefore < EffectiveDate. This check is bypassed if + // EffectiveDate is zero. Please see CheckEffective for more information. + EffectiveDate time.Time `json:"-"` + + // Lints automatically returns NE for all certificates where CheckApplies() is + // true but with NotBefore >= IneffectiveDate. This check is bypassed if + // IneffectiveDate is zero. Please see CheckEffective for more information. + IneffectiveDate time.Time `json:"-"` +} + // A Lint struct represents a single lint, e.g. // "e_basic_constraints_not_critical". It contains an implementation of LintInterface. +// +// @deprecated - use CertificateLint instead. type Lint struct { - // Name is a lowercase underscore-separated string describing what a given // Lint checks. If Name beings with "w", the lint MUST NOT return Error, only // Warn. If Name beings with "e", the Lint MUST NOT return Warn, only Error. @@ -68,21 +116,90 @@ type Lint struct { // true but with NotBefore >= IneffectiveDate. This check is bypassed if // IneffectiveDate is zero. Please see CheckEffective for more information. IneffectiveDate time.Time `json:"-"` - // A constructor which returns the implementation of the lint logic. Lint func() LintInterface `json:"-"` } +// toCertificateLint converts a Lint to a CertificateLint for backwards compatibility. +// +// @deprecated - Use CertificateLint directly. +func (l *Lint) toCertificateLint() *CertificateLint { + return &CertificateLint{ + LintMeta: LintMeta{ + Name: l.Name, + Description: l.Description, + Citation: l.Citation, + Source: l.Source, + EffectiveDate: l.EffectiveDate, + IneffectiveDate: l.IneffectiveDate, + }, + Lint: l.Lint, + } +} + // CheckEffective returns true if c was issued on or after the EffectiveDate // AND before (but not on) the Ineffective date. That is, CheckEffective // returns true if... // -// c.NotBefore in [EffectiveDate, IneffectiveDate) +// c.NotBefore in [EffectiveDate, IneffectiveDate) // // If EffectiveDate is zero, then only IneffectiveDate is checked. Conversely, // if IneffectiveDate is zero then only EffectiveDate is checked. If both EffectiveDate // and IneffectiveDate are zero then CheckEffective always returns true. +// +// @deprecated - use CertificateLint instead. func (l *Lint) CheckEffective(c *x509.Certificate) bool { + return l.toCertificateLint().CheckEffective(c) +} + +// Execute runs the lint against a certificate. For lints that are +// sourced from the CA/B Forum Baseline Requirements, we first determine +// if they are within the purview of the BRs. See LintInterface for details +// about the other methods called. The ordering is as follows: +// +// Configure() ----> only if the lint implements Configurable +// CheckApplies() +// CheckEffective() +// Execute() +// +// @deprecated - use CertificateLint instead +func (l *Lint) Execute(cert *x509.Certificate, config Configuration) *LintResult { + return l.toCertificateLint().Execute(cert, config) +} + +// CertificateLint represents a single x509 certificate linter. +type CertificateLint struct { + // Metadata associated with the linter. + LintMeta + // A constructor which returns the implementation of the linter. + Lint func() CertificateLintInterface `json:"-"` +} + +// toLint converts a CertificateLint to Lint for backwards compatibility +// +// @deprecated - use CertificateLint directly. +func (l *CertificateLint) toLint() *Lint { + return &Lint{ + Name: l.Name, + Description: l.Description, + Citation: l.Citation, + Source: l.Source, + EffectiveDate: l.EffectiveDate, + IneffectiveDate: l.IneffectiveDate, + Lint: l.Lint, + } +} + +// CheckEffective returns true if c was issued on or after the EffectiveDate +// AND before (but not on) the Ineffective date. That is, CheckEffective +// returns true if... +// +// c.NotBefore in [EffectiveDate, IneffectiveDate) +// +// If EffectiveDate is zero, then only IneffectiveDate is checked. Conversely, +// if IneffectiveDate is zero then only EffectiveDate is checked. If both EffectiveDate +// and IneffectiveDate are zero then CheckEffective always returns true. +func (l *CertificateLint) CheckEffective(c *x509.Certificate) bool { onOrAfterEffective := l.EffectiveDate.IsZero() || util.OnOrAfter(c.NotBefore, l.EffectiveDate) strictlyBeforeIneffective := l.IneffectiveDate.IsZero() || c.NotBefore.Before(l.IneffectiveDate) return onOrAfterEffective && strictlyBeforeIneffective @@ -97,14 +214,14 @@ func (l *Lint) CheckEffective(c *x509.Certificate) bool { // CheckApplies() // CheckEffective() // Execute() -func (l *Lint) Execute(cert *x509.Certificate, config Configuration) *LintResult { +func (l *CertificateLint) Execute(cert *x509.Certificate, config Configuration) *LintResult { if l.Source == CABFBaselineRequirements && !util.IsServerAuthCert(cert) { return &LintResult{Status: NA} } return l.execute(l.Lint(), cert, config) } -func (l *Lint) execute(lint LintInterface, cert *x509.Certificate, config Configuration) *LintResult { +func (l *CertificateLint) execute(lint LintInterface, cert *x509.Certificate, config Configuration) *LintResult { configurable, ok := lint.(Configurable) if ok { err := config.Configure(configurable.Configure(), l.Name) @@ -125,6 +242,62 @@ func (l *Lint) execute(lint LintInterface, cert *x509.Certificate, config Config } else if !l.CheckEffective(cert) { return &LintResult{Status: NE} } - res := lint.Execute(cert) - return res + return lint.Execute(cert) +} + +// RevocationListLint represents a single x509 CRL linter. +type RevocationListLint struct { + // Metadata associated with the linter. + LintMeta + // A constructor which returns the implementation of the linter. + Lint func() RevocationListLintInterface `json:"-"` +} + +// CheckEffective returns true if r was generated on or after the EffectiveDate +// AND before (but not on) the Ineffective date. That is, CheckEffective +// returns true if... +// +// r.ThisUpdate in [EffectiveDate, IneffectiveDate) +// +// If EffectiveDate is zero, then only IneffectiveDate is checked. Conversely, +// if IneffectiveDate is zero then only EffectiveDate is checked. If both EffectiveDate +// and IneffectiveDate are zero then CheckEffective always returns true. +func (l *RevocationListLint) CheckEffective(r *ox509.RevocationList) bool { + onOrAfterEffective := l.EffectiveDate.IsZero() || util.OnOrAfter(r.ThisUpdate, l.EffectiveDate) + strictlyBeforeIneffective := l.IneffectiveDate.IsZero() || r.ThisUpdate.Before(l.IneffectiveDate) + return onOrAfterEffective && strictlyBeforeIneffective +} + +// Execute runs the lint against a revocation list. For lints that are +// sourced from the CA/B Forum Baseline Requirements, we first determine +// if they are within the purview of the BRs. See LintInterface for details +// about the other methods called. The ordering is as follows: +// +// Configure() ----> only if the lint implements Configurable +// CheckApplies() +// CheckEffective() +// Execute() +func (l *RevocationListLint) Execute(r *ox509.RevocationList, config Configuration) *LintResult { + lint := l.Lint() + configurable, ok := lint.(Configurable) + if ok { + err := config.Configure(configurable.Configure(), l.Name) + if err != nil { + details := fmt.Sprintf( + "A fatal error occurred while attempting to configure %s. Please visit the [%s] section of "+ + "your provided configuration and compare it with the output of `zlint -exampleConfig`. Error: %s", + l.Name, + l.Name, + err.Error()) + return &LintResult{ + Status: Fatal, + Details: details} + } + } + if !lint.CheckApplies(r) { + return &LintResult{Status: NA} + } else if !l.CheckEffective(r) { + return &LintResult{Status: NE} + } + return lint.Execute(r) } diff --git a/v3/lint/registration.go b/v3/lint/registration.go index e55583601..64c1d59e8 100644 --- a/v3/lint/registration.go +++ b/v3/lint/registration.go @@ -73,6 +73,115 @@ func (f *FilterOptions) AddProfile(profile Profile) { f.IncludeNames = append(f.IncludeNames, profile.LintNames...) } +// Lints is a generic type constraint on what could be considered a "Lint" +type Lints interface { + RevocationListLint | CertificateLint +} + +// LinterLookup is an interface describing how registered lints can be looked up. +type LinterLookup[T Lints] interface { + // Names returns a list of all of the lint names that have been registered + // in string sorted order. + Names() []string + // ByName returns a pointer to the registered lint with the given name, or nil + // if there is no such lint registered in the registry. + ByName(name string) *T + // Sources returns a SourceList of registered LintSources. The list is not + // sorted but can be sorted by the caller with sort.Sort() if required. + Sources() SourceList + // BySource returns a list of registered lints that have the same LintSource as + // provided (or nil if there were no such lints in the registry). + BySource(s LintSource) []*T + // Lints returns a list of all the lints registered. + Lints() []*T +} + +type lookupImpl[T Lints] struct { + sync.RWMutex + lints []*T + // lintsByName is a map of all registered lints by name. + lintsByName map[string]*T + // lintNames is a sorted list of all of the registered lint names. It is + // equivalent to collecting the keys from lintsByName into a slice and sorting + // them lexicographically. + lintNames []string + // lintsBySource is a map of all registered lints by source category. Lints + // are added to the lintsBySource map by RegisterLint. + lintsBySource map[LintSource][]*T +} + +// register adds the provided lint to the Registry. +// +// An error is returned if the Lint has an empty Name +// or if the Name was previously registered. +func (lookup *lookupImpl[T]) register(lint *T, name string, source LintSource) error { + if name == "" { + return errEmptyName + } + if existing := lookup.ByName(name); existing != nil { + return &errDuplicateName{name} + } + + lookup.Lock() + defer lookup.Unlock() + lookup.lints = append(lookup.lints, lint) + lookup.lintNames = append(lookup.lintNames, name) + lookup.lintsByName[name] = lint + lookup.lintsBySource[source] = append(lookup.lintsBySource[source], lint) + sort.Strings(lookup.lintNames) + return nil +} + +// ByName returns the Lint previously registered under the given name with +// Register, or nil if no matching lint name has been registered. +func (lookup *lookupImpl[T]) ByName(name string) *T { + lookup.RLock() + defer lookup.RUnlock() + return lookup.lintsByName[name] +} + +// Names returns the list of lint names registered for the lint type T. +func (lookup *lookupImpl[T]) Names() []string { + lookup.RLock() + defer lookup.RUnlock() + return lookup.lintNames +} + +// BySource returns a list of registered lints that have the same LintSource as +// provided (or nil if there were no such lints). +func (lookup *lookupImpl[T]) BySource(s LintSource) []*T { + lookup.RLock() + defer lookup.RUnlock() + return lookup.lintsBySource[s] +} + +// Sources returns a SourceList of registered LintSources. The list is not +// sorted but can be sorted by the caller with sort.Sort() if required. +func (lookup *lookupImpl[T]) Sources() SourceList { + lookup.RLock() + defer lookup.RUnlock() + var results SourceList + for k := range lookup.lintsBySource { + results = append(results, k) + } + return results +} + +// Lints returns a list of all the lints registered. +func (lookup *lookupImpl[T]) Lints() []*T { + lookup.RLock() + defer lookup.RUnlock() + return lookup.lints +} + +// Creates a new LintLookup struct. +func newLintLookup[T Lints]() lookupImpl[T] { + return lookupImpl[T]{ + lintsByName: make(map[string]*T), + lintsBySource: make(map[LintSource][]*T), + } +} + // Registry is an interface describing a collection of registered lints. // A Registry instance can be given to zlint.LintCertificateEx() to control what // lints are run for a given certificate. @@ -91,9 +200,13 @@ type Registry interface { DefaultConfiguration() ([]byte, error) // ByName returns a pointer to the registered lint with the given name, or nil // if there is no such lint registered in the registry. + // + // @deprecated - use CertificateLints instead. ByName(name string) *Lint // BySource returns a list of registered lints that have the same LintSource as // provided (or nil if there were no such lints in the registry). + // + // @deprecated - use CertificateLints instead. BySource(s LintSource) []*Lint // Filter returns a new Registry containing only lints that match the // FilterOptions criteria. @@ -103,22 +216,18 @@ type Registry interface { WriteJSON(w io.Writer) SetConfiguration(config Configuration) GetConfiguration() Configuration + // CertificateLints returns an interface used to lookup CertificateLints. + CertificateLints() LinterLookup[CertificateLint] + // RevocationListLitns returns an interface used to lookup RevocationListLints. + RevocationListLints() LinterLookup[RevocationListLint] } // registryImpl implements the Registry interface to provide a global collection // of Lints that have been registered. type registryImpl struct { - sync.RWMutex - // lintsByName is a map of all registered lints by name. - lintsByName map[string]*Lint - // lintNames is a sorted list of all of the registered lint names. It is - // equivalent to collecting the keys from lintsByName into a slice and sorting - // them lexicographically. - lintNames []string - // lintsBySource is a map of all registered lints by source category. Lints - // are added to the lintsBySource map by RegisterLint. - lintsBySource map[LintSource][]*Lint - configuration Configuration + certificateLints lookupImpl[CertificateLint] + revocationListLints lookupImpl[RevocationListLint] + configuration Configuration } var ( @@ -144,67 +253,106 @@ func (e errDuplicateName) Error() string { e.lintName) } -// register adds the provided lint to the Registry. If initialize is true then -// the lint's Initialize() function will be called before registering the lint. +// registerLint registers a lint to the registry. +// +// @deprecated - use registerCertificateLint instead. +func (r *registryImpl) register(l *Lint) error { + if l == nil { + return errNilLint + } + if l.Lint() == nil { + return errNilLintPtr + } + + return r.registerCertificateLint(l.toCertificateLint()) +} + +// registerCertificateLint registers a CertificateLint to the registry. // // An error is returned if the lint or lint's Lint pointer is nil, if the Lint // has an empty Name or if the Name was previously registered. -func (r *registryImpl) register(l *Lint) error { +func (r *registryImpl) registerCertificateLint(l *CertificateLint) error { if l == nil { return errNilLint } if l.Lint() == nil { return errNilLintPtr } - if l.Name == "" { - return errEmptyName + return r.certificateLints.register(l, l.Name, l.Source) +} + +// registerCertificateLint registers a CertificateLint to the registry. +// +// An error is returned if the lint or lint's Lint pointer is nil, if the Lint +// has an empty Name or if the Name was previously registered. +func (r *registryImpl) registerRevocationlistLint(l *RevocationListLint) error { + if l == nil { + return errNilLint } - if existing := r.ByName(l.Name); existing != nil { - return &errDuplicateName{l.Name} + if l.Lint() == nil { + return errNilLintPtr } - r.Lock() - defer r.Unlock() - r.lintNames = append(r.lintNames, l.Name) - r.lintsByName[l.Name] = l - r.lintsBySource[l.Source] = append(r.lintsBySource[l.Source], l) - sort.Strings(r.lintNames) - return nil + return r.revocationListLints.register(l, l.Name, l.Source) } // ByName returns the Lint previously registered under the given name with // Register, or nil if no matching lint name has been registered. +// +// @deprecated - use r.CertificateLints.ByName() instead. func (r *registryImpl) ByName(name string) *Lint { - r.RLock() - defer r.RUnlock() - return r.lintsByName[name] + certificateLint := r.certificateLints.ByName(name) + if certificateLint == nil { + return nil + } + + return certificateLint.toLint() } // Names returns a list of all of the lint names that have been registered // in string sorted order. func (r *registryImpl) Names() []string { - r.RLock() - defer r.RUnlock() - return r.lintNames + var names []string + names = append(names, r.certificateLints.lintNames...) + names = append(names, r.revocationListLints.lintNames...) + + sort.Strings(names) + return names } // BySource returns a list of registered lints that have the same LintSource as // provided (or nil if there were no such lints). +// +// @deprecated use r.CertificateLints().BySource() instead. func (r *registryImpl) BySource(s LintSource) []*Lint { - r.RLock() - defer r.RUnlock() - return r.lintsBySource[s] + var lints []*Lint + + certificateLints := r.certificateLints.BySource(s) + for _, l := range certificateLints { + if l == nil { + continue + } + lints = append(lints, l.toLint()) + } + + return lints } // Sources returns a SourceList of registered LintSources. The list is not // sorted but can be sorted by the caller with sort.Sort() if required. func (r *registryImpl) Sources() SourceList { - r.RLock() - defer r.RUnlock() - var results SourceList - for k := range r.lintsBySource { - results = append(results, k) - } - return results + var sources SourceList + + sources = append(sources, r.certificateLints.Sources()...) + sources = append(sources, r.revocationListLints.Sources()...) + return sources +} + +func (r *registryImpl) CertificateLints() LinterLookup[CertificateLint] { + return &r.certificateLints +} + +func (r *registryImpl) RevocationListLints() LinterLookup[RevocationListLint] { + return &r.revocationListLints } // lintNamesToMap converts a list of lit names into a bool hashmap useful for @@ -218,10 +366,15 @@ func (r *registryImpl) lintNamesToMap(names []string) (map[string]bool, error) { namesMap := make(map[string]bool, len(names)) for _, n := range names { n = strings.TrimSpace(n) - if l := r.ByName(n); l == nil { - return nil, fmt.Errorf("unknown lint name %q", n) + if l := r.certificateLints.ByName(n); l != nil { + namesMap[n] = true + continue + } + if l := r.revocationListLints.ByName(n); l != nil { + namesMap[n] = true + continue } - namesMap[n] = true + return nil, fmt.Errorf("unknown lint name %q", n) } return namesMap, nil } @@ -241,7 +394,9 @@ func sourceListToMap(sources SourceList) map[LintSource]bool { // criteria included. // // FilterOptions are applied in the following order of precedence: -// ExcludeSources > IncludeSources > NameFilter > ExcludeNames > IncludeNames +// +// ExcludeSources > IncludeSources > NameFilter > ExcludeNames > IncludeNames +// //nolint:cyclop func (r *registryImpl) Filter(opts FilterOptions) (Registry, error) { // If there's no filtering to be done, return the existing Registry. @@ -271,12 +426,25 @@ func (r *registryImpl) Filter(opts FilterOptions) (Registry, error) { } for _, name := range r.Names() { - l := r.ByName(name) + var meta LintMeta + var registerFunc func() error - if sourceExcludes != nil && sourceExcludes[l.Source] { + if l := r.certificateLints.ByName(name); l != nil { + meta = l.LintMeta + registerFunc = func() error { + return filteredRegistry.registerCertificateLint(l) + } + } else if l := r.revocationListLints.ByName(name); l != nil { + meta = l.LintMeta + registerFunc = func() error { + return filteredRegistry.registerRevocationlistLint(l) + } + } + + if sourceExcludes != nil && sourceExcludes[meta.Source] { continue } - if sourceIncludes != nil && !sourceIncludes[l.Source] { + if sourceIncludes != nil && !sourceIncludes[meta.Source] { continue } if opts.NameFilter != nil && !opts.NameFilter.MatchString(name) { @@ -289,9 +457,7 @@ func (r *registryImpl) Filter(opts FilterOptions) (Registry, error) { continue } - // when adding lints to a filtered registry we do not want Initialize() to - // be called a second time, so provide false as the initialize argument. - if err := filteredRegistry.register(l); err != nil { + if err := registerFunc(); err != nil { return nil, err } } @@ -304,9 +470,14 @@ func (r *registryImpl) Filter(opts FilterOptions) (Registry, error) { func (r *registryImpl) WriteJSON(w io.Writer) { enc := json.NewEncoder(w) enc.SetEscapeHTML(false) - for _, name := range r.Names() { + for _, lint := range r.certificateLints.Lints() { + //nolint:errchkjson + _ = enc.Encode(lint) + } + + for _, lint := range r.revocationListLints.Lints() { //nolint:errchkjson - _ = enc.Encode(r.ByName(name)) + _ = enc.Encode(lint) } } @@ -331,13 +502,22 @@ func (r *registryImpl) DefaultConfiguration() ([]byte, error) { // for the sake of making unit testing easier. func (r *registryImpl) defaultConfiguration(globals []GlobalConfiguration) ([]byte, error) { configurables := map[string]interface{}{} - for name, lint := range r.lintsByName { + for name, lint := range r.certificateLints.lintsByName { + switch configurable := lint.Lint().(type) { + case Configurable: + configurables[name] = stripGlobalsFromExample(configurable.Configure()) + default: + } + } + + for name, lint := range r.revocationListLints.lintsByName { switch configurable := lint.Lint().(type) { case Configurable: configurables[name] = stripGlobalsFromExample(configurable.Configure()) default: } } + for _, config := range globals { switch config.(type) { case *Global: @@ -369,11 +549,12 @@ func (r *registryImpl) defaultConfiguration(globals []GlobalConfiguration) ([]by // NewRegistry constructs a Registry implementation that can be used to register // lints. +// //nolint:revive func NewRegistry() *registryImpl { registry := ®istryImpl{ - lintsByName: make(map[string]*Lint), - lintsBySource: make(map[LintSource][]*Lint), + certificateLints: newLintLookup[CertificateLint](), + revocationListLints: newLintLookup[RevocationListLint](), } registry.SetConfiguration(NewEmptyConfig()) return registry @@ -386,18 +567,41 @@ var globalRegistry = NewRegistry() // RegisterLint must be called once for each lint to be executed. Normally, // RegisterLint is called from the Go init() function of a lint implementation. // -// RegsterLint will call l.Lint's Initialize() function as part of the -// registration process. -// // IMPORTANT: RegisterLint will panic if given a nil lint, or a lint with a nil // Lint pointer, or if the lint's Initialize function errors, or if the lint // name matches a previously registered lint's name. These conditions all // indicate a bug that should be addressed by a developer. +// +// @deprecated - use RegisterCertificateLint or RegisterRevocationList instead. func RegisterLint(l *Lint) { + RegisterCertificateLint(l.toCertificateLint()) +} + +// RegisterCertificateLint must be called once for each CertificateLint to be executed. +// Normally, RegisterCertificateLint is called from the Go init() function of a lint implementation. +// +// IMPORTANT: RegisterCertificateLint will panic if given a nil lint, or a lint with a nil +// Lint pointer, or if the lint's Initialize function errors, or if the lint +// name matches a previously registered lint's name. These conditions all +// indicate a bug that should be addressed by a developer. +func RegisterCertificateLint(l *CertificateLint) { + if err := globalRegistry.registerCertificateLint(l); err != nil { + panic(fmt.Sprintf("RegisterLint error: %v\n", err.Error())) + } +} + +// RegisterRevocationListLint must be called once for each RevocationListLint to be executed. +// Normally, RegisterRevocationListLint is called from the Go init() function of a lint implementation. +// +// IMPORTANT: RegisterRevocationListLint will panic if given a nil lint, or a lint with a nil +// Lint pointer, or if the lint's Initialize function errors, or if the lint +// name matches a previously registered lint's name. These conditions all +// indicate a bug that should be addressed by a developer. +func RegisterRevocationListLint(l *RevocationListLint) { // RegisterLint always sets initialize to true. It's assumed this is called by // the package init() functions and therefore must be doing the first // initialization of a lint. - if err := globalRegistry.register(l); err != nil { + if err := globalRegistry.registerRevocationlistLint(l); err != nil { panic(fmt.Sprintf("RegisterLint error: %v\n", err.Error())) } } From 942739bb1c8675dc0b7866a53188640e87e0f7a3 Mon Sep 17 00:00:00 2001 From: Amir Omidi Date: Tue, 29 Nov 2022 19:33:49 +0000 Subject: [PATCH 02/12] Change the entrypoint of zlint --- v3/resultset.go | 25 ++++++++++++++++++++----- v3/zlint.go | 31 ++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/v3/resultset.go b/v3/resultset.go index 0fb3c7594..1d480ca28 100644 --- a/v3/resultset.go +++ b/v3/resultset.go @@ -15,6 +15,8 @@ package zlint import ( + ox509 "crypto/x509" + "github.com/zmap/zcrypto/x509" "github.com/zmap/zlint/v3/lint" ) @@ -31,15 +33,28 @@ type ResultSet struct { FatalsPresent bool `json:"fatals_present"` } -// Execute lints the given certificate with all of the lints in the provided +// Execute lints on the given certificate with all of the lints in the provided // registry. The ResultSet is mutated to trace the lint results obtained from // linting the certificate. -func (z *ResultSet) execute(cert *x509.Certificate, registry lint.Registry) { +func (z *ResultSet) executeCertificate(o *x509.Certificate, registry lint.Registry) { + z.Results = make(map[string]*lint.LintResult, len(registry.Names())) + // Run each lints from the registry. + for _, lint := range registry.CertificateLints().Lints() { + res := lint.Execute(o, registry.GetConfiguration()) + z.Results[lint.Name] = res + z.updateErrorStatePresent(res) + } +} + +// Execute lints on the given CRL with all of the lints in the provided +// registry. The ResultSet is mutated to trace the lint results obtained from +// linting the CRL. +func (z *ResultSet) executeRevocationList(o *ox509.RevocationList, registry lint.Registry) { z.Results = make(map[string]*lint.LintResult, len(registry.Names())) // Run each lints from the registry. - for _, name := range registry.Names() { - res := registry.ByName(name).Execute(cert, registry.GetConfiguration()) - z.Results[name] = res + for _, lint := range registry.RevocationListLints().Lints() { + res := lint.Execute(o, registry.GetConfiguration()) + z.Results[lint.Name] = res z.updateErrorStatePresent(res) } } diff --git a/v3/zlint.go b/v3/zlint.go index 56e1d405e..5dc252f3d 100644 --- a/v3/zlint.go +++ b/v3/zlint.go @@ -17,6 +17,7 @@ package zlint import ( + ox509 "crypto/x509" "time" "github.com/zmap/zcrypto/x509" @@ -55,7 +56,35 @@ func LintCertificateEx(c *x509.Certificate, registry lint.Registry) *ResultSet { registry = lint.GlobalRegistry() } res := new(ResultSet) - res.execute(c, registry) + res.executeCertificate(c, registry) + res.Version = Version + res.Timestamp = time.Now().Unix() + return res +} + +// LintRevocationList runs all registered lints on r using default options, +// producing a ResultSet. +// +// Using LintRevocationList(r) is equivalent to calling LintRevocationList(r, nil). +func LintRevocationList(r *ox509.RevocationList) *ResultSet { + return LintRevocationListEx(r, nil) +} + +// LintRevocationListEx runs lints from the provided registry on r producing +// a ResultSet. Providing an explicit registry allows the caller to filter the +// lints that will be run. (See lint.Registry.Filter()) +// +// If registry is nil then the global registry of all lints is used and this +// function is equivalent to calling LintRevocationListEx(r). +func LintRevocationListEx(r *ox509.RevocationList, registry lint.Registry) *ResultSet { + if r == nil { + return nil + } + if registry == nil { + registry = lint.GlobalRegistry() + } + res := new(ResultSet) + res.executeRevocationList(r, registry) res.Version = Version res.Timestamp = time.Now().Unix() return res From 074d4f6a22a4078353cb7fc2ac8e6be49e2065ba Mon Sep 17 00:00:00 2001 From: Amir Omidi Date: Tue, 29 Nov 2022 20:37:16 +0000 Subject: [PATCH 03/12] Add tests for the new skeleton --- v3/lint/base_test.go | 147 +++++++++++++++++++++++++++++++++ v3/lint/registration_test.go | 154 +++++++++++++++++++++++++++++++---- 2 files changed, 284 insertions(+), 17 deletions(-) diff --git a/v3/lint/base_test.go b/v3/lint/base_test.go index 10ba29d45..06d42e1f4 100644 --- a/v3/lint/base_test.go +++ b/v3/lint/base_test.go @@ -15,6 +15,7 @@ package lint */ import ( + ox509 "crypto/x509" "testing" "time" @@ -166,3 +167,149 @@ func TestLint_CheckEffective(t *testing.T) { } } } + +// This test attempts to simplify the truth table by assigning dates to the +// single digit values 1 through 5, inclusive. As per the standard library, +// 0 is taken to be the null value. +// +// E.G. +// +// If a lint is effective between 2 and 5, then the certs {2, 3, 4} return true. +// If a lint is effective between 0 and 4, then the certs {0, 1, 2, 3} return true. +// If a lint is effective between 2 and 0, then the certs {2, 3, 4, 5} return true. +// If a lint is effective between 0 and 0, then the certs {0, 1, 2, 3, 4, 5} return true. +func TestLint_RevocationListLint_CheckEffective(t *testing.T) { + zero := time.Time{} + one := time.Unix(1, 0) + two := time.Unix(2, 0) + three := time.Unix(3, 0) + four := time.Unix(4, 0) + five := time.Unix(5, 0) + lZeroZero := RevocationListLint{LintMeta: LintMeta{ + Description: "ZeroZero", + EffectiveDate: zero, IneffectiveDate: zero}, + } + lTwoZero := RevocationListLint{LintMeta: LintMeta{ + Description: "TwoZero", + EffectiveDate: two, IneffectiveDate: zero}} + lZeroFour := RevocationListLint{LintMeta: LintMeta{ + Description: "ZeroFour", + EffectiveDate: zero, IneffectiveDate: four}} + lTwoFour := RevocationListLint{LintMeta: LintMeta{ + Description: "TwoFour", + EffectiveDate: two, IneffectiveDate: four}} + + type revocationList struct { + Description string + RevocationList *ox509.RevocationList + } + + cZero := revocationList{ + Description: "cZero", + RevocationList: &ox509.RevocationList{ThisUpdate: zero}, + } + cOne := revocationList{ + Description: "cOne", + RevocationList: &ox509.RevocationList{ThisUpdate: one}, + } + cTwo := revocationList{ + Description: "cTwo", + RevocationList: &ox509.RevocationList{ThisUpdate: two}, + } + cThree := revocationList{ + Description: "cThree", + RevocationList: &ox509.RevocationList{ThisUpdate: three}, + } + cFour := revocationList{ + Description: "cFour", + RevocationList: &ox509.RevocationList{ThisUpdate: four}, + } + cFive := revocationList{ + Description: "cFive", + RevocationList: &ox509.RevocationList{ThisUpdate: five}, + } + + data := []struct { + Lint RevocationListLint + RevocationList revocationList + Want bool + }{ + /////////////// + { + Lint: lZeroZero, + RevocationList: cZero, + Want: true, + }, + { + Lint: lZeroZero, + RevocationList: cOne, + Want: true, + }, + ////////// + { + Lint: lTwoZero, + RevocationList: cOne, + Want: false, + }, + { + Lint: lTwoZero, + RevocationList: cTwo, + Want: true, + }, + { + Lint: lTwoZero, + RevocationList: cThree, + Want: true, + }, + /////////////// + { + Lint: lZeroFour, + RevocationList: cTwo, + Want: true, + }, + { + Lint: lZeroFour, + RevocationList: cFour, + Want: false, + }, + { + Lint: lZeroFour, + RevocationList: cFive, + Want: false, + }, + //////////// + { + Lint: lTwoFour, + RevocationList: cOne, + Want: false, + }, + { + Lint: lTwoFour, + RevocationList: cTwo, + Want: true, + }, + { + Lint: lTwoFour, + RevocationList: cThree, + Want: true, + }, + { + Lint: lTwoFour, + RevocationList: cFour, + Want: false, + }, + { + Lint: lTwoFour, + RevocationList: cFive, + Want: false, + }, + } + + for _, d := range data { + got := d.Lint.CheckEffective(d.RevocationList.RevocationList) + if got != d.Want { + t.Errorf("Lint %s, cert %s, got %v want %v", + d.Lint.Description, d.RevocationList.Description, got, d.Want) + } + } +} diff --git a/v3/lint/registration_test.go b/v3/lint/registration_test.go index 615afb319..d123dabf5 100644 --- a/v3/lint/registration_test.go +++ b/v3/lint/registration_test.go @@ -15,6 +15,7 @@ package lint */ import ( + ox509 "crypto/x509" "reflect" "regexp" "sort" @@ -23,28 +24,27 @@ import ( "github.com/zmap/zcrypto/x509" ) -func TestAllLintsHaveNameDescriptionSource(t *testing.T) { - for _, name := range GlobalRegistry().Names() { - lint := GlobalRegistry().ByName(name) - if lint.Name == "" { - t.Errorf("lint %s has empty name", name) +func TestAllLintsHaveValidMeta(t *testing.T) { + checkMeta := func(meta LintMeta) { + if meta.Name == "" { + t.Errorf("lint %s has empty name", meta.Name) } - if lint.Description == "" { - t.Errorf("lint %s has empty description", name) + if meta.Description == "" { + t.Errorf("lint %s has empty description", meta.Name) } - if lint.Citation == "" { - t.Errorf("lint %s has empty citation", name) + if meta.Citation == "" { + t.Errorf("lint %s has empty citation", meta.Name) } - } -} - -func TestAllLintsHaveSource(t *testing.T) { - for _, name := range globalRegistry.Names() { - lint := GlobalRegistry().ByName(name) - if lint.Source == UnknownLintSource { - t.Errorf("lint %s has unknown source", name) + if meta.Source == UnknownLintSource { + t.Errorf("lint %s has unknown source", meta.Name) } } + for _, lint := range globalRegistry.certificateLints.lints { + checkMeta(lint.LintMeta) + } + for _, lint := range globalRegistry.revocationListLints.lints { + checkMeta(lint.LintMeta) + } } func TestFilterOptionsEmpty(t *testing.T) { @@ -68,6 +68,16 @@ func (m mockLint) Execute(c *x509.Certificate) *LintResult { return nil } +type mockRevocationListLint struct{} + +func (m mockRevocationListLint) CheckApplies(c *ox509.RevocationList) bool { + return true +} + +func (m mockRevocationListLint) Execute(c *ox509.RevocationList) *LintResult { + return nil +} + func TestRegister(t *testing.T) { egLint := &Lint{ Name: "mockLint", @@ -152,6 +162,116 @@ func TestRegister(t *testing.T) { } } +func TestRegistryWithRevocationListLitns(t *testing.T) { + expectedNames := []string{ + "A-mockCertificateLint", + "B-mockLint", + "C-mockRevocationListLint", + } + + expectedSources := []LintSource{ + Community, + RFC3279, + RFC8813, + } + + egCertificateLint := &CertificateLint{ + LintMeta: LintMeta{ + Name: "A-mockCertificateLint", + Source: Community, + }, + Lint: func() CertificateLintInterface { return &mockLint{} }, + } + + egLint := &Lint{ + Name: "B-mockLint", + Lint: func() LintInterface { return &mockLint{} }, + Source: RFC8813, // arbitrary value for testing + } + + egRevocationListLint := &RevocationListLint{ + LintMeta: LintMeta{ + Name: "C-mockRevocationListLint", + Source: RFC3279, // arbitrary value for testing + }, + Lint: func() RevocationListLintInterface { return &mockRevocationListLint{} }, + } + + registry := NewRegistry() + if err := registry.register(egLint); err != nil { + t.Fatal("registry.register failed") + } + if err := registry.registerCertificateLint(egCertificateLint); err != nil { + t.Fatal("registry.registerCertificateLint failed") + } + if err := registry.registerRevocationlistLint(egRevocationListLint); err != nil { + t.Fatal("registry.registerRevocationlistLint failed") + } + t.Run("check metadata", func(t *testing.T) { + if !reflect.DeepEqual(registry.Names(), expectedNames) { + t.Fatalf("expected lint names: %v, got: %v", registry.Names(), expectedNames) + } + + sources := registry.Sources() + sort.Sort(sources) + for i, source := range sources { + if source != expectedSources[i] { + t.Fatalf("expected source names: %v, got: %v", sources, expectedSources) + } + } + }) + + + t.Run("check proper stores", func(t *testing.T) { + testCases := []struct { + name string + deprecatedStore bool + certificateStore bool + revocationListStore bool + }{ + { + name: "A-mockCertificateLint", + deprecatedStore: true, + certificateStore: true, + revocationListStore: false, + }, + { + name: "B-mockLint", + deprecatedStore: true, + certificateStore: true, + revocationListStore: false, + }, + { + name: "C-mockRevocationListLint", + deprecatedStore: false, + certificateStore: false, + revocationListStore: true, + }, + } + + for _, tc := range testCases { + { + lint := registry.ByName(tc.name) + if (lint != nil) != tc.deprecatedStore { + t.Fatalf("expected lint %s to be %t (true = present, false = absent) in deprecated store", tc.name, tc.deprecatedStore) + } + } + { + lint := registry.CertificateLints().ByName(tc.name) + if (lint != nil) != tc.certificateStore { + t.Fatalf("expected lint %s to be %t (true = present, false = absent) in certificate store", tc.name, tc.certificateStore) + } + } + { + lint := registry.RevocationListLints().ByName(tc.name) + if (lint != nil) != tc.revocationListStore { + t.Fatalf("expected lint %s to be %t (true = present, false = absent) in revocationList store", tc.name, tc.revocationListStore) + } + } + } + }) +} + func TestRegistryFilter(t *testing.T) { testLint := func(name string, source LintSource) *Lint { return &Lint{ From ab3ee4acdc7f049b8de27bce907ed314d7b4f4e9 Mon Sep 17 00:00:00 2001 From: Amir Omidi Date: Tue, 29 Nov 2022 23:03:04 +0000 Subject: [PATCH 04/12] Address reviews --- v3/lint/base.go | 11 +++++------ v3/lint/base_test.go | 10 +++++----- v3/lint/registration.go | 2 +- v3/lint/registration_test.go | 15 ++++++++------- v3/resultset.go | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/v3/lint/base.go b/v3/lint/base.go index 1b89142d6..3853de2f7 100644 --- a/v3/lint/base.go +++ b/v3/lint/base.go @@ -207,8 +207,9 @@ func (l *CertificateLint) CheckEffective(c *x509.Certificate) bool { // Execute runs the lint against a certificate. For lints that are // sourced from the CA/B Forum Baseline Requirements, we first determine -// if they are within the purview of the BRs. See LintInterface for details -// about the other methods called. The ordering is as follows: +// if they are within the purview of the BRs. See CertificateLintInterface +// for details about the other methods called. +// The ordering is as follows: // // Configure() ----> only if the lint implements Configurable // CheckApplies() @@ -268,10 +269,8 @@ func (l *RevocationListLint) CheckEffective(r *ox509.RevocationList) bool { return onOrAfterEffective && strictlyBeforeIneffective } -// Execute runs the lint against a revocation list. For lints that are -// sourced from the CA/B Forum Baseline Requirements, we first determine -// if they are within the purview of the BRs. See LintInterface for details -// about the other methods called. The ordering is as follows: +// Execute runs the lint against a revocation list. +// The ordering is as follows: // // Configure() ----> only if the lint implements Configurable // CheckApplies() diff --git a/v3/lint/base_test.go b/v3/lint/base_test.go index 06d42e1f4..9dfb8ee46 100644 --- a/v3/lint/base_test.go +++ b/v3/lint/base_test.go @@ -174,10 +174,10 @@ func TestLint_CheckEffective(t *testing.T) { // // E.G. // -// If a lint is effective between 2 and 5, then the certs {2, 3, 4} return true. -// If a lint is effective between 0 and 4, then the certs {0, 1, 2, 3} return true. -// If a lint is effective between 2 and 0, then the certs {2, 3, 4, 5} return true. -// If a lint is effective between 0 and 0, then the certs {0, 1, 2, 3, 4, 5} return true. +// If a lint is effective between 2 and 5, then the revocation lists {2, 3, 4} return true. +// If a lint is effective between 0 and 4, then the revocation lists {0, 1, 2, 3} return true. +// If a lint is effective between 2 and 0, then the revocation lists {2, 3, 4, 5} return true. +// If a lint is effective between 0 and 0, then the revocation lists {0, 1, 2, 3, 4, 5} return true. func TestLint_RevocationListLint_CheckEffective(t *testing.T) { zero := time.Time{} one := time.Unix(1, 0) @@ -308,7 +308,7 @@ func TestLint_RevocationListLint_CheckEffective(t *testing.T) { for _, d := range data { got := d.Lint.CheckEffective(d.RevocationList.RevocationList) if got != d.Want { - t.Errorf("Lint %s, cert %s, got %v want %v", + t.Errorf("Lint %s, revocation list %s, got %v want %v", d.Lint.Description, d.RevocationList.Description, got, d.Want) } } diff --git a/v3/lint/registration.go b/v3/lint/registration.go index 64c1d59e8..39e12b6d7 100644 --- a/v3/lint/registration.go +++ b/v3/lint/registration.go @@ -572,7 +572,7 @@ var globalRegistry = NewRegistry() // name matches a previously registered lint's name. These conditions all // indicate a bug that should be addressed by a developer. // -// @deprecated - use RegisterCertificateLint or RegisterRevocationList instead. +// @deprecated - use RegisterCertificateLint instead. func RegisterLint(l *Lint) { RegisterCertificateLint(l.toCertificateLint()) } diff --git a/v3/lint/registration_test.go b/v3/lint/registration_test.go index d123dabf5..97e6be347 100644 --- a/v3/lint/registration_test.go +++ b/v3/lint/registration_test.go @@ -162,7 +162,7 @@ func TestRegister(t *testing.T) { } } -func TestRegistryWithRevocationListLitns(t *testing.T) { +func TestRegistryLookupEngine(t *testing.T) { expectedNames := []string{ "A-mockCertificateLint", "B-mockLint", @@ -199,19 +199,21 @@ func TestRegistryWithRevocationListLitns(t *testing.T) { registry := NewRegistry() if err := registry.register(egLint); err != nil { - t.Fatal("registry.register failed") + t.Fatalf("registry.register failed: %v", err) } if err := registry.registerCertificateLint(egCertificateLint); err != nil { - t.Fatal("registry.registerCertificateLint failed") + t.Fatalf("registry.registerCertificateLint failed: %v", err) } if err := registry.registerRevocationlistLint(egRevocationListLint); err != nil { - t.Fatal("registry.registerRevocationlistLint failed") + t.Fatalf("registry.registerRevocationlistLint failed: %v", err) } - t.Run("check metadata", func(t *testing.T) { + t.Run("lint names are correct and sorted", func(t *testing.T) { if !reflect.DeepEqual(registry.Names(), expectedNames) { t.Fatalf("expected lint names: %v, got: %v", registry.Names(), expectedNames) } + }) + t.Run("sources are valid", func(t *testing.T) { sources := registry.Sources() sort.Sort(sources) for i, source := range sources { @@ -221,8 +223,7 @@ func TestRegistryWithRevocationListLitns(t *testing.T) { } }) - - t.Run("check proper stores", func(t *testing.T) { + t.Run("stores contain correct lints", func(t *testing.T) { testCases := []struct { name string deprecatedStore bool diff --git a/v3/resultset.go b/v3/resultset.go index 1d480ca28..df8bf0e45 100644 --- a/v3/resultset.go +++ b/v3/resultset.go @@ -38,7 +38,7 @@ type ResultSet struct { // linting the certificate. func (z *ResultSet) executeCertificate(o *x509.Certificate, registry lint.Registry) { z.Results = make(map[string]*lint.LintResult, len(registry.Names())) - // Run each lints from the registry. + // Run each lint from the registry. for _, lint := range registry.CertificateLints().Lints() { res := lint.Execute(o, registry.GetConfiguration()) z.Results[lint.Name] = res From a4c0c020ae26f8d5df66109a379241ab8a19a38e Mon Sep 17 00:00:00 2001 From: christopher-henderson Date: Sun, 4 Dec 2022 12:14:24 -0800 Subject: [PATCH 05/12] starting my own suggestions to work coopertaively on he change --- v3/lint/base.go | 91 +++++++++++++++++------------------- v3/lint/base_test.go | 8 ++-- v3/lint/configuration.go | 39 ++++++++++++++-- v3/lint/registration.go | 19 ++++---- v3/lint/registration_test.go | 14 +++--- 5 files changed, 98 insertions(+), 73 deletions(-) diff --git a/v3/lint/base.go b/v3/lint/base.go index 3853de2f7..4e5110b07 100644 --- a/v3/lint/base.go +++ b/v3/lint/base.go @@ -16,7 +16,6 @@ package lint import ( ox509 "crypto/x509" - "fmt" "time" "github.com/zmap/zcrypto/x509" @@ -36,8 +35,8 @@ type RevocationListLintInterface interface { // CheckEffective() or Run(). CheckApplies(r *ox509.RevocationList) bool - // Execute() is the body of the lint. It is called for every revocation list - // for which CheckApplies() returns true. + // Execute is the body of the lint. It is called for every revocation list + // for which CheckApplies returns true. Execute(r *ox509.RevocationList) *LintResult } @@ -49,8 +48,8 @@ type CertificateLintInterface interface { // Run(). CheckApplies(c *x509.Certificate) bool - // Execute() is the body of the lint. It is called for every certificate for - // which CheckApplies() returns true. + // Execute is the body of the lint. It is called for every certificate for + // which CheckApplies returns true. Execute(c *x509.Certificate) *LintResult } @@ -59,8 +58,14 @@ type Configurable interface { Configure() interface{} } -// LintMeta struct represents the metadata associated with a single lint. -type LintMeta struct { +// LintMetadata represents the metadata that are broadly associated across all types of lints. +// +// That is, all lints (irrespective of being a certificate lint, a CRL lint, and OCSP, etc.) +// have a Name, a Description, a Citation, and so on. +// +// In this way, this struct may be embedded in any linting type in order to maintain this +// data, while each individual linting type provides the behavior over this data. +type LintMetadata struct { // Name is a lowercase underscore-separated string describing what a given // Lint checks. If Name beings with "w", the lint MUST NOT return Error, only // Warn. If Name beings with "e", the Lint MUST NOT return Warn, only Error. @@ -125,7 +130,7 @@ type Lint struct { // @deprecated - Use CertificateLint directly. func (l *Lint) toCertificateLint() *CertificateLint { return &CertificateLint{ - LintMeta: LintMeta{ + LintMetadata: LintMetadata{ Name: l.Name, Description: l.Description, Citation: l.Citation, @@ -170,7 +175,7 @@ func (l *Lint) Execute(cert *x509.Certificate, config Configuration) *LintResult // CertificateLint represents a single x509 certificate linter. type CertificateLint struct { // Metadata associated with the linter. - LintMeta + LintMetadata // A constructor which returns the implementation of the linter. Lint func() CertificateLintInterface `json:"-"` } @@ -200,9 +205,7 @@ func (l *CertificateLint) toLint() *Lint { // if IneffectiveDate is zero then only EffectiveDate is checked. If both EffectiveDate // and IneffectiveDate are zero then CheckEffective always returns true. func (l *CertificateLint) CheckEffective(c *x509.Certificate) bool { - onOrAfterEffective := l.EffectiveDate.IsZero() || util.OnOrAfter(c.NotBefore, l.EffectiveDate) - strictlyBeforeIneffective := l.IneffectiveDate.IsZero() || c.NotBefore.Before(l.IneffectiveDate) - return onOrAfterEffective && strictlyBeforeIneffective + return checkEffective(l.EffectiveDate, l.IneffectiveDate, c.NotBefore) } // Execute runs the lint against a certificate. For lints that are @@ -219,24 +222,12 @@ func (l *CertificateLint) Execute(cert *x509.Certificate, config Configuration) if l.Source == CABFBaselineRequirements && !util.IsServerAuthCert(cert) { return &LintResult{Status: NA} } - return l.execute(l.Lint(), cert, config) -} - -func (l *CertificateLint) execute(lint LintInterface, cert *x509.Certificate, config Configuration) *LintResult { - configurable, ok := lint.(Configurable) - if ok { - err := config.Configure(configurable.Configure(), l.Name) - if err != nil { - details := fmt.Sprintf( - "A fatal error occurred while attempting to configure %s. Please visit the [%s] section of "+ - "your provided configuration and compare it with the output of `zlint -exampleConfig`. Error: %s", - l.Name, - l.Name, - err.Error()) - return &LintResult{ - Status: Fatal, - Details: details} - } + lint := l.Lint() + err := config.MaybeConfigure(lint, l.Name) + if err != nil { + return &LintResult{ + Status: Fatal, + Details: err.Error()} } if !lint.CheckApplies(cert) { return &LintResult{Status: NA} @@ -249,7 +240,7 @@ func (l *CertificateLint) execute(lint LintInterface, cert *x509.Certificate, co // RevocationListLint represents a single x509 CRL linter. type RevocationListLint struct { // Metadata associated with the linter. - LintMeta + LintMetadata // A constructor which returns the implementation of the linter. Lint func() RevocationListLintInterface `json:"-"` } @@ -264,9 +255,7 @@ type RevocationListLint struct { // if IneffectiveDate is zero then only EffectiveDate is checked. If both EffectiveDate // and IneffectiveDate are zero then CheckEffective always returns true. func (l *RevocationListLint) CheckEffective(r *ox509.RevocationList) bool { - onOrAfterEffective := l.EffectiveDate.IsZero() || util.OnOrAfter(r.ThisUpdate, l.EffectiveDate) - strictlyBeforeIneffective := l.IneffectiveDate.IsZero() || r.ThisUpdate.Before(l.IneffectiveDate) - return onOrAfterEffective && strictlyBeforeIneffective + return checkEffective(l.EffectiveDate, l.IneffectiveDate, r.ThisUpdate) } // Execute runs the lint against a revocation list. @@ -278,20 +267,11 @@ func (l *RevocationListLint) CheckEffective(r *ox509.RevocationList) bool { // Execute() func (l *RevocationListLint) Execute(r *ox509.RevocationList, config Configuration) *LintResult { lint := l.Lint() - configurable, ok := lint.(Configurable) - if ok { - err := config.Configure(configurable.Configure(), l.Name) - if err != nil { - details := fmt.Sprintf( - "A fatal error occurred while attempting to configure %s. Please visit the [%s] section of "+ - "your provided configuration and compare it with the output of `zlint -exampleConfig`. Error: %s", - l.Name, - l.Name, - err.Error()) - return &LintResult{ - Status: Fatal, - Details: details} - } + err := config.MaybeConfigure(lint, l.Name) + if err != nil { + return &LintResult{ + Status: Fatal, + Details: err.Error()} } if !lint.CheckApplies(r) { return &LintResult{Status: NA} @@ -300,3 +280,18 @@ func (l *RevocationListLint) Execute(r *ox509.RevocationList, config Configurati } return lint.Execute(r) } + +// checkEffective returns true if target was generated on or after the EffectiveDate +// AND before (but not on) the Ineffective date. That is, CheckEffective +// returns true if... +// +// target in [effective, ineffective) +// +// If effective is zero, then only ineffective is checked. Conversely, +// if ineffective is zero then only effect is checked. If both effective +// and ineffective are zero then checkEffective always returns true. +func checkEffective(effective, ineffective, target time.Time) bool { + onOrAfterEffective := effective.IsZero() || util.OnOrAfter(target, effective) + strictlyBeforeIneffective := ineffective.IsZero() || target.Before(ineffective) + return onOrAfterEffective && strictlyBeforeIneffective +} diff --git a/v3/lint/base_test.go b/v3/lint/base_test.go index 9dfb8ee46..82695ab70 100644 --- a/v3/lint/base_test.go +++ b/v3/lint/base_test.go @@ -185,17 +185,17 @@ func TestLint_RevocationListLint_CheckEffective(t *testing.T) { three := time.Unix(3, 0) four := time.Unix(4, 0) five := time.Unix(5, 0) - lZeroZero := RevocationListLint{LintMeta: LintMeta{ + lZeroZero := RevocationListLint{LintMetadata: LintMetadata{ Description: "ZeroZero", EffectiveDate: zero, IneffectiveDate: zero}, } - lTwoZero := RevocationListLint{LintMeta: LintMeta{ + lTwoZero := RevocationListLint{LintMetadata: LintMetadata{ Description: "TwoZero", EffectiveDate: two, IneffectiveDate: zero}} - lZeroFour := RevocationListLint{LintMeta: LintMeta{ + lZeroFour := RevocationListLint{LintMetadata: LintMetadata{ Description: "ZeroFour", EffectiveDate: zero, IneffectiveDate: four}} - lTwoFour := RevocationListLint{LintMeta: LintMeta{ + lTwoFour := RevocationListLint{LintMetadata: LintMetadata{ Description: "TwoFour", EffectiveDate: two, IneffectiveDate: four}} diff --git a/v3/lint/configuration.go b/v3/lint/configuration.go index d60f3aec7..7b81dcc29 100644 --- a/v3/lint/configuration.go +++ b/v3/lint/configuration.go @@ -15,6 +15,7 @@ package lint import ( + "errors" "fmt" "io" "os" @@ -30,24 +31,42 @@ type Configuration struct { tree *toml.Tree } +// MaybeConfigure is a thin wrapper over Configure. +// +// If the provided lint object does not implement the Configurable interface +// then this function is a noop and nil is always returned. +// +// Otherwise, configuration of the provided lint is attempted. +func (c Configuration) MaybeConfigure(lint interface{}, namespace string) error { + configurable, ok := lint.(Configurable) + if !ok { + return nil + } + return c.Configure(configurable.Configure(), namespace) +} + // Configure attempts to deserialize the provided namespace into the provided empty interface. // // For example, let's say that the name of your lint is MyLint, then the configuration // file might look something like the following... // // ``` +// // [MyLint] // A = 1 // B = 2 +// // ``` // // Given this, our target struct may look like the following... // // ``` +// // type MytLint struct { // A int // B uint // } +// // ``` // // So deserializing into this struct would look like... @@ -56,7 +75,17 @@ type Configuration struct { // configuration.Configure(&myLint, myLint.Name()) // ``` func (c Configuration) Configure(lint interface{}, namespace string) error { - return c.deserializeConfigInto(lint, namespace) + err := c.deserializeConfigInto(lint, namespace) + if err != nil { + details := fmt.Sprintf( + "A fatal error occurred while attempting to configure %s. Please visit the [%s] section of "+ + "your provided configuration and compare it with the output of `zlint -exampleConfig`. Error: %s", + namespace, + namespace, + err.Error()) + err = errors.New(details) + } + return err } // NewConfig attempts to instantiate a configuration by consuming the contents of the provided reader. @@ -121,9 +150,11 @@ func NewEmptyConfig() Configuration { // And the following struct definition... // // ``` -// type SomeOtherLint { -// IsWebPKI bool `toml:"is_web_pki"` -// } +// +// type SomeOtherLint { +// IsWebPKI bool `toml:"is_web_pki"` +// } +// // ``` // // Then the invocation of this function should be... diff --git a/v3/lint/registration.go b/v3/lint/registration.go index 39e12b6d7..9432c1a6b 100644 --- a/v3/lint/registration.go +++ b/v3/lint/registration.go @@ -118,12 +118,11 @@ func (lookup *lookupImpl[T]) register(lint *T, name string, source LintSource) e if name == "" { return errEmptyName } - if existing := lookup.ByName(name); existing != nil { - return &errDuplicateName{name} - } - lookup.Lock() defer lookup.Unlock() + if existing := lookup.lintsByName[name]; existing != nil { + return &errDuplicateName{name} + } lookup.lints = append(lookup.lints, lint) lookup.lintNames = append(lookup.lintNames, name) lookup.lintsByName[name] = lint @@ -285,7 +284,7 @@ func (r *registryImpl) registerCertificateLint(l *CertificateLint) error { // // An error is returned if the lint or lint's Lint pointer is nil, if the Lint // has an empty Name or if the Name was previously registered. -func (r *registryImpl) registerRevocationlistLint(l *RevocationListLint) error { +func (r *registryImpl) registerRevocationListLint(l *RevocationListLint) error { if l == nil { return errNilLint } @@ -426,18 +425,18 @@ func (r *registryImpl) Filter(opts FilterOptions) (Registry, error) { } for _, name := range r.Names() { - var meta LintMeta + var meta LintMetadata var registerFunc func() error if l := r.certificateLints.ByName(name); l != nil { - meta = l.LintMeta + meta = l.LintMetadata registerFunc = func() error { return filteredRegistry.registerCertificateLint(l) } } else if l := r.revocationListLints.ByName(name); l != nil { - meta = l.LintMeta + meta = l.LintMetadata registerFunc = func() error { - return filteredRegistry.registerRevocationlistLint(l) + return filteredRegistry.registerRevocationListLint(l) } } @@ -601,7 +600,7 @@ func RegisterRevocationListLint(l *RevocationListLint) { // RegisterLint always sets initialize to true. It's assumed this is called by // the package init() functions and therefore must be doing the first // initialization of a lint. - if err := globalRegistry.registerRevocationlistLint(l); err != nil { + if err := globalRegistry.registerRevocationListLint(l); err != nil { panic(fmt.Sprintf("RegisterLint error: %v\n", err.Error())) } } diff --git a/v3/lint/registration_test.go b/v3/lint/registration_test.go index 97e6be347..9a7815abc 100644 --- a/v3/lint/registration_test.go +++ b/v3/lint/registration_test.go @@ -25,7 +25,7 @@ import ( ) func TestAllLintsHaveValidMeta(t *testing.T) { - checkMeta := func(meta LintMeta) { + checkMeta := func(meta LintMetadata) { if meta.Name == "" { t.Errorf("lint %s has empty name", meta.Name) } @@ -40,10 +40,10 @@ func TestAllLintsHaveValidMeta(t *testing.T) { } } for _, lint := range globalRegistry.certificateLints.lints { - checkMeta(lint.LintMeta) + checkMeta(lint.LintMetadata) } for _, lint := range globalRegistry.revocationListLints.lints { - checkMeta(lint.LintMeta) + checkMeta(lint.LintMetadata) } } @@ -176,7 +176,7 @@ func TestRegistryLookupEngine(t *testing.T) { } egCertificateLint := &CertificateLint{ - LintMeta: LintMeta{ + LintMetadata: LintMetadata{ Name: "A-mockCertificateLint", Source: Community, }, @@ -190,7 +190,7 @@ func TestRegistryLookupEngine(t *testing.T) { } egRevocationListLint := &RevocationListLint{ - LintMeta: LintMeta{ + LintMetadata: LintMetadata{ Name: "C-mockRevocationListLint", Source: RFC3279, // arbitrary value for testing }, @@ -204,8 +204,8 @@ func TestRegistryLookupEngine(t *testing.T) { if err := registry.registerCertificateLint(egCertificateLint); err != nil { t.Fatalf("registry.registerCertificateLint failed: %v", err) } - if err := registry.registerRevocationlistLint(egRevocationListLint); err != nil { - t.Fatalf("registry.registerRevocationlistLint failed: %v", err) + if err := registry.registerRevocationListLint(egRevocationListLint); err != nil { + t.Fatalf("registry.registerRevocationListLint failed: %v", err) } t.Run("lint names are correct and sorted", func(t *testing.T) { if !reflect.DeepEqual(registry.Names(), expectedNames) { From c26c92d187895d72f152c4282724aba9e1920a9f Mon Sep 17 00:00:00 2001 From: Amir Omidi Date: Fri, 30 Dec 2022 15:20:34 -0500 Subject: [PATCH 06/12] Take out generics from the registration struct (#3) --- v3/lint/lint_lookup.go | 217 ++++++++++++++++++++++++++++++++++++++++ v3/lint/registration.go | 125 ++--------------------- 2 files changed, 225 insertions(+), 117 deletions(-) create mode 100644 v3/lint/lint_lookup.go diff --git a/v3/lint/lint_lookup.go b/v3/lint/lint_lookup.go new file mode 100644 index 000000000..50c4df23e --- /dev/null +++ b/v3/lint/lint_lookup.go @@ -0,0 +1,217 @@ +/* + * ZLint Copyright 2021 Regents of the University of Michigan + * + * 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 lint + +import ( + "sort" + "sync" +) + +var ( + // Verify that the interface holds + _ linterLookup = &linterLookupImpl{} + _ CertificateLinterLookup = &certificateLinterLookupImpl{} + _ RevocationListLinterLookup = &revocationListLinterLookupImpl{} +) + +type linterLookup interface { + // Names returns a list of all of the lint names that have been registered + // in string sorted order. + Names() []string + // Sources returns a SourceList of registered LintSources. The list is not + // sorted but can be sorted by the caller with sort.Sort() if required. + Sources() SourceList +} + +type linterLookupImpl struct { + sync.RWMutex + // lintNames is a sorted list of all of the registered lint names. It is + // equivalent to collecting the keys from lintsByName into a slice and sorting + // them lexicographically. + lintNames []string + sources map[LintSource]struct{} +} + +// Names returns the list of lint names registered for the lint type T. +func (lookup *linterLookupImpl) Names() []string { + lookup.RLock() + defer lookup.RUnlock() + return lookup.lintNames +} + +// Sources returns a SourceList of registered LintSources. The list is not +// sorted but can be sorted by the caller with sort.Sort() if required. +func (lookup *linterLookupImpl) Sources() SourceList { + lookup.RLock() + defer lookup.RUnlock() + var list SourceList + for lintSource := range lookup.sources { + list = append(list, lintSource) + } + return list +} + +func newLinterLookup() linterLookupImpl { + return linterLookupImpl{ + lintNames: make([]string, 0), + sources: map[LintSource]struct{}{}, + } +} + +// CertificateLinterLookup is an interface describing how registered certificate lints can be looked up. +type CertificateLinterLookup interface { + linterLookup + // ByName returns a pointer to the registered lint with the given name, or nil + // if there is no such lint registered in the registry. + ByName(name string) *CertificateLint + // BySource returns a list of registered lints that have the same LintSource as + // provided (or nil if there were no such lints in the registry). + BySource(s LintSource) []*CertificateLint + // Lints returns a list of all the lints registered. + Lints() []*CertificateLint +} + +type certificateLinterLookupImpl struct { + linterLookupImpl + // lintsByName is a map of all registered lints by name. + lintsByName map[string]*CertificateLint + lintsBySource map[LintSource][]*CertificateLint + lints []*CertificateLint +} + +// ByName returns the Lint previously registered under the given name with +// Register, or nil if no matching lint name has been registered. +func (lookup *certificateLinterLookupImpl) ByName(name string) *CertificateLint { + lookup.RLock() + defer lookup.RUnlock() + return lookup.lintsByName[name] +} + +// BySource returns a list of registered lints that have the same LintSource as +// provided (or nil if there were no such lints). +func (lookup *certificateLinterLookupImpl) BySource(s LintSource) []*CertificateLint { + lookup.RLock() + defer lookup.RUnlock() + return lookup.lintsBySource[s] +} + +// Lints returns a list of registered lints. +func (lookup *certificateLinterLookupImpl) Lints() []*CertificateLint { + lookup.RLock() + defer lookup.RUnlock() + return lookup.lints +} + +func (lookup *certificateLinterLookupImpl) register(lint *CertificateLint, name string, source LintSource) error { + if name == "" { + return errEmptyName + } + lookup.RLock() + defer lookup.RUnlock() + + if existing := lookup.lintsByName[name]; existing != nil { + return &errDuplicateName{name} + } + lookup.lints = append(lookup.lints, lint) + lookup.lintNames = append(lookup.lintNames, name) + lookup.lintsByName[name] = lint + + lookup.sources[source] = struct{}{} + lookup.lintsBySource[source] = append(lookup.lintsBySource[source], lint) + sort.Strings(lookup.lintNames) + return nil +} + +func newCertificateLintLookup() certificateLinterLookupImpl { + return certificateLinterLookupImpl{ + linterLookupImpl: newLinterLookup(), + lintsByName: make(map[string]*CertificateLint), + lintsBySource: make(map[LintSource][]*CertificateLint), + lints: make([]*CertificateLint, 0), + } +} + +// RevocationListLinterLookup is an interface describing how registered revocation list lints can be looked up. +type RevocationListLinterLookup interface { + linterLookup + // ByName returns a pointer to the registered lint with the given name, or nil + // if there is no such lint registered in the registry. + ByName(name string) *RevocationListLint + // BySource returns a list of registered lints that have the same LintSource as + // provided (or nil if there were no such lints in the registry). + BySource(s LintSource) []*RevocationListLint + // Lints returns a list of all the lints registered. + Lints() []*RevocationListLint +} + +type revocationListLinterLookupImpl struct { + linterLookupImpl + // lintsByName is a map of all registered lints by name. + lintsByName map[string]*RevocationListLint + lintsBySource map[LintSource][]*RevocationListLint + lints []*RevocationListLint +} + +// ByName returns the Lint previously registered under the given name with +// Register, or nil if no matching lint name has been registered. +func (lookup *revocationListLinterLookupImpl) ByName(name string) *RevocationListLint { + lookup.RLock() + defer lookup.RUnlock() + return lookup.lintsByName[name] +} + +// BySource returns a list of registered lints that have the same LintSource as +// provided (or nil if there were no such lints). +func (lookup *revocationListLinterLookupImpl) BySource(s LintSource) []*RevocationListLint { + lookup.RLock() + defer lookup.RUnlock() + return lookup.lintsBySource[s] +} + +// Lints returns a list of registered lints. +func (lookup *revocationListLinterLookupImpl) Lints() []*RevocationListLint { + lookup.RLock() + defer lookup.RUnlock() + return lookup.lints +} + +func (lookup *revocationListLinterLookupImpl) register(lint *RevocationListLint, name string, source LintSource) error { + if name == "" { + return errEmptyName + } + lookup.RLock() + defer lookup.RUnlock() + + if existing := lookup.lintsByName[name]; existing != nil { + return &errDuplicateName{name} + } + lookup.lints = append(lookup.lints, lint) + lookup.lintNames = append(lookup.lintNames, name) + lookup.lintsByName[name] = lint + + lookup.sources[source] = struct{}{} + lookup.lintsBySource[source] = append(lookup.lintsBySource[source], lint) + sort.Strings(lookup.lintNames) + return nil +} + +func newRevocationListLintLookup() revocationListLinterLookupImpl { + return revocationListLinterLookupImpl{ + linterLookupImpl: newLinterLookup(), + lintsByName: make(map[string]*RevocationListLint), + lintsBySource: make(map[LintSource][]*RevocationListLint), + lints: make([]*RevocationListLint, 0), + } +} diff --git a/v3/lint/registration.go b/v3/lint/registration.go index 9432c1a6b..feca2772a 100644 --- a/v3/lint/registration.go +++ b/v3/lint/registration.go @@ -23,7 +23,6 @@ import ( "regexp" "sort" "strings" - "sync" "github.com/pelletier/go-toml" ) @@ -73,114 +72,6 @@ func (f *FilterOptions) AddProfile(profile Profile) { f.IncludeNames = append(f.IncludeNames, profile.LintNames...) } -// Lints is a generic type constraint on what could be considered a "Lint" -type Lints interface { - RevocationListLint | CertificateLint -} - -// LinterLookup is an interface describing how registered lints can be looked up. -type LinterLookup[T Lints] interface { - // Names returns a list of all of the lint names that have been registered - // in string sorted order. - Names() []string - // ByName returns a pointer to the registered lint with the given name, or nil - // if there is no such lint registered in the registry. - ByName(name string) *T - // Sources returns a SourceList of registered LintSources. The list is not - // sorted but can be sorted by the caller with sort.Sort() if required. - Sources() SourceList - // BySource returns a list of registered lints that have the same LintSource as - // provided (or nil if there were no such lints in the registry). - BySource(s LintSource) []*T - // Lints returns a list of all the lints registered. - Lints() []*T -} - -type lookupImpl[T Lints] struct { - sync.RWMutex - lints []*T - // lintsByName is a map of all registered lints by name. - lintsByName map[string]*T - // lintNames is a sorted list of all of the registered lint names. It is - // equivalent to collecting the keys from lintsByName into a slice and sorting - // them lexicographically. - lintNames []string - // lintsBySource is a map of all registered lints by source category. Lints - // are added to the lintsBySource map by RegisterLint. - lintsBySource map[LintSource][]*T -} - -// register adds the provided lint to the Registry. -// -// An error is returned if the Lint has an empty Name -// or if the Name was previously registered. -func (lookup *lookupImpl[T]) register(lint *T, name string, source LintSource) error { - if name == "" { - return errEmptyName - } - lookup.Lock() - defer lookup.Unlock() - if existing := lookup.lintsByName[name]; existing != nil { - return &errDuplicateName{name} - } - lookup.lints = append(lookup.lints, lint) - lookup.lintNames = append(lookup.lintNames, name) - lookup.lintsByName[name] = lint - lookup.lintsBySource[source] = append(lookup.lintsBySource[source], lint) - sort.Strings(lookup.lintNames) - return nil -} - -// ByName returns the Lint previously registered under the given name with -// Register, or nil if no matching lint name has been registered. -func (lookup *lookupImpl[T]) ByName(name string) *T { - lookup.RLock() - defer lookup.RUnlock() - return lookup.lintsByName[name] -} - -// Names returns the list of lint names registered for the lint type T. -func (lookup *lookupImpl[T]) Names() []string { - lookup.RLock() - defer lookup.RUnlock() - return lookup.lintNames -} - -// BySource returns a list of registered lints that have the same LintSource as -// provided (or nil if there were no such lints). -func (lookup *lookupImpl[T]) BySource(s LintSource) []*T { - lookup.RLock() - defer lookup.RUnlock() - return lookup.lintsBySource[s] -} - -// Sources returns a SourceList of registered LintSources. The list is not -// sorted but can be sorted by the caller with sort.Sort() if required. -func (lookup *lookupImpl[T]) Sources() SourceList { - lookup.RLock() - defer lookup.RUnlock() - var results SourceList - for k := range lookup.lintsBySource { - results = append(results, k) - } - return results -} - -// Lints returns a list of all the lints registered. -func (lookup *lookupImpl[T]) Lints() []*T { - lookup.RLock() - defer lookup.RUnlock() - return lookup.lints -} - -// Creates a new LintLookup struct. -func newLintLookup[T Lints]() lookupImpl[T] { - return lookupImpl[T]{ - lintsByName: make(map[string]*T), - lintsBySource: make(map[LintSource][]*T), - } -} - // Registry is an interface describing a collection of registered lints. // A Registry instance can be given to zlint.LintCertificateEx() to control what // lints are run for a given certificate. @@ -216,16 +107,16 @@ type Registry interface { SetConfiguration(config Configuration) GetConfiguration() Configuration // CertificateLints returns an interface used to lookup CertificateLints. - CertificateLints() LinterLookup[CertificateLint] + CertificateLints() CertificateLinterLookup // RevocationListLitns returns an interface used to lookup RevocationListLints. - RevocationListLints() LinterLookup[RevocationListLint] + RevocationListLints() RevocationListLinterLookup } // registryImpl implements the Registry interface to provide a global collection // of Lints that have been registered. type registryImpl struct { - certificateLints lookupImpl[CertificateLint] - revocationListLints lookupImpl[RevocationListLint] + certificateLints certificateLinterLookupImpl + revocationListLints revocationListLinterLookupImpl configuration Configuration } @@ -346,11 +237,11 @@ func (r *registryImpl) Sources() SourceList { return sources } -func (r *registryImpl) CertificateLints() LinterLookup[CertificateLint] { +func (r *registryImpl) CertificateLints() CertificateLinterLookup { return &r.certificateLints } -func (r *registryImpl) RevocationListLints() LinterLookup[RevocationListLint] { +func (r *registryImpl) RevocationListLints() RevocationListLinterLookup { return &r.revocationListLints } @@ -552,8 +443,8 @@ func (r *registryImpl) defaultConfiguration(globals []GlobalConfiguration) ([]by //nolint:revive func NewRegistry() *registryImpl { registry := ®istryImpl{ - certificateLints: newLintLookup[CertificateLint](), - revocationListLints: newLintLookup[RevocationListLint](), + certificateLints: newCertificateLintLookup(), + revocationListLints: newRevocationListLintLookup(), } registry.SetConfiguration(NewEmptyConfig()) return registry From 7042d8561ec00c2924f2bd9939116fda8ef8e680 Mon Sep 17 00:00:00 2001 From: Christopher Henderson Date: Sun, 12 Feb 2023 13:49:05 -0800 Subject: [PATCH 07/12] Update to use Zcrypto instead of stdlib crypto for RevocationList (#4) * Take out generics from the registration struct (#3) * updating to use zcrypto * pointing zcrypto back to master * go tidy up --------- Co-authored-by: Amir Omidi --- v3/go.mod | 4 ++-- v3/go.sum | 12 ++++++++---- v3/lint/base.go | 9 ++++----- v3/lint/base_test.go | 15 +++++++-------- v3/lint/lint_lookup.go | 6 +++--- v3/lint/registration_test.go | 5 ++--- v3/resultset.go | 4 +--- v3/zlint.go | 7 +++---- 8 files changed, 30 insertions(+), 32 deletions(-) diff --git a/v3/go.mod b/v3/go.mod index af450c37a..d86e12b0a 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -6,13 +6,13 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/pelletier/go-toml v1.9.3 github.com/sirupsen/logrus v1.8.1 - github.com/zmap/zcrypto v0.0.0-20220402174210-599ec18ecbac + github.com/zmap/zcrypto v0.0.0-20230205235340-d51ce4775101 golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 golang.org/x/net v0.0.0-20220412020605-290c469a71a5 golang.org/x/text v0.3.7 ) require ( - github.com/weppos/publicsuffix-go v0.15.1-0.20220329081811-9a40b608a236 // indirect + github.com/weppos/publicsuffix-go v0.15.1-0.20220724114530-e087fba66a37 // indirect golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect ) diff --git a/v3/go.sum b/v3/go.sum index 0d25dba65..d035bd5eb 100644 --- a/v3/go.sum +++ b/v3/go.sum @@ -2,6 +2,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -23,12 +25,14 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/weppos/publicsuffix-go v0.15.1-0.20220329081811-9a40b608a236 h1:vMJBP3PQViZsF6cOINtvyMC8ptpLsyJ4EwyFnzuWNxc= -github.com/weppos/publicsuffix-go v0.15.1-0.20220329081811-9a40b608a236/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE= +github.com/weppos/publicsuffix-go v0.12.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= +github.com/weppos/publicsuffix-go v0.15.1-0.20220724114530-e087fba66a37 h1:oRCu5zb6sklsDvy5sOz3dFqGg5vAEYBBD2MAYhNThCQ= +github.com/weppos/publicsuffix-go v0.15.1-0.20220724114530-e087fba66a37/go.mod h1:5ZC/Uv3fIEUE0eP6o9+Yg4+5+W8V0/BieMi05feGXVA= +github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220704091424-e0182326a282/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE= github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is= -github.com/zmap/zcrypto v0.0.0-20220402174210-599ec18ecbac h1:+nr36qrZEH0RIYNjcUEnOrCUdcSG3om2ANaFA6iSVWA= -github.com/zmap/zcrypto v0.0.0-20220402174210-599ec18ecbac/go.mod h1:egdRkzUylATvPkWMpebZbXhv0FMEMJGX/ur0D3Csk2s= +github.com/zmap/zcrypto v0.0.0-20230205235340-d51ce4775101 h1:QuLjRpIBjqene8VvB+VhQ4eTcQGCQ7JDuk0/Fp4sLLw= +github.com/zmap/zcrypto v0.0.0-20230205235340-d51ce4775101/go.mod h1:bRZdjnJaHWVXKEwrfAZMd0gfRjZGNhTbZwzp07s0Abw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/v3/lint/base.go b/v3/lint/base.go index 4e5110b07..72c38c907 100644 --- a/v3/lint/base.go +++ b/v3/lint/base.go @@ -15,7 +15,6 @@ package lint */ import ( - ox509 "crypto/x509" "time" "github.com/zmap/zcrypto/x509" @@ -33,11 +32,11 @@ type RevocationListLintInterface interface { // Lint should run on the given certificate. If CheckApplies returns // false, the Lint result is automatically set to NA without calling // CheckEffective() or Run(). - CheckApplies(r *ox509.RevocationList) bool + CheckApplies(r *x509.RevocationList) bool // Execute is the body of the lint. It is called for every revocation list // for which CheckApplies returns true. - Execute(r *ox509.RevocationList) *LintResult + Execute(r *x509.RevocationList) *LintResult } // CertificateLintInterface is implemented by each certificate linter. @@ -254,7 +253,7 @@ type RevocationListLint struct { // If EffectiveDate is zero, then only IneffectiveDate is checked. Conversely, // if IneffectiveDate is zero then only EffectiveDate is checked. If both EffectiveDate // and IneffectiveDate are zero then CheckEffective always returns true. -func (l *RevocationListLint) CheckEffective(r *ox509.RevocationList) bool { +func (l *RevocationListLint) CheckEffective(r *x509.RevocationList) bool { return checkEffective(l.EffectiveDate, l.IneffectiveDate, r.ThisUpdate) } @@ -265,7 +264,7 @@ func (l *RevocationListLint) CheckEffective(r *ox509.RevocationList) bool { // CheckApplies() // CheckEffective() // Execute() -func (l *RevocationListLint) Execute(r *ox509.RevocationList, config Configuration) *LintResult { +func (l *RevocationListLint) Execute(r *x509.RevocationList, config Configuration) *LintResult { lint := l.Lint() err := config.MaybeConfigure(lint, l.Name) if err != nil { diff --git a/v3/lint/base_test.go b/v3/lint/base_test.go index 82695ab70..882f07f47 100644 --- a/v3/lint/base_test.go +++ b/v3/lint/base_test.go @@ -15,7 +15,6 @@ package lint */ import ( - ox509 "crypto/x509" "testing" "time" @@ -201,32 +200,32 @@ func TestLint_RevocationListLint_CheckEffective(t *testing.T) { type revocationList struct { Description string - RevocationList *ox509.RevocationList + RevocationList *x509.RevocationList } cZero := revocationList{ Description: "cZero", - RevocationList: &ox509.RevocationList{ThisUpdate: zero}, + RevocationList: &x509.RevocationList{ThisUpdate: zero}, } cOne := revocationList{ Description: "cOne", - RevocationList: &ox509.RevocationList{ThisUpdate: one}, + RevocationList: &x509.RevocationList{ThisUpdate: one}, } cTwo := revocationList{ Description: "cTwo", - RevocationList: &ox509.RevocationList{ThisUpdate: two}, + RevocationList: &x509.RevocationList{ThisUpdate: two}, } cThree := revocationList{ Description: "cThree", - RevocationList: &ox509.RevocationList{ThisUpdate: three}, + RevocationList: &x509.RevocationList{ThisUpdate: three}, } cFour := revocationList{ Description: "cFour", - RevocationList: &ox509.RevocationList{ThisUpdate: four}, + RevocationList: &x509.RevocationList{ThisUpdate: four}, } cFive := revocationList{ Description: "cFive", - RevocationList: &ox509.RevocationList{ThisUpdate: five}, + RevocationList: &x509.RevocationList{ThisUpdate: five}, } data := []struct { diff --git a/v3/lint/lint_lookup.go b/v3/lint/lint_lookup.go index 50c4df23e..6f9a0feab 100644 --- a/v3/lint/lint_lookup.go +++ b/v3/lint/lint_lookup.go @@ -27,8 +27,8 @@ var ( ) type linterLookup interface { - // Names returns a list of all of the lint names that have been registered - // in string sorted order. + // Names returns a list of all lint names that have been registered. + // The returned list is sorted by lexicographical ordering. Names() []string // Sources returns a SourceList of registered LintSources. The list is not // sorted but can be sorted by the caller with sort.Sort() if required. @@ -37,7 +37,7 @@ type linterLookup interface { type linterLookupImpl struct { sync.RWMutex - // lintNames is a sorted list of all of the registered lint names. It is + // lintNames is a sorted list of all registered lint names. It is // equivalent to collecting the keys from lintsByName into a slice and sorting // them lexicographically. lintNames []string diff --git a/v3/lint/registration_test.go b/v3/lint/registration_test.go index 9a7815abc..f215b8ae5 100644 --- a/v3/lint/registration_test.go +++ b/v3/lint/registration_test.go @@ -15,7 +15,6 @@ package lint */ import ( - ox509 "crypto/x509" "reflect" "regexp" "sort" @@ -70,11 +69,11 @@ func (m mockLint) Execute(c *x509.Certificate) *LintResult { type mockRevocationListLint struct{} -func (m mockRevocationListLint) CheckApplies(c *ox509.RevocationList) bool { +func (m mockRevocationListLint) CheckApplies(c *x509.RevocationList) bool { return true } -func (m mockRevocationListLint) Execute(c *ox509.RevocationList) *LintResult { +func (m mockRevocationListLint) Execute(c *x509.RevocationList) *LintResult { return nil } diff --git a/v3/resultset.go b/v3/resultset.go index df8bf0e45..e1f70a329 100644 --- a/v3/resultset.go +++ b/v3/resultset.go @@ -15,8 +15,6 @@ package zlint import ( - ox509 "crypto/x509" - "github.com/zmap/zcrypto/x509" "github.com/zmap/zlint/v3/lint" ) @@ -49,7 +47,7 @@ func (z *ResultSet) executeCertificate(o *x509.Certificate, registry lint.Regist // Execute lints on the given CRL with all of the lints in the provided // registry. The ResultSet is mutated to trace the lint results obtained from // linting the CRL. -func (z *ResultSet) executeRevocationList(o *ox509.RevocationList, registry lint.Registry) { +func (z *ResultSet) executeRevocationList(o *x509.RevocationList, registry lint.Registry) { z.Results = make(map[string]*lint.LintResult, len(registry.Names())) // Run each lints from the registry. for _, lint := range registry.RevocationListLints().Lints() { diff --git a/v3/zlint.go b/v3/zlint.go index 5dc252f3d..fea16c8bd 100644 --- a/v3/zlint.go +++ b/v3/zlint.go @@ -17,7 +17,6 @@ package zlint import ( - ox509 "crypto/x509" "time" "github.com/zmap/zcrypto/x509" @@ -65,8 +64,8 @@ func LintCertificateEx(c *x509.Certificate, registry lint.Registry) *ResultSet { // LintRevocationList runs all registered lints on r using default options, // producing a ResultSet. // -// Using LintRevocationList(r) is equivalent to calling LintRevocationList(r, nil). -func LintRevocationList(r *ox509.RevocationList) *ResultSet { +// Using LintRevocationList(r) is equivalent to calling LintRevocationListEx(r, nil). +func LintRevocationList(r *x509.RevocationList) *ResultSet { return LintRevocationListEx(r, nil) } @@ -76,7 +75,7 @@ func LintRevocationList(r *ox509.RevocationList) *ResultSet { // // If registry is nil then the global registry of all lints is used and this // function is equivalent to calling LintRevocationListEx(r). -func LintRevocationListEx(r *ox509.RevocationList, registry lint.Registry) *ResultSet { +func LintRevocationListEx(r *x509.RevocationList, registry lint.Registry) *ResultSet { if r == nil { return nil } From d670e8fc235334cc02f67daff334ea6041229449 Mon Sep 17 00:00:00 2001 From: Amir Omidi Date: Mon, 13 Mar 2023 00:52:03 -0400 Subject: [PATCH 08/12] Tidy go mod --- v3/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3/go.mod b/v3/go.mod index b6b6bacb3..5d062849e 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -13,6 +13,6 @@ require ( ) require ( - github.com/weppos/publicsuffix-go v0.15.1-0.20220329081811-9a40b608a236 // indirect + github.com/weppos/publicsuffix-go v0.15.1-0.20220724114530-e087fba66a37 // indirect golang.org/x/sys v0.5.0 // indirect ) From 6af892b4c1a59d750669fc9c918111b592232ce3 Mon Sep 17 00:00:00 2001 From: Amir Omidi Date: Mon, 13 Mar 2023 00:52:52 -0400 Subject: [PATCH 09/12] Update zcrypto --- v3/go.mod | 14 +++++++------- v3/go.sum | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/v3/go.mod b/v3/go.mod index 5d062849e..0f32059a7 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -5,14 +5,14 @@ go 1.18 require ( github.com/kr/text v0.2.0 // indirect github.com/pelletier/go-toml v1.9.3 - github.com/sirupsen/logrus v1.8.1 - github.com/zmap/zcrypto v0.0.0-20230205235340-d51ce4775101 - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 - golang.org/x/net v0.7.0 - golang.org/x/text v0.7.0 + github.com/sirupsen/logrus v1.9.0 + github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300 + golang.org/x/crypto v0.7.0 + golang.org/x/net v0.8.0 + golang.org/x/text v0.8.0 ) require ( - github.com/weppos/publicsuffix-go v0.15.1-0.20220724114530-e087fba66a37 // indirect - golang.org/x/sys v0.5.0 // indirect + github.com/weppos/publicsuffix-go v0.30.0 // indirect + golang.org/x/sys v0.6.0 // indirect ) diff --git a/v3/go.sum b/v3/go.sum index 78c49aee5..d3c9c925f 100644 --- a/v3/go.sum +++ b/v3/go.sum @@ -5,6 +5,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -12,54 +13,109 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= +github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/weppos/publicsuffix-go v0.12.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= +github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= github.com/weppos/publicsuffix-go v0.15.1-0.20220724114530-e087fba66a37 h1:oRCu5zb6sklsDvy5sOz3dFqGg5vAEYBBD2MAYhNThCQ= github.com/weppos/publicsuffix-go v0.15.1-0.20220724114530-e087fba66a37/go.mod h1:5ZC/Uv3fIEUE0eP6o9+Yg4+5+W8V0/BieMi05feGXVA= +github.com/weppos/publicsuffix-go v0.30.0 h1:QHPZ2GRu/YE7cvejH9iyavPOkVCB4dNxp2ZvtT+vQLY= +github.com/weppos/publicsuffix-go v0.30.0/go.mod h1:kBi8zwYnR0zrbm8RcuN1o9Fzgpnnn+btVN8uWPMyXAY= github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220704091424-e0182326a282/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE= +github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220927085643-dc0d00c92642/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= +github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is= +github.com/zmap/zcertificate v0.0.1/go.mod h1:q0dlN54Jm4NVSSuzisusQY0hqDWvu92C+TWveAxiVWk= +github.com/zmap/zcrypto v0.0.0-20201128221613-3719af1573cf/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= +github.com/zmap/zcrypto v0.0.0-20201211161100-e54a5822fb7e/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= github.com/zmap/zcrypto v0.0.0-20230205235340-d51ce4775101 h1:QuLjRpIBjqene8VvB+VhQ4eTcQGCQ7JDuk0/Fp4sLLw= github.com/zmap/zcrypto v0.0.0-20230205235340-d51ce4775101/go.mod h1:bRZdjnJaHWVXKEwrfAZMd0gfRjZGNhTbZwzp07s0Abw= +github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300 h1:DZH5n7L3L8RxKdSyJHZt7WePgwdhHnPhQFdQSJaHF+o= +github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300/go.mod h1:mOd4yUMgn2fe2nV9KXsa9AyQBFZGzygVPovsZR+Rl5w= +github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 7776df0c7a1d6f8954cd411887eec19f4a4beb2b Mon Sep 17 00:00:00 2001 From: Amir Omidi Date: Mon, 13 Mar 2023 00:53:06 -0400 Subject: [PATCH 10/12] go mod tidy one more time --- v3/go.sum | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/v3/go.sum b/v3/go.sum index d3c9c925f..6adff4187 100644 --- a/v3/go.sum +++ b/v3/go.sum @@ -21,24 +21,18 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/weppos/publicsuffix-go v0.12.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= -github.com/weppos/publicsuffix-go v0.15.1-0.20220724114530-e087fba66a37 h1:oRCu5zb6sklsDvy5sOz3dFqGg5vAEYBBD2MAYhNThCQ= -github.com/weppos/publicsuffix-go v0.15.1-0.20220724114530-e087fba66a37/go.mod h1:5ZC/Uv3fIEUE0eP6o9+Yg4+5+W8V0/BieMi05feGXVA= github.com/weppos/publicsuffix-go v0.30.0 h1:QHPZ2GRu/YE7cvejH9iyavPOkVCB4dNxp2ZvtT+vQLY= github.com/weppos/publicsuffix-go v0.30.0/go.mod h1:kBi8zwYnR0zrbm8RcuN1o9Fzgpnnn+btVN8uWPMyXAY= -github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220704091424-e0182326a282/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE= github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220927085643-dc0d00c92642/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= @@ -47,8 +41,6 @@ github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54t github.com/zmap/zcertificate v0.0.1/go.mod h1:q0dlN54Jm4NVSSuzisusQY0hqDWvu92C+TWveAxiVWk= github.com/zmap/zcrypto v0.0.0-20201128221613-3719af1573cf/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= github.com/zmap/zcrypto v0.0.0-20201211161100-e54a5822fb7e/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= -github.com/zmap/zcrypto v0.0.0-20230205235340-d51ce4775101 h1:QuLjRpIBjqene8VvB+VhQ4eTcQGCQ7JDuk0/Fp4sLLw= -github.com/zmap/zcrypto v0.0.0-20230205235340-d51ce4775101/go.mod h1:bRZdjnJaHWVXKEwrfAZMd0gfRjZGNhTbZwzp07s0Abw= github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300 h1:DZH5n7L3L8RxKdSyJHZt7WePgwdhHnPhQFdQSJaHF+o= github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300/go.mod h1:mOd4yUMgn2fe2nV9KXsa9AyQBFZGzygVPovsZR+Rl5w= github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8= @@ -58,8 +50,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -72,7 +62,6 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= @@ -90,7 +79,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -103,7 +91,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= @@ -116,6 +103,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From fd76904ba1ef4635a4031d24c8b3b83c01cd81cc Mon Sep 17 00:00:00 2001 From: Amir Omidi Date: Mon, 13 Mar 2023 02:03:18 -0400 Subject: [PATCH 11/12] Bypass lint for Registry --- v3/lint/registration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3/lint/registration.go b/v3/lint/registration.go index feca2772a..52fe028e0 100644 --- a/v3/lint/registration.go +++ b/v3/lint/registration.go @@ -79,7 +79,7 @@ func (f *FilterOptions) AddProfile(profile Profile) { // Typically users will interact with the global Registry returned by // GlobalRegistry(), or a filtered Registry created by applying FilterOptions to // the GlobalRegistry()'s Filter function. -type Registry interface { +type Registry interface { //nolint: interfacebloat // Somewhat unavoidable here. // Names returns a list of all of the lint names that have been registered // in string sorted order. Names() []string From f84563a6a16af0e857823cc445a10a9bcdafe4ff Mon Sep 17 00:00:00 2001 From: Amir Omidi Date: Sun, 19 Mar 2023 19:24:18 -0400 Subject: [PATCH 12/12] Add NextUpdate CRL lint (#5) --- v3/lints/rfc/lint_crl_has_next_update.go | 56 +++++++++++++ v3/lints/rfc/lint_crl_has_next_update_test.go | 42 ++++++++++ v3/test/helpers.go | 84 ++++++++++++++++++- v3/testdata/crlHasNextUpdate.pem | 11 +++ v3/testdata/crlNotHaveNextUpdate.pem | 11 +++ 5 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 v3/lints/rfc/lint_crl_has_next_update.go create mode 100644 v3/lints/rfc/lint_crl_has_next_update_test.go create mode 100644 v3/testdata/crlHasNextUpdate.pem create mode 100644 v3/testdata/crlNotHaveNextUpdate.pem diff --git a/v3/lints/rfc/lint_crl_has_next_update.go b/v3/lints/rfc/lint_crl_has_next_update.go new file mode 100644 index 000000000..4dd434d8c --- /dev/null +++ b/v3/lints/rfc/lint_crl_has_next_update.go @@ -0,0 +1,56 @@ +package rfc + +/* + * ZLint Copyright 2021 Regents of the University of Michigan + * + * 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. + */ + +import ( + "github.com/zmap/zcrypto/x509" + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/util" +) + +type crlHasNextUpdate struct{} + +/************************************************ +RFC 5280: 5.1.2.5 +Conforming CRL issuers MUST include the nextUpdate field in all CRLs. +************************************************/ + +func init() { + lint.RegisterRevocationListLint(&lint.RevocationListLint{ + LintMetadata: lint.LintMetadata{ + Name: "e_crl_has_next_update", + Description: "Conforming CRL issuers MUST include the nextUpdate field in all CRLs.", + Citation: "RFC 5280: 5.1.2.5", + Source: lint.RFC5280, + EffectiveDate: util.RFC5280Date, + }, + Lint: NewCrlHasNextUpdate, + }) +} + +func NewCrlHasNextUpdate() lint.RevocationListLintInterface { + return &crlHasNextUpdate{} +} + +func (l *crlHasNextUpdate) CheckApplies(c *x509.RevocationList) bool { + return true +} + +func (l *crlHasNextUpdate) Execute(c *x509.RevocationList) *lint.LintResult { + if c.NextUpdate.IsZero() { + return &lint.LintResult{Status: lint.Error, Details: "Confoming CRL issuers MUST include the nextUpdate field in all CRLs."} + } + return &lint.LintResult{Status: lint.Pass} +} diff --git a/v3/lints/rfc/lint_crl_has_next_update_test.go b/v3/lints/rfc/lint_crl_has_next_update_test.go new file mode 100644 index 000000000..717610478 --- /dev/null +++ b/v3/lints/rfc/lint_crl_has_next_update_test.go @@ -0,0 +1,42 @@ +package rfc + +import ( + "testing" + + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/test" +) + +/* + * ZLint Copyright 2021 Regents of the University of Michigan + * + * 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. + */ + +func TestCrlHasNextUpdate(t *testing.T) { + inputPath := "crlHasNextUpdate.pem" + want := lint.Pass + got := test.TestRevocationListLint(t, "e_crl_has_next_update", inputPath).Status + + if want != got { + t.Errorf("%s: expected %s, got %s", inputPath, want, got) + } +} + +func TestCrlNotHaveNextUpdate(t *testing.T) { + inputPath := "crlNotHaveNextUpdate.pem" + want := lint.Error + got := test.TestRevocationListLint(t, "e_crl_has_next_update", inputPath).Status + + if want != got { + t.Errorf("%s: expected %s, got %s", inputPath, want, got) + } +} diff --git a/v3/test/helpers.go b/v3/test/helpers.go index 12d99adee..aea157e60 100644 --- a/v3/test/helpers.go +++ b/v3/test/helpers.go @@ -20,6 +20,7 @@ import ( "encoding/pem" "fmt" "os" + "testing" "strings" @@ -48,6 +49,25 @@ func TestLintWithConfig(lintName string, testCertFilename string, configuration return TestLintCert(lintName, ReadTestCert(testCertFilename), config) } +// TestRevocationListLint executes the given lintName against a CRL read from +// a testcrl data file with the given filename. Filenames should be relative to +// `testdata/` and not absolute file paths. +// +//nolint:revive +func TestRevocationListLint(tb testing.TB, lintName string, testCRLFilename string) *lint.LintResult { + tb.Helper() + return TestRevocationListLintWithConfig(tb, lintName, testCRLFilename, "") +} + +func TestRevocationListLintWithConfig(tb testing.TB, lintName string, testCRLFilename string, configuration string) *lint.LintResult { + tb.Helper() + config, err := lint.NewConfigFromString(configuration) + if err != nil { + tb.Fatal(err) + } + return TestLintRevocationList(tb, lintName, ReadTestRevocationList(tb, testCRLFilename), config) +} + // TestLintCert executes a lint with the given name against an already parsed // certificate. This is useful when a unit test reads a certificate from disk // and then mutates it in some way before trying to lint it. @@ -57,7 +77,7 @@ func TestLintWithConfig(lintName string, testCertFilename string, configuration // //nolint:revive func TestLintCert(lintName string, cert *x509.Certificate, ctx lint.Configuration) *lint.LintResult { - l := lint.GlobalRegistry().ByName(lintName) + l := lint.GlobalRegistry().CertificateLints().ByName(lintName) if l == nil { panic(fmt.Sprintf( "Lint name %q does not exist in lint.Lints. "+ @@ -74,6 +94,30 @@ func TestLintCert(lintName string, cert *x509.Certificate, ctx lint.Configuratio return res } +// TestLintRevocationList executes a lint with the given name against an already parsed +// revocation list. This is useful when a unit test reads a revocation list from disk +// and then mutates it in some way before trying to lint it. +// +//nolint:revive +func TestLintRevocationList(tb testing.TB, lintName string, crl *x509.RevocationList, ctx lint.Configuration) *lint.LintResult { + tb.Helper() + l := lint.GlobalRegistry().RevocationListLints().ByName(lintName) + if l == nil { + tb.Fatalf( + "Lint name %q does not exist in lint.Lints. "+ + "Did you forget to RegisterLint?\n", + lintName) + } + res := l.Execute(crl, ctx) + // We never expect a lint to return a nil LintResult + if res == nil { + tb.Fatalf( + "Running lint %q on test revocation list generated a nil LintResult.\n", + lintName) + } + return res +} + // ReadTestCert loads a x509.Certificate from the given inPath which is assumed // to be relative to `testdata/`. // @@ -110,3 +154,41 @@ func ReadTestCert(inPath string) *x509.Certificate { return theCert } + +// ReadTestRevocationList loads a x509.RevocationList from the given inPath which is assumed +// to be relative to `testdata/`. +// +// Important: ReadTestRevocationList is only appropriate for unit tests. It will panic if +// the inPath file can not be loaded. +func ReadTestRevocationList(tb testing.TB, inPath string) *x509.RevocationList { + tb.Helper() + fullPath := fmt.Sprintf("../../testdata/%s", inPath) + data, err := os.ReadFile(fullPath) + if err != nil { + tb.Fatalf( + "Unable to read test revocation list from %q - %q "+ + "Does a unit test have an incorrect test file name?\n", + fullPath, err) + } + + if strings.Contains(string(data), "-BEGIN X509 CRL-") { + block, _ := pem.Decode(data) + if block == nil { //nolint: staticcheck // tb.Fatalf exits + tb.Fatalf( + "Failed to PEM decode test revocation list from %q - "+ + "Does a unit test have a buggy test cert file?\n", + fullPath) + } + data = block.Bytes //nolint: staticcheck // tb.Fatalf exits + } + + theCrl, err := x509.ParseRevocationList(data) + if err != nil { + tb.Fatalf( + "Failed to parse x509 test certificate from %q - %q "+ + "Does a unit test have a buggy test cert file?\n", + fullPath, err) + } + + return theCrl +} diff --git a/v3/testdata/crlHasNextUpdate.pem b/v3/testdata/crlHasNextUpdate.pem new file mode 100644 index 000000000..fb236980a --- /dev/null +++ b/v3/testdata/crlHasNextUpdate.pem @@ -0,0 +1,11 @@ +-----BEGIN X509 CRL----- +MIIBnjCBhwIBATANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDEw1BbWlyIHdhcyBI +ZXJlFw0yMzAzMTMwNTUyNTVaFw0yMzAzMTQwNTUyNTVaoDswOTArBgNVHSMEJDAi +gCAywvCJz28KsE/6Wf9E1nuiihBFWlUyq7X/RDgn5SllIDAKBgNVHRQEAwIBATAN +BgkqhkiG9w0BAQsFAAOCAQEAakioBhLs31svWHGmolDhUg6O1daN6zXSAz/avgzl +38aTKfRSNQ+vM7qgrvCoRojnamziJgXe1hz+/dc8H0/+WEBwVgp1rBzr8f25dSZC +lXBHT1cNI5RL+wU0pFMouUiwWqwUg8o9iGYkqvhuko4AQIcpAoBuf0OggjCuj48r +FX7UN7Kz4pc/4ufengKGkf7EeEQffY3zlS0DAtWv+exoQ6Dt+otDr0PbINJZg+46 +TJ/+0w6RsLGoe4Sh/PYPfaCngMyezENUgJgR1+vF6hbVUweeOB+4nFRNxvHMup0G +GEA4yfzQtHWL8rizWUCyuqXEMPZLzyJT0rv5cLgoOvs+8Q== +-----END X509 CRL----- diff --git a/v3/testdata/crlNotHaveNextUpdate.pem b/v3/testdata/crlNotHaveNextUpdate.pem new file mode 100644 index 000000000..ae95454da --- /dev/null +++ b/v3/testdata/crlNotHaveNextUpdate.pem @@ -0,0 +1,11 @@ +-----BEGIN X509 CRL----- +MIIBjjB4AgEBMA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNVBAMTDUFtaXIgd2FzIEhl +cmUXDTIzMDMxMzA1NTQwOFqgOzA5MCsGA1UdIwQkMCKAIKiGvOMhlD6FiuwaEDl+ +FxP5fyorz7E9iDke1/q+ngvkMAoGA1UdFAQDAgEBMA0GCSqGSIb3DQEBCwUAA4IB +AQAWq81ZR98KCw3Y3KiH2ShJ+mxlgYO91ovQfzsbCOSHrcV9bnVYG8k3WMWBen/v +LsXiSaVeG+9G1b459KuB6yVv24N0vtpzXOorFR1oi0wPWtYzPhkT+RD2Ov10XO2G +bk3DSwcqcjYqx1Hu1BlHzEyTUvwij6XWUx1uc+olH6scRmycn9yGBMSga/Xgx6g1 +4yM9lzN8lHeN2JLr1vnu///iBwwPvdhPMzUE0n/smH/6bkkZXHM33s0cJ6Wm0bLg +TUg9QKGR2PIehZvJg1vvhpZyIEnpGPp1hN9FsK8eKuMJWEEqP7s5URHaHNYlmagA +ylcX526EcfmL6vqtz5OIsfNC +-----END X509 CRL-----