github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/scanners/azure/arm/parser/parser_test.go (about) 1 package parser 2 3 import ( 4 "context" 5 "io/fs" 6 "os" 7 "testing" 8 9 "github.com/khulnasoft-lab/defsec/pkg/scanners/azure" 10 "github.com/khulnasoft-lab/defsec/pkg/scanners/azure/resolver" 11 "github.com/khulnasoft-lab/defsec/pkg/scanners/options" 12 13 "github.com/stretchr/testify/assert" 14 15 "github.com/liamg/memoryfs" 16 17 "github.com/khulnasoft-lab/defsec/pkg/types" 18 19 "github.com/stretchr/testify/require" 20 ) 21 22 func createMetadata(targetFS fs.FS, filename string, start, end int, ref string, parent *types.Metadata) types.Metadata { 23 child := types.NewMetadata(types.NewRange(filename, start, end, "", targetFS), ref) 24 if parent != nil { 25 child.SetParentPtr(parent) 26 } 27 return child 28 } 29 30 func TestParser_Parse(t *testing.T) { 31 32 filename := "example.json" 33 34 targetFS := memoryfs.New() 35 36 tests := []struct { 37 name string 38 input string 39 want func() azure.Deployment 40 wantDeployment bool 41 }{ 42 { 43 name: "invalid code", 44 input: `blah`, 45 wantDeployment: false, 46 }, 47 { 48 name: "basic param", 49 input: `{ 50 "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", // another one 51 "contentVersion": "1.0.0.0", 52 "parameters": { 53 "storagePrefix": { 54 "type": "string", 55 "defaultValue": "x", 56 "maxLength": 11, 57 "minLength": 3 58 } 59 }, 60 "resources": [] 61 }`, 62 want: func() azure.Deployment { 63 64 root := createMetadata(targetFS, filename, 0, 0, "", nil).WithInternal(resolver.NewResolver()) 65 metadata := createMetadata(targetFS, filename, 1, 13, "", &root) 66 parametersMetadata := createMetadata(targetFS, filename, 4, 11, "parameters", &metadata) 67 storageMetadata := createMetadata(targetFS, filename, 5, 10, "parameters.storagePrefix", ¶metersMetadata) 68 69 return azure.Deployment{ 70 Metadata: metadata, 71 TargetScope: azure.ScopeResourceGroup, 72 Parameters: []azure.Parameter{ 73 { 74 Variable: azure.Variable{ 75 Name: "storagePrefix", 76 Value: azure.NewValue("x", createMetadata(targetFS, filename, 7, 7, "parameters.storagePrefix.defaultValue", &storageMetadata)), 77 }, 78 Default: azure.NewValue("x", createMetadata(targetFS, filename, 7, 7, "parameters.storagePrefix.defaultValue", &storageMetadata)), 79 Decorators: nil, 80 }, 81 }, 82 } 83 }, 84 wantDeployment: true, 85 }, 86 { 87 name: "storageAccount", 88 input: `{ 89 "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", // another one 90 "contentVersion": "1.0.0.0", 91 "parameters": {}, 92 "resources": [ 93 { 94 "type": "Microsoft.Storage/storageAccounts", 95 "apiVersion": "2022-05-01", 96 "name": "myResource", 97 "location": "string", 98 "tags": { 99 "tagName1": "tagValue1", 100 "tagName2": "tagValue2" 101 }, 102 "sku": { 103 "name": "string" 104 }, 105 "kind": "string", 106 "extendedLocation": { 107 "name": "string", 108 "type": "EdgeZone" 109 }, 110 "identity": { 111 "type": "string", 112 "userAssignedIdentities": {} 113 }, 114 "properties": { 115 "allowSharedKeyAccess":false, 116 "customDomain": { 117 "name": "string", 118 "useSubDomainName":false, 119 "number": 123 120 }, 121 "networkAcls": [ 122 { 123 "bypass": "AzureServices1" 124 }, 125 { 126 "bypass": "AzureServices2" 127 } 128 ] 129 } 130 } 131 ] 132 }`, 133 want: func() azure.Deployment { 134 135 rootMetadata := createMetadata(targetFS, filename, 0, 0, "", nil).WithInternal(resolver.NewResolver()) 136 fileMetadata := createMetadata(targetFS, filename, 1, 45, "", &rootMetadata) 137 resourcesMetadata := createMetadata(targetFS, filename, 5, 44, "resources", &fileMetadata) 138 139 resourceMetadata := createMetadata(targetFS, filename, 6, 43, "resources[0]", &resourcesMetadata) 140 141 propertiesMetadata := createMetadata(targetFS, filename, 27, 42, "resources[0].properties", &resourceMetadata) 142 143 customDomainMetadata := createMetadata(targetFS, filename, 29, 33, "resources[0].properties.customDomain", &propertiesMetadata) 144 networkACLListMetadata := createMetadata(targetFS, filename, 34, 41, "resources[0].properties.networkAcls", &propertiesMetadata) 145 146 networkACL0Metadata := createMetadata(targetFS, filename, 35, 37, "resources[0].properties.networkAcls[0]", &networkACLListMetadata) 147 networkACL1Metadata := createMetadata(targetFS, filename, 38, 40, "resources[0].properties.networkAcls[1]", &networkACLListMetadata) 148 149 return azure.Deployment{ 150 Metadata: fileMetadata, 151 TargetScope: azure.ScopeResourceGroup, 152 Resources: []azure.Resource{ 153 { 154 Metadata: resourceMetadata, 155 APIVersion: azure.NewValue( 156 "2022-05-01", 157 createMetadata(targetFS, filename, 8, 8, "resources[0].apiVersion", &resourceMetadata), 158 ), 159 Type: azure.NewValue( 160 "Microsoft.Storage/storageAccounts", 161 createMetadata(targetFS, filename, 7, 7, "resources[0].type", &resourceMetadata), 162 ), 163 Kind: azure.NewValue( 164 "string", 165 createMetadata(targetFS, filename, 18, 18, "resources[0].kind", &resourceMetadata), 166 ), 167 Name: azure.NewValue( 168 "myResource", 169 createMetadata(targetFS, filename, 9, 9, "resources[0].name", &resourceMetadata), 170 ), 171 Location: azure.NewValue( 172 "string", 173 createMetadata(targetFS, filename, 10, 10, "resources[0].location", &resourceMetadata), 174 ), 175 Properties: azure.NewValue( 176 map[string]azure.Value{ 177 "allowSharedKeyAccess": azure.NewValue(false, createMetadata(targetFS, filename, 28, 28, "resources[0].properties.allowSharedKeyAccess", &propertiesMetadata)), 178 "customDomain": azure.NewValue( 179 map[string]azure.Value{ 180 "name": azure.NewValue("string", createMetadata(targetFS, filename, 30, 30, "resources[0].properties.customDomain.name", &customDomainMetadata)), 181 "useSubDomainName": azure.NewValue(false, createMetadata(targetFS, filename, 31, 31, "resources[0].properties.customDomain.useSubDomainName", &customDomainMetadata)), 182 "number": azure.NewValue(int64(123), createMetadata(targetFS, filename, 32, 32, "resources[0].properties.customDomain.number", &customDomainMetadata)), 183 }, customDomainMetadata), 184 "networkAcls": azure.NewValue( 185 []azure.Value{ 186 azure.NewValue( 187 map[string]azure.Value{ 188 "bypass": azure.NewValue("AzureServices1", createMetadata(targetFS, filename, 36, 36, "resources[0].properties.networkAcls[0].bypass", &networkACL0Metadata)), 189 }, 190 networkACL0Metadata, 191 ), 192 azure.NewValue( 193 map[string]azure.Value{ 194 "bypass": azure.NewValue("AzureServices2", createMetadata(targetFS, filename, 39, 39, "resources[0].properties.networkAcls[1].bypass", &networkACL1Metadata)), 195 }, 196 networkACL1Metadata, 197 ), 198 }, networkACLListMetadata), 199 }, 200 propertiesMetadata, 201 ), 202 }, 203 }, 204 } 205 }, 206 207 wantDeployment: true, 208 }, 209 } 210 211 for _, tt := range tests { 212 t.Run(tt.name, func(t *testing.T) { 213 214 require.NoError(t, targetFS.WriteFile(filename, []byte(tt.input), 0644)) 215 216 p := New(targetFS, options.ParserWithDebug(os.Stderr)) 217 got, err := p.ParseFS(context.Background(), ".") 218 require.NoError(t, err) 219 220 if !tt.wantDeployment { 221 assert.Len(t, got, 0) 222 return 223 } 224 225 require.Len(t, got, 1) 226 want := tt.want() 227 g := got[0] 228 229 require.Equal(t, want, g) 230 }) 231 } 232 } 233 234 func Test_NestedResourceParsing(t *testing.T) { 235 236 input := ` 237 { 238 "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 239 "contentVersion": "1.0.0.0", 240 "parameters": { 241 "environment": { 242 "type": "string", 243 "allowedValues": [ 244 "dev", 245 "test", 246 "prod" 247 ] 248 }, 249 "location": { 250 "type": "string", 251 "defaultValue": "[resourceGroup().location]", 252 "metadata": { 253 "description": "Location for all resources." 254 } 255 }, 256 "storageAccountSkuName": { 257 "type": "string", 258 "defaultValue": "Standard_LRS" 259 }, 260 "storageAccountSkuTier": { 261 "type": "string", 262 "defaultValue": "Standard" 263 } 264 }, 265 "variables": { 266 "uniquePart": "[take(uniqueString(resourceGroup().id), 4)]", 267 "storageAccountName": "[concat('mystorageaccount', variables('uniquePart'), parameters('environment'))]", 268 "queueName": "myqueue" 269 }, 270 "resources": [ 271 { 272 "type": "Microsoft.Storage/storageAccounts", 273 "name": "[variables('storageAccountName')]", 274 "location": "[parameters('location')]", 275 "apiVersion": "2019-06-01", 276 "sku": { 277 "name": "[parameters('storageAccountSkuName')]", 278 "tier": "[parameters('storageAccountSkuTier')]" 279 }, 280 "kind": "StorageV2", 281 "properties": {}, 282 "resources": [ 283 { 284 "name": "[concat('default/', variables('queueName'))]", 285 "type": "queueServices/queues", 286 "apiVersion": "2019-06-01", 287 "dependsOn": [ 288 "[variables('storageAccountName')]" 289 ], 290 "properties": { 291 "metadata": {} 292 } 293 } 294 ] 295 } 296 ] 297 } 298 ` 299 300 targetFS := memoryfs.New() 301 302 require.NoError(t, targetFS.WriteFile("nested.json", []byte(input), 0644)) 303 304 p := New(targetFS, options.ParserWithDebug(os.Stderr)) 305 got, err := p.ParseFS(context.Background(), ".") 306 require.NoError(t, err) 307 require.Len(t, got, 1) 308 309 deployment := got[0] 310 311 require.Len(t, deployment.Resources, 1) 312 313 storageAccountResource := deployment.Resources[0] 314 315 require.Len(t, storageAccountResource.Resources, 1) 316 317 queue := storageAccountResource.Resources[0] 318 319 assert.Equal(t, "queueServices/queues", queue.Type.AsString()) 320 } 321 322 // 323 // func Test_JsonFile(t *testing.T) { 324 // 325 // input, err := os.ReadFile("testdata/postgres.json") 326 // require.NoError(t, err) 327 // 328 // targetFS := memoryfs.New() 329 // 330 // require.NoError(t, targetFS.WriteFile("postgres.json", input, 0644)) 331 // 332 // p := New(targetFS, options.ParserWithDebug(os.Stderr)) 333 // got, err := p.ParseFS(context.Background(), ".") 334 // require.NoError(t, err) 335 // 336 // got[0].Resources[3].Name.Resolve() 337 // 338 // name := got[0].Resources[3].Name.AsString() 339 // assert.Equal(t, "myserver", name) 340 // 341 // }