github.com/khulnasoft-lab/tunnel-db@v0.0.0-20231117205118-74e1113bd007/pkg/vulnsrc/amazon/amazon.go (about)

     1  package amazon
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"log"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	bolt "go.etcd.io/bbolt"
    12  	"golang.org/x/xerrors"
    13  
    14  	"github.com/khulnasoft-lab/tunnel-db/pkg/db"
    15  	"github.com/khulnasoft-lab/tunnel-db/pkg/types"
    16  	"github.com/khulnasoft-lab/tunnel-db/pkg/utils"
    17  	ustrings "github.com/khulnasoft-lab/tunnel-db/pkg/utils/strings"
    18  	"github.com/khulnasoft-lab/tunnel-db/pkg/vulnsrc/vulnerability"
    19  )
    20  
    21  const (
    22  	amazonDir      = "amazon"
    23  	platformFormat = "amazon linux %s"
    24  )
    25  
    26  var (
    27  	targetVersions = []string{"1", "2", "2022", "2023"}
    28  
    29  	source = types.DataSource{
    30  		ID:   vulnerability.Amazon,
    31  		Name: "Amazon Linux Security Center",
    32  		URL:  "https://alas.aws.amazon.com/",
    33  	}
    34  )
    35  
    36  type VulnSrc struct {
    37  	dbc        db.Operation
    38  	advisories map[string][]ALAS
    39  }
    40  
    41  // ALAS has detailed data of ALAS
    42  type ALAS struct {
    43  	ID          string      `json:"id,omitempty"`
    44  	Title       string      `json:"title,omitempty"`
    45  	Severity    string      `json:"severity,omitempty"`
    46  	Description string      `json:"description,omitempty"`
    47  	Packages    []Package   `json:"packages,omitempty"`
    48  	References  []Reference `json:"references,omitempty"`
    49  	CveIDs      []string    `json:"cveids,omitempty"`
    50  }
    51  
    52  // Package has affected package information
    53  type Package struct {
    54  	Name    string `json:"name,omitempty"`
    55  	Epoch   string `json:"epoch,omitempty"`
    56  	Version string `json:"version,omitempty"`
    57  	Release string `json:"release,omitempty"`
    58  	Arch    string `json:"arch,omitempty"`
    59  }
    60  
    61  // Reference has reference information
    62  type Reference struct {
    63  	Href string `json:"href,omitempty"`
    64  }
    65  
    66  func NewVulnSrc() VulnSrc {
    67  	return VulnSrc{
    68  		dbc:        db.Config{},
    69  		advisories: map[string][]ALAS{},
    70  	}
    71  }
    72  
    73  func (vs VulnSrc) Name() types.SourceID {
    74  	return source.ID
    75  }
    76  
    77  func (vs VulnSrc) Update(dir string) error {
    78  	rootDir := filepath.Join(dir, "vuln-list", amazonDir)
    79  
    80  	err := utils.FileWalk(rootDir, vs.walkFunc)
    81  	if err != nil {
    82  		return xerrors.Errorf("error in Amazon walk: %w", err)
    83  	}
    84  
    85  	if err = vs.save(); err != nil {
    86  		return xerrors.Errorf("error in Amazon save: %w", err)
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  func (vs *VulnSrc) walkFunc(r io.Reader, path string) error {
    93  	paths := strings.Split(path, string(filepath.Separator))
    94  	if len(paths) < 2 {
    95  		return nil
    96  	}
    97  	version := paths[len(paths)-2]
    98  	if !ustrings.InSlice(version, targetVersions) {
    99  		log.Printf("unsupported Amazon version: %s\n", version)
   100  		return nil
   101  	}
   102  
   103  	var alas ALAS
   104  	if err := json.NewDecoder(r).Decode(&alas); err != nil {
   105  		return xerrors.Errorf("failed to decode Amazon JSON: %w", err)
   106  	}
   107  
   108  	vs.advisories[version] = append(vs.advisories[version], alas)
   109  	return nil
   110  }
   111  
   112  func (vs VulnSrc) save() error {
   113  	log.Println("Saving Amazon DB")
   114  	err := vs.dbc.BatchUpdate(func(tx *bolt.Tx) error {
   115  		return vs.commit(tx)
   116  	})
   117  	if err != nil {
   118  		return xerrors.Errorf("error in batch update: %w", err)
   119  	}
   120  	return nil
   121  }
   122  
   123  func (vs VulnSrc) commit(tx *bolt.Tx) error {
   124  	for majorVersion, alasList := range vs.advisories {
   125  		platformName := fmt.Sprintf(platformFormat, majorVersion)
   126  		if err := vs.dbc.PutDataSource(tx, platformName, source); err != nil {
   127  			return xerrors.Errorf("failed to put data source: %w", err)
   128  		}
   129  		for _, alas := range alasList {
   130  			for _, cveID := range alas.CveIDs {
   131  				for _, pkg := range alas.Packages {
   132  					advisory := types.Advisory{
   133  						FixedVersion: utils.ConstructVersion(pkg.Epoch, pkg.Version, pkg.Release),
   134  					}
   135  					if err := vs.dbc.PutAdvisoryDetail(tx, cveID, pkg.Name, []string{platformName}, advisory); err != nil {
   136  						return xerrors.Errorf("failed to save Amazon advisory: %w", err)
   137  					}
   138  
   139  					var references []string
   140  					for _, ref := range alas.References {
   141  						references = append(references, ref.Href)
   142  					}
   143  
   144  					vuln := types.VulnerabilityDetail{
   145  						Severity:    severityFromPriority(alas.Severity),
   146  						References:  references,
   147  						Description: alas.Description,
   148  						Title:       "",
   149  					}
   150  					if err := vs.dbc.PutVulnerabilityDetail(tx, cveID, source.ID, vuln); err != nil {
   151  						return xerrors.Errorf("failed to save Amazon vulnerability detail: %w", err)
   152  					}
   153  
   154  					// for optimization
   155  					if err := vs.dbc.PutVulnerabilityID(tx, cveID); err != nil {
   156  						return xerrors.Errorf("failed to save the vulnerability ID: %w", err)
   157  					}
   158  				}
   159  			}
   160  		}
   161  	}
   162  	return nil
   163  }
   164  
   165  // Get returns a security advisory
   166  func (vs VulnSrc) Get(version string, pkgName string) ([]types.Advisory, error) {
   167  	bucket := fmt.Sprintf(platformFormat, version)
   168  	advisories, err := vs.dbc.GetAdvisories(bucket, pkgName)
   169  	if err != nil {
   170  		return nil, xerrors.Errorf("failed to get Amazon advisories: %w", err)
   171  	}
   172  	return advisories, nil
   173  }
   174  
   175  func severityFromPriority(priority string) types.Severity {
   176  	switch priority {
   177  	case "low":
   178  		return types.SeverityLow
   179  	case "medium":
   180  		return types.SeverityMedium
   181  	case "important":
   182  		return types.SeverityHigh
   183  	case "critical":
   184  		return types.SeverityCritical
   185  	default:
   186  		return types.SeverityUnknown
   187  	}
   188  }