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", &parametersMetadata)
    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  // }