github.com/aquasecurity/trivy-iac@v0.8.1-0.20240127024015-3d8e412cf0ab/pkg/detection/detect_test.go (about)

     1  package detection
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  )
    13  
    14  func Test_Detection(t *testing.T) {
    15  	tests := []struct {
    16  		name     string
    17  		path     string
    18  		r        io.ReadSeeker
    19  		expected []FileType
    20  	}{
    21  		{
    22  			name:     "text file, no reader",
    23  			path:     "something.txt",
    24  			expected: nil,
    25  		},
    26  		{
    27  			name:     "text file, with reader",
    28  			path:     "something.txt",
    29  			r:        strings.NewReader("some file content"),
    30  			expected: nil,
    31  		},
    32  		{
    33  			name: "terraform, no reader",
    34  			path: "main.tf",
    35  			expected: []FileType{
    36  				FileTypeTerraform,
    37  			},
    38  		},
    39  		{
    40  			name: "terraform, with reader",
    41  			path: "main.tf",
    42  			r:    strings.NewReader("some file content"),
    43  			expected: []FileType{
    44  				FileTypeTerraform,
    45  			},
    46  		},
    47  		{
    48  			name: "terraform json, no reader",
    49  			path: "main.tf.json",
    50  			expected: []FileType{
    51  				FileTypeTerraform,
    52  				FileTypeJSON,
    53  			},
    54  		},
    55  		{
    56  			name: "terraform json, with reader",
    57  			path: "main.tf.json",
    58  			r: strings.NewReader(`
    59  {
    60    "variable": {
    61      "example": {
    62        "default": "hello"
    63      }
    64    }
    65  }
    66  `),
    67  			expected: []FileType{
    68  				FileTypeTerraform,
    69  				FileTypeJSON,
    70  			},
    71  		},
    72  		{
    73  			name: "terraform vars, no reader",
    74  			path: "main.tfvars",
    75  			expected: []FileType{
    76  				FileTypeTerraform,
    77  			},
    78  		},
    79  		{
    80  			name: "terraform vars, with reader",
    81  			path: "main.tfvars",
    82  			r:    strings.NewReader("some_var = \"some value\""),
    83  			expected: []FileType{
    84  				FileTypeTerraform,
    85  			},
    86  		},
    87  		{
    88  			name: "cloudformation, no reader",
    89  			path: "main.yaml",
    90  			expected: []FileType{
    91  				FileTypeYAML,
    92  				FileTypeHelm,
    93  			},
    94  		},
    95  		{
    96  			name: "terraform plan, with reader",
    97  			path: "plan.json",
    98  			r: strings.NewReader(`{
    99  				"format_version": "0.2",
   100  				"terraform_version": "1.0.3",
   101  				"variables": {
   102  					"bucket_name": {
   103  						"value": "tfsec-plan-testing"
   104  					}
   105  				},
   106  				"planned_values": {},
   107  				"resource_changes": [],
   108  				"prior_state": {},
   109  				"configuration": {}
   110  			}`),
   111  			expected: []FileType{
   112  				FileTypeTerraformPlan,
   113  				FileTypeJSON,
   114  			},
   115  		},
   116  		{
   117  			name: "cloudformation, with reader",
   118  			path: "main.yaml",
   119  			r: strings.NewReader(`---
   120  AWSTemplateFormatVersion: 2010-09-09
   121  
   122  Description: CodePipeline for continuous integration and continuous deployment
   123  
   124  Parameters:
   125    RepositoryName:
   126      Type: String
   127      Description: Name of the CodeCommit repository
   128    BuildDockerImage:
   129      Type: String
   130      Default: aws/codebuild/ubuntu-base:14.04
   131      Description: Docker image to use for the build phase
   132    DeployDockerImage:
   133      Type: String
   134      Default: aws/codebuild/ubuntu-base:14.04
   135      Description: Docker image to use for the deployment phase
   136  
   137  Resources:
   138    PipelineS3Bucket:
   139      Type: AWS::S3::Bucket
   140  `),
   141  			expected: []FileType{
   142  				FileTypeCloudFormation,
   143  				FileTypeYAML,
   144  				FileTypeHelm,
   145  			},
   146  		},
   147  		{
   148  			name: "JSON with Resources, not cloudformation",
   149  			path: "whatever.json",
   150  			r: strings.NewReader(`{
   151    "Resources": ["something"]
   152  }`),
   153  			expected: []FileType{
   154  				FileTypeJSON,
   155  			},
   156  		},
   157  		{
   158  			name: "Dockerfile, no reader",
   159  			path: "Dockerfile",
   160  			r:    nil,
   161  			expected: []FileType{
   162  				FileTypeDockerfile,
   163  			},
   164  		},
   165  		{
   166  			name: "Containerfile, no reader",
   167  			path: "Containerfile",
   168  			r:    nil,
   169  			expected: []FileType{
   170  				FileTypeDockerfile,
   171  			},
   172  		},
   173  		{
   174  			name: "Dockerfile, reader",
   175  			path: "Dockerfile",
   176  			r:    strings.NewReader("FROM ubuntu\n"),
   177  			expected: []FileType{
   178  				FileTypeDockerfile,
   179  			},
   180  		},
   181  		{
   182  			name: "Dockerfile extension",
   183  			path: "lol.Dockerfile",
   184  			r:    nil,
   185  			expected: []FileType{
   186  				FileTypeDockerfile,
   187  			},
   188  		},
   189  		{
   190  			name: "kubernetes, no reader",
   191  			path: "k8s.yml",
   192  			r:    nil,
   193  			expected: []FileType{
   194  				FileTypeYAML,
   195  			},
   196  		},
   197  		{
   198  			name: "kubernetes, reader",
   199  			path: "k8s.yml",
   200  			r: strings.NewReader(`apiVersion: apps/v1
   201  kind: Deployment
   202  metadata:
   203    name: nginx-deployment
   204    labels:
   205      app: nginx
   206  spec:
   207    replicas: 3
   208    selector:
   209      matchLabels:
   210        app: nginx
   211    template:
   212      metadata:
   213        labels:
   214          app: nginx
   215      spec:
   216        containers:
   217        - name: nginx
   218          image: nginx:1.14.2
   219          ports:
   220          - containerPort: 80`),
   221  			expected: []FileType{
   222  				FileTypeKubernetes,
   223  				FileTypeYAML,
   224  			},
   225  		},
   226  		{
   227  			name: "kubernetes, reader, JSON",
   228  			path: "k8s.json",
   229  			r: strings.NewReader(`{
   230    "apiVersion": "apps/v1",
   231    "kind": "Deployment",
   232    "metadata": {
   233      "name": "nginx-deployment",
   234      "labels": {
   235        "app": "nginx"
   236      }
   237    },
   238    "spec": {
   239      "replicas": 3,
   240      "selector": {
   241        "matchLabels": {
   242          "app": "nginx"
   243        }
   244      },
   245      "template": {
   246        "metadata": {
   247          "labels": {
   248            "app": "nginx"
   249          }
   250        },
   251        "spec": {
   252          "containers": [
   253            {
   254              "name": "nginx",
   255              "image": "nginx:1.14.2",
   256              "ports": [
   257                {
   258                  "containerPort": 80
   259                }
   260              ]
   261            }
   262          ]
   263        }
   264      }
   265    }
   266  }`),
   267  			expected: []FileType{
   268  				FileTypeKubernetes,
   269  				FileTypeJSON,
   270  			},
   271  		},
   272  		{
   273  			name: "YAML, no reader",
   274  			path: "file.yaml",
   275  			r:    nil,
   276  			expected: []FileType{
   277  				FileTypeYAML,
   278  				FileTypeHelm,
   279  			},
   280  		},
   281  		{
   282  			name: "YML, no reader",
   283  			path: "file.yml",
   284  			r:    nil,
   285  			expected: []FileType{
   286  				FileTypeYAML,
   287  			},
   288  		},
   289  		{
   290  			name: "YML uppercase",
   291  			path: "file.YML",
   292  			r:    nil,
   293  			expected: []FileType{
   294  				FileTypeYAML,
   295  			},
   296  		},
   297  		{
   298  			name: "TOML, no reader",
   299  			path: "file.toml",
   300  			r:    nil,
   301  			expected: []FileType{
   302  				FileTypeTOML,
   303  			},
   304  		},
   305  		{
   306  			name: "JSON, no reader",
   307  			path: "file.json",
   308  			r:    nil,
   309  			expected: []FileType{
   310  				FileTypeJSON,
   311  			},
   312  		},
   313  		{
   314  			name: "kubernetes, configmap",
   315  			path: "k8s.yml",
   316  			r: strings.NewReader(`apiVersion: v1
   317  kind: ConfigMap
   318  metadata:
   319    name: test
   320    namespace: default
   321  data:
   322    AWS_ACCESS_KEY_ID: "XXX"
   323    AWS_SECRET_ACCESS_KEY: "XXX"`),
   324  			expected: []FileType{
   325  				FileTypeKubernetes,
   326  				FileTypeYAML,
   327  			},
   328  		},
   329  		{
   330  			name: "kubernetes, clusterRole",
   331  			path: "k8s.yml",
   332  			r: strings.NewReader(`apiVersion: rbac.authorization.k8s.io/v1
   333  kind: ClusterRole
   334  metadata:
   335    annotations:
   336      rbac.authorization.kubernetes.io/autoupdate: "true"
   337    labels:
   338      kubernetes.io/bootstrapping: rbac-defaults
   339      rbac.authorization.k8s.io/aggregate-to-edit: "true"
   340    name: view
   341  rules:
   342  - apiGroups:
   343    - networking.k8s.io
   344    resources:
   345    - ingresses
   346    - ingresses/status
   347    - networkpolicies
   348    verbs:
   349    - get
   350    - list
   351    - watch`),
   352  			expected: []FileType{
   353  				FileTypeKubernetes,
   354  				FileTypeYAML,
   355  			},
   356  		},
   357  	}
   358  
   359  	for _, test := range tests {
   360  		t.Run(test.name, func(t *testing.T) {
   361  			t.Run("GetTypes", func(t *testing.T) {
   362  				actualDetections := GetTypes(test.path, test.r)
   363  				assert.Equal(t, len(test.expected), len(actualDetections))
   364  				for _, expected := range test.expected {
   365  					resetReader(test.r)
   366  					var found bool
   367  					for _, actual := range actualDetections {
   368  						if actual == expected {
   369  							found = true
   370  							break
   371  						}
   372  					}
   373  					assert.True(t, found, "%s should be detected", expected)
   374  				}
   375  			})
   376  			for _, expected := range test.expected {
   377  				resetReader(test.r)
   378  				t.Run(fmt.Sprintf("IsType_%s", expected), func(t *testing.T) {
   379  					assert.True(t, IsType(test.path, test.r, expected))
   380  				})
   381  			}
   382  			t.Run("IsType_invalid", func(t *testing.T) {
   383  				resetReader(test.r)
   384  				assert.False(t, IsType(test.path, test.r, "invalid"))
   385  			})
   386  		})
   387  	}
   388  }
   389  
   390  func BenchmarkIsType_SmallFile(b *testing.B) {
   391  	data, err := os.ReadFile(fmt.Sprintf("./testdata/%s", "small.file"))
   392  	assert.Nil(b, err)
   393  
   394  	b.ReportAllocs()
   395  	b.ResetTimer()
   396  	for i := 0; i < b.N; i++ {
   397  		_ = IsType(fmt.Sprintf("./testdata/%s", "small.file"), bytes.NewReader(data), FileTypeAzureARM)
   398  	}
   399  }
   400  
   401  func BenchmarkIsType_BigFile(b *testing.B) {
   402  	data, err := os.ReadFile(fmt.Sprintf("./testdata/%s", "big.file"))
   403  	assert.Nil(b, err)
   404  
   405  	b.ReportAllocs()
   406  	b.ResetTimer()
   407  	for i := 0; i < b.N; i++ {
   408  		_ = IsType(fmt.Sprintf("./testdata/%s", "big.file"), bytes.NewReader(data), FileTypeAzureARM)
   409  	}
   410  }