From b6e394dc803c4558cb81334726ab921e3efc0bab Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Thu, 15 Sep 2022 13:13:20 +0600 Subject: [PATCH] feat(secret): add line from dockerfile where secret was added to secret result (#2780) Co-authored-by: knqyf263 --- pkg/fanal/applier/docker.go | 11 +- pkg/fanal/applier/docker_test.go | 15 +- pkg/fanal/artifact/image/image.go | 79 ++++++-- pkg/fanal/artifact/image/image_test.go | 11 + pkg/fanal/image/daemon/containerd.go | 21 +- pkg/fanal/image/daemon/docker.go | 2 +- pkg/fanal/image/daemon/image.go | 93 ++++----- pkg/fanal/image/daemon/podman.go | 31 +++ pkg/fanal/image/daemon/podman_test.go | 15 +- pkg/fanal/test/integration/containerd_test.go | 189 ++++++++++++++++++ pkg/fanal/types/artifact.go | 20 +- pkg/fanal/types/secret.go | 1 - pkg/report/table/secret.go | 12 +- pkg/report/table/secret_test.go | 18 +- pkg/rpc/convert.go | 12 +- rpc/common/service.pb.go | 162 +++++++-------- rpc/common/service.proto | 8 +- 17 files changed, 508 insertions(+), 192 deletions(-) diff --git a/pkg/fanal/applier/docker.go b/pkg/fanal/applier/docker.go index ac5f00be7dc..712b8ae0311 100644 --- a/pkg/fanal/applier/docker.go +++ b/pkg/fanal/applier/docker.go @@ -134,8 +134,9 @@ func ApplyLayers(layers []types.BlobInfo) types.ArtifactDetail { // Apply secrets for _, secret := range layer.Secrets { l := types.Layer{ - Digest: layer.Digest, - DiffID: layer.DiffID, + Digest: layer.Digest, + DiffID: layer.DiffID, + CreatedBy: layer.CreatedBy, } secretsMap = mergeSecrets(secretsMap, secret, l) } @@ -178,13 +179,7 @@ func ApplyLayers(layers []types.BlobInfo) types.ArtifactDetail { return nil }) - lastDiffID := layers[len(layers)-1].DiffID for _, s := range secretsMap { - for i, finding := range s.Findings { - if finding.Layer.DiffID != lastDiffID { - s.Findings[i].Deleted = true // This secret is deleted in the upper layer - } - } mergedLayer.Secrets = append(mergedLayer.Secrets, s) } diff --git a/pkg/fanal/applier/docker_test.go b/pkg/fanal/applier/docker_test.go index 60d4c724ad8..dbee2bd3b0f 100644 --- a/pkg/fanal/applier/docker_test.go +++ b/pkg/fanal/applier/docker_test.go @@ -335,6 +335,7 @@ func TestApplyLayers(t *testing.T) { SchemaVersion: 2, Digest: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02", DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72", + CreatedBy: "Line_1", Secrets: []types.Secret{ { FilePath: "usr/secret.txt", @@ -368,6 +369,7 @@ func TestApplyLayers(t *testing.T) { SchemaVersion: 2, Digest: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7", DiffID: "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819", + CreatedBy: "Line_2", Secrets: []types.Secret{ { FilePath: "usr/secret.txt", @@ -422,6 +424,7 @@ func TestApplyLayers(t *testing.T) { SchemaVersion: 2, Digest: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02", DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72", + CreatedBy: "Line_3", WhiteoutFiles: []string{ "usr/secret.txt", }, @@ -437,13 +440,13 @@ func TestApplyLayers(t *testing.T) { Category: "GitHub", Severity: "CRITICAL", Title: "GitHub Personal Access Token", - Deleted: true, StartLine: 1, EndLine: 1, Match: "GITHUB_PAT=****************************************", Layer: types.Layer{ - Digest: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7", - DiffID: "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819", + Digest: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7", + DiffID: "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819", + CreatedBy: "Line_2", }, Code: types.Code{ Lines: []types.Line{ @@ -463,13 +466,13 @@ func TestApplyLayers(t *testing.T) { Category: "AWS", Severity: "CRITICAL", Title: "AWS Access Key ID", - Deleted: true, StartLine: 2, EndLine: 2, Match: "AWS_ACCESS_KEY_ID=********************", Layer: types.Layer{ - Digest: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7", - DiffID: "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819", + Digest: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7", + DiffID: "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819", + CreatedBy: "Line_2", }, Code: types.Code{ Lines: []types.Line{ diff --git a/pkg/fanal/artifact/image/image.go b/pkg/fanal/artifact/image/image.go index 21d52db069f..d4bad42ec6a 100644 --- a/pkg/fanal/artifact/image/image.go +++ b/pkg/fanal/artifact/image/image.go @@ -37,6 +37,11 @@ type Artifact struct { artifactOption artifact.Option } +type LayerInfo struct { + DiffID string + CreatedBy string // can be empty +} + func NewArtifact(img types.Image, c cache.ArtifactCache, opt artifact.Option) (artifact.Artifact, error) { // Initialize handlers handlerManager, err := handler.NewManager(opt) @@ -90,11 +95,14 @@ func (a Artifact) Inspect(ctx context.Context) (types.ArtifactReference, error) log.Logger.Debugf("Base Layers: %v", baseDiffIDs) // Convert image ID and layer IDs to cache keys - imageKey, layerKeys, layerKeyMap, err := a.calcCacheKeys(imageID, diffIDs) + imageKey, layerKeys, err := a.calcCacheKeys(imageID, diffIDs) if err != nil { return types.ArtifactReference{}, err } + // Parse histories and extract a list of "created_by" + layerKeyMap := a.consolidateCreatedBy(diffIDs, layerKeys, configFile) + missingImage, missingLayers, err := a.cache.MissingBlobs(imageKey, layerKeys) if err != nil { return types.ArtifactReference{}, xerrors.Errorf("unable to get missing layers: %w", err) @@ -130,45 +138,77 @@ func (Artifact) Clean(_ types.ArtifactReference) error { return nil } -func (a Artifact) calcCacheKeys(imageID string, diffIDs []string) (string, []string, map[string]string, error) { +func (a Artifact) calcCacheKeys(imageID string, diffIDs []string) (string, []string, error) { // Pass an empty config scanner option so that the cache key can be the same, even when policies are updated. imageKey, err := cache.CalcKey(imageID, a.analyzer.ImageConfigAnalyzerVersions(), nil, artifact.Option{}) if err != nil { - return "", nil, nil, err + return "", nil, err } - layerKeyMap := map[string]string{} hookVersions := a.handlerManager.Versions() var layerKeys []string for _, diffID := range diffIDs { blobKey, err := cache.CalcKey(diffID, a.analyzer.AnalyzerVersions(), hookVersions, a.artifactOption) if err != nil { - return "", nil, nil, err + return "", nil, err } layerKeys = append(layerKeys, blobKey) - layerKeyMap[blobKey] = diffID } - return imageKey, layerKeys, layerKeyMap, nil + return imageKey, layerKeys, nil +} + +func (a Artifact) consolidateCreatedBy(diffIDs, layerKeys []string, configFile *v1.ConfigFile) map[string]LayerInfo { + // save createdBy fields in order of layers + var createdBy []string + for _, h := range configFile.History { + // skip histories for empty layers + if h.EmptyLayer { + continue + } + c := strings.TrimPrefix(h.CreatedBy, "/bin/sh -c ") + c = strings.TrimPrefix(c, "#(nop) ") + createdBy = append(createdBy, c) + } + + // If history detected incorrect - use only diffID + // TODO: our current logic may not detect empty layers correctly in rare cases. + validCreatedBy := len(diffIDs) == len(createdBy) + + layerKeyMap := map[string]LayerInfo{} + for i, diffID := range diffIDs { + + c := "" + if validCreatedBy { + c = createdBy[i] + } + + layerKey := layerKeys[i] + layerKeyMap[layerKey] = LayerInfo{ + DiffID: diffID, + CreatedBy: c, + } + } + return layerKeyMap } -func (a Artifact) inspect(ctx context.Context, missingImage string, layerKeys, baseDiffIDs []string, layerKeyMap map[string]string) error { +func (a Artifact) inspect(ctx context.Context, missingImage string, layerKeys, baseDiffIDs []string, layerKeyMap map[string]LayerInfo) error { done := make(chan struct{}) errCh := make(chan error) var osFound types.OS for _, k := range layerKeys { go func(ctx context.Context, layerKey string) { - diffID := layerKeyMap[layerKey] + layer := layerKeyMap[layerKey] // If it is a base layer, secret scanning should not be performed. var disabledAnalyers []analyzer.Type - if slices.Contains(baseDiffIDs, diffID) { + if slices.Contains(baseDiffIDs, layer.DiffID) { disabledAnalyers = append(disabledAnalyers, analyzer.TypeSecret) } - layerInfo, err := a.inspectLayer(ctx, diffID, disabledAnalyers) + layerInfo, err := a.inspectLayer(ctx, layer, disabledAnalyers) if err != nil { - errCh <- xerrors.Errorf("failed to analyze layer: %s : %w", diffID, err) + errCh <- xerrors.Errorf("failed to analyze layer: %s : %w", layerInfo.DiffID, err) return } if err = a.cache.PutBlob(layerKey, layerInfo); err != nil { @@ -202,12 +242,12 @@ func (a Artifact) inspect(ctx context.Context, missingImage string, layerKeys, b } -func (a Artifact) inspectLayer(ctx context.Context, diffID string, disabled []analyzer.Type) (types.BlobInfo, error) { - log.Logger.Debugf("Missing diff ID in cache: %s", diffID) +func (a Artifact) inspectLayer(ctx context.Context, layerInfo LayerInfo, disabled []analyzer.Type) (types.BlobInfo, error) { + log.Logger.Debugf("Missing diff ID in cache: %s", layerInfo.DiffID) - layerDigest, r, err := a.uncompressedLayer(diffID) + layerDigest, r, err := a.uncompressedLayer(layerInfo.DiffID) if err != nil { - return types.BlobInfo{}, xerrors.Errorf("unable to get uncompressed layer %s: %w", diffID, err) + return types.BlobInfo{}, xerrors.Errorf("unable to get uncompressed layer %s: %w", layerInfo.DiffID, err) } // Prepare variables @@ -236,15 +276,16 @@ func (a Artifact) inspectLayer(ctx context.Context, diffID string, disabled []an blobInfo := types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: layerDigest, - DiffID: diffID, + DiffID: layerInfo.DiffID, + CreatedBy: layerInfo.CreatedBy, + OpaqueDirs: opqDirs, + WhiteoutFiles: whFiles, OS: result.OS, Repository: result.Repository, PackageInfos: result.PackageInfos, Applications: result.Applications, Secrets: result.Secrets, Licenses: result.Licenses, - OpaqueDirs: opqDirs, - WhiteoutFiles: whFiles, CustomResources: result.CustomResources, // For Red Hat diff --git a/pkg/fanal/artifact/image/image_test.go b/pkg/fanal/artifact/image/image_test.go index 997c975205f..23f1cd1b9ee 100644 --- a/pkg/fanal/artifact/image/image_test.go +++ b/pkg/fanal/artifact/image/image_test.go @@ -65,6 +65,7 @@ func TestArtifact_Inspect(t *testing.T) { SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", DiffID: "sha256:beee9f30bc1f711043e78d4a2be0668955d4b761d587d6f60c2c8dc081efb203", + CreatedBy: "ADD file:0c4555f363c2672e350001f1293e689875a3760afe7b3f9146886afe67121cba in / ", OS: &types.OS{ Family: "alpine", Name: "3.11.5", @@ -275,6 +276,7 @@ func TestArtifact_Inspect(t *testing.T) { SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", DiffID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02", + CreatedBy: "bazel build ...", OS: &types.OS{ Family: "debian", Name: "9.9", @@ -342,6 +344,7 @@ func TestArtifact_Inspect(t *testing.T) { SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", DiffID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5", + CreatedBy: "bazel build ...", PackageInfos: []types.PackageInfo{ { FilePath: "var/lib/dpkg/status.d/libc6", @@ -408,6 +411,7 @@ func TestArtifact_Inspect(t *testing.T) { SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", DiffID: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7", + CreatedBy: "COPY file:842584685f26edb24dc305d76894f51cfda2bad0c24a05e727f9d4905d184a70 in /php-app/composer.lock ", Applications: []types.Application{ { Type: "composer", FilePath: "php-app/composer.lock", @@ -440,6 +444,7 @@ func TestArtifact_Inspect(t *testing.T) { SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", DiffID: "sha256:a4595c43a874856bf95f3bfc4fbf78bbaa04c92c726276d4f64193a47ced0566", + CreatedBy: "COPY file:c6d0373d380252b91829a5bb3c81d5b1afa574c91cef7752d18170a231c31f6d in /ruby-app/Gemfile.lock ", Applications: []types.Application{ { Type: types.Bundler, FilePath: "ruby-app/Gemfile.lock", @@ -625,6 +630,7 @@ func TestArtifact_Inspect(t *testing.T) { SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", DiffID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02", + CreatedBy: "bazel build ...", }, }, }, @@ -635,6 +641,7 @@ func TestArtifact_Inspect(t *testing.T) { SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", DiffID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5", + CreatedBy: "bazel build ...", }, }, }, @@ -645,6 +652,7 @@ func TestArtifact_Inspect(t *testing.T) { SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", DiffID: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7", + CreatedBy: "COPY file:842584685f26edb24dc305d76894f51cfda2bad0c24a05e727f9d4905d184a70 in /php-app/composer.lock ", OpaqueDirs: []string{"php-app/"}, }, }, @@ -656,6 +664,7 @@ func TestArtifact_Inspect(t *testing.T) { SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", DiffID: "sha256:a4595c43a874856bf95f3bfc4fbf78bbaa04c92c726276d4f64193a47ced0566", + CreatedBy: "COPY file:c6d0373d380252b91829a5bb3c81d5b1afa574c91cef7752d18170a231c31f6d in /ruby-app/Gemfile.lock ", OpaqueDirs: []string{"ruby-app/"}, }, }, @@ -780,6 +789,7 @@ func TestArtifact_Inspect(t *testing.T) { SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", DiffID: "sha256:beee9f30bc1f711043e78d4a2be0668955d4b761d587d6f60c2c8dc081efb203", + CreatedBy: "ADD file:0c4555f363c2672e350001f1293e689875a3760afe7b3f9146886afe67121cba in / ", OS: &types.OS{ Family: "alpine", Name: "3.11.5", @@ -911,6 +921,7 @@ func TestArtifact_Inspect(t *testing.T) { SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", DiffID: "sha256:beee9f30bc1f711043e78d4a2be0668955d4b761d587d6f60c2c8dc081efb203", + CreatedBy: "ADD file:0c4555f363c2672e350001f1293e689875a3760afe7b3f9146886afe67121cba in / ", OS: &types.OS{ Family: "alpine", Name: "3.11.5", diff --git a/pkg/fanal/image/daemon/containerd.go b/pkg/fanal/image/daemon/containerd.go index 9c95aa3bb0d..8280d57ad1e 100644 --- a/pkg/fanal/image/daemon/containerd.go +++ b/pkg/fanal/image/daemon/containerd.go @@ -19,6 +19,7 @@ import ( api "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/go-connections/nat" + v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/samber/lo" @@ -90,7 +91,7 @@ func ContainerdImage(ctx context.Context, imageName string) (Image, func(), erro _ = os.Remove(f.Name()) } - insp, err := inspect(ctx, img, ref) + insp, history, err := inspect(ctx, img, ref) if err != nil { return nil, nil, xerrors.Errorf("inspect error: %w", err) } @@ -98,6 +99,7 @@ func ContainerdImage(ctx context.Context, imageName string) (Image, func(), erro return &image{ opener: imageOpener(ctx, ref.String(), f, imageWriter(client, img)), inspect: insp, + history: history, }, cleanup, nil } @@ -121,7 +123,7 @@ func readImageConfig(ctx context.Context, img containerd.Image) (ocispec.Image, } // ported from https://github.com/containerd/nerdctl/blob/d110fea18018f13c3f798fa6565e482f3ff03591/pkg/inspecttypes/dockercompat/dockercompat.go#L279-L321 -func inspect(ctx context.Context, img containerd.Image, ref docker.Named) (api.ImageInspect, error) { +func inspect(ctx context.Context, img containerd.Image, ref docker.Named) (api.ImageInspect, []v1.History, error) { var tag string if tagged, ok := ref.(refdocker.Tagged); ok { tag = tagged.Tag() @@ -130,7 +132,7 @@ func inspect(ctx context.Context, img containerd.Image, ref docker.Named) (api.I imgConfig, imgConfigDesc, err := readImageConfig(ctx, img) if err != nil { - return api.ImageInspect{}, err + return api.ImageInspect{}, nil, err } var lastHistory ocispec.History @@ -138,6 +140,17 @@ func inspect(ctx context.Context, img containerd.Image, ref docker.Named) (api.I lastHistory = imgConfig.History[len(imgConfig.History)-1] } + var history []v1.History + for _, h := range imgConfig.History { + history = append(history, v1.History{ + Author: h.Author, + Created: v1.Time{Time: *h.Created}, + CreatedBy: h.CreatedBy, + Comment: h.Comment, + EmptyLayer: h.EmptyLayer, + }) + } + portSet := make(nat.PortSet) for k := range imgConfig.Config.ExposedPorts { portSet[nat.Port(k)] = struct{}{} @@ -168,5 +181,5 @@ func inspect(ctx context.Context, img containerd.Image, ref docker.Named) (api.I return d.String() }), }, - }, nil + }, history, nil } diff --git a/pkg/fanal/image/daemon/docker.go b/pkg/fanal/image/daemon/docker.go index 4c3b96c0992..26a707390ca 100644 --- a/pkg/fanal/image/daemon/docker.go +++ b/pkg/fanal/image/daemon/docker.go @@ -56,6 +56,6 @@ func DockerImage(ref name.Reference) (Image, func(), error) { return &image{ opener: imageOpener(context.Background(), imageID, f, c.ImageSave), inspect: inspect, - history: history, + history: configHistory(history), }, cleanup, nil } diff --git a/pkg/fanal/image/daemon/image.go b/pkg/fanal/image/daemon/image.go index d1292480c78..2cce0da1703 100644 --- a/pkg/fanal/image/daemon/image.go +++ b/pkg/fanal/image/daemon/image.go @@ -59,7 +59,7 @@ type image struct { v1.Image opener opener inspect types.ImageInspect - history []dimage.HistoryResponseItem + history []v1.History } // populateImage initializes an "image" struct. @@ -112,7 +112,7 @@ func (img *image) ConfigFile() (*v1.ConfigFile, error) { Created: v1.Time{Time: created}, DockerVersion: img.inspect.DockerVersion, Config: img.imageConfig(img.inspect.Config), - History: img.configHistory(), + History: img.history, OS: img.inspect.Os, RootFS: v1.RootFS{ Type: img.inspect.RootFS.Type, @@ -143,50 +143,6 @@ func (img *image) RepoDigests() []string { return img.inspect.RepoDigests } -func (img *image) configHistory() []v1.History { - // Fill only required metadata - var history []v1.History - for i := len(img.history) - 1; i >= 0; i-- { - h := img.history[i] - history = append(history, v1.History{ - Created: v1.Time{ - Time: time.Unix(h.Created, 0).UTC(), - }, - CreatedBy: h.CreatedBy, - Comment: h.Comment, - EmptyLayer: emptyLayer(h), - }) - } - return history -} - -func emptyLayer(history dimage.HistoryResponseItem) bool { - if history.Size != 0 { - return false - } - createdBy := strings.TrimSpace(strings.TrimLeft(history.CreatedBy, "/bin/sh -c #(nop)")) - // This logic is taken from https://github.com/moby/buildkit/blob/2942d13ff489a2a49082c99e6104517e357e53ad/frontend/dockerfile/dockerfile2llb/convert.go - if strings.HasPrefix(createdBy, "ENV") || - strings.HasPrefix(createdBy, "MAINTAINER") || - strings.HasPrefix(createdBy, "LABEL") || - strings.HasPrefix(createdBy, "CMD") || - strings.HasPrefix(createdBy, "ENTRYPOINT") || - strings.HasPrefix(createdBy, "HEALTHCHECK") || - strings.HasPrefix(createdBy, "EXPOSE") || - strings.HasPrefix(createdBy, "USER") || - strings.HasPrefix(createdBy, "VOLUME") || - strings.HasPrefix(createdBy, "STOPSIGNAL") || - strings.HasPrefix(createdBy, "SHELL") || - strings.HasPrefix(createdBy, "ARG") || - createdBy == "WORKDIR /" { // only when workdir == "/" then layer is empty - return true - } - // commands here: 'ADD', COPY, RUN and WORKDIR != "/" - // Also RUN command may not include 'RUN' prefix - // e.g. '/bin/sh -c mkdir test ' - return false -} - func (img *image) diffIDs() ([]v1.Hash, error) { var diffIDs []v1.Hash for _, l := range img.inspect.RootFS.Layers { @@ -248,3 +204,48 @@ func (img *image) imageConfig(config *container.Config) v1.Config { return c } + +func configHistory(dhistory []dimage.HistoryResponseItem) []v1.History { + // Fill only required metadata + var history []v1.History + + for i := len(dhistory) - 1; i >= 0; i-- { + h := dhistory[i] + history = append(history, v1.History{ + Created: v1.Time{ + Time: time.Unix(h.Created, 0).UTC(), + }, + CreatedBy: h.CreatedBy, + Comment: h.Comment, + EmptyLayer: emptyLayer(h), + }) + } + return history +} + +func emptyLayer(history dimage.HistoryResponseItem) bool { + if history.Size != 0 { + return false + } + createdBy := strings.TrimSpace(strings.TrimLeft(history.CreatedBy, "/bin/sh -c #(nop)")) + // This logic is taken from https://github.com/moby/buildkit/blob/2942d13ff489a2a49082c99e6104517e357e53ad/frontend/dockerfile/dockerfile2llb/convert.go + if strings.HasPrefix(createdBy, "ENV") || + strings.HasPrefix(createdBy, "MAINTAINER") || + strings.HasPrefix(createdBy, "LABEL") || + strings.HasPrefix(createdBy, "CMD") || + strings.HasPrefix(createdBy, "ENTRYPOINT") || + strings.HasPrefix(createdBy, "HEALTHCHECK") || + strings.HasPrefix(createdBy, "EXPOSE") || + strings.HasPrefix(createdBy, "USER") || + strings.HasPrefix(createdBy, "VOLUME") || + strings.HasPrefix(createdBy, "STOPSIGNAL") || + strings.HasPrefix(createdBy, "SHELL") || + strings.HasPrefix(createdBy, "ARG") || + createdBy == "WORKDIR /" { // only when workdir == "/" then layer is empty + return true + } + // commands here: 'ADD', COPY, RUN and WORKDIR != "/" + // Also RUN command may not include 'RUN' prefix + // e.g. '/bin/sh -c mkdir test ' + return false +} diff --git a/pkg/fanal/image/daemon/podman.go b/pkg/fanal/image/daemon/podman.go index fb1cc113fc7..399fff96602 100644 --- a/pkg/fanal/image/daemon/podman.go +++ b/pkg/fanal/image/daemon/podman.go @@ -11,11 +11,13 @@ import ( "path/filepath" api "github.com/docker/docker/api/types" + dimage "github.com/docker/docker/api/types/image" "golang.org/x/xerrors" ) var ( inspectURL = "http://podman/images/%s/json" + historyURL = "http://podman/images/%s/history" saveURL = "http://podman/images/%s/get" ) @@ -70,6 +72,29 @@ func (p podmanClient) imageInspect(imageName string) (api.ImageInspect, error) { return inspect, nil } +func (p podmanClient) imageHistoryInspect(imageName string) ([]dimage.HistoryResponseItem, error) { + url := fmt.Sprintf(historyURL, imageName) + resp, err := p.c.Get(url) + if err != nil { + return []dimage.HistoryResponseItem{}, xerrors.Errorf("http error: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var res errResponse + if err = json.NewDecoder(resp.Body).Decode(&res); err != nil { + return []dimage.HistoryResponseItem{}, xerrors.Errorf("unknown status code from Podman: %d", resp.StatusCode) + } + return []dimage.HistoryResponseItem{}, xerrors.New(res.Message) + } + + var history []dimage.HistoryResponseItem + if err = json.NewDecoder(resp.Body).Decode(&history); err != nil { + return []dimage.HistoryResponseItem{}, xerrors.Errorf("unable to decode JSON: %w", err) + } + return history, nil +} + func (p podmanClient) imageSave(_ context.Context, imageNames []string) (io.ReadCloser, error) { if len(imageNames) < 1 { return nil, xerrors.Errorf("no specified image") @@ -96,6 +121,11 @@ func PodmanImage(ref string) (Image, func(), error) { return nil, cleanup, xerrors.Errorf("unable to inspect the image (%s): %w", ref, err) } + history, err := c.imageHistoryInspect(ref) + if err != nil { + return nil, cleanup, xerrors.Errorf("unable to inspect the image (%s): %w", ref, err) + } + f, err := os.CreateTemp("", "fanal-*") if err != nil { return nil, cleanup, xerrors.Errorf("failed to create a temporary file") @@ -109,5 +139,6 @@ func PodmanImage(ref string) (Image, func(), error) { return &image{ opener: imageOpener(context.Background(), ref, f, c.imageSave), inspect: inspect, + history: configHistory(history), }, cleanup, nil } diff --git a/pkg/fanal/image/daemon/podman_test.go b/pkg/fanal/image/daemon/podman_test.go index f5b633fbd93..6c66fa62baa 100644 --- a/pkg/fanal/image/daemon/podman_test.go +++ b/pkg/fanal/image/daemon/podman_test.go @@ -53,13 +53,18 @@ func TestPodmanImage(t *testing.T) { imageName string fields fields wantConfigName string + wantCreateBy []string wantErr bool }{ { name: "happy path", imageName: "alpine:3.11", wantConfigName: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72", - wantErr: false, + wantCreateBy: []string{ + "/bin/sh -c #(nop) CMD [\"/bin/sh\"]", + "/bin/sh -c #(nop) ADD file:0c4555f363c2672e350001f1293e689875a3760afe7b3f9146886afe67121cba in / ", + }, + wantErr: false, }, { name: "unknown image", @@ -88,6 +93,14 @@ func TestPodmanImage(t *testing.T) { confName, err := img.ConfigName() require.NoError(t, err) assert.Equal(t, tt.wantConfigName, confName.String()) + + confFile, err := img.ConfigFile() + require.NoError(t, err) + + assert.Equal(t, len(confFile.History), len(tt.wantCreateBy)) + for _, h := range confFile.History { + assert.Contains(t, tt.wantCreateBy, h.CreatedBy) + } }) } } diff --git a/pkg/fanal/test/integration/containerd_test.go b/pkg/fanal/test/integration/containerd_test.go index 26343e90610..c205c692428 100644 --- a/pkg/fanal/test/integration/containerd_test.go +++ b/pkg/fanal/test/integration/containerd_test.go @@ -112,6 +112,17 @@ func TestContainerd_LocalImage(t *testing.T) { "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", }, }, + History: []v1.History{ + { + Created: v1.Time{Time: time.Date(2019, 8, 20, 20, 19, 55, 62606894, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ADD file:fe64057fbb83dccb960efabbf1cd8777920ef279a7fa8dbca0a8801c651bdf7c in / ", + }, + { + Created: v1.Time{Time: time.Date(2019, 8, 20, 20, 19, 55, 211423266, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) CMD [\"/bin/sh\"]", + EmptyLayer: true, + }, + }, }, }, }, @@ -201,6 +212,173 @@ func TestContainerd_LocalImage(t *testing.T) { "/docker-entrypoint.sh", }, }, + History: []v1.History{ + { + Created: v1.Time{Time: time.Date(2018, 9, 11, 22, 19, 38, 885299940, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ADD file:49f9e47e678d868d5b023482aa8dded71276a241a665c4f8b55ca77269321b34 in / ", + }, + { + Created: v1.Time{Time: time.Date(2018, 9, 11, 22, 19, 39, 58628442, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) CMD [\"/bin/sh\"]", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 9, 12, 1, 26, 59, 951316015, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENV PHPIZE_DEPS=autoconf \t\tdpkg-dev dpkg \t\tfile \t\tg++ \t\tgcc \t\tlibc-dev \t\tmake \t\tpkgconf \t\tre2c", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 9, 12, 1, 27, 1, 470388635, time.UTC)}, + CreatedBy: "/bin/sh -c apk add --no-cache --virtual .persistent-deps \t\tca-certificates \t\tcurl \t\ttar \t\txz \t\tlibressl", + }, + { + Created: v1.Time{Time: time.Date(2018, 9, 12, 1, 27, 2, 432381785, time.UTC)}, + CreatedBy: "/bin/sh -c set -x \t&& addgroup -g 82 -S www-data \t&& adduser -u 82 -D -S -G www-data www-data", + }, + { + Created: v1.Time{Time: time.Date(2018, 9, 12, 1, 27, 2, 715120309, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENV PHP_INI_DIR=/usr/local/etc/php", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 9, 12, 1, 27, 3, 655421341, time.UTC)}, + CreatedBy: "/bin/sh -c mkdir -p $PHP_INI_DIR/conf.d", + }, + { + Created: v1.Time{Time: time.Date(2018, 9, 12, 1, 27, 3, 931799562, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENV PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 9, 12, 1, 27, 4, 210945499, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENV PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 9, 12, 1, 27, 4, 523116501, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENV PHP_LDFLAGS=-Wl,-O1 -Wl,--hash-style=both -pie", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 9, 12, 1, 27, 4, 795176159, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENV GPG_KEYS=1729F83938DA44E27BA0F4D3DBDB397470D12172 B1B44D8F021E4E2D6021E995DC9FF8D3EE5AF27F", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 19, 2, 18, 415761689, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENV PHP_VERSION=7.2.11", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 19, 2, 18, 599097853, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENV PHP_URL=https://secure.php.net/get/php-7.2.11.tar.xz/from/this/mirror PHP_ASC_URL=https://secure.php.net/get/php-7.2.11.tar.xz.asc/from/this/mirror", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 19, 2, 18, 782890412, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENV PHP_SHA256=da1a705c0bc46410e330fc6baa967666c8cd2985378fb9707c01a8e33b01d985 PHP_MD5=", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 19, 2, 22, 795846753, time.UTC)}, + CreatedBy: "/bin/sh -c set -xe; \t\tapk add --no-cache --virtual .fetch-deps \t\tgnupg \t\twget \t; \t\tmkdir -p /usr/src; \tcd /usr/src; \t\twget -O php.tar.xz \"$PHP_URL\"; \t\tif [ -n \"$PHP_SHA256\" ]; then \t\techo \"$PHP_SHA256 *php.tar.xz\" | sha256sum -c -; \tfi; \tif [ -n \"$PHP_MD5\" ]; then \t\techo \"$PHP_MD5 *php.tar.xz\" | md5sum -c -; \tfi; \t\tif [ -n \"$PHP_ASC_URL\" ]; then \t\twget -O php.tar.xz.asc \"$PHP_ASC_URL\"; \t\texport GNUPGHOME=\"$(mktemp -d)\"; \t\tfor key in $GPG_KEYS; do \t\t\tgpg --keyserver ha.pool.sks-keyservers.net --recv-keys \"$key\"; \t\tdone; \t\tgpg --batch --verify php.tar.xz.asc php.tar.xz; \t\tcommand -v gpgconf > /dev/null && gpgconf --kill all; \t\trm -rf \"$GNUPGHOME\"; \tfi; \t\tapk del .fetch-deps", + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 19, 2, 23, 71406376, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) COPY file:207c686e3fed4f71f8a7b245d8dcae9c9048d276a326d82b553c12a90af0c0ca in /usr/local/bin/ ", + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 19, 7, 13, 93396680, time.UTC)}, + CreatedBy: "/bin/sh -c set -xe \t&& apk add --no-cache --virtual .build-deps \t\t$PHPIZE_DEPS \t\tcoreutils \t\tcurl-dev \t\tlibedit-dev \t\tlibressl-dev \t\tlibsodium-dev \t\tlibxml2-dev \t\tsqlite-dev \t\t&& export CFLAGS=\"$PHP_CFLAGS\" \t\tCPPFLAGS=\"$PHP_CPPFLAGS\" \t\tLDFLAGS=\"$PHP_LDFLAGS\" \t&& docker-php-source extract \t&& cd /usr/src/php \t&& gnuArch=\"$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)\" \t&& ./configure \t\t--build=\"$gnuArch\" \t\t--with-config-file-path=\"$PHP_INI_DIR\" \t\t--with-config-file-scan-dir=\"$PHP_INI_DIR/conf.d\" \t\t\t\t--enable-option-checking=fatal \t\t\t\t--with-mhash \t\t\t\t--enable-ftp \t\t--enable-mbstring \t\t--enable-mysqlnd \t\t--with-sodium=shared \t\t\t\t--with-curl \t\t--with-libedit \t\t--with-openssl \t\t--with-zlib \t\t\t\t$(test \"$gnuArch\" = 's390x-linux-gnu' && echo '--without-pcre-jit') \t\t\t\t$PHP_EXTRA_CONFIGURE_ARGS \t&& make -j \"$(nproc)\" \t&& make install \t&& { find /usr/local/bin /usr/local/sbin -type f -perm +0111 -exec strip --strip-all '{}' + || true; } \t&& make clean \t\t&& cp -v php.ini-* \"$PHP_INI_DIR/\" \t\t&& cd / \t&& docker-php-source delete \t\t&& runDeps=\"$( \t\tscanelf --needed --nobanner --format '%n#p' --recursive /usr/local \t\t\t| tr ',' '\\n' \t\t\t| sort -u \t\t\t| awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }' \t)\" \t&& apk add --no-cache --virtual .php-rundeps $runDeps \t\t&& apk del .build-deps \t\t&& pecl update-channels \t&& rm -rf /tmp/pear ~/.pearrc", + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 19, 7, 13, 722586262, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) COPY multi:2cdcedabcf5a3b9ae610fab7848e94bc2f64b4d85710d55fd6f79e44dacf73d8 in /usr/local/bin/ ", + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 19, 7, 14, 618087104, time.UTC)}, + CreatedBy: "/bin/sh -c docker-php-ext-enable sodium", + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 19, 7, 14, 826981756, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENTRYPOINT [\"docker-php-entrypoint\"]", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 19, 7, 15, 10831572, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) CMD [\"php\" \"-a\"]", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 21, 28, 21, 919735971, time.UTC)}, + CreatedBy: "/bin/sh -c apk --no-cache add git subversion openssh mercurial tini bash patch", + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 21, 28, 22, 611763893, time.UTC)}, + CreatedBy: "/bin/sh -c echo \"memory_limit=-1\" > \"$PHP_INI_DIR/conf.d/memory-limit.ini\" && echo \"date.timezone=${PHP_TIMEZONE:-UTC}\" > \"$PHP_INI_DIR/conf.d/date_timezone.ini\"", + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 21, 28, 50, 224278478, time.UTC)}, + CreatedBy: "/bin/sh -c apk add --no-cache --virtual .build-deps zlib-dev && docker-php-ext-install zip && runDeps=\"$( scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions | tr ',' '\\n' | sort -u | awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }' )\" && apk add --virtual .composer-phpext-rundeps $runDeps && apk del .build-deps", + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 21, 28, 50, 503010161, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENV COMPOSER_ALLOW_SUPERUSER=1", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 21, 28, 50, 775378559, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENV COMPOSER_HOME=/tmp", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 21, 28, 51, 35012363, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENV COMPOSER_VERSION=1.7.2", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 21, 28, 52, 491402624, time.UTC)}, + CreatedBy: "/bin/sh -c curl --silent --fail --location --retry 3 --output /tmp/installer.php --url https://raw.githubusercontent.com/composer/getcomposer.org/b107d959a5924af895807021fcef4ffec5a76aa9/web/installer && php -r \" \\$signature = '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061'; \\$hash = hash('SHA384', file_get_contents('/tmp/installer.php')); if (!hash_equals(\\$signature, \\$hash)) { unlink('/tmp/installer.php'); echo 'Integrity check failed, installer is either corrupt or worse.' . PHP_EOL; exit(1); }\" && php /tmp/installer.php --no-ansi --install-dir=/usr/bin --filename=composer --version=${COMPOSER_VERSION} && composer --ansi --version --no-interaction && rm -rf /tmp/* /tmp/.htaccess", + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 21, 28, 52, 948859545, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) COPY file:295943a303e8f27de4302b6aa3687bce4b1d1392335efaaab9ecd37bec5ab4c5 in /docker-entrypoint.sh ", + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 21, 28, 53, 295399872, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) WORKDIR /app", + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 21, 28, 53, 582920705, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ENTRYPOINT [\"/bin/sh\" \"/docker-entrypoint.sh\"]", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2018, 10, 15, 21, 28, 53, 798628678, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) CMD [\"composer\"]", + EmptyLayer: true, + }, + { + Created: v1.Time{Time: time.Date(2019, 8, 7, 7, 25, 57, 211142800, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ADD file:842584685f26edb24dc305d76894f51cfda2bad0c24a05e727f9d4905d184a70 in /php-app/composer.lock ", + }, + { + Created: v1.Time{Time: time.Date(2019, 8, 7, 7, 25, 57, 583779000, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ADD file:c6d0373d380252b91829a5bb3c81d5b1afa574c91cef7752d18170a231c31f6d in /ruby-app/Gemfile.lock ", + }, + { + Created: v1.Time{Time: time.Date(2019, 8, 7, 7, 25, 57, 921730100, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ADD file:54a1c52556a5ebe98fd124f51c25d071f9e29e2714c72c80d6d3d254b9e83386 in /node-app/package-lock.json ", + }, + { + Created: v1.Time{Time: time.Date(2019, 8, 7, 7, 25, 58, 311593100, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ADD file:097d32f46acde76c4da9e55f17110d69d02cc6d16c86da907980da335fc0fc5f in /python-app/Pipfile.lock ", + }, + { + Created: v1.Time{Time: time.Date(2019, 8, 7, 7, 25, 58, 651649800, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ADD file:7f147d85de19bfb905c260a0c175f227a433259022c163017b96d0efacdcd105 in /rust-app/Cargo.lock ", + }, + }, }, }, }, @@ -320,6 +498,17 @@ func TestContainerd_PullImage(t *testing.T) { }, ArgsEscaped: false, }, + History: []v1.History{ + { + Created: v1.Time{time.Date(2019, 8, 20, 20, 19, 55, 62606894, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) ADD file:fe64057fbb83dccb960efabbf1cd8777920ef279a7fa8dbca0a8801c651bdf7c in / ", + }, + { + Created: v1.Time{time.Date(2019, 8, 20, 20, 19, 55, 211423266, time.UTC)}, + CreatedBy: "/bin/sh -c #(nop) CMD [\"/bin/sh\"]", + EmptyLayer: true, + }, + }, }, }, }, diff --git a/pkg/fanal/types/artifact.go b/pkg/fanal/types/artifact.go index 636cf39aaaf..494d0d7a05f 100644 --- a/pkg/fanal/types/artifact.go +++ b/pkg/fanal/types/artifact.go @@ -18,8 +18,9 @@ type Repository struct { } type Layer struct { - Digest string `json:",omitempty"` - DiffID string `json:",omitempty"` + Digest string `json:",omitempty"` + DiffID string `json:",omitempty"` + CreatedBy string `json:",omitempty"` } type Package struct { @@ -133,9 +134,16 @@ type ArtifactInfo struct { // BlobInfo is stored in cache type BlobInfo struct { - SchemaVersion int - Digest string `json:",omitempty"` - DiffID string `json:",omitempty"` + SchemaVersion int + + // Layer information + Digest string `json:",omitempty"` + DiffID string `json:",omitempty"` + CreatedBy string `json:",omitempty"` + OpaqueDirs []string `json:",omitempty"` + WhiteoutFiles []string `json:",omitempty"` + + // Analysis result OS *OS `json:",omitempty"` Repository *Repository `json:",omitempty"` PackageInfos []PackageInfo `json:",omitempty"` @@ -143,8 +151,6 @@ type BlobInfo struct { Misconfigurations []Misconfiguration `json:",omitempty"` Secrets []Secret `json:",omitempty"` Licenses []LicenseFile `json:",omitempty"` - OpaqueDirs []string `json:",omitempty"` - WhiteoutFiles []string `json:",omitempty"` // Red Hat distributions have build info per layer. // This information will be embedded into packages when applying layers. diff --git a/pkg/fanal/types/secret.go b/pkg/fanal/types/secret.go index 3d2e3ca5f70..a2747cad75f 100644 --- a/pkg/fanal/types/secret.go +++ b/pkg/fanal/types/secret.go @@ -16,6 +16,5 @@ type SecretFinding struct { EndLine int Code Code Match string - Deleted bool Layer Layer `json:",omitempty"` } diff --git a/pkg/report/table/secret.go b/pkg/report/table/secret.go index ee8804108e7..0d1b228ecc1 100644 --- a/pkg/report/table/secret.go +++ b/pkg/report/table/secret.go @@ -119,12 +119,20 @@ func (r *secretRenderer) renderCode(secret types.SecretFinding) { lineInfo = tml.Sprintf("%s-%d", lineInfo, secret.EndLine) } } + var note string - if secret.Deleted { - note = " (deleted in the intermediate layer)" + if c := secret.Layer.CreatedBy; c != "" { + if len(c) > 40 { + // Too long + c = c[:40] + } + note = fmt.Sprintf(" (added by '%s')", c) + } else if secret.Layer.DiffID != "" { + note = fmt.Sprintf(" (added in layer '%s')", strings.TrimPrefix(secret.Layer.DiffID, "sha256:")[:12]) } r.printf(" %s%s%s\r\n", r.target, lineInfo, note) r.printSingleDivider() + for i, line := range lines { if line.Truncated { r.printf("%4s ", strings.Repeat(".", len(fmt.Sprintf("%d", line.Number)))) diff --git a/pkg/report/table/secret_test.go b/pkg/report/table/secret_test.go index a853c34d9ed..5f19db9a076 100644 --- a/pkg/report/table/secret_test.go +++ b/pkg/report/table/secret_test.go @@ -27,6 +27,7 @@ func TestSecretRenderer(t *testing.T) { Category: ftypes.SecretRuleCategory("category"), Title: "this is a title", Severity: "HIGH", + Layer: ftypes.Layer{DiffID: "sha256:beee9f30bc1f711043e78d4a2be0668955d4b761d587d6f60c2c8dc081efb203"}, StartLine: 1, EndLine: 1, Code: ftypes.Code{ @@ -52,7 +53,7 @@ HIGH: category (rule-id) ════════════════════════════════════════ this is a title ──────────────────────────────────────── - my-file:1 + my-file:1 (added in layer 'beee9f30bc1f') ──────────────────────────────────────── 1 [ password=secret ──────────────────────────────────────── @@ -64,11 +65,14 @@ this is a title name: "multiple line", input: []ftypes.SecretFinding{ { - RuleID: "rule-id", - Category: ftypes.SecretRuleCategory("category"), - Title: "this is a title", - Severity: "HIGH", - Deleted: true, + RuleID: "rule-id", + Category: ftypes.SecretRuleCategory("category"), + Title: "this is a title", + Severity: "HIGH", + Layer: ftypes.Layer{ + DiffID: "sha256:beee9f30bc1f711043e78d4a2be0668955d4b761d587d6f60c2c8dc081efb203", + CreatedBy: "COPY my-file my-file", + }, StartLine: 3, EndLine: 4, Code: ftypes.Code{ @@ -115,7 +119,7 @@ HIGH: category (rule-id) ════════════════════════════════════════ this is a title ──────────────────────────────────────── - my-file:3-4 (deleted in the intermediate layer) + my-file:3-4 (added by 'COPY my-file my-file') ──────────────────────────────────────── 1 #!/bin/bash 2 diff --git a/pkg/rpc/convert.go b/pkg/rpc/convert.go index 78110f74af4..cae062fe1ec 100644 --- a/pkg/rpc/convert.go +++ b/pkg/rpc/convert.go @@ -102,7 +102,6 @@ func ConvertToRPCSecretFindings(findings []ftypes.SecretFinding) []*common.Secre StartLine: int32(f.StartLine), Code: ConvertToRPCCode(f.Code), Match: f.Match, - Deleted: f.Deleted, Layer: ConvertToRPCLayer(f.Layer), }) } @@ -230,8 +229,9 @@ func ConvertToRPCMisconfs(misconfs []types.DetectedMisconfiguration) []*common.D // ConvertToRPCLayer returns common.Layer func ConvertToRPCLayer(layer ftypes.Layer) *common.Layer { return &common.Layer{ - Digest: layer.Digest, - DiffId: layer.DiffID, + Digest: layer.Digest, + DiffId: layer.DiffID, + CreatedBy: layer.CreatedBy, } } @@ -313,10 +313,10 @@ func ConvertFromRPCSecretFindings(rpcFindings []*common.SecretFinding) []ftypes. EndLine: int(finding.EndLine), Code: ConvertFromRPCCode(finding.Code), Match: finding.Match, - Deleted: finding.Deleted, Layer: ftypes.Layer{ - Digest: finding.Layer.Digest, - DiffID: finding.Layer.DiffId, + Digest: finding.Layer.Digest, + DiffID: finding.Layer.DiffId, + CreatedBy: finding.Layer.CreatedBy, }, }) } diff --git a/rpc/common/service.pb.go b/rpc/common/service.pb.go index 7fc194607e4..bd3b842a3bb 100644 --- a/rpc/common/service.pb.go +++ b/rpc/common/service.pb.go @@ -1060,8 +1060,9 @@ type Layer struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Digest string `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"` - DiffId string `protobuf:"bytes,2,opt,name=diff_id,json=diffId,proto3" json:"diff_id,omitempty"` + Digest string `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"` + DiffId string `protobuf:"bytes,2,opt,name=diff_id,json=diffId,proto3" json:"diff_id,omitempty"` + CreatedBy string `protobuf:"bytes,3,opt,name=created_by,json=createdBy,proto3" json:"created_by,omitempty"` } func (x *Layer) Reset() { @@ -1110,6 +1111,13 @@ func (x *Layer) GetDiffId() string { return "" } +func (x *Layer) GetCreatedBy() string { + if x != nil { + return x.CreatedBy + } + return "" +} + type CVSS struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1415,7 +1423,6 @@ type SecretFinding struct { EndLine int32 `protobuf:"varint,6,opt,name=end_line,json=endLine,proto3" json:"end_line,omitempty"` Code *Code `protobuf:"bytes,7,opt,name=code,proto3" json:"code,omitempty"` Match string `protobuf:"bytes,8,opt,name=match,proto3" json:"match,omitempty"` - Deleted bool `protobuf:"varint,9,opt,name=deleted,proto3" json:"deleted,omitempty"` Layer *Layer `protobuf:"bytes,10,opt,name=layer,proto3" json:"layer,omitempty"` } @@ -1507,13 +1514,6 @@ func (x *SecretFinding) GetMatch() string { return "" } -func (x *SecretFinding) GetDeleted() bool { - if x != nil { - return x.Deleted - } - return false -} - func (x *SecretFinding) GetLayer() *Layer { if x != nil { return x.Layer @@ -1764,80 +1764,80 @@ var file_rpc_common_service_proto_rawDesc = []byte{ 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x38, 0x0a, 0x05, 0x4c, 0x61, 0x79, 0x65, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x57, 0x0a, 0x05, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x66, 0x66, - 0x49, 0x64, 0x22, 0x76, 0x0a, 0x04, 0x43, 0x56, 0x53, 0x53, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x32, - 0x5f, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, - 0x32, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x33, 0x5f, 0x76, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x33, 0x56, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x32, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x76, 0x32, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, - 0x19, 0x0a, 0x08, 0x76, 0x33, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x07, 0x76, 0x33, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x98, 0x01, 0x0a, 0x0e, 0x43, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x29, - 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, - 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, - 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xf3, 0x01, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, - 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x43, 0x61, 0x75, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x61, - 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x74, - 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x68, 0x69, 0x67, - 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x66, - 0x69, 0x72, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0a, 0x66, 0x69, 0x72, 0x73, 0x74, 0x43, 0x61, 0x75, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, - 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x43, 0x61, 0x75, 0x73, 0x65, 0x22, 0x30, 0x0a, 0x04, 0x43, - 0x6f, 0x64, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0xb3, 0x02, - 0x0a, 0x0d, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, - 0x17, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x72, 0x75, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, - 0x67, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, - 0x67, 0x6f, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, - 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x6e, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x4c, 0x69, 0x6e, 0x65, - 0x12, 0x26, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, - 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, - 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x18, - 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x22, 0x5d, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x69, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, - 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x73, 0x2a, 0x44, 0x0a, 0x08, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x0b, - 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, - 0x4f, 0x57, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x45, 0x44, 0x49, 0x55, 0x4d, 0x10, 0x02, - 0x12, 0x08, 0x0a, 0x04, 0x48, 0x49, 0x47, 0x48, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x52, - 0x49, 0x54, 0x49, 0x43, 0x41, 0x4c, 0x10, 0x04, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x71, 0x75, 0x61, 0x73, 0x65, 0x63, 0x75, 0x72, - 0x69, 0x74, 0x79, 0x2f, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, + 0x79, 0x22, 0x76, 0x0a, 0x04, 0x43, 0x56, 0x53, 0x53, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x32, 0x5f, + 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x32, + 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x33, 0x5f, 0x76, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x33, 0x56, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x32, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x76, 0x32, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x19, + 0x0a, 0x08, 0x76, 0x33, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x07, 0x76, 0x33, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x98, 0x01, 0x0a, 0x0e, 0x43, 0x75, + 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x29, 0x0a, + 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, + 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, + 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x22, 0xf3, 0x01, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, + 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x69, 0x73, 0x43, 0x61, 0x75, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x72, + 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x74, + 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x68, 0x69, 0x67, 0x68, + 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, + 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0a, 0x66, 0x69, 0x72, 0x73, 0x74, 0x43, 0x61, 0x75, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x6c, 0x61, 0x73, 0x74, 0x43, 0x61, 0x75, 0x73, 0x65, 0x22, 0x30, 0x0a, 0x04, 0x43, 0x6f, + 0x64, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x2e, 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x9f, 0x02, 0x0a, + 0x0d, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x17, + 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x72, 0x75, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, + 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, + 0x6f, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, + 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x12, + 0x26, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x64, + 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x29, 0x0a, + 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, + 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, + 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x22, 0x5d, + 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, + 0x70, 0x61, 0x74, 0x68, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x46, 0x69, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x2a, 0x44, 0x0a, + 0x08, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, + 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x4f, 0x57, 0x10, 0x01, 0x12, + 0x0a, 0x0a, 0x06, 0x4d, 0x45, 0x44, 0x49, 0x55, 0x4d, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x48, + 0x49, 0x47, 0x48, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x52, 0x49, 0x54, 0x49, 0x43, 0x41, + 0x4c, 0x10, 0x04, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x61, 0x71, 0x75, 0x61, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x74, + 0x72, 0x69, 0x76, 0x79, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x3b, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/rpc/common/service.proto b/rpc/common/service.proto index f29706795db..d6d61a5c6e0 100644 --- a/rpc/common/service.proto +++ b/rpc/common/service.proto @@ -115,8 +115,9 @@ message DataSource { } message Layer { - string digest = 1; - string diff_id = 2; + string digest = 1; + string diff_id = 2; + string created_by = 3; } enum Severity { @@ -165,8 +166,9 @@ message SecretFinding { int32 end_line = 6; Code code = 7; string match = 8; - bool deleted = 9; Layer layer = 10; + + reserved 9; // deprecated 'deleted' } message Secret {