github.com/aquasecurity/trivy-iac@v0.8.1-0.20240127024015-3d8e412cf0ab/cmd/avd_generator/main.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	goast "go/ast"
     6  	"go/parser"
     7  	"go/token"
     8  	"io"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  	"text/template"
    13  
    14  	"github.com/aquasecurity/defsec/pkg/framework"
    15  	_ "github.com/aquasecurity/defsec/pkg/rego"
    16  	registered "github.com/aquasecurity/defsec/pkg/rules"
    17  	types "github.com/aquasecurity/defsec/pkg/types/rules"
    18  	policies "github.com/aquasecurity/trivy-policies"
    19  )
    20  
    21  func main() {
    22  	var generateCount int
    23  
    24  	for _, metadata := range registered.GetRegistered(framework.ALL) {
    25  		writeDocsFile(metadata, "avd_docs")
    26  		generateCount++
    27  	}
    28  
    29  	fmt.Printf("\nGenerated %d files in avd_docs\n", generateCount)
    30  }
    31  
    32  // nolint: cyclop
    33  func writeDocsFile(meta types.RegisteredRule, path string) {
    34  
    35  	tmpl, err := template.New("defsec").Parse(docsMarkdownTemplate)
    36  	if err != nil {
    37  		fail("error occurred creating the template %v\n", err)
    38  	}
    39  
    40  	docpath := filepath.Join(path,
    41  		strings.ToLower(meta.GetRule().Provider.ConstName()),
    42  		strings.ToLower(strings.ReplaceAll(meta.GetRule().Service, "-", "")),
    43  		meta.GetRule().AVDID,
    44  	)
    45  
    46  	if err := os.MkdirAll(docpath, os.ModePerm); err != nil {
    47  		panic(err)
    48  	}
    49  
    50  	file, err := os.Create(filepath.Join(docpath, "docs.md"))
    51  	if err != nil {
    52  		fail("error occurred creating the docs file for %s", docpath)
    53  	}
    54  
    55  	if err := tmpl.Execute(file, meta.GetRule()); err != nil {
    56  		fail("error occurred generating the document %v", err)
    57  	}
    58  	fmt.Printf("Generating docs file for policy %s\n", meta.GetRule().AVDID)
    59  
    60  	if meta.GetRule().Terraform != nil {
    61  		if len(meta.GetRule().Terraform.GoodExamples) > 0 || len(meta.GetRule().Terraform.Links) > 0 {
    62  			if meta.GetRule().RegoPackage != "" { // get examples from file as rego rules don't have embedded
    63  				value, err := GetExampleValueFromFile(meta.GetRule().Terraform.GoodExamples[0], "GoodExamples")
    64  				if err != nil {
    65  					fail("error retrieving examples from metadata: %v\n", err)
    66  				}
    67  				meta.GetRule().Terraform.GoodExamples = []string{value}
    68  			}
    69  
    70  			tmpl, err := template.New("terraform").Parse(terraformMarkdownTemplate)
    71  			if err != nil {
    72  				fail("error occurred creating the template %v\n", err)
    73  			}
    74  			file, err := os.Create(filepath.Join(docpath, "Terraform.md"))
    75  			if err != nil {
    76  				fail("error occurred creating the Terraform file for %s", docpath)
    77  			}
    78  			defer func() { _ = file.Close() }()
    79  
    80  			if err := tmpl.Execute(file, meta.GetRule()); err != nil {
    81  				fail("error occurred generating the document %v", err)
    82  			}
    83  			fmt.Printf("Generating Terraform file for policy %s\n", meta.GetRule().AVDID)
    84  		}
    85  	}
    86  
    87  	if meta.GetRule().CloudFormation != nil {
    88  		if len(meta.GetRule().CloudFormation.GoodExamples) > 0 || len(meta.GetRule().CloudFormation.Links) > 0 {
    89  			if meta.GetRule().RegoPackage != "" { // get examples from file as rego rules don't have embedded
    90  				value, err := GetExampleValueFromFile(meta.GetRule().CloudFormation.GoodExamples[0], "GoodExamples")
    91  				if err != nil {
    92  					fail("error retrieving examples from metadata: %v\n", err)
    93  				}
    94  				meta.GetRule().CloudFormation.GoodExamples = []string{value}
    95  			}
    96  
    97  			tmpl, err := template.New("cloudformation").Parse(cloudformationMarkdownTemplate)
    98  			if err != nil {
    99  				fail("error occurred creating the template %v\n", err)
   100  			}
   101  			file, err := os.Create(filepath.Join(docpath, "CloudFormation.md"))
   102  			if err != nil {
   103  				fail("error occurred creating the CloudFormation file for %s", docpath)
   104  			}
   105  			defer func() { _ = file.Close() }()
   106  
   107  			if err := tmpl.Execute(file, meta.GetRule()); err != nil {
   108  				fail("error occurred generating the document %v", err)
   109  			}
   110  			fmt.Printf("Generating CloudFormation file for policy %s\n", meta.GetRule().AVDID)
   111  		}
   112  	}
   113  }
   114  
   115  func fail(msg string, args ...interface{}) {
   116  	fmt.Printf(msg, args...)
   117  	os.Exit(1)
   118  }
   119  
   120  func readFileFromPolicyFS(path string) (io.Reader, error) {
   121  	path = strings.TrimPrefix(path, "rules/")
   122  	return policies.EmbeddedPolicyFileSystem.Open(path)
   123  
   124  }
   125  
   126  func GetExampleValueFromFile(filename string, exampleType string) (string, error) {
   127  	r, err := readFileFromPolicyFS(filename)
   128  	if err != nil {
   129  		return "", err
   130  	}
   131  	f, err := parser.ParseFile(token.NewFileSet(), filename, r, parser.AllErrors)
   132  	if err != nil {
   133  		return "", err
   134  	}
   135  
   136  	for _, d := range f.Decls {
   137  		switch decl := d.(type) {
   138  		case *goast.GenDecl:
   139  			for _, spec := range decl.Specs {
   140  				switch spec := spec.(type) {
   141  				case *goast.ValueSpec:
   142  					for _, id := range spec.Names {
   143  						switch v := id.Obj.Decl.(*goast.ValueSpec).Values[0].(type) {
   144  						case *goast.CompositeLit:
   145  							value := v.Elts[0].(*goast.BasicLit).Value
   146  							if strings.Contains(id.Name, exampleType) {
   147  								return strings.ReplaceAll(value, "`", ""), nil
   148  							}
   149  						}
   150  					}
   151  				}
   152  			}
   153  		}
   154  	}
   155  	return "", fmt.Errorf("exampleType %s not found in file: %s", exampleType, filename)
   156  }
   157  
   158  var docsMarkdownTemplate = `
   159  {{ .Explanation }}
   160  
   161  ### Impact
   162  {{ if .Impact }}{{ .Impact }}{{ else }}<!-- Add Impact here -->{{ end }}
   163  
   164  <!-- DO NOT CHANGE -->
   165  {{ ` + "`{{ " + `remediationActions ` + "`}}" + `}}
   166  
   167  {{ if .Links }}### Links{{ range .Links }}
   168  - {{ . }}
   169  {{ end}}
   170  {{ end }}
   171  `
   172  
   173  var terraformMarkdownTemplate = `
   174  {{ .Resolution }}
   175  
   176  {{ if .Terraform.GoodExamples }}{{ range .Terraform.GoodExamples }}` + "```hcl" + `{{ . }}
   177  ` + "```" + `
   178  {{ end}}{{ end }}
   179  {{ if .Terraform.Links }}#### Remediation Links{{ range .Terraform.Links }}
   180   - {{ . }}
   181  {{ end}}{{ end }}
   182  `
   183  
   184  var cloudformationMarkdownTemplate = `
   185  {{ .Resolution }}
   186  
   187  {{ if .CloudFormation.GoodExamples }}{{ range .CloudFormation.GoodExamples }}` + "```yaml" + `{{ . }}
   188  ` + "```" + `
   189  {{ end}}{{ end }}
   190  {{ if .CloudFormation.Links }}#### Remediation Links{{ range .CloudFormation.Links }}
   191   - {{ . }}
   192  {{ end}}{{ end }}
   193  `