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 }