github.com/quay/claircore@v1.5.28/aws/updater.go (about) 1 package aws 2 3 import ( 4 "context" 5 "encoding/xml" 6 "fmt" 7 "io" 8 "net/http" 9 "strings" 10 "time" 11 12 "github.com/quay/zlog" 13 14 "github.com/quay/claircore" 15 "github.com/quay/claircore/aws/internal/alas" 16 "github.com/quay/claircore/internal/xmlutil" 17 "github.com/quay/claircore/libvuln/driver" 18 ) 19 20 var _ driver.Updater = (*Updater)(nil) 21 22 // Updater implements the claircore.Updater.Fetcher and claircore.Updater.Parser 23 // interfaces making it eligible to be used as a claircore.Updater 24 type Updater struct { 25 release Release 26 c *http.Client 27 } 28 29 func NewUpdater(release Release) (*Updater, error) { 30 return &Updater{ 31 release: release, 32 }, nil 33 } 34 35 func (u *Updater) Name() string { 36 return fmt.Sprintf("aws-%v-updater", u.release) 37 } 38 39 func (u *Updater) Configure(ctx context.Context, _ driver.ConfigUnmarshaler, c *http.Client) error { 40 // TODO This should be able to configure things, actually. 41 u.c = c 42 return nil 43 } 44 45 func (u *Updater) Fetch(ctx context.Context, fingerprint driver.Fingerprint) (io.ReadCloser, driver.Fingerprint, error) { 46 ctx = zlog.ContextWithValues(ctx, "component", "aws/Updater.Fetch") 47 client, err := NewClient(ctx, u.c, u.release) 48 if err != nil { 49 return nil, "", fmt.Errorf("failed to create client: %v", err) 50 } 51 52 tctx, cancel := context.WithTimeout(ctx, defaultOpTimeout) 53 defer cancel() 54 repoMD, err := client.RepoMD(tctx) 55 if err != nil { 56 return nil, "", fmt.Errorf("failed to retrieve repo metadata: %v", err) 57 } 58 59 updatesRepoMD, err := repoMD.Repo(alas.UpdateInfo, "") 60 if err != nil { 61 return nil, "", fmt.Errorf("updates repo metadata could not be retrieved: %v", err) 62 } 63 if updatesRepoMD.Checksum.Sum == string(fingerprint) { 64 return nil, fingerprint, driver.Unchanged 65 } 66 67 tctx, cancel = context.WithTimeout(ctx, defaultOpTimeout) 68 defer cancel() 69 rc, err := client.Updates(tctx) 70 if err != nil { 71 return nil, "", fmt.Errorf("failed to retrieve update info: %v", err) 72 } 73 74 return rc, driver.Fingerprint(updatesRepoMD.Checksum.Sum), nil 75 } 76 77 func (u *Updater) Parse(ctx context.Context, contents io.ReadCloser) ([]*claircore.Vulnerability, error) { 78 var updates alas.Updates 79 dec := xml.NewDecoder(contents) 80 dec.CharsetReader = xmlutil.CharsetReader 81 if err := dec.Decode(&updates); err != nil { 82 return nil, fmt.Errorf("failed to unmarshal updates xml: %v", err) 83 } 84 dist := releaseToDist(u.release) 85 86 vulns := []*claircore.Vulnerability{} 87 for _, update := range updates.Updates { 88 partial := &claircore.Vulnerability{ 89 Updater: u.Name(), 90 Name: update.ID, 91 Description: update.Description, 92 Issued: time.Time(update.Issued.Date), 93 Links: refsToLinks(update), 94 Severity: update.Severity, 95 NormalizedSeverity: NormalizeSeverity(update.Severity), 96 Dist: dist, 97 ArchOperation: claircore.OpEquals, 98 } 99 vulns = append(vulns, u.unpack(partial, update.Packages)...) 100 } 101 102 return vulns, nil 103 } 104 105 // unpack takes the partially populated vulnerability and creates a fully populated vulnerability for each 106 // provided alas.Package 107 func (u *Updater) unpack(partial *claircore.Vulnerability, packages []alas.Package) []*claircore.Vulnerability { 108 out := []*claircore.Vulnerability{} 109 110 var b strings.Builder 111 for _, alasPKG := range packages { 112 // make copy 113 v := *partial 114 115 v.Package = &claircore.Package{ 116 Name: alasPKG.Name, 117 Kind: claircore.BINARY, 118 Arch: alasPKG.Arch, 119 } 120 v.FixedInVersion = versionString(&b, alasPKG) 121 122 out = append(out, &v) 123 } 124 125 b.Reset() 126 return out 127 } 128 129 func versionString(b *strings.Builder, p alas.Package) string { 130 b.Reset() 131 132 if p.Epoch != "" && p.Epoch != "0" { 133 b.WriteString(p.Epoch) 134 b.WriteByte(':') 135 } 136 b.WriteString(p.Version) 137 b.WriteByte('-') 138 b.WriteString(p.Release) 139 140 return b.String() 141 } 142 143 // refsToLinks takes an alas.Update and creates a string with all the href links 144 func refsToLinks(u alas.Update) string { 145 out := []string{} 146 for _, ref := range u.References { 147 out = append(out, ref.Href) 148 } 149 150 return strings.Join(out, " ") 151 }