Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(deps): Replace trivy-iac/pkg with defsec/pkg #39

Merged
merged 9 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Let's break the metadata down.
- `title` is fairly self-explanatory - it is a title for the rule. The title should clearly and succinctly state the problem which is being detected.
- `description` is also fairly self-explanatory - it is a description of the problem which is being detected. The description should be a little more verbose than the title, and should describe what the rule is trying to achieve. Imagine it completing a sentence starting with `You should...`.
- `scope` is used to define the scope of the policy. In this case, we are defining a policy that applies to the entire package. _defsec_ only supports using package scope for metadata at the moment, so this should always be the same.
- `schemas` tells Rego that it should use the `AWS` schema to validate the use of the input data in the policy. We currently support [these](https://github.com/aquasecurity/trivy-iac/tree/00033a7bd2a98bb07fb1c2cfa17a4cd85c6e0676/pkg/rego/schemas) schemas. Using a schema can help you validate your policy faster for syntax issues.
- `schemas` tells Rego that it should use the `AWS` schema to validate the use of the input data in the policy. We currently support [these](https://github.com/aquasecurity/defsec/tree/9b3cc255faff5dc57de5ff77ed0ce0009c80a4bb/pkg/rego/schemas) schemas. Using a schema can help you validate your policy faster for syntax issues.
- `custom` is used to define custom fields that can be used by defsec to provide additional context to the policy and any related detections. This can contain the following:
- `avd_id` is the ID of the rule in the [AWS Vulnerability Database](https://avd.aquasec.com/). This is used to link the rule to the AVD entry. You can generate an ID to use for this field using `make id`.
- `provider` is the name of the provider the rule targets. This should be the same as the provider name in the `pkg/providers` directory, e.g. `aws`.
Expand Down Expand Up @@ -90,6 +90,6 @@ In the example above, you'll notice properties are being accessed from the `inpu

You should also write a test for your rule(s). There are many examples of these in the `rules/cloud/policies` directory.

Finally, you'll want to generate documentation for your newly added rule. Please run `make docs` to within the [trivy-iac](https://github.com/aquasecurity/trivy-iac) repo generate the documentation for your new policy and submit a PR for us to take a look at.
Finally, you'll want to generate documentation for your newly added rule. Please run `make docs` to generate the documentation for your new policy and submit a PR for us to take a look at.

You can see a full example PR for a new rule being added here: [https://github.com/aquasecurity/defsec/pull/1000](https://github.com/aquasecurity/defsec/pull/1000).
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,10 @@ id:
outdated-api-updated:
sed -i.bak "s|recommendedVersions :=.*|recommendedVersions := $(OUTDATE_API_DATA)|" $(DYNAMIC_REGO_FOLDER)/outdated_api.rego && rm $(DYNAMIC_REGO_FOLDER)/outdated_api.rego.bak

.PHONY: docs
docs:
go run ./cmd/avd_generator

.PHONY: docs-test
docs-test:
go test -v ./cmd/avd_generator/...
4 changes: 3 additions & 1 deletion avd_docs/aws/cloudfront/AVD-AWS-0013/docs.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

You should not use outdated/insecure TLS versions for encryption. You should be using TLS v1.2+.

Note: that setting *minimum_protocol_version = "TLSv1.2_2021"* is only possible when *cloudfront_default_certificate* is false (eg. you are not using the cloudfront.net domain name).
Note: that setting *minimum_protocol_version = "TLSv1.2_2021"* is only possible when *cloudfront_default_certificate* is false (eg. you are not using the cloudfront.net domain name) and *ssl_support_method* is *sni-only*.
If *cloudfront_default_certificate* is true then the Cloudfront API will only allow setting *minimum_protocol_version = "TLSv1"*, and setting it to any other value will result in a perpetual diff in your *terraform plan*'s.
The only option when using the cloudfront.net domain name is to ignore this rule.

Expand All @@ -14,4 +14,6 @@ Outdated SSL policies increase exposure to known vulnerabilities
### Links
- https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/secure-connections-supported-viewer-protocols-ciphers.html

- https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#DownloadDistValuesGeneral


2 changes: 1 addition & 1 deletion avd_docs/aws/dynamodb/AVD-AWS-0023/docs.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

Amazon DynamoDB Accelerator (DAX) and table encryption at rest provides an additional layer of data protection by helping secure your data from unauthorized access to the underlying storage.
Amazon DynamoDB Accelerator (DAX) encryption at rest provides an additional layer of data protection by helping secure your data from unauthorized access to the underlying storage.

### Impact
Data can be freely read if compromised
Expand Down
2 changes: 1 addition & 1 deletion avd_docs/aws/rds/AVD-AWS-0078/CloudFormation.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

Enable encryption for RDS clusters and instances
Use Customer Managed Keys to encrypt Performance Insights data

```yaml---
AWSTemplateFormatVersion: 2010-09-09
Expand Down
2 changes: 1 addition & 1 deletion avd_docs/aws/rds/AVD-AWS-0078/Terraform.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

Enable encryption for RDS clusters and instances
Use Customer Managed Keys to encrypt Performance Insights data

```hcl
resource "aws_rds_cluster_instance" "good_example" {
Expand Down
8 changes: 5 additions & 3 deletions avd_docs/aws/rds/AVD-AWS-0078/docs.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@

When enabling Performance Insights on an RDS cluster or RDS DB Instance, and encryption key should be provided.
Amazon RDS uses the AWS managed key for your new DB instance. For complete control over KMS keys, including establishing and maintaining their key policies, IAM policies, and grants, enabling and disabling them, and rotating their cryptographic material, use a customer managed keys.

The encryption key specified in `performance_insights_kms_key_id` references a KMS ARN

### Impact
Data can be read from the RDS Performance Insights if it is compromised
Using AWS managed keys does not allow for fine grained control

<!-- DO NOT CHANGE -->
{{ remediationActions }}

### Links
- https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Encryption.htm
- https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_PerfInsights.access-control.html#USER_PerfInsights.access-control.cmk-policy

- https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-mgmt


2 changes: 1 addition & 1 deletion avd_docs/aws/rds/AVD-AWS-0180/CloudFormation.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

Remove the public endpoint from the RDS instance'
Remove the public endpoint from the RDS instance.

```yaml---
AWSTemplateFormatVersion: 2010-09-09
Expand Down
2 changes: 1 addition & 1 deletion avd_docs/aws/rds/AVD-AWS-0180/Terraform.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

Remove the public endpoint from the RDS instance'
Remove the public endpoint from the RDS instance.

```hcl
resource "aws_db_instance" "good_example" {
Expand Down
2 changes: 1 addition & 1 deletion avd_docs/aws/rds/AVD-AWS-0180/docs.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

Ensures RDS instances are not launched into the public cloud.
Ensures RDS instances and RDS Cluster instances are not launched into the public cloud.

### Impact
<!-- Add Impact here -->
Expand Down
37 changes: 16 additions & 21 deletions avd_docs/google/dns/AVD-GCP-0012/Terraform.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,22 @@
Use RSA SHA512

```hcl
resource "google_dns_managed_zone" "foo" {
name = "foobar"
dns_name = "foo.bar."

dnssec_config {
state = "on"
non_existence = "nsec3"
}
}

data "google_dns_keys" "foo_dns_keys" {
managed_zone = google_dns_managed_zone.foo.id
zone_signing_keys {
algorithm = "rsasha512"
}
}

output "foo_dns_ds_record" {
description = "DS record of the foo subdomain."
value = data.google_dns_keys.foo_dns_keys.key_signing_keys[0].ds_record
}
resource "google_dns_managed_zone" "example-zone" {
name = "example-zone"
dns_name = "example-${random_id.rnd.hex}.com."

dnssec_config {
state = "on"
default_key_specs {
algorithm = "rsasha512"
key_type = "keySigning"
}
default_key_specs {
algorithm = "rsasha512"
key_type = "zoneSigning"
}
}
}

```

Expand Down
194 changes: 194 additions & 0 deletions cmd/avd_generator/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package main

import (
"fmt"
goast "go/ast"
"go/parser"
"go/token"
"io"
"os"
"path/filepath"
"strings"
"text/template"

"github.com/aquasecurity/defsec/pkg/framework"
"github.com/aquasecurity/trivy-policies/rules"

_ "github.com/aquasecurity/defsec/pkg/rego"
registered "github.com/aquasecurity/defsec/pkg/rules"
drules "github.com/aquasecurity/defsec/pkg/types/rules"
)

func main() {
var generateCount int

for _, metadata := range registered.GetRegistered(framework.ALL) {
writeDocsFile(metadata, "avd_docs")
generateCount++
}

fmt.Printf("\nGenerated %d files in avd_docs\n", generateCount)
}

// nolint: cyclop
func writeDocsFile(meta drules.RegisteredRule, path string) {

tmpl, err := template.New("defsec").Parse(docsMarkdownTemplate)
if err != nil {
fail("error occurred creating the template %v\n", err)
}

docpath := filepath.Join(path,
strings.ToLower(meta.GetRule().Provider.ConstName()),
strings.ToLower(strings.ReplaceAll(meta.GetRule().Service, "-", "")),
meta.GetRule().AVDID,
)

if err := os.MkdirAll(docpath, os.ModePerm); err != nil {
panic(err)
}

file, err := os.Create(filepath.Join(docpath, "docs.md"))
if err != nil {
fail("error occurred creating the docs file for %s", docpath)
}

if err := tmpl.Execute(file, meta.GetRule()); err != nil {
fail("error occurred generating the document %v", err)
}
fmt.Printf("Generating docs file for policy %s\n", meta.GetRule().AVDID)

if meta.GetRule().Terraform != nil {
if len(meta.GetRule().Terraform.GoodExamples) > 0 || len(meta.GetRule().Terraform.Links) > 0 {
if meta.GetRule().RegoPackage != "" { // get examples from file as rego rules don't have embedded
value, err := GetExampleValueFromFile(meta.GetRule().Terraform.GoodExamples[0], "GoodExamples")
if err != nil {
fail("error retrieving examples from metadata: %v\n", err)
}
meta.GetRule().Terraform.GoodExamples = []string{value}
}

tmpl, err := template.New("terraform").Parse(terraformMarkdownTemplate)
if err != nil {
fail("error occurred creating the template %v\n", err)
}
file, err := os.Create(filepath.Join(docpath, "Terraform.md"))
if err != nil {
fail("error occurred creating the Terraform file for %s", docpath)
}
defer func() { _ = file.Close() }()

if err := tmpl.Execute(file, meta.GetRule()); err != nil {
fail("error occurred generating the document %v", err)
}
fmt.Printf("Generating Terraform file for policy %s\n", meta.GetRule().AVDID)
}
}

if meta.GetRule().CloudFormation != nil {
if len(meta.GetRule().CloudFormation.GoodExamples) > 0 || len(meta.GetRule().CloudFormation.Links) > 0 {
if meta.GetRule().RegoPackage != "" { // get examples from file as rego rules don't have embedded
value, err := GetExampleValueFromFile(meta.GetRule().CloudFormation.GoodExamples[0], "GoodExamples")
if err != nil {
fail("error retrieving examples from metadata: %v\n", err)
}
meta.GetRule().CloudFormation.GoodExamples = []string{value}
}

tmpl, err := template.New("cloudformation").Parse(cloudformationMarkdownTemplate)
if err != nil {
fail("error occurred creating the template %v\n", err)
}
file, err := os.Create(filepath.Join(docpath, "CloudFormation.md"))
if err != nil {
fail("error occurred creating the CloudFormation file for %s", docpath)
}
defer func() { _ = file.Close() }()

if err := tmpl.Execute(file, meta.GetRule()); err != nil {
fail("error occurred generating the document %v", err)
}
fmt.Printf("Generating CloudFormation file for policy %s\n", meta.GetRule().AVDID)
}
}
}

func fail(msg string, args ...interface{}) {
fmt.Printf(msg, args...)
os.Exit(1)
}

func readFileFromPolicyFS(path string) (io.Reader, error) {
path = strings.TrimPrefix(path, "rules/")
return rules.EmbeddedPolicyFileSystem.Open(path)

}

func GetExampleValueFromFile(filename string, exampleType string) (string, error) {
r, err := readFileFromPolicyFS(filename)
if err != nil {
return "", err
}
f, err := parser.ParseFile(token.NewFileSet(), filename, r, parser.AllErrors)
if err != nil {
return "", err
}

for _, d := range f.Decls {
switch decl := d.(type) {
case *goast.GenDecl:
for _, spec := range decl.Specs {
switch spec := spec.(type) {
case *goast.ValueSpec:
for _, id := range spec.Names {
switch v := id.Obj.Decl.(*goast.ValueSpec).Values[0].(type) {
case *goast.CompositeLit:
value := v.Elts[0].(*goast.BasicLit).Value
if strings.Contains(id.Name, exampleType) {
return strings.ReplaceAll(value, "`", ""), nil
}
}
}
}
}
}
}
return "", fmt.Errorf("exampleType %s not found in file: %s", exampleType, filename)
}

var docsMarkdownTemplate = `
{{ .Explanation }}

### Impact
{{ if .Impact }}{{ .Impact }}{{ else }}<!-- Add Impact here -->{{ end }}

<!-- DO NOT CHANGE -->
{{ ` + "`{{ " + `remediationActions ` + "`}}" + `}}

{{ if .Links }}### Links{{ range .Links }}
- {{ . }}
{{ end}}
{{ end }}
`

var terraformMarkdownTemplate = `
{{ .Resolution }}

{{ if .Terraform.GoodExamples }}{{ range .Terraform.GoodExamples }}` + "```hcl" + `{{ . }}
` + "```" + `
{{ end}}{{ end }}
{{ if .Terraform.Links }}#### Remediation Links{{ range .Terraform.Links }}
- {{ . }}
{{ end}}{{ end }}
`

var cloudformationMarkdownTemplate = `
{{ .Resolution }}

{{ if .CloudFormation.GoodExamples }}{{ range .CloudFormation.GoodExamples }}` + "```yaml" + `{{ . }}
` + "```" + `
{{ end}}{{ end }}
{{ if .CloudFormation.Links }}#### Remediation Links{{ range .CloudFormation.Links }}
- {{ . }}
{{ end}}{{ end }}
`
Loading