Skip to content

Commit

Permalink
Merge pull request #33 from aquasecurity/update-docs
Browse files Browse the repository at this point in the history
chore(docs): Update docs
  • Loading branch information
simar7 authored Nov 4, 2023
2 parents 7fcb7b1 + 385f836 commit 275c474
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 7 deletions.
14 changes: 14 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Architecture

This document aims to answer the question *Where is the code that does X?*

## Project Layout

The directory structure is broken down as follows:

- `cmd/` - These CLI tools are primarily used during development for end-to-end testing without needing to pull the library into trivy/tfsec etc.
- `rules` - All of the rules and policies are defined in this directory.
- `pkg/spec` - Logic to handle standardized specs such as CIS.
- `pkg/rules` - This package exposes internal rules, and imports them accordingly (see _rules.go_).
- `test` - Integration tests and other high-level tests that require a full build of the project.
- `scripts` - Usefule generation scripts for bundle generation and verification purposes.
95 changes: 95 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Contributing

Welcome, and thank you for considering contributing to trivy-policies!

The following guide gives an overview of the project and some directions on how to make common types of contribution. If something is missing, or you get stuck, please [start a discussion](https://github.com/aquasecurity/trivy/discussions/new) and we'll do our best to help.

### Writing Rules

Writing a new rule can be relatively simple, but there are a few things to keep in mind. The following guide will help you get started.

First of all, you should check if the provider your rule targets is supported by _defsec_. If it's not, you'll need to add support for it. See [Adding Support for a New Cloud Provider](https://github.com/aquasecurity/defsec/blob/master/CONTRIBUTING.md#adding-support-for-a-new-cloud-provider) for more information. You can check if support exists by looking for a directory with the provider name in `pkg/providers`. If you find your provider, navigate into the directory and check for a directory with the name of the service you're targeting. If you can't find that, you'll need to add support for it. See [Adding Support for a New Service](https://github.com/aquasecurity/defsec/blob/master/CONTRIBUTING.md#adding-support-for-a-new-service) for more information.

Next up, you'll need to check if the properties you want to target are supported, and if not, add support for them. The guide on [Adding Support for a New Service](https://github.com/aquasecurity/defsec/blob/master/CONTRIBUTING.md#adding-support-for-a-new-service) covers adding new properties.

At last, it's time to write your rule code! Rules are defined using _OPA Rego_. You can find a number of examples in the `rules/cloud/policies` directory. The [OPA documentation](https://www.openpolicyagent.org/docs/latest/policy-language/) is a great place to start learning Rego. You can also check out the [Rego Playground](https://play.openpolicyagent.org/) to experiment with Rego, and [join the OPA Slack](https://slack.openpolicyagent.org/).

Create a new file in `rules/cloud/policies` with the name of your rule. You should nest it in the existing directory structure as applicable. The package name should be in the format `builtin.PROVIDER.SERVICE.ID`, e.g. `builtin.aws.rds.aws0176`.

Running `make id` will provide you with the next available _ID_ for your rule. You can use this ID in your rule code to identify it.

A simple rule looks like the following example:

```rego
# METADATA
# title: "RDS IAM Database Authentication Disabled"
# description: "Ensure IAM Database Authentication is enabled for RDS database instances to manage database access"
# scope: package
# schemas:
# - input: schema["aws"]
# related_resources:
# - https://docs.aws.amazon.com/neptune/latest/userguide/iam-auth.html
# custom:
# avd_id: AVD-AWS-0176
# provider: aws
# service: rds
# severity: MEDIUM
# short_code: enable-iam-auth
# recommended_action: "Modify the PostgreSQL and MySQL type RDS instances to enable IAM database authentication."
# input:
# selector:
# - type: cloud
# subtypes:
# - service: rds
# provider: aws
package builtin.aws.rds.aws0176
deny[res] {
instance := input.aws.rds.instances[_]
instance.engine.value == ["postgres", "mysql"][_]
not instance.iamauthenabled.value
res := result.new("Instance does not have IAM Authentication enabled", instance.iamauthenabled)
}
```

In fact, this is the code for an actual rule. You can find it in `rules/cloud/policies/aws/rds/enable_iam_auth.rego`.

The metadata is the top section that starts with `# METADATA`, and is fairly verbose. You can copy and paste from another rule as a starting point. This format is effectively _yaml_ within a Rego comment, and is [defined as part of Rego itself](https://www.openpolicyagent.org/docs/latest/policy-language/#metadata).

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.
- `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`.
- `service` is the name of the service the rule targets. This should be the same as the service name in the `pkg/providers` directory, e.g. `rds`.
- `severity` is the severity of the rule. This should be one of `LOW`, `MEDIUM`, `HIGH`, or `CRITICAL`.
- `short_code` is a short code for the rule. This should be a short, descriptive name for the rule, separating words with hyphens. You should omit provider/service from this.
- `recommended_action` is a recommended remediation action for the rule. This should be a short, descriptive sentence describing what the user should do to resolve the issue.
- `input` tells _defsec_ what inputs this rule should be applied to. Cloud provider rules should always use the `selector` input, and should always use the `type` selector with `cloud`. Rules targeting Kubernetes yaml can use `kubenetes`, RBAC can use `rbac`, and so on.
- `subtypes` aid the engine to determine if it should load this policy or not for scanning. This can aid with the performance of scanning, especially if you have a lot of checks but not all apply to the IaC that you are trying to scan.

Now you'll need to write the rule logic. This is the code that will be executed to detect the issue. You should define a rule named `deny` and place your code inside this.

```rego
deny[res] {
instance := input.aws.rds.instances[_]
instance.engine.value == ["postgres", "mysql"][_]
not instance.iamauthenabled.value
res := result.new("Instance does not have IAM Authentication enabled", instance.iamauthenabled)
}
```

The rule should return a result, which can be created using `result.new` (this function does not need to be imported, it is defined internally and provided at runtime). The first argument is the message to display, and the second argument is the resource that the issue was detected on.

In the example above, you'll notice properties are being accessed from the `input.aws` object. The full set of schemas containing all of these properties is [available here](https://github.com/aquasecurity/defsec/tree/master/pkg/rego/schemas). You can match the schema name to the type of input you want to scan.

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.

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).
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,11 @@ bundle:
go run ./scripts/verify-bundle.go
rm scripts/bundle.tar.gz

.PHONY: id
id:
@go run ./cmd/id

.PHONY: outdated-api-updated
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
sed -i.bak "s|recommendedVersions :=.*|recommendedVersions := $(OUTDATE_API_DATA)|" $(DYNAMIC_REGO_FOLDER)/outdated_api.rego && rm $(DYNAMIC_REGO_FOLDER)/outdated_api.rego.bak

9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# trivy-policies

_trivy-policies_ contains misconfiguration checks for Trivy

Please see [ARCHITECTURE.md](ARCHITECTURE.md) for more information.

_trivy-aws_ is an [Aqua Security](https://aquasec.com) open source project.
Learn about our open source work and portfolio [here](https://www.aquasec.com/products/open-source-projects/).
Join the community, and talk to us about any matter in [GitHub Discussion](https://github.com/aquasecurity/trivy/discussions).
52 changes: 52 additions & 0 deletions cmd/id/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

import (
"fmt"
"os"
"sort"
"strconv"
"strings"

"github.com/aquasecurity/defsec/pkg/framework"

_ "github.com/aquasecurity/trivy-iac/pkg/rego"
"github.com/aquasecurity/trivy-iac/pkg/rules"
)

func main() {

// organise existing rules by provider
keyMap := make(map[string][]string)
for _, rule := range rules.GetRegistered(framework.ALL) {
id := rule.GetRule().AVDID
if id == "" {
continue
}
parts := strings.Split(id, "-")
if len(parts) != 3 {
continue
}
keyMap[parts[1]] = append(keyMap[parts[1]], parts[2])
}

fmt.Print("\nThe following IDs are free - choose the one for the service you are targeting.\n\n")

var freeIDs []string
for key := range keyMap {
sort.Strings(keyMap[key])
all := keyMap[key]
max := all[len(all)-1]
i, err := strconv.Atoi(max)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Error, invalid AVD ID: AVD-%s-%s\n", key, max)
}
free := fmt.Sprintf("AVD-%s-%04d", key, i+1)
freeIDs = append(freeIDs, fmt.Sprintf("%16s: %s", key, free))
}

sort.Slice(freeIDs, func(i, j int) bool {
return strings.TrimSpace(freeIDs[i]) < strings.TrimSpace(freeIDs[j])
})
fmt.Println(strings.Join(freeIDs, "\n"))

}
31 changes: 29 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ module github.com/aquasecurity/trivy-policies
go 1.20

require (
github.com/aquasecurity/defsec v0.93.2-0.20231020041402-7ccc46780c09
github.com/aquasecurity/defsec v0.93.2-0.20231024055158-015ab97ce898
github.com/aquasecurity/trivy-iac v0.5.2
github.com/docker/docker v24.0.7+incompatible
github.com/liamg/iamgo v0.0.9
github.com/liamg/memoryfs v1.6.0
Expand All @@ -18,13 +19,17 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.11.1 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/alecthomas/chroma v0.10.0 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/containerd/containerd v1.7.7 // indirect
github.com/containerd/log v0.1.0 // indirect
Expand All @@ -38,40 +43,61 @@ require (
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.4.1 // indirect
github.com/go-git/go-git/v5 v5.8.1 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/hashicorp/hcl/v2 v2.18.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/hashicorp/hcl/v2 v2.18.1 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/liamg/jfather v0.0.7 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/open-policy-agent/opa v0.57.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/opencontainers/runc v1.1.5 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/shirou/gopsutil/v3 v3.23.9 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.2.0 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yashtewari/glob-intersection v0.2.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
github.com/zclconf/go-cty v1.13.0 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/sdk v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect
golang.org/x/mod v0.10.0 // indirect
Expand All @@ -84,4 +110,5 @@ require (
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
Loading

0 comments on commit 275c474

Please sign in to comment.