0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00

fix: queries with images as arguments without a reference should return an error (#1040)

Currently there is no push-back on queries that should contain image names but
have only the repo name. This commit adds a check that will return an error for images
w/o a reference(tag or digest).

Signed-off-by: Alex Stan <alexandrustan96@yahoo.ro>
This commit is contained in:
alexstan12 2022-12-09 21:40:06 +02:00 committed by GitHub
parent 024b13efe6
commit 8fb11180d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 159 additions and 11 deletions

View file

@ -62,6 +62,16 @@ type RepoWithNewestImageResponse struct {
Errors []ErrorGQL `json:"errors"`
}
type DerivedImageListResponse struct {
DerivedImageList DerivedImageList `json:"data"`
Errors []ErrorGQL `json:"errors"`
}
type BaseImageListResponse struct {
BaseImageList BaseImageList `json:"data"`
Errors []ErrorGQL `json:"errors"`
}
type ImageListResponse struct {
ImageList ImageList `json:"data"`
Errors []ErrorGQL `json:"errors"`
@ -71,6 +81,13 @@ type ImageList struct {
SummaryList []common.ImageSummary `json:"imageList"`
}
type DerivedImageList struct {
DerivedList []common.ImageSummary `json:"derivedImageList"`
}
type BaseImageList struct {
BaseList []common.ImageSummary `json:"baseImageList"`
}
type ExpandedRepoInfoResp struct {
ExpandedRepoInfo ExpandedRepoInfo `json:"data"`
Errors []ErrorGQL `json:"errors"`
@ -1454,7 +1471,7 @@ func TestDerivedImageList(t *testing.T) {
query := `
{
DerivedImageList(image:"test-repo"){
DerivedImageList(image:"test-repo:latest"){
RepoName,
Tag,
Digest,
@ -1477,7 +1494,7 @@ func TestDerivedImageList(t *testing.T) {
Convey("Inexistent repository", t, func() {
query := `
{
DerivedImageList(image:"inexistent-image"){
DerivedImageList(image:"inexistent-image:latest"){
RepoName,
Tag,
Digest,
@ -1493,13 +1510,43 @@ func TestDerivedImageList(t *testing.T) {
So(err, ShouldBeNil)
})
Convey("Invalid query, no reference provided", t, func() {
query := `
{
DerivedImageList(image:"inexistent-image"){
RepoName,
Tag,
Digest,
ConfigDigest,
LastUpdated,
IsSigned,
Size
}
}`
responseStruct := &DerivedImageListResponse{}
contains := false
resp, err := resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(query))
So(err, ShouldBeNil)
err = json.Unmarshal(resp.Body(), responseStruct)
So(err, ShouldBeNil)
for _, err := range responseStruct.Errors {
result := strings.Contains(err.Message, "no reference provided")
if result {
contains = result
}
}
So(contains, ShouldBeTrue)
})
Convey("Failed to get manifest", t, func() {
err := os.Mkdir(path.Join(rootDir, "fail-image"), 0o000)
So(err, ShouldBeNil)
query := `
{
DerivedImageList(image:"fail-image"){
DerivedImageList(image:"fail-image:latest"){
RepoName,
Tag,
Digest,
@ -1560,7 +1607,7 @@ func TestDerivedImageListNoRepos(t *testing.T) {
query := `
{
DerivedImageList(image:"test-image"){
DerivedImageList(image:"test-image:latest"){
RepoName,
Tag,
Digest,
@ -1968,7 +2015,7 @@ func TestBaseImageList(t *testing.T) {
query := `
{
BaseImageList(image:"test-repo"){
BaseImageList(image:"test-repo:latest"){
RepoName,
Tag,
Digest,
@ -1986,7 +2033,7 @@ func TestBaseImageList(t *testing.T) {
So(strings.Contains(string(resp.Body()), "less-layers-false"), ShouldBeFalse)
So(strings.Contains(string(resp.Body()), "more-layers"), ShouldBeFalse)
So(strings.Contains(string(resp.Body()), "diff-layers"), ShouldBeFalse)
So(strings.Contains(string(resp.Body()), "test-repo"), ShouldBeTrue) //nolint:goconst // should not list given image
So(strings.Contains(string(resp.Body()), "test-repo"), ShouldBeFalse) //nolint:goconst // should not list given image
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
})
@ -1994,7 +2041,7 @@ func TestBaseImageList(t *testing.T) {
Convey("Nonexistent repository", t, func() {
query := `
{
BaseImageList(image:"nonexistent-image"){
BaseImageList(image:"nonexistent-image:latest"){
RepoName,
Tag,
Digest,
@ -2010,13 +2057,43 @@ func TestBaseImageList(t *testing.T) {
So(err, ShouldBeNil)
})
Convey("Invalid query, no reference provided", t, func() {
query := `
{
BaseImageList(image:"nonexistent-image"){
RepoName,
Tag,
Digest,
ConfigDigest,
LastUpdated,
IsSigned,
Size
}
}`
responseStruct := &BaseImageListResponse{}
contains := false
resp, err := resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(query))
So(err, ShouldBeNil)
err = json.Unmarshal(resp.Body(), responseStruct)
So(err, ShouldBeNil)
for _, err := range responseStruct.Errors {
result := strings.Contains(err.Message, "no reference provided")
if result {
contains = result
}
}
So(contains, ShouldBeTrue)
})
Convey("Failed to get manifest", t, func() {
err := os.Mkdir(path.Join(rootDir, "fail-image"), 0o000)
So(err, ShouldBeNil)
query := `
{
BaseImageList(image:"fail-image"){
BaseImageList(image:"fail-image:latest"){
RepoName,
Tag,
Digest,
@ -2077,7 +2154,7 @@ func TestBaseImageListNoRepos(t *testing.T) {
query := `
{
BaseImageList(image:"test-image"){
BaseImageList(image:"test-image:latest"){
RepoName,
Tag,
Digest,
@ -3216,6 +3293,20 @@ func TestImageSummary(t *testing.T) {
}
}`
noTagQuery := `
{
Image(image:"%s"){
RepoName,
Tag,
Digest,
ConfigDigest,
LastUpdated,
IsSigned,
Size
Layers { Digest Size }
}
}`
gqlEndpoint := fmt.Sprintf("%s%s?query=", baseURL, graphqlQueryPrefix)
config, layers, manifest, err := GetImageComponents(100)
So(err, ShouldBeNil)
@ -3252,6 +3343,27 @@ func TestImageSummary(t *testing.T) {
resp *resty.Response
)
t.Log("starting test to retrieve image without reference")
strQuery = fmt.Sprintf(noTagQuery, repoName)
targetURL = fmt.Sprintf("%s%s", gqlEndpoint, url.QueryEscape(strQuery))
contains := false
resp, err = resty.R().Get(targetURL)
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
So(resp.Body(), ShouldNotBeNil)
err = json.Unmarshal(resp.Body(), &imgSummaryResponse)
So(err, ShouldBeNil)
for _, err := range imgSummaryResponse.Errors {
result := strings.Contains(err.Message, "no reference provided")
if result {
contains = result
}
}
So(contains, ShouldBeTrue)
t.Log("starting Test retrieve image based on image identifier")
// gql is parametrized with the repo.
strQuery = fmt.Sprintf(gqlQuery, repoName, tagTarget)

View file

@ -12,6 +12,7 @@ import (
"net/http"
"os"
"path"
"strings"
"testing"
"time"
@ -51,7 +52,8 @@ const (
)
type CveResult struct {
ImgList ImgList `json:"data"`
ImgList ImgList `json:"data"`
Errors []ErrorGQL `json:"errors"`
}
//nolint:tagliatelle // graphQL schema
@ -69,6 +71,11 @@ type ImgList struct {
CVEResultForImage CVEResultForImage `json:"CVEListForImage"`
}
type ErrorGQL struct {
Message string `json:"message"`
Path []string `json:"path"`
}
//nolint:tagliatelle // graphQL schema
type CVEResultForImage struct {
Tag string `json:"Tag"`
@ -490,11 +497,23 @@ func TestCVESearch(t *testing.T) {
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 422)
var cveResult CveResult
contains := false
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
err = json.Unmarshal(resp.Body(), &cveResult)
So(err, ShouldBeNil)
for _, err := range cveResult.Errors {
result := strings.Contains(err.Message, "no reference provided")
if result {
contains = result
}
}
So(contains, ShouldBeTrue)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
var cveResult CveResult
err = json.Unmarshal(resp.Body(), &cveResult)
So(err, ShouldBeNil)
So(len(cveResult.ImgList.CVEResultForImage.CVEList), ShouldNotBeZeroValue)

View file

@ -8,6 +8,7 @@ import (
"context"
"fmt"
"github.com/vektah/gqlparser/v2/gqlerror"
"zotregistry.io/zot/pkg/extensions/search/common"
"zotregistry.io/zot/pkg/extensions/search/gql_generated"
)
@ -21,6 +22,10 @@ func (r *queryResolver) CVEListForImage(ctx context.Context, image string) (*gql
_, copyImgTag := common.GetImageDirAndTag(image)
if copyImgTag == "" {
return &gql_generated.CVEResultForImage{}, gqlerror.Errorf("no reference provided")
}
cveids := []*gql_generated.Cve{}
for id, cveDetail := range cveidMap {
@ -421,6 +426,10 @@ func (r *queryResolver) DerivedImageList(ctx context.Context, image string) ([]*
imageDir, imageTag := common.GetImageDirAndTag(image)
if imageTag == "" {
return []*gql_generated.ImageSummary{}, gqlerror.Errorf("no reference provided")
}
imageManifest, _, err := layoutUtils.GetImageManifest(imageDir, imageTag)
if err != nil {
r.log.Info().Str("image", image).Msg("image not found")
@ -489,6 +498,10 @@ func (r *queryResolver) BaseImageList(ctx context.Context, image string) ([]*gql
imageDir, imageTag := common.GetImageDirAndTag(image)
if imageTag == "" {
return []*gql_generated.ImageSummary{}, gqlerror.Errorf("no reference provided")
}
imageManifest, _, err := layoutUtils.GetImageManifest(imageDir, imageTag)
if err != nil {
r.log.Info().Str("image", image).Msg("image not found")
@ -552,6 +565,10 @@ func (r *queryResolver) Image(ctx context.Context, image string) (*gql_generated
repo, tag := common.GetImageDirAndTag(image)
layoutUtils := common.NewBaseOciLayoutUtils(r.storeController, r.log)
if tag == "" {
return &gql_generated.ImageSummary{}, gqlerror.Errorf("no reference provided")
}
digest, manifest, imageConfig, err := extractImageDetails(ctx, layoutUtils, repo, tag, r.log)
if err != nil {
r.log.Error().Err(err).Msg("unable to get image details")