github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/compliance/spec/compliance.go (about)

     1  package spec
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strings"
     7  
     8  	"golang.org/x/exp/maps"
     9  	"golang.org/x/xerrors"
    10  	"gopkg.in/yaml.v3"
    11  
    12  	defsecTypes "github.com/aquasecurity/defsec/pkg/types"
    13  	sp "github.com/aquasecurity/trivy-policies/pkg/spec"
    14  	"github.com/devseccon/trivy/pkg/types"
    15  )
    16  
    17  type Severity string
    18  
    19  // ComplianceSpec represent the compliance specification
    20  type ComplianceSpec struct {
    21  	Spec defsecTypes.Spec `yaml:"spec"`
    22  }
    23  
    24  const (
    25  	FailStatus defsecTypes.ControlStatus = "FAIL"
    26  	PassStatus defsecTypes.ControlStatus = "PASS"
    27  	WarnStatus defsecTypes.ControlStatus = "WARN"
    28  )
    29  
    30  // Scanners reads spec control and determines the scanners by check ID prefix
    31  func (cs *ComplianceSpec) Scanners() (types.Scanners, error) {
    32  	scannerTypes := make(map[types.Scanner]struct{})
    33  	for _, control := range cs.Spec.Controls {
    34  		for _, check := range control.Checks {
    35  			scannerType := scannerByCheckID(check.ID)
    36  			if scannerType == types.UnknownScanner {
    37  				return nil, xerrors.Errorf("unsupported check ID: %s", check.ID)
    38  			}
    39  			scannerTypes[scannerType] = struct{}{}
    40  		}
    41  	}
    42  	return maps.Keys(scannerTypes), nil
    43  }
    44  
    45  // CheckIDs return list of compliance check IDs
    46  func (cs *ComplianceSpec) CheckIDs() map[types.Scanner][]string {
    47  	checkIDsMap := make(map[types.Scanner][]string)
    48  	for _, control := range cs.Spec.Controls {
    49  		for _, check := range control.Checks {
    50  			scannerType := scannerByCheckID(check.ID)
    51  			checkIDsMap[scannerType] = append(checkIDsMap[scannerType], check.ID)
    52  		}
    53  	}
    54  	return checkIDsMap
    55  }
    56  
    57  func scannerByCheckID(checkID string) types.Scanner {
    58  	checkID = strings.ToLower(checkID)
    59  	switch {
    60  	case strings.HasPrefix(checkID, "cve-") || strings.HasPrefix(checkID, "dla-"):
    61  		return types.VulnerabilityScanner
    62  	case strings.HasPrefix(checkID, "avd-"):
    63  		return types.MisconfigScanner
    64  	case strings.HasPrefix(checkID, "vuln-"): // custom id for filtering vulnerabilities by severity
    65  		return types.VulnerabilityScanner
    66  	case strings.HasPrefix(checkID, "secret-"): // custom id for filtering secrets by severity
    67  		return types.SecretScanner
    68  	default:
    69  		return types.UnknownScanner
    70  	}
    71  }
    72  
    73  // GetComplianceSpec accepct compliance flag name/path and return builtin or file system loaded spec
    74  func GetComplianceSpec(specNameOrPath string) (ComplianceSpec, error) {
    75  	var b []byte
    76  	var err error
    77  	if strings.HasPrefix(specNameOrPath, "@") {
    78  		b, err = os.ReadFile(strings.TrimPrefix(specNameOrPath, "@"))
    79  		if err != nil {
    80  			return ComplianceSpec{}, fmt.Errorf("error retrieving compliance spec from path: %w", err)
    81  		}
    82  	} else {
    83  		// TODO: GetSpecByName() should return []byte
    84  		b = []byte(sp.NewSpecLoader().GetSpecByName(specNameOrPath))
    85  	}
    86  
    87  	var complianceSpec ComplianceSpec
    88  	if err = yaml.Unmarshal(b, &complianceSpec); err != nil {
    89  		return ComplianceSpec{}, xerrors.Errorf("spec yaml decode error: %w", err)
    90  	}
    91  	return complianceSpec, nil
    92  
    93  }