@@ -3,25 +3,112 @@ package photon
33import (
44 "context"
55 "fmt"
6+ "io"
7+ "net/http"
8+ "net/url"
9+ "regexp"
10+ "strings"
611
712 "github.com/quay/claircore/libvuln/driver"
813)
914
10- var photonReleases = []Release {
11- Photon1 ,
12- Photon2 ,
13- Photon3 ,
15+ // UpdaterSet dynamically discovers available Photon OVAL databases from the
16+ // upstream index and returns one updater per discovered major release.
17+ //
18+ // Discovery rules:
19+ // - Match files named com.vmware.phsa-photon<MAJOR>.xml.gz
20+ // Factory implements a dynamic UpdaterSetFactory for Photon that discovers
21+ // available OVAL feeds and constructs per-release updaters.
22+ type Factory struct {
23+ c * http.Client
24+ base * url.URL
1425}
1526
16- func UpdaterSet (_ context.Context ) (driver.UpdaterSet , error ) {
27+ var (
28+ _ driver.UpdaterSetFactory = (* Factory )(nil )
29+ _ driver.Configurable = (* Factory )(nil )
30+ )
31+
32+ // FactoryConfig is the configuration accepted by the Factory.
33+ //
34+ // By convention, this is keyed by the string "photon".
35+ type FactoryConfig struct {
36+ // URL indicates the base URL for the OVAL layout. It should have a trailing slash.
37+ URL string `json:"url" yaml:"url"`
38+ }
39+
40+ // NewFactory returns an unconfigured Factory.
41+ func NewFactory (_ context.Context ) (* Factory , error ) {
42+ return & Factory {}, nil
43+ }
44+
45+ // Configure implements driver.Configurable.
46+ func (f * Factory ) Configure (_ context.Context , cf driver.ConfigUnmarshaler , c * http.Client ) error {
47+ f .c = c
48+ var cfg FactoryConfig
49+ if err := cf (& cfg ); err != nil {
50+ return err
51+ }
52+ u := upstreamBase .String ()
53+ if cfg .URL != "" {
54+ u = cfg .URL
55+ if ! strings .HasSuffix (u , "/" ) {
56+ u += "/"
57+ }
58+ }
59+ var err error
60+ f .base , err = url .Parse (u )
61+ return err
62+ }
63+
64+ // UpdaterSet dynamically discovers available Photon OVAL databases from the
65+ // configured index and returns one updater per discovered major release.
66+ //
67+ // This will match files named com.vmware.phsa-photon<MAJOR>.xml.gz
68+ func (f * Factory ) UpdaterSet (ctx context.Context ) (driver.UpdaterSet , error ) {
1769 us := driver .NewUpdaterSet ()
18- for _ , release := range photonReleases {
19- u , err := NewUpdater (release )
70+ c := f .c
71+ if c == nil {
72+ c = http .DefaultClient
73+ }
74+ base := f .base
75+ if base == nil {
76+ base = upstreamBase
77+ }
78+
79+ res , err := c .Get (base .String ())
80+ if err != nil {
81+ return us , fmt .Errorf ("photon: discovery request failed: %w" , err )
82+ }
83+ defer res .Body .Close ()
84+ if res .StatusCode != http .StatusOK {
85+ return us , fmt .Errorf ("photon: unexpected status from index: %s" , res .Status )
86+ }
87+ body , err := io .ReadAll (res .Body )
88+ if err != nil {
89+ return us , fmt .Errorf ("photon: reading index body: %w" , err )
90+ }
91+
92+ re := regexp .MustCompile (`href="com\.vmware\.phsa-photon(\d+)\.xml\.gz"` )
93+ matches := re .FindAllStringSubmatch (string (body ), - 1 )
94+ if len (matches ) == 0 {
95+ return us , fmt .Errorf ("photon: no OVAL entries discovered at index" )
96+ }
97+ for _ , m := range matches {
98+ if len (m ) < 2 {
99+ continue
100+ }
101+ filename := "com.vmware.phsa-photon" + m [1 ] + ".xml.gz"
102+ u , err := base .Parse (filename )
20103 if err != nil {
21- return us , fmt .Errorf ("failed to create updater : %v " , err )
104+ return us , fmt .Errorf ("photon: building feed url : %w " , err )
22105 }
23- err = us .Add (u )
106+ rel := Release (m [1 ] + ".0" )
107+ up , err := NewUpdater (rel , WithURL (u .String (), "gz" ))
24108 if err != nil {
109+ return us , fmt .Errorf ("photon: creating updater for %s: %w" , rel , err )
110+ }
111+ if err := us .Add (up ); err != nil {
25112 return us , err
26113 }
27114 }
0 commit comments