mirror of
https://github.com/aquasecurity/trivy.git
synced 2026-01-31 13:53:14 +08:00
feat(image): secret scanning on container image config (#3495)
This commit is contained in:
@@ -21,6 +21,7 @@ linters-settings:
|
||||
local-prefixes: github.com/aquasecurity
|
||||
gosec:
|
||||
excludes:
|
||||
- G101
|
||||
- G114
|
||||
- G204
|
||||
- G402
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/executable"
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/imgconf/apk"
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/imgconf/dockerfile"
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/imgconf/secret"
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/c/conan"
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/conda/meta"
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/dart/pub"
|
||||
|
||||
@@ -39,6 +39,7 @@ type ConfigAnalyzerOptions struct {
|
||||
FilePatterns []string
|
||||
DisabledAnalyzers []Type
|
||||
MisconfScannerOption misconf.ScannerOption
|
||||
SecretScannerOption SecretScannerOption
|
||||
}
|
||||
|
||||
type ConfigAnalysisInput struct {
|
||||
@@ -48,6 +49,7 @@ type ConfigAnalysisInput struct {
|
||||
|
||||
type ConfigAnalysisResult struct {
|
||||
Misconfiguration *types.Misconfiguration
|
||||
Secret *types.Secret
|
||||
HistoryPackages types.Packages
|
||||
}
|
||||
|
||||
@@ -58,6 +60,9 @@ func (r *ConfigAnalysisResult) Merge(new *ConfigAnalysisResult) {
|
||||
if new.Misconfiguration != nil {
|
||||
r.Misconfiguration = new.Misconfiguration
|
||||
}
|
||||
if new.Secret != nil {
|
||||
r.Secret = new.Secret
|
||||
}
|
||||
if new.HistoryPackages != nil {
|
||||
r.HistoryPackages = new.HistoryPackages
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ const (
|
||||
// ============
|
||||
TypeApkCommand Type = "apk-command"
|
||||
TypeHistoryDockerfile Type = "history-dockerfile"
|
||||
TypeImageConfigSecret Type = "image-config-secret"
|
||||
|
||||
// =================
|
||||
// Structured Config
|
||||
@@ -127,30 +128,93 @@ const (
|
||||
var (
|
||||
// TypeOSes has all OS-related analyzers
|
||||
TypeOSes = []Type{
|
||||
TypeOSRelease, TypeAlpine, TypeAmazon, TypeCBLMariner, TypeDebian, TypePhoton, TypeCentOS,
|
||||
TypeRocky, TypeAlma, TypeFedora, TypeOracle, TypeRedHatBase, TypeSUSE, TypeUbuntu,
|
||||
TypeApk, TypeDpkg, TypeDpkgLicense, TypeRpm, TypeRpmqa,
|
||||
TypeOSRelease,
|
||||
TypeAlpine,
|
||||
TypeAmazon,
|
||||
TypeCBLMariner,
|
||||
TypeDebian,
|
||||
TypePhoton,
|
||||
TypeCentOS,
|
||||
TypeRocky,
|
||||
TypeAlma,
|
||||
TypeFedora,
|
||||
TypeOracle,
|
||||
TypeRedHatBase,
|
||||
TypeSUSE,
|
||||
TypeUbuntu,
|
||||
TypeApk,
|
||||
TypeDpkg,
|
||||
TypeDpkgLicense,
|
||||
TypeRpm,
|
||||
TypeRpmqa,
|
||||
TypeApkRepo,
|
||||
}
|
||||
|
||||
// TypeLanguages has all language analyzers
|
||||
TypeLanguages = []Type{
|
||||
TypeBundler, TypeGemSpec, TypeCargo, TypeComposer, TypeJar, TypePom, TypeGradleLock,
|
||||
TypeNpmPkgLock, TypeNodePkg, TypeYarn, TypePnpm, TypeNuget, TypeDotNetCore, TypeCondaPkg,
|
||||
TypePythonPkg, TypePip, TypePipenv, TypePoetry, TypeGoBinary, TypeGoMod, TypeRustBinary, TypeConanLock,
|
||||
TypeCocoaPods, TypePubSpecLock, TypeMixLock,
|
||||
TypeBundler,
|
||||
TypeGemSpec,
|
||||
TypeCargo,
|
||||
TypeComposer,
|
||||
TypeJar,
|
||||
TypePom,
|
||||
TypeGradleLock,
|
||||
TypeNpmPkgLock,
|
||||
TypeNodePkg,
|
||||
TypeYarn,
|
||||
TypePnpm,
|
||||
TypeNuget,
|
||||
TypeDotNetCore,
|
||||
TypeCondaPkg,
|
||||
TypePythonPkg,
|
||||
TypePip,
|
||||
TypePipenv,
|
||||
TypePoetry,
|
||||
TypeGoBinary,
|
||||
TypeGoMod,
|
||||
TypeRustBinary,
|
||||
TypeConanLock,
|
||||
TypeCocoaPods,
|
||||
TypePubSpecLock,
|
||||
TypeMixLock,
|
||||
}
|
||||
|
||||
// TypeLockfiles has all lock file analyzers
|
||||
TypeLockfiles = []Type{
|
||||
TypeBundler, TypeNpmPkgLock, TypeYarn,
|
||||
TypePnpm, TypePip, TypePipenv, TypePoetry, TypeGoMod, TypePom, TypeConanLock, TypeGradleLock,
|
||||
TypeCocoaPods, TypePubSpecLock, TypeMixLock,
|
||||
TypeBundler,
|
||||
TypeNpmPkgLock,
|
||||
TypeYarn,
|
||||
TypePnpm,
|
||||
TypePip,
|
||||
TypePipenv,
|
||||
TypePoetry,
|
||||
TypeGoMod,
|
||||
TypePom,
|
||||
TypeConanLock,
|
||||
TypeGradleLock,
|
||||
TypeCocoaPods,
|
||||
TypePubSpecLock,
|
||||
TypeMixLock,
|
||||
}
|
||||
|
||||
// TypeIndividualPkgs has all analyzers for individual packages
|
||||
TypeIndividualPkgs = []Type{TypeGemSpec, TypeNodePkg, TypeCondaPkg, TypePythonPkg, TypeGoBinary, TypeJar, TypeRustBinary}
|
||||
TypeIndividualPkgs = []Type{
|
||||
TypeGemSpec,
|
||||
TypeNodePkg,
|
||||
TypeCondaPkg,
|
||||
TypePythonPkg,
|
||||
TypeGoBinary,
|
||||
TypeJar,
|
||||
TypeRustBinary,
|
||||
}
|
||||
|
||||
// TypeConfigFiles has all config file analyzers
|
||||
TypeConfigFiles = []Type{TypeYaml, TypeJSON, TypeDockerfile, TypeTerraform, TypeCloudFormation, TypeHelm}
|
||||
TypeConfigFiles = []Type{
|
||||
TypeYaml,
|
||||
TypeJSON,
|
||||
TypeDockerfile,
|
||||
TypeTerraform,
|
||||
TypeCloudFormation,
|
||||
TypeHelm,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -123,6 +123,7 @@ func Test_historyAnalyzer_Analyze(t *testing.T) {
|
||||
if got != nil && got.Misconfiguration != nil {
|
||||
got.Misconfiguration.Successes = nil // Not compare successes in this test
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
|
||||
74
pkg/fanal/analyzer/imgconf/secret/secret.go
Normal file
74
pkg/fanal/analyzer/imgconf/secret/secret.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/secret"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
)
|
||||
|
||||
const analyzerVersion = 1
|
||||
|
||||
func init() {
|
||||
analyzer.RegisterConfigAnalyzer(analyzer.TypeImageConfigSecret, newSecretAnalyzer)
|
||||
}
|
||||
|
||||
// secretAnalyzer detects secrets in container image config.
|
||||
type secretAnalyzer struct {
|
||||
scanner secret.Scanner
|
||||
}
|
||||
|
||||
func newSecretAnalyzer(opts analyzer.ConfigAnalyzerOptions) (analyzer.ConfigAnalyzer, error) {
|
||||
configPath := opts.SecretScannerOption.ConfigPath
|
||||
c, err := secret.ParseConfig(configPath)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("secret config error: %w", err)
|
||||
}
|
||||
scanner := secret.NewScanner(c)
|
||||
|
||||
return &secretAnalyzer{
|
||||
scanner: scanner,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *secretAnalyzer) Analyze(_ context.Context, input analyzer.ConfigAnalysisInput) (*analyzer.
|
||||
ConfigAnalysisResult, error) {
|
||||
if input.Config == nil {
|
||||
return nil, nil
|
||||
}
|
||||
b, err := json.MarshalIndent(input.Config, " ", "")
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("json marshal error: %w", err)
|
||||
}
|
||||
|
||||
result := a.scanner.Scan(secret.ScanArgs{
|
||||
FilePath: "config.json",
|
||||
Content: b,
|
||||
})
|
||||
|
||||
if len(result.Findings) == 0 {
|
||||
log.Logger.Debug("No secrets found in container image config")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &analyzer.ConfigAnalysisResult{
|
||||
Secret: &result,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *secretAnalyzer) Required(_ types.OS) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *secretAnalyzer) Type() analyzer.Type {
|
||||
return analyzer.TypeImageConfigSecret
|
||||
}
|
||||
|
||||
func (a *secretAnalyzer) Version() int {
|
||||
return analyzerVersion
|
||||
}
|
||||
109
pkg/fanal/analyzer/imgconf/secret/secret_test.go
Normal file
109
pkg/fanal/analyzer/imgconf/secret/secret_test.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func Test_secretAnalyzer_Analyze(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config *v1.ConfigFile
|
||||
want *analyzer.ConfigAnalysisResult
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
config: &v1.ConfigFile{
|
||||
Config: v1.Config{
|
||||
Env: []string{
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"secret=ghp_eifae6eigh3aeSah1shahd6oi1tague6vaey", // dummy token
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &analyzer.ConfigAnalysisResult{
|
||||
Secret: &types.Secret{
|
||||
FilePath: "config.json",
|
||||
Findings: []types.SecretFinding{
|
||||
{
|
||||
RuleID: "github-pat",
|
||||
Category: "GitHub",
|
||||
Severity: "CRITICAL",
|
||||
Title: "GitHub Personal Access Token",
|
||||
StartLine: 12,
|
||||
EndLine: 12,
|
||||
Code: types.Code{
|
||||
Lines: []types.Line{
|
||||
{
|
||||
Number: 10,
|
||||
Content: " \"Env\": [",
|
||||
Highlighted: " \"Env\": [",
|
||||
},
|
||||
{
|
||||
Number: 11,
|
||||
Content: " \"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\",",
|
||||
Highlighted: " \"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\",",
|
||||
},
|
||||
{
|
||||
Number: 12,
|
||||
Content: " \"secret=****************************************\"",
|
||||
IsCause: true,
|
||||
Highlighted: " \"secret=****************************************\"",
|
||||
FirstCause: true,
|
||||
LastCause: true,
|
||||
},
|
||||
{
|
||||
Number: 13,
|
||||
Content: " ]",
|
||||
Highlighted: " ]",
|
||||
},
|
||||
},
|
||||
},
|
||||
Match: " \"secret=****************************************\"",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no secret",
|
||||
config: &v1.ConfigFile{
|
||||
Config: v1.Config{
|
||||
Env: []string{
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "nil config",
|
||||
config: nil,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a, err := newSecretAnalyzer(analyzer.ConfigAnalyzerOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := a.Analyze(context.Background(), analyzer.ConfigAnalysisInput{
|
||||
Config: tt.config,
|
||||
})
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,11 @@ func (a Applier) ApplyLayers(imageID string, layerKeys []string) (types.Artifact
|
||||
mergedLayer := ApplyLayers(layers)
|
||||
|
||||
imageInfo, _ := a.cache.GetArtifact(imageID) // nolint
|
||||
mergedLayer.HistoryPackages = imageInfo.HistoryPackages
|
||||
mergedLayer.ImageMisconfiguration = imageInfo.Misconfiguration
|
||||
mergedLayer.ImageConfig = types.ImageConfigDetail{
|
||||
Packages: imageInfo.HistoryPackages,
|
||||
Misconfiguration: imageInfo.Misconfiguration,
|
||||
Secret: imageInfo.Secret,
|
||||
}
|
||||
|
||||
if !mergedLayer.OS.Detected() {
|
||||
return mergedLayer, analyzer.ErrUnknownOS // send back package and apps info regardless
|
||||
|
||||
@@ -337,38 +337,40 @@ func TestApplier_ApplyLayers(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
HistoryPackages: []types.Package{
|
||||
{
|
||||
Name: "musl",
|
||||
Version: "1.1.23",
|
||||
},
|
||||
{
|
||||
Name: "busybox",
|
||||
Version: "1.31",
|
||||
},
|
||||
{
|
||||
Name: "ncurses-libs",
|
||||
Version: "6.1_p20190518-r0",
|
||||
},
|
||||
{
|
||||
Name: "ncurses-terminfo-base",
|
||||
Version: "6.1_p20190518-r0",
|
||||
},
|
||||
{
|
||||
Name: "ncurses",
|
||||
Version: "6.1_p20190518-r0",
|
||||
},
|
||||
{
|
||||
Name: "ncurses-terminfo",
|
||||
Version: "6.1_p20190518-r0",
|
||||
},
|
||||
{
|
||||
Name: "bash",
|
||||
Version: "5.0.0-r0",
|
||||
},
|
||||
{
|
||||
Name: "readline",
|
||||
Version: "8.0.0-r0",
|
||||
ImageConfig: types.ImageConfigDetail{
|
||||
Packages: []types.Package{
|
||||
{
|
||||
Name: "musl",
|
||||
Version: "1.1.23",
|
||||
},
|
||||
{
|
||||
Name: "busybox",
|
||||
Version: "1.31",
|
||||
},
|
||||
{
|
||||
Name: "ncurses-libs",
|
||||
Version: "6.1_p20190518-r0",
|
||||
},
|
||||
{
|
||||
Name: "ncurses-terminfo-base",
|
||||
Version: "6.1_p20190518-r0",
|
||||
},
|
||||
{
|
||||
Name: "ncurses",
|
||||
Version: "6.1_p20190518-r0",
|
||||
},
|
||||
{
|
||||
Name: "ncurses-terminfo",
|
||||
Version: "6.1_p20190518-r0",
|
||||
},
|
||||
{
|
||||
Name: "bash",
|
||||
Version: "5.0.0-r0",
|
||||
},
|
||||
{
|
||||
Name: "readline",
|
||||
Version: "8.0.0-r0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -379,6 +379,7 @@ func (a Artifact) inspectConfig(ctx context.Context, imageID string, osFound typ
|
||||
DockerVersion: config.DockerVersion,
|
||||
OS: config.OS,
|
||||
Misconfiguration: result.Misconfiguration,
|
||||
Secret: result.Secret,
|
||||
HistoryPackages: result.HistoryPackages,
|
||||
}
|
||||
|
||||
|
||||
@@ -47,56 +47,83 @@ var tests = []testCase{
|
||||
name: "happy path, alpine:3.10",
|
||||
remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310",
|
||||
imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz",
|
||||
wantOS: types.OS{Name: "3.10.2", Family: "alpine"},
|
||||
wantOS: types.OS{
|
||||
Name: "3.10.2",
|
||||
Family: "alpine",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path, amazonlinux:2",
|
||||
remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:amazon-2",
|
||||
imageFile: "../../../../integration/testdata/fixtures/images/amazon-2.tar.gz",
|
||||
wantOS: types.OS{Name: "2 (Karoo)", Family: "amazon"},
|
||||
wantOS: types.OS{
|
||||
Name: "2 (Karoo)",
|
||||
Family: "amazon",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path, debian:buster",
|
||||
remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:debian-buster",
|
||||
imageFile: "../../../../integration/testdata/fixtures/images/debian-buster.tar.gz",
|
||||
wantOS: types.OS{Name: "10.1", Family: "debian"},
|
||||
wantOS: types.OS{
|
||||
Name: "10.1",
|
||||
Family: "debian",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path, photon:3.0",
|
||||
remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:photon-30",
|
||||
imageFile: "../../../../integration/testdata/fixtures/images/photon-30.tar.gz",
|
||||
wantOS: types.OS{Name: "3.0", Family: "photon"},
|
||||
wantOS: types.OS{
|
||||
Name: "3.0",
|
||||
Family: "photon",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path, registry.redhat.io/ubi7",
|
||||
remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:ubi-7",
|
||||
imageFile: "../../../../integration/testdata/fixtures/images/ubi-7.tar.gz",
|
||||
wantOS: types.OS{Name: "7.7", Family: "redhat"},
|
||||
wantOS: types.OS{
|
||||
Name: "7.7",
|
||||
Family: "redhat",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path, opensuse leap 15.1",
|
||||
remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:opensuse-leap-151",
|
||||
imageFile: "../../../../integration/testdata/fixtures/images/opensuse-leap-151.tar.gz",
|
||||
wantOS: types.OS{Name: "15.1", Family: "opensuse.leap"},
|
||||
wantOS: types.OS{
|
||||
Name: "15.1",
|
||||
Family: "opensuse.leap",
|
||||
},
|
||||
},
|
||||
{
|
||||
// from registry.suse.com/suse/sle15:15.3.17.8.16
|
||||
name: "happy path, suse 15.3 (NDB)",
|
||||
remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:suse-15.3_ndb",
|
||||
imageFile: "../../../../integration/testdata/fixtures/images/suse-15.3_ndb.tar.gz",
|
||||
wantOS: types.OS{Name: "15.3", Family: "suse linux enterprise server"},
|
||||
wantOS: types.OS{
|
||||
Name: "15.3",
|
||||
Family: "suse linux enterprise server",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path, Fedora 35",
|
||||
remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:fedora-35",
|
||||
imageFile: "../../../../integration/testdata/fixtures/images/fedora-35.tar.gz",
|
||||
wantOS: types.OS{Name: "35", Family: "fedora"},
|
||||
wantOS: types.OS{
|
||||
Name: "35",
|
||||
Family: "fedora",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path, vulnimage with lock files",
|
||||
remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:vulnimage",
|
||||
imageFile: "../../../../integration/testdata/fixtures/images/vulnimage.tar.gz",
|
||||
wantOS: types.OS{Name: "3.7.1", Family: "alpine"},
|
||||
name: "happy path, vulnimage with lock files",
|
||||
remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:vulnimage",
|
||||
imageFile: "../../../../integration/testdata/fixtures/images/vulnimage.tar.gz",
|
||||
wantOS: types.OS{
|
||||
Name: "3.7.1",
|
||||
Family: "alpine",
|
||||
},
|
||||
wantApplicationFile: "testdata/goldens/vuln-image1.2.3.expectedlibs.golden",
|
||||
wantPkgsFromCmds: "testdata/goldens/vuln-image1.2.3.expectedpkgsfromcmds.golden",
|
||||
},
|
||||
@@ -333,8 +360,8 @@ func checkPackageFromCommands(t *testing.T, detail types.ArtifactDetail, tc test
|
||||
|
||||
err := json.Unmarshal(data, &expectedPkgsFromCmds)
|
||||
require.NoError(t, err)
|
||||
assert.ElementsMatch(t, expectedPkgsFromCmds, detail.HistoryPackages, tc.name)
|
||||
assert.ElementsMatch(t, expectedPkgsFromCmds, detail.ImageConfig.Packages, tc.name)
|
||||
} else {
|
||||
assert.Equal(t, []types.Package(nil), detail.HistoryPackages, tc.name)
|
||||
assert.Equal(t, []types.Package(nil), detail.ImageConfig.Packages, tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +202,9 @@ type ArtifactInfo struct {
|
||||
// Misconfiguration holds misconfiguration in container image config
|
||||
Misconfiguration *Misconfiguration `json:",omitempty"`
|
||||
|
||||
// Secret holds secrets in container image config such as environment variables
|
||||
Secret *Secret `json:",omitempty"`
|
||||
|
||||
// HistoryPackages are packages extracted from RUN instructions
|
||||
HistoryPackages Packages `json:",omitempty"`
|
||||
}
|
||||
@@ -246,17 +249,26 @@ type ArtifactDetail struct {
|
||||
Secrets []Secret `json:",omitempty"`
|
||||
Licenses []LicenseFile `json:",omitempty"`
|
||||
|
||||
// ImageMisconfiguration holds misconfigurations in container image config
|
||||
ImageMisconfiguration *Misconfiguration `json:",omitempty"`
|
||||
|
||||
// HistoryPackages are packages extracted from RUN instructions
|
||||
HistoryPackages []Package `json:",omitempty"`
|
||||
// ImageConfig has information from container image config
|
||||
ImageConfig ImageConfigDetail
|
||||
|
||||
// CustomResources hold analysis results from custom analyzers.
|
||||
// It is for extensibility and not used in OSS.
|
||||
CustomResources []CustomResource `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ImageConfigDetail has information from container image config
|
||||
type ImageConfigDetail struct {
|
||||
// Packages are packages extracted from RUN instructions in history
|
||||
Packages []Package `json:",omitempty"`
|
||||
|
||||
// Misconfiguration holds misconfigurations in container image config
|
||||
Misconfiguration *Misconfiguration `json:",omitempty"`
|
||||
|
||||
// Secret holds secrets in container image config
|
||||
Secret *Secret `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ToBlobInfo is used to store a merged layer in cache.
|
||||
func (a *ArtifactDetail) ToBlobInfo() BlobInfo {
|
||||
return BlobInfo{
|
||||
|
||||
@@ -149,9 +149,9 @@ func (s Scanner) Scan(ctx context.Context, target, artifactKey string, blobKeys
|
||||
results = append(results, licenseResults...)
|
||||
}
|
||||
|
||||
// Scan misconfiguration on container image config
|
||||
// Scan misconfigurations on container image config
|
||||
if options.ImageConfigScanners.Enabled(types.MisconfigScanner) {
|
||||
if im := artifactDetail.ImageMisconfiguration; im != nil {
|
||||
if im := artifactDetail.ImageConfig.Misconfiguration; im != nil {
|
||||
im.FilePath = target // Set the target name to the file path as container image config is not a real file.
|
||||
results = append(results, s.MisconfsToResults([]ftypes.Misconfiguration{*im})...)
|
||||
}
|
||||
@@ -159,7 +159,10 @@ func (s Scanner) Scan(ctx context.Context, target, artifactKey string, blobKeys
|
||||
|
||||
// Scan secrets on container image config
|
||||
if options.ImageConfigScanners.Enabled(types.SecretScanner) {
|
||||
// TODO
|
||||
if is := artifactDetail.ImageConfig.Secret; is != nil {
|
||||
is.FilePath = target // Set the target name to the file path as container image config is not a real file.
|
||||
results = append(results, s.secretsToResults([]ftypes.Secret{*is})...)
|
||||
}
|
||||
}
|
||||
|
||||
// For WASM plugins and custom analyzers
|
||||
@@ -191,7 +194,7 @@ func (s Scanner) osPkgsToResult(target string, detail ftypes.ArtifactDetail, opt
|
||||
|
||||
pkgs := detail.Packages
|
||||
if options.ScanRemovedPackages {
|
||||
pkgs = mergePkgs(pkgs, detail.HistoryPackages)
|
||||
pkgs = mergePkgs(pkgs, detail.ImageConfig.Packages)
|
||||
}
|
||||
sort.Sort(pkgs)
|
||||
return &types.Result{
|
||||
@@ -260,7 +263,7 @@ func (s Scanner) scanOSPkgs(target string, detail ftypes.ArtifactDetail, options
|
||||
|
||||
pkgs := detail.Packages
|
||||
if options.ScanRemovedPackages {
|
||||
pkgs = mergePkgs(pkgs, detail.HistoryPackages)
|
||||
pkgs = mergePkgs(pkgs, detail.ImageConfig.Packages)
|
||||
}
|
||||
|
||||
if detail.OS.Extended {
|
||||
|
||||
Reference in New Issue
Block a user