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

     1  package composer
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"strings"
     7  
     8  	bolt "go.etcd.io/bbolt"
     9  	"golang.org/x/xerrors"
    10  	"gopkg.in/yaml.v2"
    11  
    12  	"github.com/khulnasoft-lab/tunnel-db/pkg/db"
    13  	"github.com/khulnasoft-lab/tunnel-db/pkg/types"
    14  	"github.com/khulnasoft-lab/tunnel-db/pkg/vulnsrc/bucket"
    15  	"github.com/khulnasoft-lab/tunnel-db/pkg/vulnsrc/vulnerability"
    16  )
    17  
    18  const composerDir = "php-security-advisories"
    19  
    20  var (
    21  	source = types.DataSource{
    22  		ID:   vulnerability.PhpSecurityAdvisories,
    23  		Name: "PHP Security Advisories Database",
    24  		URL:  "https://github.com/FriendsOfPHP/security-advisories",
    25  	}
    26  
    27  	bucketName = bucket.Name(vulnerability.Composer, source.Name)
    28  )
    29  
    30  type RawAdvisory struct {
    31  	Cve       string
    32  	Title     string
    33  	Link      string
    34  	Reference string
    35  	Branches  map[string]Branch
    36  }
    37  
    38  type Branch struct {
    39  	Versions []string `json:",omitempty"`
    40  }
    41  
    42  type Advisory struct {
    43  	VulnerabilityID string            `json:",omitempty"`
    44  	Branches        map[string]Branch `json:",omitempty"`
    45  }
    46  
    47  type VulnSrc struct {
    48  	dbc db.Operation
    49  }
    50  
    51  func NewVulnSrc() VulnSrc {
    52  	return VulnSrc{
    53  		dbc: db.Config{},
    54  	}
    55  }
    56  
    57  func (vs VulnSrc) Name() types.SourceID {
    58  	return source.ID
    59  }
    60  
    61  func (vs VulnSrc) Update(dir string) (err error) {
    62  	repoPath := filepath.Join(dir, composerDir)
    63  	if err := vs.update(repoPath); err != nil {
    64  		return xerrors.Errorf("failed to update compose vulnerabilities: %w", err)
    65  	}
    66  	return nil
    67  }
    68  
    69  func (vs VulnSrc) update(repoPath string) error {
    70  	err := vs.dbc.BatchUpdate(func(tx *bolt.Tx) error {
    71  		if err := vs.dbc.PutDataSource(tx, bucketName, source); err != nil {
    72  			return xerrors.Errorf("failed to put data source: %w", err)
    73  		}
    74  		if err := vs.walk(tx, repoPath); err != nil {
    75  			return xerrors.Errorf("failed to walk compose advisories: %w", err)
    76  		}
    77  		return nil
    78  	})
    79  	if err != nil {
    80  		return xerrors.Errorf("batch update failed: %w", err)
    81  	}
    82  	return nil
    83  }
    84  
    85  func (vs VulnSrc) walk(tx *bolt.Tx, root string) error {
    86  	return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
    87  		if err != nil {
    88  			return err
    89  		}
    90  		if info.IsDir() || !strings.HasPrefix(info.Name(), "CVE-") {
    91  			return nil
    92  		}
    93  		buf, err := os.ReadFile(path)
    94  		if err != nil {
    95  			return xerrors.Errorf("failed to read a file: %w", err)
    96  		}
    97  
    98  		advisory := RawAdvisory{}
    99  		err = yaml.Unmarshal(buf, &advisory)
   100  		if err != nil {
   101  			return xerrors.Errorf("failed to unmarshal YAML: %w", err)
   102  		}
   103  
   104  		// for detecting vulnerabilities
   105  		vulnID := advisory.Cve
   106  		if vulnID == "" {
   107  			// e.g. CVE-2019-12139.yaml => CVE-2019-12139
   108  			vulnID = strings.TrimSuffix(info.Name(), ".yaml")
   109  		}
   110  
   111  		var vulnerableVersions []string
   112  		for _, branch := range advisory.Branches {
   113  			vulnerableVersions = append(vulnerableVersions, strings.Join(branch.Versions, ", "))
   114  		}
   115  
   116  		a := types.Advisory{
   117  			VulnerableVersions: vulnerableVersions,
   118  		}
   119  
   120  		pkgName := strings.TrimPrefix(advisory.Reference, "composer://")
   121  		pkgName = vulnerability.NormalizePkgName(vulnerability.Composer, pkgName)
   122  
   123  		err = vs.dbc.PutAdvisoryDetail(tx, vulnID, pkgName, []string{bucketName}, a)
   124  		if err != nil {
   125  			return xerrors.Errorf("failed to save php advisory: %w", err)
   126  		}
   127  
   128  		// for displaying vulnerability detail
   129  		vuln := types.VulnerabilityDetail{
   130  			ID:         vulnID,
   131  			References: []string{advisory.Link},
   132  			Title:      advisory.Title,
   133  		}
   134  		if err = vs.dbc.PutVulnerabilityDetail(tx, vulnID, source.ID, vuln); err != nil {
   135  			return xerrors.Errorf("failed to save php vulnerability detail: %w", err)
   136  		}
   137  
   138  		// for optimization
   139  		if err = vs.dbc.PutVulnerabilityID(tx, vulnID); err != nil {
   140  			return xerrors.Errorf("failed to save the vulnerability ID: %w", err)
   141  		}
   142  		return nil
   143  	})
   144  }