cmd/cpe2cve/config.go (141 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. // // 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 main import ( "encoding/json" "flag" "fmt" "io" "os" "path" "github.com/BurntSushi/toml" ) type config struct { // input fields CPEsAt int // output fields CVEsAt int MatchesAt int CWEsAt int ProviderAt int // output score fields CVSS2At int CVSS3At int CVSSAt int // output deleted fields EraseFields fieldsToSkip // []int // separators InFieldSeparator string InRecordSeparator string OutFieldSeparator string OutRecordSeparator string // optimizations NumProcessors int IndexDict bool CacheSize int64 RequireVersion bool // profiling CPUProfile string MemoryProfile string // feeds FeedOverrides multiString // []string Feeds map[string][]string } func (cfg *config) addFlags() { // input flag.IntVar(&cfg.CPEsAt, "cpe", 0, "look for CPE names in input at this position (starts with 1)") // output flag.IntVar(&cfg.CVEsAt, "cve", 0, "output CVEs at this position (starts with 1)") flag.IntVar(&cfg.MatchesAt, "matches", 0, "output CPEs that matches CVE at this position; 0 disables the output") flag.IntVar(&cfg.CWEsAt, "cwe", 0, "output problem types (CWEs) at this position (starts with 1)") flag.IntVar(&cfg.ProviderAt, "provider_field", 0, "where should the provider be placed in the output (starts with 1).") flag.IntVar(&cfg.CVSS2At, "cvss2", 0, "output CVSS 2.0 base score at this position (starts with 1)") flag.IntVar(&cfg.CVSS3At, "cvss3", 0, "output CVSS 3.0 base score at this position (starts with 1)") flag.IntVar(&cfg.CVSSAt, "cvss", 0, "output CVSS base score (v3 if available, v2 otherwise) at this position (starts with 1)") flag.Var(&cfg.EraseFields, "e", "comma separated list of fields to erase from output; starts at 1, supports ranges (e.g. 1-3); processed before the vulnerablitie field added") // separators flag.StringVar(&cfg.InFieldSeparator, "d", "\t", "input columns delimiter") flag.StringVar(&cfg.InRecordSeparator, "d2", ",", "inner input columns delimiter: separates elements of list passed into a CSV columns") flag.StringVar(&cfg.OutFieldSeparator, "o", "\t", "output columns delimiter") flag.StringVar(&cfg.OutRecordSeparator, "o2", ",", "inner output columns delimiter: separates elements of lists in output CSV columns") // optimizations flag.IntVar(&cfg.NumProcessors, "nproc", 1, "number of concurrent goroutines that perform CVE lookup") flag.BoolVar(&cfg.IndexDict, "idxd", false, "build and use an index for CVE dictionary: increases the processing speed, but might miss some matches") flag.Int64Var(&cfg.CacheSize, "cache_size", 0, "limit the cache size to this amount in bytes; 0 removes the limit, -1 disables caching") flag.BoolVar(&cfg.RequireVersion, "require_version", false, "ignore matches of CPEs with version ANY") // profiling flag.StringVar(&cfg.CPUProfile, "cpuprofile", "", "file to store CPU profile data to; empty value disables CPU profiling") flag.StringVar(&cfg.MemoryProfile, "memprofile", "", "file to store memory profile data to; empty value disables memory profiling") // feeds flag.Var(&cfg.FeedOverrides, "r", "overRide: path to override feed, can be specified multiple times") } func (cfg *config) addFeedsFromArgs(provider string, feedFiles ...string) { if cfg.Feeds == nil { cfg.Feeds = make(map[string][]string) } cfg.Feeds[provider] = append(cfg.Feeds[provider], feedFiles...) } func (cfg *config) validate() error { if len(cfg.Feeds) == 0 { return fmt.Errorf("feed files weren't provided") } if cfg.ProviderAt != 0 { for provider, feed := range cfg.Feeds { if provider == "" { return fmt.Errorf("need to specify all providers when using provider in the output, but wasn't specified for feed %q", feed) } } } if cfg.CPEsAt <= 0 { return fmt.Errorf("-cpe flag wasn't provided") } if cfg.CVEsAt <= 0 { return fmt.Errorf("-cve flag wasn't provided") } if cfg.MatchesAt < 0 { return fmt.Errorf("-matches value is invalid %d", cfg.MatchesAt) } if cfg.CWEsAt < 0 { return fmt.Errorf("-cwe value is invalid %d", cfg.CWEsAt) } if cfg.CVSSAt < 0 { return fmt.Errorf("-cvss2 value is invalid %d", cfg.CVSS2At) } if cfg.CVSS3At < 0 { return fmt.Errorf("-cvss2 value is invalid %d", cfg.CVSS3At) } if cfg.CVSSAt < 0 { return fmt.Errorf("-cvss value is invalid %d", cfg.CVSSAt) } return nil } func readConfigFile(file string) (config, error) { f, err := os.Open(file) if err != nil { return config{}, err } defer f.Close() var cfg config switch ext := path.Ext(file); ext { case ".json": // Example: // { // "CVEsAt": 3, // "ProviderAt": 2, // "Feeds": { // "foo": ["foo.json"], // "bar": ["bar.json", "bar2.json.gz"] // } // } err = json.NewDecoder(f).Decode(&cfg) case ".toml": // Example: // CVEsAt = 3 // ProviderAt = 2 // [Feeds] // foo = ["foo.json"] // bar = ["bar.json", "bar2.json.gz"] _, err = toml.NewDecoder(f).Decode(&cfg) default: return cfg, fmt.Errorf("unsupported file extension: %q", ext) } if err == nil { // set defaults if cfg.InFieldSeparator == "" { cfg.InFieldSeparator = "\t" } if cfg.InRecordSeparator == "" { cfg.InRecordSeparator = "," } if cfg.OutFieldSeparator == "" { cfg.OutFieldSeparator = "\t" } if cfg.OutRecordSeparator == "" { cfg.OutRecordSeparator = "," } if cfg.NumProcessors == 0 { cfg.NumProcessors = 1 } } return cfg, err } func writeConfigFileDefinition(w io.Writer) { cfg := config{ EraseFields: fieldsToSkip{1: true}, FeedOverrides: multiString{"override feed path"}, Feeds: map[string][]string{"provider": []string{"feed file 1", "feed file 2"}}, } e := json.NewEncoder(w) e.SetIndent("\t", "\t") fmt.Fprint(w, "\nConfig file definition:\n\t") e.Encode(cfg) fmt.Fprint(w, "\n") }