github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/scanners/cloud/aws/scanner_test.go (about) 1 package aws 2 3 import ( 4 "context" 5 "io/fs" 6 "testing" 7 8 "github.com/khulnasoft-lab/defsec/pkg/providers/azure" 9 "github.com/khulnasoft-lab/defsec/pkg/providers/azure/authorization" 10 11 "github.com/khulnasoft-lab/defsec/pkg/providers/aws/iam" 12 13 "github.com/khulnasoft-lab/defsec/pkg/framework" 14 "github.com/khulnasoft-lab/defsec/pkg/providers/aws" 15 "github.com/khulnasoft-lab/defsec/pkg/providers/aws/rds" 16 "github.com/khulnasoft-lab/defsec/pkg/scanners/options" 17 "github.com/khulnasoft-lab/defsec/pkg/state" 18 defsecTypes "github.com/khulnasoft-lab/defsec/pkg/types" 19 "github.com/khulnasoft-lab/defsec/test/testutil" 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 ) 23 24 func TestScanner_GetRegisteredRules(t *testing.T) { 25 testCases := []struct { 26 name string 27 scanner *Scanner 28 }{ 29 { 30 name: "get framework rules", 31 scanner: &Scanner{ 32 frameworks: []framework.Framework{framework.CIS_AWS_1_2}, 33 }, 34 }, 35 { 36 name: "get spec rules", 37 scanner: &Scanner{ 38 spec: "awscis1.2", 39 }, 40 }, 41 { 42 name: "invalid spec", 43 scanner: &Scanner{ 44 spec: "invalid spec", 45 // we still expect default rules to work 46 }, 47 }, 48 } 49 50 for _, tc := range testCases { 51 t.Run(tc.name, func(t *testing.T) { 52 for _, i := range tc.scanner.getRegisteredRules() { 53 if _, ok := i.Rule().Frameworks[framework.CIS_AWS_1_2]; !ok { 54 assert.FailNow(t, "unexpected rule found: ", i.Rule().AVDID, tc.name) 55 } 56 } 57 }) 58 } 59 } 60 61 func Test_AWSInputSelectors(t *testing.T) { 62 testCases := []struct { 63 name string 64 srcFS fs.FS 65 dataFS fs.FS 66 state state.State 67 expectedResults struct { 68 totalResults int 69 summaries []string 70 } 71 }{ 72 { 73 name: "selector is not defined", 74 srcFS: testutil.CreateFS(t, map[string]string{ 75 "policies/rds_policy.rego": `# METADATA 76 # title: "RDS Publicly Accessible" 77 # custom: 78 # input: 79 package builtin.aws.rds.aws0999 80 81 deny[res] { 82 res := true 83 } 84 `, 85 "policies/cloudtrail_policy.rego": `# METADATA 86 # title: "CloudTrail Bucket Delete Policy" 87 # custom: 88 # input: 89 package builtin.aws.cloudtrail.aws0888 90 91 deny[res] { 92 res := true 93 } 94 `, 95 }), 96 state: state.State{AWS: aws.AWS{ 97 RDS: rds.RDS{ 98 Instances: []rds.Instance{ 99 {Metadata: defsecTypes.Metadata{}, 100 PublicAccess: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 101 }, 102 }, 103 }, 104 // note: there is no CloudTrail resource in our AWS state (so we expect no results for it) 105 }}, 106 expectedResults: struct { 107 totalResults int 108 summaries []string 109 }{totalResults: 2, summaries: []string{"RDS Publicly Accessible", "CloudTrail Bucket Delete Policy"}}, 110 }, 111 { 112 name: "selector is empty", 113 srcFS: testutil.CreateFS(t, map[string]string{ 114 "policies/rds_policy.rego": `# METADATA 115 # title: "RDS Publicly Accessible" 116 # custom: 117 # input: 118 # selector: 119 120 package builtin.aws.rds.aws0999 121 122 deny[res] { 123 res := true 124 } 125 `, 126 "policies/cloudtrail_policy.rego": `# METADATA 127 # title: "CloudTrail Bucket Delete Policy" 128 # custom: 129 # input: 130 # selector: 131 package builtin.aws.cloudtrail.aws0888 132 133 deny[res] { 134 res := true 135 } 136 `, 137 }), 138 state: state.State{AWS: aws.AWS{ 139 RDS: rds.RDS{ 140 Instances: []rds.Instance{ 141 {Metadata: defsecTypes.Metadata{}, 142 PublicAccess: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 143 }, 144 }, 145 }, 146 }}, 147 expectedResults: struct { 148 totalResults int 149 summaries []string 150 }{totalResults: 2, summaries: []string{"RDS Publicly Accessible", "CloudTrail Bucket Delete Policy"}}, 151 }, 152 { 153 name: "selector without subtype", 154 srcFS: testutil.CreateFS(t, map[string]string{ 155 "policies/rds_policy.rego": `# METADATA 156 # title: "RDS Publicly Accessible" 157 # custom: 158 # input: 159 # selector: 160 # - type: cloud 161 package builtin.aws.rds.aws0999 162 163 deny[res] { 164 res := true 165 } 166 `, 167 "policies/cloudtrail_policy.rego": `# METADATA 168 # title: "CloudTrail Bucket Delete Policy" 169 # custom: 170 # input: 171 # selector: 172 # - type: cloud 173 package builtin.aws.cloudtrail.aws0888 174 175 deny[res] { 176 res := true 177 } 178 `, 179 }), 180 state: state.State{AWS: aws.AWS{ 181 RDS: rds.RDS{ 182 Instances: []rds.Instance{ 183 {Metadata: defsecTypes.Metadata{}, 184 PublicAccess: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 185 }, 186 }, 187 }, 188 // note: there is no CloudTrail resource in our AWS state (so we expect no results for it) 189 }}, 190 expectedResults: struct { 191 totalResults int 192 summaries []string 193 }{totalResults: 2, summaries: []string{"RDS Publicly Accessible", "CloudTrail Bucket Delete Policy"}}, 194 }, 195 { 196 name: "conflicting selectors", 197 srcFS: testutil.CreateFS(t, map[string]string{ 198 "policies/rds_policy.rego": `# METADATA 199 # title: "RDS Publicly Accessible" 200 # custom: 201 # provider: aws 202 # service: rds 203 # input: 204 # selector: 205 # - type: cloud 206 # subtypes: 207 # - provider: aws 208 # service: ec2 209 package builtin.aws.rds.aws0999 210 211 deny[res] { 212 res := true 213 } 214 `, 215 }), 216 217 state: state.State{AWS: aws.AWS{ 218 RDS: rds.RDS{ 219 Instances: []rds.Instance{ 220 {Metadata: defsecTypes.Metadata{}, 221 PublicAccess: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 222 }, 223 }, 224 }, 225 }}, 226 expectedResults: struct { 227 totalResults int 228 summaries []string 229 }{totalResults: 0}, 230 }, 231 { 232 name: "selector is defined with empty subtype", 233 srcFS: testutil.CreateFS(t, map[string]string{ 234 "policies/rds_policy.rego": `# METADATA 235 # title: "RDS Publicly Accessible" 236 # custom: 237 # input: 238 # selector: 239 # - type: cloud 240 # subtypes: 241 242 package builtin.aws.rds.aws0999 243 244 deny[res] { 245 res := true 246 } 247 `, 248 "policies/cloudtrail_policy.rego": `# METADATA 249 # title: "CloudTrail Bucket Delete Policy" 250 # custom: 251 # input: 252 # selector: 253 # - type: cloud 254 # subtypes: 255 package builtin.aws.cloudtrail.aws0888 256 257 deny[res] { 258 res := true 259 } 260 `, 261 }), 262 state: state.State{AWS: aws.AWS{ 263 RDS: rds.RDS{ 264 Instances: []rds.Instance{ 265 {Metadata: defsecTypes.Metadata{}, 266 PublicAccess: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 267 }, 268 }, 269 }, 270 // note: there is no CloudTrail resource in our AWS state (so we expect no results for it) 271 }}, 272 expectedResults: struct { 273 totalResults int 274 summaries []string 275 }{totalResults: 2, summaries: []string{"RDS Publicly Accessible", "CloudTrail Bucket Delete Policy"}}, 276 }, 277 { 278 name: "single cloud, single selector", 279 srcFS: testutil.CreateFS(t, map[string]string{ 280 "policies/rds_policy.rego": `# METADATA 281 # title: "RDS Publicly Accessible" 282 # description: "Ensures RDS instances are not launched into the public cloud." 283 # scope: package 284 # schemas: 285 # - input: schema.input 286 # related_resources: 287 # - http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_VPC.html 288 # custom: 289 # avd_id: AVD-AWS-0999 290 # provider: aws 291 # service: rds 292 # severity: HIGH 293 # short_code: enable-public-access 294 # recommended_action: "Remove the public endpoint from the RDS instance'" 295 # input: 296 # selector: 297 # - type: cloud 298 # subtypes: 299 # - provider: aws 300 # service: rds 301 package builtin.aws.rds.aws0999 302 303 deny[res] { 304 instance := input.aws.rds.instances[_] 305 instance.publicaccess.value 306 res := result.new("Instance has Public Access enabled", instance.publicaccess) 307 } 308 `, 309 "policies/cloudtrail_policy.rego": `# METADATA 310 # title: "CloudTrail Bucket Delete Policy" 311 # description: "Ensures CloudTrail logging bucket has a policy to prevent deletion of logs without an MFA token" 312 # scope: package 313 # schemas: 314 # - input: schema.input 315 # related_resources: 316 # - http://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html#MultiFactorAuthenticationDelete 317 # custom: 318 # avd_id: AVD-AWS-0888 319 # provider: aws 320 # service: cloudtrail 321 # severity: HIGH 322 # short_code: bucket_delete 323 # recommended_action: "Enable MFA delete on the CloudTrail bucket" 324 # input: 325 # selector: 326 # - type: cloud 327 # subtypes: 328 # - provider: aws 329 # service: cloudtrail 330 package builtin.aws.cloudtrail.aws0888 331 332 deny[res] { 333 trail := input.aws.cloudtrail.trails[_] 334 trail.bucketname.value != "" 335 bucket := input.aws.s3.buckets[_] 336 bucket.name.value == trail.bucketname.value 337 not bucket.versioning.mfadelete.value 338 res := result.new("Bucket has MFA delete disabled", bucket.name) 339 } 340 `, 341 }), 342 state: state.State{AWS: aws.AWS{ 343 RDS: rds.RDS{ 344 Instances: []rds.Instance{ 345 {Metadata: defsecTypes.Metadata{}, 346 PublicAccess: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()), 347 }, 348 }, 349 }, 350 // note: there is no CloudTrail resource in our AWS state (so we expect no results for it) 351 }}, 352 expectedResults: struct { 353 totalResults int 354 summaries []string 355 }{totalResults: 1, summaries: []string{"RDS Publicly Accessible"}}, 356 }, 357 { 358 name: "multi cloud, single selector, same named service", 359 srcFS: testutil.CreateFS(t, map[string]string{ 360 "policies/azure_iam_policy.rego": `# METADATA 361 # title: "Azure IAM Policy" 362 # custom: 363 # input: 364 # selector: 365 # - type: cloud 366 # subtypes: 367 # - provider: azure 368 # service: iam 369 package builtin.azure.iam.iam1234 370 371 deny[res] { 372 res := true 373 } 374 `, 375 "policies/aws_iam_policy.rego": `# METADATA 376 # title: "AWS IAM Policy" 377 # custom: 378 # input: 379 # selector: 380 # - type: cloud 381 # subtypes: 382 # - provider: aws 383 # service: iam 384 package builtin.aws.iam.iam5678 385 386 deny[res] { 387 res := true 388 } 389 `, 390 }), 391 state: state.State{ 392 AWS: aws.AWS{ 393 IAM: iam.IAM{ 394 PasswordPolicy: iam.PasswordPolicy{ 395 MinimumLength: defsecTypes.Int(1, defsecTypes.NewTestMetadata()), 396 }}, 397 }, 398 Azure: azure.Azure{ 399 Authorization: authorization.Authorization{ 400 RoleDefinitions: []authorization.RoleDefinition{{ 401 Metadata: defsecTypes.NewTestMetadata(), 402 Permissions: []authorization.Permission{ 403 { 404 Metadata: defsecTypes.NewTestMetadata(), 405 Actions: []defsecTypes.StringValue{ 406 defsecTypes.String("*", defsecTypes.NewTestMetadata()), 407 }, 408 }, 409 }, 410 AssignableScopes: []defsecTypes.StringValue{ 411 defsecTypes.StringUnresolvable(defsecTypes.NewTestMetadata()), 412 }}, 413 }}, 414 }, 415 // note: there is no Azure IAM in our cloud state (so we expect no results for it) 416 }, 417 expectedResults: struct { 418 totalResults int 419 summaries []string 420 }{totalResults: 1, summaries: []string{"AWS IAM Policy"}}, 421 }, 422 { 423 name: "single cloud, single selector with config data", 424 srcFS: testutil.CreateFS(t, map[string]string{ 425 "policies/rds_policy.rego": `# METADATA 426 # title: "RDS Publicly Accessible" 427 # description: "Ensures RDS instances are not launched into the public cloud." 428 # scope: package 429 # schemas: 430 # - input: schema.input 431 # related_resources: 432 # - http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_VPC.html 433 # custom: 434 # avd_id: AVD-AWS-0999 435 # provider: aws 436 # service: rds 437 # severity: HIGH 438 # short_code: enable-public-access 439 # recommended_action: "Remove the public endpoint from the RDS instance'" 440 # input: 441 # selector: 442 # - type: cloud 443 # subtypes: 444 # - provider: aws 445 # service: rds 446 package builtin.aws.rds.aws0999 447 import data.settings.DS0999.ignore_deletion_protection 448 deny[res] { 449 instance := input.aws.rds.instances[_] 450 instance.publicaccess.value 451 not ignore_deletion_protection 452 res := result.new("Instance has Public Access enabled", instance.publicaccess) 453 } 454 `, 455 "policies/rds_cmk_encryption.rego": `# METADATA 456 # title: "RDS CMK Encryption" 457 # description: "Ensures RDS instances are encrypted with CMK." 458 # scope: package 459 # schemas: 460 # - input: schema.input 461 # related_resources: 462 # - http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_VPC.html 463 # custom: 464 # avd_id: AVD-AWS-0998 465 # provider: aws 466 # service: rds 467 # severity: HIGH 468 # short_code: rds_cmk_encryption 469 # recommended_action: "CMK Encrypt RDS instance'" 470 # input: 471 # selector: 472 # - type: cloud 473 # subtypes: 474 # - provider: aws 475 # service: rds 476 package builtin.aws.rds.aws0998 477 import data.settings.DS0998.rds_desired_encryption_level 478 deny[res] { 479 instance := input.aws.rds.instances[_] 480 rds_desired_encryption_level <= 2 481 res := result.new("Instance is not CMK encrypted", instance.publicaccess) 482 } 483 `, 484 }), 485 dataFS: testutil.CreateFS(t, map[string]string{ 486 "config-data/data.json": `{ 487 "settings": { 488 "DS0999": { 489 "ignore_deletion_protection": false 490 }, 491 "DS0998": { 492 "rds_desired_encryption_level": 2 493 } 494 } 495 } 496 `, 497 }), 498 state: state.State{AWS: aws.AWS{ 499 RDS: rds.RDS{ 500 Instances: []rds.Instance{ 501 {Metadata: defsecTypes.Metadata{}, 502 PublicAccess: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()), 503 }, 504 }, 505 }, 506 }}, 507 expectedResults: struct { 508 totalResults int 509 summaries []string 510 }{totalResults: 2, summaries: []string{"RDS Publicly Accessible", "RDS CMK Encryption"}}, 511 }, 512 } 513 514 for _, tc := range testCases { 515 t.Run(tc.name, func(t *testing.T) { 516 var scannerOpts []options.ScannerOption 517 if tc.dataFS != nil { 518 scannerOpts = append(scannerOpts, options.ScannerWithPolicyDirs("config-data")) 519 } 520 scannerOpts = append(scannerOpts, options.ScannerWithEmbeddedPolicies(false)) 521 scannerOpts = append(scannerOpts, options.ScannerWithPolicyFilesystem(tc.srcFS)) 522 scannerOpts = append(scannerOpts, options.ScannerWithRegoOnly(true)) 523 scannerOpts = append(scannerOpts, options.ScannerWithPolicyDirs("policies/")) 524 scanner := New(scannerOpts...) 525 526 results, err := scanner.Scan(context.TODO(), &tc.state) 527 require.NoError(t, err, tc.name) 528 require.Equal(t, tc.expectedResults.totalResults, len(results), tc.name) 529 for i := range results.GetFailed() { 530 require.Contains(t, tc.expectedResults.summaries, results.GetFailed()[i].Rule().Summary, tc.name) 531 } 532 }) 533 } 534 }