package local import ( "context" "errors" "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/artifact" "github.com/aquasecurity/trivy/pkg/fanal/cache" "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/fanal/walker" "github.com/aquasecurity/trivy/pkg/misconf" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/python/pip" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/alpine" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/apk" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/secret" _ "github.com/aquasecurity/trivy/pkg/fanal/handler/sysfile" ) func TestArtifact_Inspect(t *testing.T) { type fields struct { dir string } tests := []struct { name string fields fields artifactOpt artifact.Option scannerOpt misconf.ScannerOption disabledAnalyzers []analyzer.Type disabledHandlers []types.HandlerType putBlobExpectation cache.ArtifactCachePutBlobExpectation want artifact.Reference wantErr string }{ { name: "happy path", fields: fields{ dir: "./testdata/alpine", }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobID: "sha256:afc2bc421aac8c61d89d4dd1c1865efb5441e3877c8a4c919232729d7c574dab", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, OS: types.OS{ Family: "alpine", Name: "3.11.6", }, PackageInfos: []types.PackageInfo{ { FilePath: "lib/apk/db/installed", Packages: types.Packages{ { ID: "musl@1.1.24-r2", Name: "musl", Version: "1.1.24-r2", SrcName: "musl", SrcVersion: "1.1.24-r2", Licenses: []string{"MIT"}, Arch: "x86_64", Digest: "sha1:cb2316a189ebee5282c4a9bd98794cc2477a74c6", InstalledFiles: []string{ "lib/libc.musl-x86_64.so.1", "lib/ld-musl-x86_64.so.1", }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "host", Type: artifact.TypeFilesystem, ID: "sha256:afc2bc421aac8c61d89d4dd1c1865efb5441e3877c8a4c919232729d7c574dab", BlobIDs: []string{ "sha256:afc2bc421aac8c61d89d4dd1c1865efb5441e3877c8a4c919232729d7c574dab", }, }, }, { name: "disable analyzers", fields: fields{ dir: "./testdata/alpine", }, artifactOpt: artifact.Option{ DisabledAnalyzers: []analyzer.Type{ analyzer.TypeAlpine, analyzer.TypeApk, analyzer.TypePip, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobID: "sha256:7db98974b2231d3e25f4890008c4d42f6f26a7da5a8aba99e954dec97f050bd6", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "host", Type: artifact.TypeFilesystem, ID: "sha256:7db98974b2231d3e25f4890008c4d42f6f26a7da5a8aba99e954dec97f050bd6", BlobIDs: []string{ "sha256:7db98974b2231d3e25f4890008c4d42f6f26a7da5a8aba99e954dec97f050bd6", }, }, }, { name: "sad path PutBlob returns an error", fields: fields{ dir: "./testdata/alpine", }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobID: "sha256:afc2bc421aac8c61d89d4dd1c1865efb5441e3877c8a4c919232729d7c574dab", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, OS: types.OS{ Family: "alpine", Name: "3.11.6", }, PackageInfos: []types.PackageInfo{ { FilePath: "lib/apk/db/installed", Packages: types.Packages{ { ID: "musl@1.1.24-r2", Name: "musl", Version: "1.1.24-r2", SrcName: "musl", SrcVersion: "1.1.24-r2", Licenses: []string{"MIT"}, Arch: "x86_64", Digest: "sha1:cb2316a189ebee5282c4a9bd98794cc2477a74c6", InstalledFiles: []string{ "lib/libc.musl-x86_64.so.1", "lib/ld-musl-x86_64.so.1", }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{ Err: errors.New("error"), }, }, wantErr: "failed to store blob", }, { name: "sad path with no such directory", fields: fields{ dir: "./testdata/unknown", }, wantErr: "walk dir error", }, { name: "happy path with single file", fields: fields{ dir: "testdata/requirements.txt", }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobID: "sha256:0c41376dbbc0dbf18e9dbd36c5de85627007dcf9357fd98f191864d48dd35537", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Applications: []types.Application{ { Type: "pip", FilePath: "requirements.txt", Packages: types.Packages{ { Name: "Flask", Version: "2.0.0", Locations: []types.Location{ { StartLine: 1, EndLine: 1, }, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/requirements.txt", Type: artifact.TypeFilesystem, ID: "sha256:0c41376dbbc0dbf18e9dbd36c5de85627007dcf9357fd98f191864d48dd35537", BlobIDs: []string{ "sha256:0c41376dbbc0dbf18e9dbd36c5de85627007dcf9357fd98f191864d48dd35537", }, }, }, { name: "happy path with single file using relative path", fields: fields{ dir: "./testdata/requirements.txt", }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobID: "sha256:0c41376dbbc0dbf18e9dbd36c5de85627007dcf9357fd98f191864d48dd35537", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Applications: []types.Application{ { Type: "pip", FilePath: "requirements.txt", Packages: types.Packages{ { Name: "Flask", Version: "2.0.0", Locations: []types.Location{ { StartLine: 1, EndLine: 1, }, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/requirements.txt", Type: artifact.TypeFilesystem, ID: "sha256:0c41376dbbc0dbf18e9dbd36c5de85627007dcf9357fd98f191864d48dd35537", BlobIDs: []string{ "sha256:0c41376dbbc0dbf18e9dbd36c5de85627007dcf9357fd98f191864d48dd35537", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := new(cache.MockArtifactCache) c.ApplyPutBlobExpectation(tt.putBlobExpectation) a, err := NewArtifact(tt.fields.dir, c, walker.NewFS(), tt.artifactOpt) require.NoError(t, err) got, err := a.Inspect(context.Background()) if tt.wantErr != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.wantErr) return } else { require.NoError(t, err) } assert.Equal(t, tt.want, got) }) } } var terraformPolicyMetadata = types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Terraform Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, } func TestTerraformMisconfigurationScan(t *testing.T) { type fields struct { dir string } tests := []struct { name string fields fields putBlobExpectation cache.ArtifactCachePutBlobExpectation artifactOpt artifact.Option want artifact.Reference }{ { name: "single failure", fields: fields{ dir: "./testdata/misconfig/terraform/single-failure", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/terraform/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: "terraform", FilePath: "main.tf", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.asd", Provider: "Generic", Service: "general", StartLine: 1, EndLine: 3, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/terraform/single-failure", Type: artifact.TypeFilesystem, ID: "sha256:1a6ce0acc3b57eb6c830c96fcd868fec1eb4d3b57ad51e481c76d85f22870a65", BlobIDs: []string{ "sha256:1a6ce0acc3b57eb6c830c96fcd868fec1eb4d3b57ad51e481c76d85f22870a65", }, }, }, { name: "multiple failures", fields: fields{ dir: "./testdata/misconfig/terraform/multiple-failures", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/terraform/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: "terraform", FilePath: "main.tf", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.one", Provider: "Generic", Service: "general", StartLine: 1, EndLine: 3, }, }, { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.two", Provider: "Generic", Service: "general", StartLine: 5, EndLine: 7, }, }, }, }, { FileType: "terraform", FilePath: "more.tf", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.three", Provider: "Generic", Service: "general", StartLine: 1, EndLine: 3, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/terraform/multiple-failures", Type: artifact.TypeFilesystem, ID: "sha256:afc20cf0fc99c62bbc79b00cb9fbc70ba7ee76c946a6d560639ba9279344787d", BlobIDs: []string{ "sha256:afc20cf0fc99c62bbc79b00cb9fbc70ba7ee76c946a6d560639ba9279344787d", }, }, }, { name: "no results", fields: fields{ dir: "./testdata/misconfig/terraform/no-results", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/terraform/rego"}, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/terraform/no-results", Type: artifact.TypeFilesystem, ID: "sha256:1827b6a0b0a17e0d623a2045e9d9c331ef613390eda2fed823969ee0dd730257", BlobIDs: []string{ "sha256:1827b6a0b0a17e0d623a2045e9d9c331ef613390eda2fed823969ee0dd730257", }, }, }, { name: "passed", fields: fields{ dir: "./testdata/misconfig/terraform/passed", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/terraform/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: "terraform", FilePath: ".", Successes: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/terraform/passed", Type: artifact.TypeFilesystem, ID: "sha256:eec58ef10d1b04df4af76b2472e615f8c27e253a16f90d7542670a7001d88915", BlobIDs: []string{ "sha256:eec58ef10d1b04df4af76b2472e615f8c27e253a16f90d7542670a7001d88915", }, }, }, { name: "multiple failures busted relative paths", fields: fields{ dir: "./testdata/misconfig/terraform/busted-relative-paths/child/main.tf", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/terraform/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: "terraform", FilePath: "main.tf", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.one", Provider: "Generic", Service: "general", StartLine: 1, EndLine: 3, }, }, { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.two", Provider: "Generic", Service: "general", StartLine: 5, EndLine: 7, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/terraform/busted-relative-paths/child/main.tf", Type: artifact.TypeFilesystem, ID: "sha256:8db34d644bfb98077180616caeab0b41a26d9029a47a23d4b36e1d6e45584919", BlobIDs: []string{ "sha256:8db34d644bfb98077180616caeab0b41a26d9029a47a23d4b36e1d6e45584919", }, }, }, { name: "tfvars outside the scan folder", fields: fields{ dir: "./testdata/misconfig/terraform/tfvar-outside/tf", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/terraform/rego"}, TerraformTFVars: []string{"./testdata/misconfig/terraform/tfvar-outside/main.tfvars"}, TfExcludeDownloaded: true, DisableEmbeddedPolicies: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: types.Terraform, FilePath: ".", Successes: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/terraform/tfvar-outside/tf", Type: artifact.TypeFilesystem, ID: "sha256:eec58ef10d1b04df4af76b2472e615f8c27e253a16f90d7542670a7001d88915", BlobIDs: []string{ "sha256:eec58ef10d1b04df4af76b2472e615f8c27e253a16f90d7542670a7001d88915", }, }, }, { name: "relative paths", fields: fields{ dir: "./testdata/misconfig/terraform/relative-paths/child", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/terraform/rego"}, DisableEmbeddedPolicies: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: types.Terraform, FilePath: "../parent/main.tf", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.three", Provider: "Generic", Service: "general", StartLine: 1, EndLine: 3, }, }, }, }, { FileType: types.Terraform, FilePath: "main.tf", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.one", Provider: "Generic", Service: "general", StartLine: 1, EndLine: 3, }, }, }, }, { FileType: types.Terraform, FilePath: "nested/main.tf", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.two", Provider: "Generic", Service: "general", StartLine: 1, EndLine: 3, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/terraform/relative-paths/child", Type: artifact.TypeFilesystem, ID: "sha256:f13b89447db61be1c1e4099ef18aec7272091f8f2d3581643a9d1fabc74eda83", BlobIDs: []string{ "sha256:f13b89447db61be1c1e4099ef18aec7272091f8f2d3581643a9d1fabc74eda83", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := new(cache.MockArtifactCache) c.ApplyPutBlobExpectation(tt.putBlobExpectation) tt.artifactOpt.DisabledHandlers = []types.HandlerType{ types.SystemFileFilteringPostHandler, } tt.artifactOpt.MisconfScannerOption.DisableEmbeddedPolicies = true a, err := NewArtifact(tt.fields.dir, c, walker.NewFS(), tt.artifactOpt) require.NoError(t, err) got, err := a.Inspect(context.Background()) require.NoError(t, err) assert.Equal(t, tt.want, got) }) } } const emptyBucketCheck = `package user.something __rego_metadata__ := { "id": "TEST001", "avd_id": "AVD-TEST-0001", "title": "Test policy", "short_code": "empty-bucket-name", "severity": "LOW", "description": "This is a test policy.", "recommended_actions": "Have a cup of tea.", "url": "https://trivy.dev/", } # taken from defsec rego lib to mimic behaviour result(msg, cause) = result { metadata := object.get(cause, "__defsec_metadata", cause) result := { "msg": msg, "startline": object.get(metadata, "startline", 0), "endline": object.get(metadata, "endline", 0), "filepath": object.get(metadata, "filepath", ""), "explicit": object.get(metadata, "explicit", false), "managed": object.get(metadata, "managed", true), "fskey": object.get(metadata, "fskey", ""), "resource": object.get(metadata, "resource", ""), } } deny[res] { bucket := input.aws.s3.buckets[_] bucket.name.value == "" res := result("Empty bucket name!", bucket) }` var terraformPlanPolicyMetadata = types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Terraform Plan Snapshot Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, } func TestTerraformPlanSnapshotMisconfScan(t *testing.T) { type fields struct { dir string } tests := []struct { name string fields fields putBlobExpectation cache.ArtifactCachePutBlobExpectation want artifact.Reference }{ { name: "single failure", fields: fields{ dir: "./testdata/misconfig/terraformplan/snapshots/single-failure", }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: types.TerraformPlanSnapshot, FilePath: "main.tf", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", PolicyMetadata: terraformPlanPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.this", Provider: "Generic", Service: "general", StartLine: 10, EndLine: 12, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/terraformplan/snapshots/single-failure", Type: artifact.TypeFilesystem, ID: "sha256:732c38451bde877a94d1ff5b6f2019655bed04fd24b1169de195eeee1199045e", BlobIDs: []string{ "sha256:732c38451bde877a94d1ff5b6f2019655bed04fd24b1169de195eeee1199045e", }, }, }, { name: "multiple failures", fields: fields{ dir: "./testdata/misconfig/terraformplan/snapshots/multiple-failures", }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: types.TerraformPlanSnapshot, FilePath: "main.tf", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", PolicyMetadata: terraformPlanPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.one", Provider: "Generic", Service: "general", StartLine: 10, EndLine: 12, }, }, { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", PolicyMetadata: terraformPlanPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.two", Provider: "Generic", Service: "general", StartLine: 14, EndLine: 16, }, }, }, }, { FileType: types.TerraformPlanSnapshot, FilePath: "more.tf", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", PolicyMetadata: terraformPlanPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.three", Provider: "Generic", Service: "general", StartLine: 1, EndLine: 3, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/terraformplan/snapshots/multiple-failures", Type: artifact.TypeFilesystem, ID: "sha256:752c0b470adfcfe7e21892cf4c6fc3bc28dba873c9c1696f40b71b7a51ad7231", BlobIDs: []string{ "sha256:752c0b470adfcfe7e21892cf4c6fc3bc28dba873c9c1696f40b71b7a51ad7231", }, }, }, { name: "passed", fields: fields{ dir: "./testdata/misconfig/terraformplan/snapshots/passed", }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: types.TerraformPlanSnapshot, FilePath: ".", Successes: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", PolicyMetadata: terraformPlanPolicyMetadata, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/terraformplan/snapshots/passed", Type: artifact.TypeFilesystem, ID: "sha256:8947704a08f54ab1df32cd905d6bca72edf1785a42702968bafa331172da7176", BlobIDs: []string{ "sha256:8947704a08f54ab1df32cd905d6bca72edf1785a42702968bafa331172da7176", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tmpDir := t.TempDir() f, err := os.Create(filepath.Join(tmpDir, "policy.rego")) require.NoError(t, err) defer f.Close() _, err = f.WriteString(emptyBucketCheck) require.NoError(t, err) c := new(cache.MockArtifactCache) c.ApplyPutBlobExpectation(tt.putBlobExpectation) opt := artifact.Option{ DisabledHandlers: []types.HandlerType{ types.SystemFileFilteringPostHandler, }, MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: false, Namespaces: []string{"user"}, PolicyPaths: []string{tmpDir}, }, WalkerOption: walker.Option{ SkipFiles: []string{"*.tf"}, }, } a, err := NewArtifact(tt.fields.dir, c, walker.NewFS(), opt) require.NoError(t, err) got, err := a.Inspect(context.Background()) require.NoError(t, err) assert.Equal(t, tt.want, got) }) } } func TestCloudFormationMisconfigurationScan(t *testing.T) { type fields struct { dir string } tests := []struct { name string fields fields putBlobExpectation cache.ArtifactCachePutBlobExpectation artifactOpt artifact.Option want artifact.Reference }{ { name: "single failure", fields: fields{ dir: "./testdata/misconfig/cloudformation/single-failure/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/cloudformation/single-failure/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Misconfigurations: []types.Misconfiguration{ { FileType: "cloudformation", FilePath: "main.yaml", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "No buckets allowed!", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "CloudFormation Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, }, CauseMetadata: types.CauseMetadata{ Resource: "main.yaml:3-6", Provider: "Generic", Service: "general", StartLine: 3, EndLine: 6, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/cloudformation/single-failure/src", Type: artifact.TypeFilesystem, ID: "sha256:889a94522970c6e55f1f7543914b2f0131f79f9c4526445fb95309f64a9947d7", BlobIDs: []string{ "sha256:889a94522970c6e55f1f7543914b2f0131f79f9c4526445fb95309f64a9947d7", }, }, }, { name: "multiple failures", fields: fields{ dir: "./testdata/misconfig/cloudformation/multiple-failures/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/cloudformation/multiple-failures/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: "cloudformation", FilePath: "main.yaml", Failures: types.MisconfResults{ types.MisconfResult{ Namespace: "user.something", Query: "data.user.something.deny", Message: "No buckets allowed!", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "CloudFormation Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, }, CauseMetadata: types.CauseMetadata{ Resource: "main.yaml:2-5", Provider: "Generic", Service: "general", StartLine: 2, EndLine: 5, }, }, { Namespace: "user.something", Query: "data.user.something.deny", Message: "No buckets allowed!", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "CloudFormation Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, }, CauseMetadata: types.CauseMetadata{ Resource: "main.yaml:6-9", Provider: "Generic", Service: "general", StartLine: 6, EndLine: 9, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/cloudformation/multiple-failures/src", Type: artifact.TypeFilesystem, ID: "sha256:17c9c72a759856445e6d3847b2d5ed90c3bad3e4ee50cea0c812ef53c179f8ca", BlobIDs: []string{ "sha256:17c9c72a759856445e6d3847b2d5ed90c3bad3e4ee50cea0c812ef53c179f8ca", }, }, }, { name: "no results", fields: fields{ dir: "./testdata/misconfig/cloudformation/no-results/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/cloudformation/no-results/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/cloudformation/no-results/src", Type: artifact.TypeFilesystem, ID: "sha256:26c76a2cb55cb0ef2c3a2dd79e237bddb508ca2c4cefdb103698a1972c8a9c2d", BlobIDs: []string{ "sha256:26c76a2cb55cb0ef2c3a2dd79e237bddb508ca2c4cefdb103698a1972c8a9c2d", }, }, }, { name: "CloudFormation parameters outside the scan directory", fields: fields{ dir: "./testdata/misconfig/cloudformation/params/code/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/cloudformation/params/code/rego"}, CloudFormationParamVars: []string{"./testdata/misconfig/cloudformation/params/cfparams.json"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Misconfigurations: []types.Misconfiguration{ { FileType: "cloudformation", FilePath: "main.yaml", Successes: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "CloudFormation Security Check", Title: "Bad stuff is bad", Description: "Its not good!", Severity: "HIGH", RecommendedActions: "Remove bad stuff", }, CauseMetadata: types.CauseMetadata{ Provider: "AWS", Service: "sqs", }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/cloudformation/params/code/src", Type: artifact.TypeFilesystem, ID: "sha256:267b572211115db6a2a4484a02317fbb6d4f050da0e95b1db4243d49889483de", BlobIDs: []string{ "sha256:267b572211115db6a2a4484a02317fbb6d4f050da0e95b1db4243d49889483de", }, }, }, { name: "passed", fields: fields{ dir: "./testdata/misconfig/cloudformation/passed/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/cloudformation/passed/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Misconfigurations: []types.Misconfiguration{ { FileType: "cloudformation", FilePath: "main.yaml", Successes: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "CloudFormation Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, }, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/cloudformation/passed/src", Type: artifact.TypeFilesystem, ID: "sha256:8ca92725ce2f47b7ffb1b0a9e0359d59ac2b3b3f517ba42f66a859436057e54a", BlobIDs: []string{ "sha256:8ca92725ce2f47b7ffb1b0a9e0359d59ac2b3b3f517ba42f66a859436057e54a", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := new(cache.MockArtifactCache) c.ApplyPutBlobExpectation(tt.putBlobExpectation) tt.artifactOpt.DisabledHandlers = []types.HandlerType{ types.SystemFileFilteringPostHandler, } tt.artifactOpt.MisconfScannerOption.DisableEmbeddedPolicies = true a, err := NewArtifact(tt.fields.dir, c, walker.NewFS(), tt.artifactOpt) require.NoError(t, err) got, err := a.Inspect(context.Background()) require.NoError(t, err) assert.Equal(t, tt.want, got) }) } } func TestDockerfileMisconfigurationScan(t *testing.T) { type fields struct { dir string } tests := []struct { name string fields fields putBlobExpectation cache.ArtifactCachePutBlobExpectation artifactOpt artifact.Option want artifact.Reference }{ { name: "single failure", fields: fields{ dir: "./testdata/misconfig/dockerfile/single-failure/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/dockerfile/single-failure/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Misconfigurations: []types.Misconfiguration{ { FileType: "dockerfile", FilePath: "Dockerfile", Successes: types.MisconfResults{ types.MisconfResult{ Namespace: "user.something", Query: "data.user.something.deny", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Dockerfile Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, }, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/dockerfile/single-failure/src", Type: artifact.TypeFilesystem, ID: "sha256:627cbf451ec7929dfe5151dfe0e2305ed855906bf79136f198528a0cc3f6e4f9", BlobIDs: []string{ "sha256:627cbf451ec7929dfe5151dfe0e2305ed855906bf79136f198528a0cc3f6e4f9", }, }, }, { name: "multiple failures", fields: fields{ dir: "./testdata/misconfig/dockerfile/multiple-failures/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/dockerfile/multiple-failures/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Misconfigurations: []types.Misconfiguration{ { FileType: "dockerfile", FilePath: "Dockerfile", Successes: types.MisconfResults{ types.MisconfResult{ Namespace: "user.something", Query: "data.user.something.deny", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Dockerfile Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, }, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/dockerfile/multiple-failures/src", Type: artifact.TypeFilesystem, ID: "sha256:627cbf451ec7929dfe5151dfe0e2305ed855906bf79136f198528a0cc3f6e4f9", BlobIDs: []string{ "sha256:627cbf451ec7929dfe5151dfe0e2305ed855906bf79136f198528a0cc3f6e4f9", }, }, }, { name: "no results", fields: fields{ dir: "./testdata/misconfig/dockerfile/no-results/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/dockerfile/no-results/rego"}, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/dockerfile/no-results/src", Type: artifact.TypeFilesystem, ID: "sha256:26c76a2cb55cb0ef2c3a2dd79e237bddb508ca2c4cefdb103698a1972c8a9c2d", BlobIDs: []string{ "sha256:26c76a2cb55cb0ef2c3a2dd79e237bddb508ca2c4cefdb103698a1972c8a9c2d", }, }, }, { name: "passed", fields: fields{ dir: "./testdata/misconfig/dockerfile/passed/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/dockerfile/passed/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Misconfigurations: []types.Misconfiguration{ { FileType: "dockerfile", FilePath: "Dockerfile", Successes: []types.MisconfResult{ { Namespace: "user.something", Query: "data.user.something.deny", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Dockerfile Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{ "https://trivy.dev/", }, }, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/dockerfile/passed/src", Type: artifact.TypeFilesystem, ID: "sha256:4cc7f6bba417cc65c5391bc9c07fd1e205e21bdec87b271889433af18be1e454", BlobIDs: []string{ "sha256:4cc7f6bba417cc65c5391bc9c07fd1e205e21bdec87b271889433af18be1e454", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := new(cache.MockArtifactCache) c.ApplyPutBlobExpectation(tt.putBlobExpectation) tt.artifactOpt.DisabledHandlers = []types.HandlerType{ types.SystemFileFilteringPostHandler, } a, err := NewArtifact(tt.fields.dir, c, walker.NewFS(), tt.artifactOpt) require.NoError(t, err) got, err := a.Inspect(context.Background()) require.NoError(t, err) assert.Equal(t, tt.want, got) }) } } func TestKubernetesMisconfigurationScan(t *testing.T) { type fields struct { dir string } tests := []struct { name string fields fields putBlobExpectation cache.ArtifactCachePutBlobExpectation artifactOpt artifact.Option want artifact.Reference }{ { name: "single failure", fields: fields{ dir: "./testdata/misconfig/kubernetes/single-failure/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/kubernetes/single-failure/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Misconfigurations: []types.Misconfiguration{ { FileType: "kubernetes", FilePath: "test.yaml", Failures: []types.MisconfResult{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "No evil containers allowed!", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Kubernetes Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{ "https://trivy.dev/", }, }, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", StartLine: 7, EndLine: 9, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/kubernetes/single-failure/src", Type: artifact.TypeFilesystem, ID: "sha256:d5ca0b4e96aaaeafa424a2250db6297a5182cb6ca5db49bc1ff11790f6cdbee9", BlobIDs: []string{ "sha256:d5ca0b4e96aaaeafa424a2250db6297a5182cb6ca5db49bc1ff11790f6cdbee9", }, }, }, { name: "multiple failures", fields: fields{ dir: "./testdata/misconfig/kubernetes/multiple-failures/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/kubernetes/multiple-failures/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Misconfigurations: []types.Misconfiguration{ { FileType: "kubernetes", FilePath: "test.yaml", Failures: []types.MisconfResult{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "No evil containers allowed!", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Kubernetes Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{ "https://trivy.dev/", }, }, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", StartLine: 7, EndLine: 9, }, }, { Namespace: "user.something", Query: "data.user.something.deny", Message: "No evil containers allowed!", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Kubernetes Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{ "https://trivy.dev/", }, }, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", StartLine: 10, EndLine: 12, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/kubernetes/multiple-failures/src", Type: artifact.TypeFilesystem, ID: "sha256:eef9fff2fe8f5c4a123c018b4f91db25d9676e7d171a3a683c2fbfbbbe82fa54", BlobIDs: []string{ "sha256:eef9fff2fe8f5c4a123c018b4f91db25d9676e7d171a3a683c2fbfbbbe82fa54", }, }, }, { name: "no results", fields: fields{ dir: "./testdata/misconfig/kubernetes/no-results/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/kubernetes/no-results/rego"}, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/kubernetes/no-results/src", Type: artifact.TypeFilesystem, ID: "sha256:2b54cf33feaa1fe1f5bf223f873ca6c3f7c3693b0bb3b0ce9e2e7fd79cd37b5a", BlobIDs: []string{ "sha256:2b54cf33feaa1fe1f5bf223f873ca6c3f7c3693b0bb3b0ce9e2e7fd79cd37b5a", }, }, }, { name: "passed", fields: fields{ dir: "./testdata/misconfig/kubernetes/passed/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/kubernetes/passed/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Misconfigurations: []types.Misconfiguration{ { FileType: "kubernetes", FilePath: "test.yaml", Successes: []types.MisconfResult{ { Namespace: "user.something", Query: "data.user.something.deny", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Kubernetes Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{ "https://trivy.dev/", }, }, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/kubernetes/passed/src", Type: artifact.TypeFilesystem, ID: "sha256:dc7a0fd3ea2f13b0ea05f4e517a16e602b0fc17fbd72aa5e34107ef12a91a30b", BlobIDs: []string{ "sha256:dc7a0fd3ea2f13b0ea05f4e517a16e602b0fc17fbd72aa5e34107ef12a91a30b", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := new(cache.MockArtifactCache) c.ApplyPutBlobExpectation(tt.putBlobExpectation) tt.artifactOpt.DisabledHandlers = []types.HandlerType{ types.SystemFileFilteringPostHandler, } a, err := NewArtifact(tt.fields.dir, c, walker.NewFS(), tt.artifactOpt) require.NoError(t, err) got, err := a.Inspect(context.Background()) require.NoError(t, err) assert.Equal(t, tt.want, got) }) } } func TestAzureARMMisconfigurationScan(t *testing.T) { type fields struct { dir string } tests := []struct { name string fields fields putBlobExpectation cache.ArtifactCachePutBlobExpectation artifactOpt artifact.Option want artifact.Reference }{ { name: "single failure", fields: fields{ dir: "./testdata/misconfig/azurearm/single-failure/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/azurearm/single-failure/rego"}, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: "azure-arm", FilePath: "deploy.json", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "No account allowed!", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Azure ARM Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, }, CauseMetadata: types.CauseMetadata{ Resource: "resources[0]", Provider: "Generic", Service: "general", StartLine: 30, EndLine: 40, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/azurearm/single-failure/src", Type: artifact.TypeFilesystem, ID: "sha256:c1a8bfd544b9041ad194382cc42b54289f70966d061ef501b267aec8fd07c5df", BlobIDs: []string{ "sha256:c1a8bfd544b9041ad194382cc42b54289f70966d061ef501b267aec8fd07c5df", }, }, }, { name: "multiple failures", fields: fields{ dir: "./testdata/misconfig/azurearm/multiple-failures/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/azurearm/multiple-failures/rego"}, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: "azure-arm", FilePath: "deploy.json", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "No account allowed!", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Azure ARM Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, }, CauseMetadata: types.CauseMetadata{ Resource: "resources[0]", Provider: "Generic", Service: "general", StartLine: 30, EndLine: 40, }, }, { Namespace: "user.something", Query: "data.user.something.deny", Message: "No account allowed!", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Azure ARM Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, }, CauseMetadata: types.CauseMetadata{ Resource: "resources[1]", Provider: "Generic", Service: "general", StartLine: 41, EndLine: 51, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/azurearm/multiple-failures/src", Type: artifact.TypeFilesystem, ID: "sha256:75bf0e88f8d2857be90fb8d10a350c04c1532ba7f510e1eb404a8bae30ce97d8", BlobIDs: []string{ "sha256:75bf0e88f8d2857be90fb8d10a350c04c1532ba7f510e1eb404a8bae30ce97d8", }, }, }, { name: "no results", fields: fields{ dir: "./testdata/misconfig/azurearm/no-results/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/azurearm/no-results/rego"}, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/azurearm/no-results/src", Type: artifact.TypeFilesystem, ID: "sha256:26c76a2cb55cb0ef2c3a2dd79e237bddb508ca2c4cefdb103698a1972c8a9c2d", BlobIDs: []string{ "sha256:26c76a2cb55cb0ef2c3a2dd79e237bddb508ca2c4cefdb103698a1972c8a9c2d", }, }, }, { name: "passed", fields: fields{ dir: "./testdata/misconfig/azurearm/passed/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/azurearm/passed/rego"}, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: "azure-arm", FilePath: "deploy.json", Successes: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Azure ARM Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, }, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/azurearm/passed/src", Type: artifact.TypeFilesystem, ID: "sha256:b9ba7c4eafec405c8b6998dbb98ee1c7f7830caf8487fd1461433ff82d8779e9", BlobIDs: []string{ "sha256:b9ba7c4eafec405c8b6998dbb98ee1c7f7830caf8487fd1461433ff82d8779e9", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := new(cache.MockArtifactCache) c.ApplyPutBlobExpectation(tt.putBlobExpectation) tt.artifactOpt.DisabledHandlers = []types.HandlerType{ types.SystemFileFilteringPostHandler, } a, err := NewArtifact(tt.fields.dir, c, walker.NewFS(), tt.artifactOpt) require.NoError(t, err) got, err := a.Inspect(context.Background()) require.NoError(t, err) assert.Equal(t, tt.want, got) }) } } func TestMixedConfigurationScan(t *testing.T) { type fields struct { dir string } tests := []struct { name string fields fields putBlobExpectation cache.ArtifactCachePutBlobExpectation artifactOpt artifact.Option want artifact.Reference }{ { name: "single failure each within terraform and cloudformation", fields: fields{ dir: "./testdata/misconfig/mixed/src", }, artifactOpt: artifact.Option{ MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/mixed/rego"}, DisableEmbeddedPolicies: true, DisableEmbeddedLibraries: true, }, }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ BlobIDAnything: true, BlobInfo: types.BlobInfo{ SchemaVersion: 2, Misconfigurations: []types.Misconfiguration{ { FileType: "terraform", FilePath: "main.tf", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "No buckets allowed!", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Terraform Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, }, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.asd", Provider: "Generic", Service: "general", StartLine: 1, EndLine: 3, }, }, }, }, { FileType: "cloudformation", FilePath: "main.yaml", Failures: types.MisconfResults{ { Namespace: "user.something", Query: "data.user.something.deny", Message: "No buckets allowed!", PolicyMetadata: types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "CloudFormation Security Check", Title: "Test policy", Description: "This is a test policy.", Severity: "LOW", RecommendedActions: "Have a cup of tea.", References: []string{"https://trivy.dev/"}, }, CauseMetadata: types.CauseMetadata{ Resource: "main.yaml:3-6", Provider: "Generic", Service: "general", StartLine: 3, EndLine: 6, }, }, }, }, }, }, }, Returns: cache.ArtifactCachePutBlobReturns{}, }, want: artifact.Reference{ Name: "testdata/misconfig/mixed/src", Type: artifact.TypeFilesystem, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := new(cache.MockArtifactCache) c.ApplyPutBlobExpectation(tt.putBlobExpectation) tt.artifactOpt.DisabledHandlers = []types.HandlerType{ types.SystemFileFilteringPostHandler, } a, err := NewArtifact(tt.fields.dir, c, walker.NewFS(), tt.artifactOpt) require.NoError(t, err) got, err := a.Inspect(context.Background()) require.NoError(t, err) require.NotNil(t, got) assert.Equal(t, tt.want.Name, got.Name) assert.Equal(t, tt.want.Type, got.Type) }) } }