github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/cloud/aws/commands/run_test.go (about)

     1  package commands
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"github.com/devseccon/trivy/pkg/clock"
     7  	"os"
     8  	"path/filepath"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	defsecTypes "github.com/aquasecurity/defsec/pkg/types"
    16  	dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
    17  	"github.com/devseccon/trivy/pkg/compliance/spec"
    18  	"github.com/devseccon/trivy/pkg/flag"
    19  )
    20  
    21  const expectedS3ScanResult = `{
    22    "CreatedAt": "2021-08-25T12:20:30.000000005Z",
    23    "ArtifactName": "12345678",
    24    "ArtifactType": "aws_account",
    25    "Metadata": {
    26      "ImageConfig": {
    27        "architecture": "",
    28        "created": "0001-01-01T00:00:00Z",
    29        "os": "",
    30        "rootfs": {
    31          "type": "",
    32          "diff_ids": null
    33        },
    34        "config": {}
    35      }
    36    },
    37    "Results": [
    38      {
    39        "Target": "arn:aws:s3:::examplebucket",
    40        "Class": "config",
    41        "Type": "cloud",
    42        "MisconfSummary": {
    43          "Successes": 1,
    44          "Failures": 8,
    45          "Exceptions": 0
    46        },
    47        "Misconfigurations": [
    48          {
    49            "Type": "AWS",
    50            "ID": "AVD-AWS-0086",
    51            "AVDID": "AVD-AWS-0086",
    52            "Title": "S3 Access block should block public ACL",
    53            "Description": "S3 buckets should block public ACLs on buckets and any objects they contain. By blocking, PUTs with fail if the object has any public ACL a.",
    54            "Message": "No public access block so not blocking public acls",
    55            "Resolution": "Enable blocking any PUT calls with a public ACL specified",
    56            "Severity": "HIGH",
    57            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0086",
    58            "References": [
    59              "https://avd.aquasec.com/misconfig/avd-aws-0086"
    60            ],
    61            "Status": "FAIL",
    62            "Layer": {},
    63            "CauseMetadata": {
    64              "Resource": "arn:aws:s3:::examplebucket",
    65              "Provider": "aws",
    66              "Service": "s3",
    67              "Code": {
    68                "Lines": null
    69              }
    70            }
    71          },
    72          {
    73            "Type": "AWS",
    74            "ID": "AVD-AWS-0087",
    75            "AVDID": "AVD-AWS-0087",
    76            "Title": "S3 Access block should block public policy",
    77            "Description": "S3 bucket policy should have block public policy to prevent users from putting a policy that enable public access.",
    78            "Message": "No public access block so not blocking public policies",
    79            "Resolution": "Prevent policies that allow public access being PUT",
    80            "Severity": "HIGH",
    81            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0087",
    82            "References": [
    83              "https://avd.aquasec.com/misconfig/avd-aws-0087"
    84            ],
    85            "Status": "FAIL",
    86            "Layer": {},
    87            "CauseMetadata": {
    88              "Resource": "arn:aws:s3:::examplebucket",
    89              "Provider": "aws",
    90              "Service": "s3",
    91              "Code": {
    92                "Lines": null
    93              }
    94            }
    95          },
    96          {
    97            "Type": "AWS",
    98            "ID": "AVD-AWS-0088",
    99            "AVDID": "AVD-AWS-0088",
   100            "Title": "Unencrypted S3 bucket.",
   101            "Description": "S3 Buckets should be encrypted to protect the data that is stored within them if access is compromised.",
   102            "Message": "Bucket does not have encryption enabled",
   103            "Resolution": "Configure bucket encryption",
   104            "Severity": "HIGH",
   105            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0088",
   106            "References": [
   107              "https://avd.aquasec.com/misconfig/avd-aws-0088"
   108            ],
   109            "Status": "FAIL",
   110            "Layer": {},
   111            "CauseMetadata": {
   112              "Resource": "arn:aws:s3:::examplebucket",
   113              "Provider": "aws",
   114              "Service": "s3",
   115              "Code": {
   116                "Lines": null
   117              }
   118            }
   119          },
   120          {
   121            "Type": "AWS",
   122            "ID": "AVD-AWS-0090",
   123            "AVDID": "AVD-AWS-0090",
   124            "Title": "S3 Data should be versioned",
   125            "Description": "Versioning in Amazon S3 is a means of keeping multiple variants of an object in the same bucket. \nYou can use the S3 Versioning feature to preserve, retrieve, and restore every version of every object stored in your buckets. \nWith versioning you can recover more easily from both unintended user actions and application failures.",
   126            "Message": "Bucket does not have versioning enabled",
   127            "Resolution": "Enable versioning to protect against accidental/malicious removal or modification",
   128            "Severity": "MEDIUM",
   129            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0090",
   130            "References": [
   131              "https://avd.aquasec.com/misconfig/avd-aws-0090"
   132            ],
   133            "Status": "FAIL",
   134            "Layer": {},
   135            "CauseMetadata": {
   136              "Resource": "arn:aws:s3:::examplebucket",
   137              "Provider": "aws",
   138              "Service": "s3",
   139              "Code": {
   140                "Lines": null
   141              }
   142            }
   143          },
   144          {
   145            "Type": "AWS",
   146            "ID": "AVD-AWS-0132",
   147            "AVDID": "AVD-AWS-0132",
   148            "Title": "S3 encryption should use Customer Managed Keys",
   149            "Description": "Encryption using AWS keys provides protection for your S3 buckets. To increase control of the encryption and manage factors like rotation use customer managed keys.",
   150            "Message": "Bucket does not encrypt data with a customer managed key.",
   151            "Resolution": "Enable encryption using customer managed keys",
   152            "Severity": "HIGH",
   153            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0132",
   154            "References": [
   155              "https://avd.aquasec.com/misconfig/avd-aws-0132"
   156            ],
   157            "Status": "FAIL",
   158            "Layer": {},
   159            "CauseMetadata": {
   160              "Resource": "arn:aws:s3:::examplebucket",
   161              "Provider": "aws",
   162              "Service": "s3",
   163              "Code": {
   164                "Lines": null
   165              }
   166            }
   167          },
   168          {
   169            "Type": "AWS",
   170            "ID": "AVD-AWS-0091",
   171            "AVDID": "AVD-AWS-0091",
   172            "Title": "S3 Access Block should Ignore Public Acl",
   173            "Description": "S3 buckets should ignore public ACLs on buckets and any objects they contain. By ignoring rather than blocking, PUT calls with public ACLs will still be applied but the ACL will be ignored.",
   174            "Message": "No public access block so not ignoring public acls",
   175            "Resolution": "Enable ignoring the application of public ACLs in PUT calls",
   176            "Severity": "HIGH",
   177            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0091",
   178            "References": [
   179              "https://avd.aquasec.com/misconfig/avd-aws-0091"
   180            ],
   181            "Status": "FAIL",
   182            "Layer": {},
   183            "CauseMetadata": {
   184              "Resource": "arn:aws:s3:::examplebucket",
   185              "Provider": "aws",
   186              "Service": "s3",
   187              "Code": {
   188                "Lines": null
   189              }
   190            }
   191          },
   192          {
   193            "Type": "AWS",
   194            "ID": "AVD-AWS-0092",
   195            "AVDID": "AVD-AWS-0092",
   196            "Title": "S3 Buckets not publicly accessible through ACL.",
   197            "Description": "Buckets should not have ACLs that allow public access",
   198            "Resolution": "Don't use canned ACLs or switch to private acl",
   199            "Severity": "HIGH",
   200            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0092",
   201            "References": [
   202              "https://avd.aquasec.com/misconfig/avd-aws-0092"
   203            ],
   204            "Status": "PASS",
   205            "Layer": {},
   206            "CauseMetadata": {
   207              "Resource": "arn:aws:s3:::examplebucket",
   208              "Provider": "aws",
   209              "Service": "s3",
   210              "Code": {
   211                "Lines": null
   212              }
   213            }
   214          },
   215          {
   216            "Type": "AWS",
   217            "ID": "AVD-AWS-0093",
   218            "AVDID": "AVD-AWS-0093",
   219            "Title": "S3 Access block should restrict public bucket to limit access",
   220            "Description": "S3 buckets should restrict public policies for the bucket. By enabling, the restrict_public_buckets, only the bucket owner and AWS Services can access if it has a public policy.",
   221            "Message": "No public access block so not restricting public buckets",
   222            "Resolution": "Limit the access to public buckets to only the owner or AWS Services (eg; CloudFront)",
   223            "Severity": "HIGH",
   224            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0093",
   225            "References": [
   226              "https://avd.aquasec.com/misconfig/avd-aws-0093"
   227            ],
   228            "Status": "FAIL",
   229            "Layer": {},
   230            "CauseMetadata": {
   231              "Resource": "arn:aws:s3:::examplebucket",
   232              "Provider": "aws",
   233              "Service": "s3",
   234              "Code": {
   235                "Lines": null
   236              }
   237            }
   238          },
   239          {
   240            "Type": "AWS",
   241            "ID": "AVD-AWS-0094",
   242            "AVDID": "AVD-AWS-0094",
   243            "Title": "S3 buckets should each define an aws_s3_bucket_public_access_block",
   244            "Description": "The \"block public access\" settings in S3 override individual policies that apply to a given bucket, meaning that all public access can be controlled in one central types for that bucket. It is therefore good practice to define these settings for each bucket in order to clearly define the public access that can be allowed for it.",
   245            "Message": "Bucket does not have a corresponding public access block.",
   246            "Resolution": "Define a aws_s3_bucket_public_access_block for the given bucket to control public access policies",
   247            "Severity": "LOW",
   248            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0094",
   249            "References": [
   250              "https://avd.aquasec.com/misconfig/avd-aws-0094"
   251            ],
   252            "Status": "FAIL",
   253            "Layer": {},
   254            "CauseMetadata": {
   255              "Resource": "arn:aws:s3:::examplebucket",
   256              "Provider": "aws",
   257              "Service": "s3",
   258              "Code": {
   259                "Lines": null
   260              }
   261            }
   262          }
   263        ]
   264      }
   265    ]
   266  }
   267  `
   268  
   269  const expectedCustomScanResult = `{
   270    "CreatedAt": "2021-08-25T12:20:30.000000005Z",
   271    "ArtifactName": "12345678",
   272    "ArtifactType": "aws_account",
   273    "Metadata": {
   274      "ImageConfig": {
   275        "architecture": "",
   276        "created": "0001-01-01T00:00:00Z",
   277        "os": "",
   278        "rootfs": {
   279          "type": "",
   280          "diff_ids": null
   281        },
   282        "config": {}
   283      }
   284    },
   285    "Results": [
   286      {
   287        "Target": "",
   288        "Class": "config",
   289        "Type": "cloud",
   290        "MisconfSummary": {
   291          "Successes": 0,
   292          "Failures": 1,
   293          "Exceptions": 0
   294        },
   295        "Misconfigurations": [
   296          {
   297            "Type": "AWS",
   298            "Title": "Bad input data",
   299            "Description": "Just failing rule with input data",
   300            "Message": "Rego policy resulted in DENY",
   301            "Namespace": "user.whatever",
   302            "Query": "deny",
   303            "Severity": "LOW",
   304            "References": [
   305              ""
   306            ],
   307            "Status": "FAIL",
   308            "Layer": {},
   309            "CauseMetadata": {
   310              "Provider": "cloud",
   311              "Service": "s3",
   312              "Code": {
   313                "Lines": null
   314              }
   315            }
   316          }
   317        ]
   318      },
   319      {
   320        "Target": "arn:aws:s3:::examplebucket",
   321        "Class": "config",
   322        "Type": "cloud",
   323        "MisconfSummary": {
   324          "Successes": 1,
   325          "Failures": 8,
   326          "Exceptions": 0
   327        },
   328        "Misconfigurations": [
   329          {
   330            "Type": "AWS",
   331            "ID": "AVD-AWS-0086",
   332            "AVDID": "AVD-AWS-0086",
   333            "Title": "S3 Access block should block public ACL",
   334            "Description": "S3 buckets should block public ACLs on buckets and any objects they contain. By blocking, PUTs with fail if the object has any public ACL a.",
   335            "Message": "No public access block so not blocking public acls",
   336            "Resolution": "Enable blocking any PUT calls with a public ACL specified",
   337            "Severity": "HIGH",
   338            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0086",
   339            "References": [
   340              "https://avd.aquasec.com/misconfig/avd-aws-0086"
   341            ],
   342            "Status": "FAIL",
   343            "Layer": {},
   344            "CauseMetadata": {
   345              "Resource": "arn:aws:s3:::examplebucket",
   346              "Provider": "aws",
   347              "Service": "s3",
   348              "Code": {
   349                "Lines": null
   350              }
   351            }
   352          },
   353          {
   354            "Type": "AWS",
   355            "ID": "AVD-AWS-0087",
   356            "AVDID": "AVD-AWS-0087",
   357            "Title": "S3 Access block should block public policy",
   358            "Description": "S3 bucket policy should have block public policy to prevent users from putting a policy that enable public access.",
   359            "Message": "No public access block so not blocking public policies",
   360            "Resolution": "Prevent policies that allow public access being PUT",
   361            "Severity": "HIGH",
   362            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0087",
   363            "References": [
   364              "https://avd.aquasec.com/misconfig/avd-aws-0087"
   365            ],
   366            "Status": "FAIL",
   367            "Layer": {},
   368            "CauseMetadata": {
   369              "Resource": "arn:aws:s3:::examplebucket",
   370              "Provider": "aws",
   371              "Service": "s3",
   372              "Code": {
   373                "Lines": null
   374              }
   375            }
   376          },
   377          {
   378            "Type": "AWS",
   379            "ID": "AVD-AWS-0088",
   380            "AVDID": "AVD-AWS-0088",
   381            "Title": "Unencrypted S3 bucket.",
   382            "Description": "S3 Buckets should be encrypted to protect the data that is stored within them if access is compromised.",
   383            "Message": "Bucket does not have encryption enabled",
   384            "Resolution": "Configure bucket encryption",
   385            "Severity": "HIGH",
   386            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0088",
   387            "References": [
   388              "https://avd.aquasec.com/misconfig/avd-aws-0088"
   389            ],
   390            "Status": "FAIL",
   391            "Layer": {},
   392            "CauseMetadata": {
   393              "Resource": "arn:aws:s3:::examplebucket",
   394              "Provider": "aws",
   395              "Service": "s3",
   396              "Code": {
   397                "Lines": null
   398              }
   399            }
   400          },
   401          {
   402            "Type": "AWS",
   403            "ID": "AVD-AWS-0090",
   404            "AVDID": "AVD-AWS-0090",
   405            "Title": "S3 Data should be versioned",
   406            "Description": "Versioning in Amazon S3 is a means of keeping multiple variants of an object in the same bucket. \nYou can use the S3 Versioning feature to preserve, retrieve, and restore every version of every object stored in your buckets. \nWith versioning you can recover more easily from both unintended user actions and application failures.",
   407            "Message": "Bucket does not have versioning enabled",
   408            "Resolution": "Enable versioning to protect against accidental/malicious removal or modification",
   409            "Severity": "MEDIUM",
   410            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0090",
   411            "References": [
   412              "https://avd.aquasec.com/misconfig/avd-aws-0090"
   413            ],
   414            "Status": "FAIL",
   415            "Layer": {},
   416            "CauseMetadata": {
   417              "Resource": "arn:aws:s3:::examplebucket",
   418              "Provider": "aws",
   419              "Service": "s3",
   420              "Code": {
   421                "Lines": null
   422              }
   423            }
   424          },
   425          {
   426            "Type": "AWS",
   427            "ID": "AVD-AWS-0132",
   428            "AVDID": "AVD-AWS-0132",
   429            "Title": "S3 encryption should use Customer Managed Keys",
   430            "Description": "Encryption using AWS keys provides protection for your S3 buckets. To increase control of the encryption and manage factors like rotation use customer managed keys.",
   431            "Message": "Bucket does not encrypt data with a customer managed key.",
   432            "Resolution": "Enable encryption using customer managed keys",
   433            "Severity": "HIGH",
   434            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0132",
   435            "References": [
   436              "https://avd.aquasec.com/misconfig/avd-aws-0132"
   437            ],
   438            "Status": "FAIL",
   439            "Layer": {},
   440            "CauseMetadata": {
   441              "Resource": "arn:aws:s3:::examplebucket",
   442              "Provider": "aws",
   443              "Service": "s3",
   444              "Code": {
   445                "Lines": null
   446              }
   447            }
   448          },
   449          {
   450            "Type": "AWS",
   451            "ID": "AVD-AWS-0091",
   452            "AVDID": "AVD-AWS-0091",
   453            "Title": "S3 Access Block should Ignore Public Acl",
   454            "Description": "S3 buckets should ignore public ACLs on buckets and any objects they contain. By ignoring rather than blocking, PUT calls with public ACLs will still be applied but the ACL will be ignored.",
   455            "Message": "No public access block so not ignoring public acls",
   456            "Resolution": "Enable ignoring the application of public ACLs in PUT calls",
   457            "Severity": "HIGH",
   458            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0091",
   459            "References": [
   460              "https://avd.aquasec.com/misconfig/avd-aws-0091"
   461            ],
   462            "Status": "FAIL",
   463            "Layer": {},
   464            "CauseMetadata": {
   465              "Resource": "arn:aws:s3:::examplebucket",
   466              "Provider": "aws",
   467              "Service": "s3",
   468              "Code": {
   469                "Lines": null
   470              }
   471            }
   472          },
   473          {
   474            "Type": "AWS",
   475            "ID": "AVD-AWS-0092",
   476            "AVDID": "AVD-AWS-0092",
   477            "Title": "S3 Buckets not publicly accessible through ACL.",
   478            "Description": "Buckets should not have ACLs that allow public access",
   479            "Resolution": "Don't use canned ACLs or switch to private acl",
   480            "Severity": "HIGH",
   481            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0092",
   482            "References": [
   483              "https://avd.aquasec.com/misconfig/avd-aws-0092"
   484            ],
   485            "Status": "PASS",
   486            "Layer": {},
   487            "CauseMetadata": {
   488              "Resource": "arn:aws:s3:::examplebucket",
   489              "Provider": "aws",
   490              "Service": "s3",
   491              "Code": {
   492                "Lines": null
   493              }
   494            }
   495          },
   496          {
   497            "Type": "AWS",
   498            "ID": "AVD-AWS-0093",
   499            "AVDID": "AVD-AWS-0093",
   500            "Title": "S3 Access block should restrict public bucket to limit access",
   501            "Description": "S3 buckets should restrict public policies for the bucket. By enabling, the restrict_public_buckets, only the bucket owner and AWS Services can access if it has a public policy.",
   502            "Message": "No public access block so not restricting public buckets",
   503            "Resolution": "Limit the access to public buckets to only the owner or AWS Services (eg; CloudFront)",
   504            "Severity": "HIGH",
   505            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0093",
   506            "References": [
   507              "https://avd.aquasec.com/misconfig/avd-aws-0093"
   508            ],
   509            "Status": "FAIL",
   510            "Layer": {},
   511            "CauseMetadata": {
   512              "Resource": "arn:aws:s3:::examplebucket",
   513              "Provider": "aws",
   514              "Service": "s3",
   515              "Code": {
   516                "Lines": null
   517              }
   518            }
   519          },
   520          {
   521            "Type": "AWS",
   522            "ID": "AVD-AWS-0094",
   523            "AVDID": "AVD-AWS-0094",
   524            "Title": "S3 buckets should each define an aws_s3_bucket_public_access_block",
   525            "Description": "The \"block public access\" settings in S3 override individual policies that apply to a given bucket, meaning that all public access can be controlled in one central types for that bucket. It is therefore good practice to define these settings for each bucket in order to clearly define the public access that can be allowed for it.",
   526            "Message": "Bucket does not have a corresponding public access block.",
   527            "Resolution": "Define a aws_s3_bucket_public_access_block for the given bucket to control public access policies",
   528            "Severity": "LOW",
   529            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0094",
   530            "References": [
   531              "https://avd.aquasec.com/misconfig/avd-aws-0094"
   532            ],
   533            "Status": "FAIL",
   534            "Layer": {},
   535            "CauseMetadata": {
   536              "Resource": "arn:aws:s3:::examplebucket",
   537              "Provider": "aws",
   538              "Service": "s3",
   539              "Code": {
   540                "Lines": null
   541              }
   542            }
   543          }
   544        ]
   545      }
   546    ]
   547  }
   548  `
   549  
   550  const expectedS3AndCloudTrailResult = `{
   551    "CreatedAt": "2021-08-25T12:20:30.000000005Z",
   552    "ArtifactName": "123456789",
   553    "ArtifactType": "aws_account",
   554    "Metadata": {
   555      "ImageConfig": {
   556        "architecture": "",
   557        "created": "0001-01-01T00:00:00Z",
   558        "os": "",
   559        "rootfs": {
   560          "type": "",
   561          "diff_ids": null
   562        },
   563        "config": {}
   564      }
   565    },
   566    "Results": [
   567      {
   568        "Target": "arn:aws:cloudtrail:us-east-1:12345678:trail/management-events",
   569        "Class": "config",
   570        "Type": "cloud",
   571        "MisconfSummary": {
   572          "Successes": 1,
   573          "Failures": 3,
   574          "Exceptions": 0
   575        },
   576        "Misconfigurations": [
   577          {
   578            "Type": "AWS",
   579            "ID": "AVD-AWS-0014",
   580            "AVDID": "AVD-AWS-0014",
   581            "Title": "Cloudtrail should be enabled in all regions regardless of where your AWS resources are generally homed",
   582            "Description": "When creating Cloudtrail in the AWS Management Console the trail is configured by default to be multi-region, this isn't the case with the Terraform resource. Cloudtrail should cover the full AWS account to ensure you can track changes in regions you are not actively operting in.",
   583            "Resolution": "Enable Cloudtrail in all regions",
   584            "Severity": "MEDIUM",
   585            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0014",
   586            "References": [
   587              "https://avd.aquasec.com/misconfig/avd-aws-0014"
   588            ],
   589            "Status": "PASS",
   590            "Layer": {},
   591            "CauseMetadata": {
   592              "Resource": "arn:aws:cloudtrail:us-east-1:12345678:trail/management-events",
   593              "Provider": "aws",
   594              "Service": "cloudtrail",
   595              "Code": {
   596                "Lines": null
   597              }
   598            }
   599          },
   600          {
   601            "Type": "AWS",
   602            "ID": "AVD-AWS-0015",
   603            "AVDID": "AVD-AWS-0015",
   604            "Title": "Cloudtrail should be encrypted at rest to secure access to sensitive trail data",
   605            "Description": "Cloudtrail logs should be encrypted at rest to secure the sensitive data. Cloudtrail logs record all activity that occurs in the the account through API calls and would be one of the first places to look when reacting to a breach.",
   606            "Message": "Trail is not encrypted.",
   607            "Resolution": "Enable encryption at rest",
   608            "Severity": "HIGH",
   609            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0015",
   610            "References": [
   611              "https://avd.aquasec.com/misconfig/avd-aws-0015"
   612            ],
   613            "Status": "FAIL",
   614            "Layer": {},
   615            "CauseMetadata": {
   616              "Resource": "arn:aws:cloudtrail:us-east-1:12345678:trail/management-events",
   617              "Provider": "aws",
   618              "Service": "cloudtrail",
   619              "Code": {
   620                "Lines": null
   621              }
   622            }
   623          },
   624          {
   625            "Type": "AWS",
   626            "ID": "AVD-AWS-0016",
   627            "AVDID": "AVD-AWS-0016",
   628            "Title": "Cloudtrail log validation should be enabled to prevent tampering of log data",
   629            "Description": "Log validation should be activated on Cloudtrail logs to prevent the tampering of the underlying data in the S3 bucket. It is feasible that a rogue actor compromising an AWS account might want to modify the log data to remove trace of their actions.",
   630            "Message": "Trail does not have log validation enabled.",
   631            "Resolution": "Turn on log validation for Cloudtrail",
   632            "Severity": "HIGH",
   633            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0016",
   634            "References": [
   635              "https://avd.aquasec.com/misconfig/avd-aws-0016"
   636            ],
   637            "Status": "FAIL",
   638            "Layer": {},
   639            "CauseMetadata": {
   640              "Resource": "arn:aws:cloudtrail:us-east-1:12345678:trail/management-events",
   641              "Provider": "aws",
   642              "Service": "cloudtrail",
   643              "Code": {
   644                "Lines": null
   645              }
   646            }
   647          },
   648          {
   649            "Type": "AWS",
   650            "ID": "AVD-AWS-0162",
   651            "AVDID": "AVD-AWS-0162",
   652            "Title": "CloudTrail logs should be stored in S3 and also sent to CloudWatch Logs",
   653            "Description": "CloudTrail is a web service that records AWS API calls made in a given account. The recorded information includes the identity of the API caller, the time of the API call, the source IP address of the API caller, the request parameters, and the response elements returned by the AWS service.\n\nCloudTrail uses Amazon S3 for log file storage and delivery, so log files are stored durably. In addition to capturing CloudTrail logs in a specified Amazon S3 bucket for long-term analysis, you can perform real-time analysis by configuring CloudTrail to send logs to CloudWatch Logs.\n\nFor a trail that is enabled in all Regions in an account, CloudTrail sends log files from all those Regions to a CloudWatch Logs log group.",
   654            "Message": "Trail does not have CloudWatch logging configured",
   655            "Resolution": "Enable logging to CloudWatch",
   656            "Severity": "LOW",
   657            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0162",
   658            "References": [
   659              "https://avd.aquasec.com/misconfig/avd-aws-0162"
   660            ],
   661            "Status": "FAIL",
   662            "Layer": {},
   663            "CauseMetadata": {
   664              "Resource": "arn:aws:cloudtrail:us-east-1:12345678:trail/management-events",
   665              "Provider": "aws",
   666              "Service": "cloudtrail",
   667              "Code": {
   668                "Lines": null
   669              }
   670            }
   671          }
   672        ]
   673      },
   674      {
   675        "Target": "arn:aws:s3:::examplebucket",
   676        "Class": "config",
   677        "Type": "cloud",
   678        "MisconfSummary": {
   679          "Successes": 1,
   680          "Failures": 8,
   681          "Exceptions": 0
   682        },
   683        "Misconfigurations": [
   684          {
   685            "Type": "AWS",
   686            "ID": "AVD-AWS-0086",
   687            "AVDID": "AVD-AWS-0086",
   688            "Title": "S3 Access block should block public ACL",
   689            "Description": "S3 buckets should block public ACLs on buckets and any objects they contain. By blocking, PUTs with fail if the object has any public ACL a.",
   690            "Message": "No public access block so not blocking public acls",
   691            "Resolution": "Enable blocking any PUT calls with a public ACL specified",
   692            "Severity": "HIGH",
   693            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0086",
   694            "References": [
   695              "https://avd.aquasec.com/misconfig/avd-aws-0086"
   696            ],
   697            "Status": "FAIL",
   698            "Layer": {},
   699            "CauseMetadata": {
   700              "Resource": "arn:aws:s3:::examplebucket",
   701              "Provider": "aws",
   702              "Service": "s3",
   703              "Code": {
   704                "Lines": null
   705              }
   706            }
   707          },
   708          {
   709            "Type": "AWS",
   710            "ID": "AVD-AWS-0087",
   711            "AVDID": "AVD-AWS-0087",
   712            "Title": "S3 Access block should block public policy",
   713            "Description": "S3 bucket policy should have block public policy to prevent users from putting a policy that enable public access.",
   714            "Message": "No public access block so not blocking public policies",
   715            "Resolution": "Prevent policies that allow public access being PUT",
   716            "Severity": "HIGH",
   717            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0087",
   718            "References": [
   719              "https://avd.aquasec.com/misconfig/avd-aws-0087"
   720            ],
   721            "Status": "FAIL",
   722            "Layer": {},
   723            "CauseMetadata": {
   724              "Resource": "arn:aws:s3:::examplebucket",
   725              "Provider": "aws",
   726              "Service": "s3",
   727              "Code": {
   728                "Lines": null
   729              }
   730            }
   731          },
   732          {
   733            "Type": "AWS",
   734            "ID": "AVD-AWS-0088",
   735            "AVDID": "AVD-AWS-0088",
   736            "Title": "Unencrypted S3 bucket.",
   737            "Description": "S3 Buckets should be encrypted to protect the data that is stored within them if access is compromised.",
   738            "Message": "Bucket does not have encryption enabled",
   739            "Resolution": "Configure bucket encryption",
   740            "Severity": "HIGH",
   741            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0088",
   742            "References": [
   743              "https://avd.aquasec.com/misconfig/avd-aws-0088"
   744            ],
   745            "Status": "FAIL",
   746            "Layer": {},
   747            "CauseMetadata": {
   748              "Resource": "arn:aws:s3:::examplebucket",
   749              "Provider": "aws",
   750              "Service": "s3",
   751              "Code": {
   752                "Lines": null
   753              }
   754            }
   755          },
   756          {
   757            "Type": "AWS",
   758            "ID": "AVD-AWS-0090",
   759            "AVDID": "AVD-AWS-0090",
   760            "Title": "S3 Data should be versioned",
   761            "Description": "Versioning in Amazon S3 is a means of keeping multiple variants of an object in the same bucket. \nYou can use the S3 Versioning feature to preserve, retrieve, and restore every version of every object stored in your buckets. \nWith versioning you can recover more easily from both unintended user actions and application failures.",
   762            "Message": "Bucket does not have versioning enabled",
   763            "Resolution": "Enable versioning to protect against accidental/malicious removal or modification",
   764            "Severity": "MEDIUM",
   765            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0090",
   766            "References": [
   767              "https://avd.aquasec.com/misconfig/avd-aws-0090"
   768            ],
   769            "Status": "FAIL",
   770            "Layer": {},
   771            "CauseMetadata": {
   772              "Resource": "arn:aws:s3:::examplebucket",
   773              "Provider": "aws",
   774              "Service": "s3",
   775              "Code": {
   776                "Lines": null
   777              }
   778            }
   779          },
   780          {
   781            "Type": "AWS",
   782            "ID": "AVD-AWS-0132",
   783            "AVDID": "AVD-AWS-0132",
   784            "Title": "S3 encryption should use Customer Managed Keys",
   785            "Description": "Encryption using AWS keys provides protection for your S3 buckets. To increase control of the encryption and manage factors like rotation use customer managed keys.",
   786            "Message": "Bucket does not encrypt data with a customer managed key.",
   787            "Resolution": "Enable encryption using customer managed keys",
   788            "Severity": "HIGH",
   789            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0132",
   790            "References": [
   791              "https://avd.aquasec.com/misconfig/avd-aws-0132"
   792            ],
   793            "Status": "FAIL",
   794            "Layer": {},
   795            "CauseMetadata": {
   796              "Resource": "arn:aws:s3:::examplebucket",
   797              "Provider": "aws",
   798              "Service": "s3",
   799              "Code": {
   800                "Lines": null
   801              }
   802            }
   803          },
   804          {
   805            "Type": "AWS",
   806            "ID": "AVD-AWS-0091",
   807            "AVDID": "AVD-AWS-0091",
   808            "Title": "S3 Access Block should Ignore Public Acl",
   809            "Description": "S3 buckets should ignore public ACLs on buckets and any objects they contain. By ignoring rather than blocking, PUT calls with public ACLs will still be applied but the ACL will be ignored.",
   810            "Message": "No public access block so not ignoring public acls",
   811            "Resolution": "Enable ignoring the application of public ACLs in PUT calls",
   812            "Severity": "HIGH",
   813            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0091",
   814            "References": [
   815              "https://avd.aquasec.com/misconfig/avd-aws-0091"
   816            ],
   817            "Status": "FAIL",
   818            "Layer": {},
   819            "CauseMetadata": {
   820              "Resource": "arn:aws:s3:::examplebucket",
   821              "Provider": "aws",
   822              "Service": "s3",
   823              "Code": {
   824                "Lines": null
   825              }
   826            }
   827          },
   828          {
   829            "Type": "AWS",
   830            "ID": "AVD-AWS-0092",
   831            "AVDID": "AVD-AWS-0092",
   832            "Title": "S3 Buckets not publicly accessible through ACL.",
   833            "Description": "Buckets should not have ACLs that allow public access",
   834            "Resolution": "Don't use canned ACLs or switch to private acl",
   835            "Severity": "HIGH",
   836            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0092",
   837            "References": [
   838              "https://avd.aquasec.com/misconfig/avd-aws-0092"
   839            ],
   840            "Status": "PASS",
   841            "Layer": {},
   842            "CauseMetadata": {
   843              "Resource": "arn:aws:s3:::examplebucket",
   844              "Provider": "aws",
   845              "Service": "s3",
   846              "Code": {
   847                "Lines": null
   848              }
   849            }
   850          },
   851          {
   852            "Type": "AWS",
   853            "ID": "AVD-AWS-0093",
   854            "AVDID": "AVD-AWS-0093",
   855            "Title": "S3 Access block should restrict public bucket to limit access",
   856            "Description": "S3 buckets should restrict public policies for the bucket. By enabling, the restrict_public_buckets, only the bucket owner and AWS Services can access if it has a public policy.",
   857            "Message": "No public access block so not restricting public buckets",
   858            "Resolution": "Limit the access to public buckets to only the owner or AWS Services (eg; CloudFront)",
   859            "Severity": "HIGH",
   860            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0093",
   861            "References": [
   862              "https://avd.aquasec.com/misconfig/avd-aws-0093"
   863            ],
   864            "Status": "FAIL",
   865            "Layer": {},
   866            "CauseMetadata": {
   867              "Resource": "arn:aws:s3:::examplebucket",
   868              "Provider": "aws",
   869              "Service": "s3",
   870              "Code": {
   871                "Lines": null
   872              }
   873            }
   874          },
   875          {
   876            "Type": "AWS",
   877            "ID": "AVD-AWS-0094",
   878            "AVDID": "AVD-AWS-0094",
   879            "Title": "S3 buckets should each define an aws_s3_bucket_public_access_block",
   880            "Description": "The \"block public access\" settings in S3 override individual policies that apply to a given bucket, meaning that all public access can be controlled in one central types for that bucket. It is therefore good practice to define these settings for each bucket in order to clearly define the public access that can be allowed for it.",
   881            "Message": "Bucket does not have a corresponding public access block.",
   882            "Resolution": "Define a aws_s3_bucket_public_access_block for the given bucket to control public access policies",
   883            "Severity": "LOW",
   884            "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0094",
   885            "References": [
   886              "https://avd.aquasec.com/misconfig/avd-aws-0094"
   887            ],
   888            "Status": "FAIL",
   889            "Layer": {},
   890            "CauseMetadata": {
   891              "Resource": "arn:aws:s3:::examplebucket",
   892              "Provider": "aws",
   893              "Service": "s3",
   894              "Code": {
   895                "Lines": null
   896              }
   897            }
   898          }
   899        ]
   900      }
   901    ]
   902  }
   903  `
   904  
   905  func Test_Run(t *testing.T) {
   906  	regoDir := t.TempDir()
   907  
   908  	tests := []struct {
   909  		name         string
   910  		options      flag.Options
   911  		want         string
   912  		expectErr    bool
   913  		cacheContent string
   914  		regoPolicy   string
   915  		allServices  []string
   916  		inputData    string
   917  	}{
   918  		{
   919  			name: "succeed with cached infra",
   920  			options: flag.Options{
   921  				RegoOptions: flag.RegoOptions{SkipPolicyUpdate: true},
   922  				AWSOptions: flag.AWSOptions{
   923  					Region:   "us-east-1",
   924  					Services: []string{"s3"},
   925  					Account:  "12345678",
   926  				},
   927  				CloudOptions: flag.CloudOptions{
   928  					MaxCacheAge: time.Hour * 24 * 365 * 100,
   929  				},
   930  				MisconfOptions: flag.MisconfOptions{IncludeNonFailures: true},
   931  			},
   932  			cacheContent: "testdata/s3onlycache.json",
   933  			allServices:  []string{"s3"},
   934  			want:         expectedS3ScanResult,
   935  		},
   936  		{
   937  			name: "custom rego rule with passed results",
   938  			options: flag.Options{
   939  				AWSOptions: flag.AWSOptions{
   940  					Region:   "us-east-1",
   941  					Services: []string{"s3"},
   942  					Account:  "12345678",
   943  				},
   944  				CloudOptions: flag.CloudOptions{
   945  					MaxCacheAge: time.Hour * 24 * 365 * 100,
   946  				},
   947  				RegoOptions: flag.RegoOptions{
   948  					Trace: true,
   949  					PolicyPaths: []string{
   950  						filepath.Join(regoDir, "policies"),
   951  					},
   952  					PolicyNamespaces: []string{
   953  						"user",
   954  					},
   955  					DataPaths: []string{
   956  						filepath.Join(regoDir, "data"),
   957  					},
   958  					SkipPolicyUpdate: true,
   959  				},
   960  				MisconfOptions: flag.MisconfOptions{IncludeNonFailures: true},
   961  			},
   962  			regoPolicy: `# METADATA
   963  # title: Bad input data
   964  # description: Just failing rule with input data
   965  # scope: package
   966  # schemas:
   967  # - input: schema["input"]
   968  # custom:
   969  #   severity: LOW
   970  #   service: s3
   971  #   input:
   972  #     selector:
   973  #     - type: cloud
   974  package user.whatever
   975  import data.settings.DS123.foo
   976  
   977  deny {
   978  	foo == true
   979  }
   980  `,
   981  			inputData: `{
   982  	"settings": {
   983  		"DS123": {
   984  			"foo": true
   985  		}
   986  	}
   987  }`,
   988  			cacheContent: filepath.Join("testdata", "s3onlycache.json"),
   989  			allServices:  []string{"s3"},
   990  			want:         expectedCustomScanResult,
   991  		},
   992  		{
   993  			name: "compliance report summary",
   994  			options: flag.Options{
   995  				AWSOptions: flag.AWSOptions{
   996  					Region:   "us-east-1",
   997  					Services: []string{"s3"},
   998  					Account:  "12345678",
   999  				},
  1000  				CloudOptions: flag.CloudOptions{
  1001  					MaxCacheAge: time.Hour * 24 * 365 * 100,
  1002  				},
  1003  				ReportOptions: flag.ReportOptions{
  1004  					Compliance: spec.ComplianceSpec{
  1005  						Spec: defsecTypes.Spec{
  1006  							// TODO: refactor defsec so that the parsed spec can be passed
  1007  							ID:          "@testdata/example-spec.yaml",
  1008  							Title:       "my-custom-spec",
  1009  							Description: "My fancy spec",
  1010  							Version:     "1.2",
  1011  							Controls: []defsecTypes.Control{
  1012  								{
  1013  									ID:          "1.1",
  1014  									Name:        "Unencrypted S3 bucket",
  1015  									Description: "S3 Buckets should be encrypted to protect the data that is stored within them if access is compromised.",
  1016  									Checks: []defsecTypes.SpecCheck{
  1017  										{ID: "AVD-AWS-0088"},
  1018  									},
  1019  									Severity: "HIGH",
  1020  								},
  1021  							},
  1022  						},
  1023  					},
  1024  					Format:       "table",
  1025  					ReportFormat: "summary",
  1026  				},
  1027  				RegoOptions: flag.RegoOptions{SkipPolicyUpdate: true},
  1028  			},
  1029  			cacheContent: "testdata/s3onlycache.json",
  1030  			allServices:  []string{"s3"},
  1031  			want: `
  1032  Summary Report for compliance: my-custom-spec
  1033  ┌─────┬──────────┬───────────────────────┬────────┬────────┐
  1034  │ ID  │ Severity │     Control Name      │ Status │ Issues │
  1035  ├─────┼──────────┼───────────────────────┼────────┼────────┤
  1036  │ 1.1 │   HIGH   │ Unencrypted S3 bucket │  FAIL  │   1    │
  1037  └─────┴──────────┴───────────────────────┴────────┴────────┘
  1038  `,
  1039  		},
  1040  		{
  1041  			name: "scan an unsupported service",
  1042  			options: flag.Options{
  1043  				RegoOptions: flag.RegoOptions{SkipPolicyUpdate: true},
  1044  				AWSOptions: flag.AWSOptions{
  1045  					Region:   "us-east-1",
  1046  					Account:  "123456789",
  1047  					Services: []string{"theultimateservice"},
  1048  				},
  1049  				CloudOptions: flag.CloudOptions{
  1050  					MaxCacheAge: time.Hour * 24 * 365 * 100,
  1051  				},
  1052  				MisconfOptions: flag.MisconfOptions{IncludeNonFailures: true},
  1053  			},
  1054  			cacheContent: "testdata/s3onlycache.json",
  1055  			expectErr:    true,
  1056  		},
  1057  		{
  1058  			name: "scan every service",
  1059  			options: flag.Options{
  1060  				RegoOptions: flag.RegoOptions{SkipPolicyUpdate: true},
  1061  				AWSOptions: flag.AWSOptions{
  1062  					Region:  "us-east-1",
  1063  					Account: "123456789",
  1064  				},
  1065  				CloudOptions: flag.CloudOptions{
  1066  					MaxCacheAge: time.Hour * 24 * 365 * 100,
  1067  				},
  1068  				MisconfOptions: flag.MisconfOptions{IncludeNonFailures: true},
  1069  			},
  1070  			cacheContent: "testdata/s3andcloudtrailcache.json",
  1071  			allServices:  []string{"s3", "cloudtrail"},
  1072  			want:         expectedS3AndCloudTrailResult,
  1073  		},
  1074  		{
  1075  			name: "skip certain services and include specific services",
  1076  			options: flag.Options{
  1077  				RegoOptions: flag.RegoOptions{SkipPolicyUpdate: true},
  1078  				AWSOptions: flag.AWSOptions{
  1079  					Region:       "us-east-1",
  1080  					Services:     []string{"s3"},
  1081  					SkipServices: []string{"cloudtrail"},
  1082  					Account:      "123456789",
  1083  				},
  1084  				CloudOptions: flag.CloudOptions{
  1085  					MaxCacheAge: time.Hour * 24 * 365 * 100,
  1086  				},
  1087  				MisconfOptions: flag.MisconfOptions{IncludeNonFailures: true},
  1088  			},
  1089  			cacheContent: "testdata/s3andcloudtrailcache.json",
  1090  			allServices:  []string{"s3", "cloudtrail"},
  1091  			// we skip cloudtrail but still expect results from it as it is cached
  1092  			want: expectedS3AndCloudTrailResult,
  1093  		},
  1094  		{
  1095  			name: "only skip certain services but scan the rest",
  1096  			options: flag.Options{
  1097  				RegoOptions: flag.RegoOptions{SkipPolicyUpdate: true},
  1098  				AWSOptions: flag.AWSOptions{
  1099  					Region:       "us-east-1",
  1100  					SkipServices: []string{"cloudtrail", "iam"},
  1101  					Account:      "12345678",
  1102  				},
  1103  				CloudOptions: flag.CloudOptions{
  1104  					MaxCacheAge: time.Hour * 24 * 365 * 100,
  1105  				},
  1106  				MisconfOptions: flag.MisconfOptions{IncludeNonFailures: true},
  1107  			},
  1108  			allServices:  []string{"s3", "cloudtrail", "iam"},
  1109  			cacheContent: "testdata/s3onlycache.json",
  1110  			want:         expectedS3ScanResult,
  1111  		},
  1112  		{
  1113  			name: "fail - service specified to both include and exclude",
  1114  			options: flag.Options{
  1115  				RegoOptions: flag.RegoOptions{SkipPolicyUpdate: true},
  1116  				AWSOptions: flag.AWSOptions{
  1117  					Region:       "us-east-1",
  1118  					Services:     []string{"s3"},
  1119  					SkipServices: []string{"s3"},
  1120  					Account:      "123456789",
  1121  				},
  1122  				CloudOptions: flag.CloudOptions{
  1123  					MaxCacheAge: time.Hour * 24 * 365 * 100,
  1124  				},
  1125  				MisconfOptions: flag.MisconfOptions{IncludeNonFailures: true},
  1126  			},
  1127  			cacheContent: "testdata/s3andcloudtrailcache.json",
  1128  			expectErr:    true,
  1129  		},
  1130  	}
  1131  
  1132  	clock.SetFakeTime(t, time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC))
  1133  	for _, test := range tests {
  1134  		t.Run(test.name, func(t *testing.T) {
  1135  			if test.allServices != nil {
  1136  				oldAllSupportedServicesFunc := allSupportedServicesFunc
  1137  				allSupportedServicesFunc = func() []string {
  1138  					return test.allServices
  1139  				}
  1140  				defer func() {
  1141  					allSupportedServicesFunc = oldAllSupportedServicesFunc
  1142  				}()
  1143  			}
  1144  
  1145  			output := bytes.NewBuffer(nil)
  1146  			test.options.SetOutputWriter(output)
  1147  			test.options.Debug = true
  1148  			test.options.GlobalOptions.Timeout = time.Minute
  1149  			if test.options.Format == "" {
  1150  				test.options.Format = "json"
  1151  			}
  1152  			test.options.Severities = []dbTypes.Severity{
  1153  				dbTypes.SeverityUnknown,
  1154  				dbTypes.SeverityLow,
  1155  				dbTypes.SeverityMedium,
  1156  				dbTypes.SeverityHigh,
  1157  				dbTypes.SeverityCritical,
  1158  			}
  1159  
  1160  			if test.regoPolicy != "" {
  1161  				require.NoError(t, os.MkdirAll(filepath.Join(regoDir, "policies"), 0755))
  1162  				require.NoError(t, os.WriteFile(filepath.Join(regoDir, "policies", "user.rego"), []byte(test.regoPolicy), 0644))
  1163  			}
  1164  
  1165  			if test.inputData != "" {
  1166  				require.NoError(t, os.MkdirAll(filepath.Join(regoDir, "data"), 0755))
  1167  				require.NoError(t, os.WriteFile(filepath.Join(regoDir, "data", "data.json"), []byte(test.inputData), 0644))
  1168  			}
  1169  
  1170  			if test.cacheContent != "" {
  1171  				cacheRoot := t.TempDir()
  1172  				test.options.CacheDir = cacheRoot
  1173  				cacheFile := filepath.Join(cacheRoot, "cloud", "aws", test.options.Account, test.options.Region, "data.json")
  1174  				require.NoError(t, os.MkdirAll(filepath.Dir(cacheFile), 0700))
  1175  
  1176  				cacheData, err := os.ReadFile(test.cacheContent)
  1177  				require.NoError(t, err, test.name)
  1178  
  1179  				require.NoError(t, os.WriteFile(cacheFile, cacheData, 0600))
  1180  			}
  1181  
  1182  			err := Run(context.Background(), test.options)
  1183  			if test.expectErr {
  1184  				assert.Error(t, err)
  1185  				return
  1186  			}
  1187  			assert.NoError(t, err)
  1188  			assert.Equal(t, test.want, output.String())
  1189  		})
  1190  	}
  1191  }