diff --git a/cmd/clairctl/client.go b/cmd/clairctl/client.go index 2fed786230..f16ff4a688 100644 --- a/cmd/clairctl/client.go +++ b/cmd/clairctl/client.go @@ -16,6 +16,7 @@ import ( "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/quay/claircore" "github.com/quay/zlog" + spdxtools "github.com/spdx/tools-golang/spdx/v2/v2_3" "github.com/tomnomnom/linkheader" "github.com/quay/clair/v4/cmd" @@ -105,7 +106,28 @@ var ( errNovelManifest = errors.New("manifest unknown to the system") ) -func (c *Client) IndexReport(ctx context.Context, id claircore.Digest, m *claircore.Manifest) error { +func (c *Client) SPDXReport(ctx context.Context, id claircore.Digest, m *claircore.Manifest) (*spdxtools.Document, error) { + var report = spdxtools.Document{} + err := c.GetIndexReport(ctx, id, m, &report, "application/spdx+json") + if err != nil { + return nil, err + } + return &report, nil +} + +func (c *Client) IndexReport(ctx context.Context, id claircore.Digest, m *claircore.Manifest) (*claircore.IndexReport, error) { + var report = claircore.IndexReport{} + err := c.GetIndexReport(ctx, id, m, &report, "application/json") + if err != nil { + return nil, err + } + if !report.Success && report.Err != "" { + return nil, errors.New("indexer error: " + report.Err) + } + return &report, nil +} + +func (c *Client) GetIndexReport(ctx context.Context, id claircore.Digest, m *claircore.Manifest, dest interface{}, mediaType string) error { var ( req *http.Request res *http.Response @@ -121,6 +143,7 @@ func (c *Client) IndexReport(ctx context.Context, id claircore.Digest, m *clairc if err != nil { return err } + req.Header.Add("Accept", mediaType) res, err = c.client.Do(req) if err != nil { zlog.Debug(ctx). @@ -130,97 +153,90 @@ func (c *Client) IndexReport(ctx context.Context, id claircore.Digest, m *clairc return err } defer res.Body.Close() - ev := zlog.Debug(ctx). + zlog.Debug(ctx). Str("method", res.Request.Method). Str("path", res.Request.URL.Path). Str("status", res.Status) - if ev.Enabled() && res.ContentLength > 0 && res.ContentLength <= 256 { - var buf bytes.Buffer - buf.ReadFrom(io.LimitReader(res.Body, 256)) - ev.Stringer("body", &buf) - } - ev.Send() - switch res.StatusCode { - case http.StatusNotFound, http.StatusOK: - case http.StatusNotModified: - return nil - default: - return fmt.Errorf("unexpected return status: %d", res.StatusCode) - } - if m == nil { - ev := zlog.Debug(ctx). - Stringer("manifest", id) - if res.StatusCode == http.StatusNotFound { - ev.Msg("don't have needed manifest") - return errNovelManifest + var rd io.Reader + switch res.StatusCode { + case http.StatusOK, http.StatusNotModified: + rd = res.Body + case http.StatusNotFound: + if m == nil { + ev := zlog.Debug(ctx). + Stringer("manifest", id) + if res.StatusCode == http.StatusNotFound { + ev.Msg("don't have needed manifest") + return errNovelManifest + } + ev.Msg("manifest may be out-of-date") + return errNeedManifest + } + ru, err := c.host.Parse(path.Join(c.host.RequestURI(), httptransport.IndexAPIPath)) + if err != nil { + zlog.Debug(ctx). + Err(err). + Msg("unable to construct index_report url") + return err } - ev.Msg("manifest may be out-of-date") - return errNeedManifest - } - ru, err := c.host.Parse(path.Join(c.host.RequestURI(), httptransport.IndexAPIPath)) - if err != nil { - zlog.Debug(ctx). - Err(err). - Msg("unable to construct index_report url") - return err - } - req, err = c.request(ctx, ru, http.MethodPost) - if err != nil { - return err - } - req.Body = codec.JSONReader(m) - res, err = c.client.Do(req) - if err != nil { + req, err = c.request(ctx, ru, http.MethodPost) + if err != nil { + return err + } + req.Header.Add("Accept", mediaType) + req.Body = codec.JSONReader(m) + res, err = c.client.Do(req) + if err != nil { + zlog.Debug(ctx). + Err(err). + Stringer("url", req.URL). + Msg("request failed") + return err + } + defer res.Body.Close() zlog.Debug(ctx). - Err(err). - Stringer("url", req.URL). - Msg("request failed") - return err - } - defer res.Body.Close() - zlog.Debug(ctx). - Str("method", res.Request.Method). - Str("path", res.Request.URL.Path). - Str("status", res.Status). - Send() - switch res.StatusCode { - case http.StatusOK: - case http.StatusCreated: - // + Str("method", res.Request.Method). + Str("path", res.Request.URL.Path). + Str("status", res.Status). + Send() + switch res.StatusCode { + case http.StatusOK: + case http.StatusCreated: + // + default: + return fmt.Errorf("unexpected return status: %d", res.StatusCode) + } + switch { + case res.ContentLength > 0 && res.ContentLength < 32+9: + // Less than the size of the digest representation, something's up. + var buf bytes.Buffer + // Ignore error, because what would we do with it here? + ct, _ := buf.ReadFrom(res.Body) + zlog.Info(ctx). + Int64("size", ct). + Stringer("response", &buf). + Msg("body seems short") + return fmt.Errorf("body seems short: %d bytes", ct) + case res.ContentLength < 0: // Streaming + fallthrough + default: + rd = res.Body + } default: return fmt.Errorf("unexpected return status: %d", res.StatusCode) } - var rd io.Reader - switch { - case res.ContentLength > 0 && res.ContentLength < 32+9: - // Less than the size of the digest representation, something's up. - var buf bytes.Buffer - // Ignore error, because what would we do with it here? - ct, _ := buf.ReadFrom(res.Body) - zlog.Info(ctx). - Int64("size", ct). - Stringer("response", &buf). - Msg("body seems short") - rd = &buf - case res.ContentLength < 0: // Streaming - fallthrough - default: - rd = res.Body - } - var report claircore.IndexReport + dec := codec.GetDecoder(rd) defer codec.PutDecoder(dec) - if err := dec.Decode(&report); err != nil { + if err := dec.Decode(&dest); err != nil { zlog.Debug(ctx). Err(err). Msg("unable to decode json payload") return err } - if !report.Success && report.Err != "" { - return errors.New("indexer error: " + report.Err) - } + if v := res.Header.Get("etag"); v != "" { ls := linkheader.ParseMultiple(res.Header[http.CanonicalHeaderKey("link")]). FilterByRel("https://projectquay.io/clair/v1/index_report") @@ -232,6 +248,7 @@ func (c *Client) IndexReport(ctx context.Context, id claircore.Digest, m *clairc c.setValidator(ctx, u.Path, v) } } + return nil } diff --git a/cmd/clairctl/main.go b/cmd/clairctl/main.go index d6ae4036e2..4f6956f713 100644 --- a/cmd/clairctl/main.go +++ b/cmd/clairctl/main.go @@ -58,6 +58,7 @@ func main() { DeleteCmd, CheckConfigCmd, AdminCmd, + IndexCmd, }, Flags: []cli.Flag{ &cli.BoolFlag{ diff --git a/cmd/clairctl/report.go b/cmd/clairctl/report.go index fc330b78a8..129e95cbbc 100644 --- a/cmd/clairctl/report.go +++ b/cmd/clairctl/report.go @@ -199,7 +199,7 @@ func reportAction(c *cli.Context) error { zlog.Debug(ctx). Int("attempt", ct). Msg("requesting index_report") - err = cc.IndexReport(ctx, d, m) + _, err = cc.IndexReport(ctx, d, m) switch { case err == nil: case errors.Is(err, errNeedManifest): diff --git a/go.mod b/go.mod index f003513aad..ae7213bc11 100644 --- a/go.mod +++ b/go.mod @@ -31,13 +31,14 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.20.0 go.opentelemetry.io/otel/sdk v1.21.0 go.opentelemetry.io/otel/trace v1.21.0 - golang.org/x/net v0.17.0 + golang.org/x/net v0.18.0 golang.org/x/sync v0.5.0 golang.org/x/time v0.4.0 gopkg.in/yaml.v3 v3.0.1 ) require ( + github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect @@ -80,24 +81,31 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.9.1 // indirect + github.com/spdx/tools-golang v0.5.3 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/vbatts/tar-split v0.11.3 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.opentelemetry.io/otel/metric v1.21.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/mod v0.12.0 // indirect + golang.org/x/crypto v0.15.0 // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/sys v0.14.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.12.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.15.0 // indirect google.golang.org/protobuf v1.31.0 // indirect lukechampine.com/uint128 v1.2.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect modernc.org/ccgo/v3 v3.16.13 // indirect - modernc.org/libc v1.24.1 // indirect - modernc.org/mathutil v1.5.0 // indirect - modernc.org/memory v1.6.0 // indirect + modernc.org/libc v1.29.0 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect modernc.org/opt v0.1.3 // indirect - modernc.org/sqlite v1.26.0 // indirect + modernc.org/sqlite v1.27.0 // indirect modernc.org/strutil v1.1.3 // indirect modernc.org/token v1.0.1 // indirect ) + +replace github.com/quay/claircore => ../claircore + +replace github.com/quay/clair/config => ./config + +replace github.com/quay/claircore/toolkit => ../claircore/toolkit diff --git a/go.sum b/go.sum index 4aea275830..c2e778eba7 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3Q github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc= +github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -65,7 +67,6 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -187,13 +188,6 @@ github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwa github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= github.com/pyroscope-io/godeltaprof v0.1.2 h1:MdlEmYELd5w+lvIzmZvXGNMVzW2Qc9jDMuJaPOR75g4= github.com/pyroscope-io/godeltaprof v0.1.2/go.mod h1:psMITXp90+8pFenXkKIpNhrfmI9saQnPbba27VIaiQE= -github.com/quay/clair/config v1.3.0 h1:UqJIwvgHaWj6yTWrpVnYnlEIKcVYxJItlEF2EsCnw5s= -github.com/quay/clair/config v1.3.0/go.mod h1:XrxQFjAt0W+TUk5GkkSjNPxu1QgfycdRJr/+GVqUxBw= -github.com/quay/claircore v1.5.20 h1:pR4lKRgdznk9wBqTXHSmYJPdYAPMRQTtSMzi6P3Vg9s= -github.com/quay/claircore v1.5.20/go.mod h1:vz6VeOwzk2QLWGWAWrzjmPlUPkL9WwdeV3myHOwGwvA= -github.com/quay/claircore/toolkit v1.0.0/go.mod h1:3ELtgf92x7o1JCTSKVOAqhcnCTXc4s5qiGaEDx62i20= -github.com/quay/claircore/toolkit v1.1.1 h1:9GFy14ffOkIOpl0fbR+bHr4i19VEwms1pXw8S8up0e4= -github.com/quay/claircore/toolkit v1.1.1/go.mod h1:ZZHA/b/qpfUcNHFJeYVA0bOp7aL4r3CTFhlBV/ezoFI= github.com/quay/claircore/updater/driver v1.0.0 h1:w7dAUjO3GBK6RjNyTZ2Kwz0l/Wuic3ykKJWPB80uA94= github.com/quay/claircore/updater/driver v1.0.0/go.mod h1:My5aY1wBpgxcWaHQZ0VoPmmj/EzuH7fq4ntzJbos4OI= github.com/quay/goval-parser v0.8.8 h1:Uf+f9iF2GIR5GPUY2pGoa9il2+4cdES44ZlM0mWm4cA= @@ -204,7 +198,6 @@ github.com/rabbitmq/amqp091-go v1.9.0 h1:qrQtyzB4H8BQgEuJwhmVQqVHB9O4+MNDJCCAcpc github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc= github.com/remind101/migrate v0.0.0-20170729031349-52c1edff7319 h1:ukjThsA2ou7AmovpwtMVkNQSuoN/v5U16+JomTz3c7o= github.com/remind101/migrate v0.0.0-20170729031349-52c1edff7319/go.mod h1:rhSvwcijY9wfmrBYrfCvapX8/xOTV46NAUjBRgUyJqc= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -226,6 +219,9 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.1 h1:Ou41VVR3nMWWmTiEUnj0OlsgOSCUFgsPAOl6jRIcVtQ= github.com/sirupsen/logrus v1.9.1/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM= +github.com/spdx/tools-golang v0.5.3 h1:ialnHeEYUC4+hkm5vJm4qz2x+oEJbS0mAMFrNXdQraY= +github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -242,6 +238,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= @@ -300,15 +297,15 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= @@ -318,8 +315,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -362,8 +359,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -379,8 +376,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= -golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -413,19 +410,20 @@ modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= -modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM= -modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak= -modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o= -modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/libc v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs= +modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.26.0 h1:SocQdLRSYlA8W99V8YH0NES75thx19d9sB/aFc4R8Lw= -modernc.org/sqlite v1.26.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU= +modernc.org/sqlite v1.27.0 h1:MpKAHoyYB7xqcwnUwkuD+npwEa0fojF0B5QRbN+auJ8= +modernc.org/sqlite v1.27.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/httptransport/common.go b/httptransport/common.go index f8d523f489..d71f167ee4 100644 --- a/httptransport/common.go +++ b/httptransport/common.go @@ -67,6 +67,9 @@ func pickContentType(w http.ResponseWriter, r *http.Request, allow []string) err a.Q, _ = strconv.ParseFloat(qs, 64) } typ := strings.Split(mt, "/") + if len(typ) != 2 { + return fmt.Errorf("malformed Accept value %s", mt) + } a.Type = typ[0] a.Subtype = typ[1] acceptable = append(acceptable, a) diff --git a/httptransport/indexer_v1.go b/httptransport/indexer_v1.go index 86fa050bb3..44fc291910 100644 --- a/httptransport/indexer_v1.go +++ b/httptransport/indexer_v1.go @@ -9,6 +9,7 @@ import ( "time" "github.com/quay/claircore" + "github.com/quay/claircore/pkg/sbom/spdx" "github.com/quay/claircore/pkg/tarfs" "github.com/quay/zlog" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" @@ -95,6 +96,17 @@ func (h *IndexerV1) indexReport(w http.ResponseWriter, r *http.Request) { defer codec.PutDecoder(dec) switch r.Method { case http.MethodPost: + allow := []string{"application/vnd.clair.indexreport.v1+json", "application/json", "application/spdx+json"} + switch err := pickContentType(w, r, allow); { + case errors.Is(err, nil): // OK + case errors.Is(err, ErrMediaType): + apiError(ctx, w, http.StatusUnsupportedMediaType, "unable to negotiate common media type for %v", allow) + return + default: + apiError(ctx, w, http.StatusBadRequest, "malformed request: %v", err) + return + } + state, err := h.srv.State(ctx) if err != nil { apiError(ctx, w, http.StatusInternalServerError, "could not retrieve indexer state: %v", err) @@ -131,9 +143,21 @@ func (h *IndexerV1) indexReport(w http.ResponseWriter, r *http.Request) { w.Header().Set("location", next) defer writerError(w, &err)() w.WriteHeader(http.StatusCreated) + var resp interface{} + switch w.Header().Get("content-type") { + case "", "application/vnd.clair.indexreport.v1+json", "application/json": + resp = report + case "application/spdx+json": + resp, err = spdx.ParseIndexReport(report) + if err != nil { + apiError(ctx, w, http.StatusInternalServerError, "could not convert index report to spdx: %v", err) + return + } + } + enc := codec.GetEncoder(w) defer codec.PutEncoder(enc) - err = enc.Encode(report) + err = enc.Encode(resp) case http.MethodDelete: var ds []claircore.Digest if err := dec.Decode(&ds); err != nil { @@ -173,7 +197,7 @@ func (h *IndexerV1) indexReportOne(w http.ResponseWriter, r *http.Request) { } switch r.Method { case http.MethodGet: - allow := []string{"application/vnd.clair.indexreport.v1+json", "application/json"} + allow := []string{"application/vnd.clair.indexreport.v1+json", "application/json", "application/spdx+json"} switch err := pickContentType(w, r, allow); { case errors.Is(err, nil): // OK case errors.Is(err, ErrMediaType): @@ -199,12 +223,25 @@ func (h *IndexerV1) indexReportOne(w http.ResponseWriter, r *http.Request) { if err != nil { apiError(ctx, w, http.StatusInternalServerError, "could not retrieve index report: %v", err) } - w.Header().Add("etag", validator) defer writerError(w, &err)() + + // Check response's headers to see what kind of encoding we've been + // asked to return. + var resp interface{} + switch w.Header().Get("content-type") { + case "", "application/vnd.clair.indexreport.v1+json", "application/json": + resp = report + case "application/spdx+json": + resp, err = spdx.ParseIndexReport(report) + if err != nil { + apiError(ctx, w, http.StatusInternalServerError, "could not convert index report to spdx: %v", err) + return + } + } enc := codec.GetEncoder(w) defer codec.PutEncoder(enc) - err = enc.Encode(report) + err = enc.Encode(resp) case http.MethodDelete: if _, err := h.srv.DeleteManifests(ctx, d); err != nil { apiError(ctx, w, http.StatusInternalServerError, "unable to delete manifest: %v", err)