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:
parent
024b13efe6
commit
8fb11180d4
3 changed files with 159 additions and 11 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in a new issue