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

feat: add examples of custom checks #295

Merged
merged 3 commits into from
Nov 30, 2024
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 Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ rego: fmt-rego test-rego

.PHONY: fmt-rego
fmt-rego:
opa fmt -w lib/ checks/
opa fmt -w lib/ checks/ examples/

.PHONY: test-rego
test-rego:
go run ./cmd/opa test --explain=fails lib/ checks/ --ignore '*.yaml'
go run ./cmd/opa test --explain=fails lib/ checks/ examples/ --ignore '*.yaml'

.PHONY: bundle
bundle: create-bundle verify-bundle
Expand Down
3 changes: 3 additions & 0 deletions examples/cloudformation/data/tags.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
required_tags:
- Environment
- Owner
48 changes: 48 additions & 0 deletions examples/cloudformation/ensure_required_tags.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# METADATA
# title: Ensure resources have required tags
# description: |
# Ensure that all resources in the CloudFormation template have the required tags such as "Environment" and "Owner".
# These tags help in resource tracking, management, and categorization, making it easier to automate processes
# and manage AWS infrastructure.
# scope: package
# related_resources:
# - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/working-with-templates.html
# custom:
# id: USR-CF-0001
# avd_id: USR-CF-0001
# severity: MEDIUM
# short_code: ensure-required-tags
# recommended_action: Ensure all resources are tagged with the appropriate metadata tags to facilitate resource management.
# input:
# selector:
# - type: json
package user.cf.ensure_required_tags

import data.required_tags

import rego.v1

deny contains res if {
some resource in input.Resources
not resource.Tags
res := result.new(
sprintf("Resource $q does not have required tags %v", [resource.Type, required_tags]),
{},
)
}

deny contains res if {
some resource in input.Resources
some required_tag in required_tags
not has_required_tag(resource.Tags, required_tag)
res := result.new(
sprintf("Resource %q does not have the required %q tag", [resource.Type, required_tag]),
{},
)
}

# Helper function to check if the tag exists
has_required_tag(tags, tag_name) if {
some tag in tags
tag.Key == tag_name
}
80 changes: 80 additions & 0 deletions examples/cloudformation/ensure_required_tags_test.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package user.cf.ensure_required_tags_test

import rego.v1

import data.user.cf.ensure_required_tags as check

test_deny_resources_without_tags if {
inp := {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {"MyEC2Instance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t2.micro",
"ImageId": "ami-0abcdef1234567890",
},
}},
}

res := check.deny with input as inp with data.required_tags as {
"Environment",
"Owner",
}

count(res) == 1
}

test_deny_resources_without_required_tags if {
inp := {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {"MyEC2Instance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t2.micro",
"ImageId": "ami-0abcdef1234567890",
},
"Tags": [
{
"Key": "Foo",
"Value": "foo",
},
{
"Key": "Bar",
"Value": "bar",
},
],
}},
}

res := check.deny with input as inp with data.required_tags as {
"Environment",
"Owner",
}
count(res) == 2
}

test_allow_resources_with_required_tags if {
inp := {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {"MyEC2Instance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t2.micro",
"ImageId": "ami-0abcdef1234567890",
},
"Tags": [
{
"Key": "Environment",
"Value": "Production",
},
{
"Key": "Owner",
"Value": "JohnDoe",
},
],
}},
}

res := check.deny with input as inp
res == set()
}
12 changes: 12 additions & 0 deletions examples/cloudformation/template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"MyEC2Instance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t2.micro",
"ImageId": "ami-0abcdef1234567890"
}
}
}
}
9 changes: 9 additions & 0 deletions examples/docker-compose/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: '3.8'

services:
web:
build: .
ports:
- "8000:5000"
redis:
image: "redis:latest"
30 changes: 30 additions & 0 deletions examples/docker-compose/latest_tag.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# METADATA
# title: Avoid using 'latest' tag in container images
# description: |
# The 'latest' tag in container images does not guarantee consistency across deployments.
# Using explicit version tags ensures that the exact image version is used,
# reducing the risk of unexpected changes or vulnerabilities in your environment.
#
# Avoid using 'latest' in your `docker-compose.yaml` files to maintain predictable deployments.
# scope: package
# related_resources:
# - https://docs.docker.com/reference/compose-file/services/#image
# custom:
# id: USR-COMPOSE-0001
# avd_id: USR-COMPOSE-0001
# provider: generic
# severity: MEDIUM
# short_code: avoid-latest-tag
# recommended_action: Use specific image tags instead of 'latest' for reliable deployments.
# input:
# selector:
# - type: yaml
package user.compose.latest_tag

import rego.v1

deny contains res if {
some name, service in input.services
endswith(service.image, ":latest")
res := result.new(sprintf("Avoid using 'latest' tag in container image for %q", [name]), {})
}
31 changes: 31 additions & 0 deletions examples/docker-compose/latest_tag_test.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package user.compose.latest_tag_test

import rego.v1

import data.user.compose.latest_tag as check

test_deny_latest_tag if {
inp := {"services": {
"web": {
"build": ".",
"ports": ["8000:5000"],
},
"redis": {"image": "redis:latest"},
}}

res := check.deny with input as inp
count(res) == 1
}

test_allow_specific_tag if {
inp := {"services": {
"web": {
"build": ".",
"ports": ["8000:5000"],
},
"redis": {"image": "redis:7.4"},
}}

res := check.deny with input as inp
res == set()
}
5 changes: 5 additions & 0 deletions examples/dockerfile/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM ubuntu:20.04

RUN apt-get update && apt-get install curl

CMD ["bash"]
50 changes: 50 additions & 0 deletions examples/dockerfile/avoid_unstable_packages.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# METADATA
# title: Avoid installing packages without specific versions
# description: |
# Installing packages without specifying the version can lead to instability or unexpected behavior.
# Always specify the exact version of the package to ensure predictable builds and avoid pulling in unintended updates.
# schemas:
# - input: schema["dockerfile"]
# related_resources:
# - https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#apt-get
# custom:
# id: USR-DF-0001
# avd_id: USR-DF-0001
# severity: HIGH
# short_code: avoid-unstable-packages
# recommended_action: Specify the exact version of packages when using RUN instructions.
# input:
# selector:
# - type: dockerfile
package user.dockerfile.avoid_unstable_packages

import rego.v1

deny contains res if {
some stage in input.Stages
some instruction in stage.Commands

instruction.Cmd == "run"

some val in instruction.Value

# custom function
cmds := sh.parse_commands(val)

some cmd in cmds
cmd[0] == "apt-get"
cmd[1] == "install"

args := array.slice(cmd, 2, count(cmd))

some arg in args

# skip flags
not startswith(arg, "-")
not contains(arg, "=")

res := result.new(
sprintf("Avoid installing package %q without specifying a version.", [arg]),
{},
)
}
43 changes: 43 additions & 0 deletions examples/dockerfile/avoid_unstable_packages_test.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package user.dockerfile.avoid_unstable_packages_test

import rego.v1

import data.user.dockerfile.avoid_unstable_packages as check

test_allow_package_with_pinned_version if {
inp := {"Stages": [{
"Name": "ubuntu",
"Commands": [
{
"Cmd": "run",
"Value": ["apt-get update && apt-get install curl=7.68.0-1ubuntu2.6"],
},
{
"Cmd": "cmd",
"Value": ["bash"],
},
],
}]}

res := check.deny with input as inp
res == set()
}

test_deny_package_without_version if {
inp := {"Stages": [{
"Name": "ubuntu",
"Commands": [
{
"Cmd": "run",
"Value": ["apt-get update && apt-get install curl"],
},
{
"Cmd": "cmd",
"Value": ["bash"],
},
],
}]}

res := check.deny with input as inp
count(res) = 1
}
4 changes: 4 additions & 0 deletions examples/ignore.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package trivy

# disable all built-in checks
ignore := startswith(input.AVDID, "AVD-")
19 changes: 19 additions & 0 deletions examples/kubernetes/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-app-container
image: web-app:1.0
ports:
- containerPort: 80
32 changes: 32 additions & 0 deletions examples/kubernetes/no_deployment_allowed.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# METADATA
# title: Deployment not allowed
# description: |
# This check ensures that Kubernetes Deployments are not used in your environment.
# Deployments may be restricted for various reasons, such as the need to use other controllers (e.g., StatefulSets, DaemonSets) or the avoidance of certain deployment strategies.
#
# Avoid using the 'Deployment' kind to ensure compliance with your organization's deployment strategy.
# scope: package
# schemas:
# - input: schema["kubernetes"]
# related_resources:
# - https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
# custom:
# id: USR-KUBE-0001
# avd_id: USR-KUBE-0001
# severity: HIGH
# short_code: no-deployment-allowed
# recommended_action: Avoid using Kubernetes Deployments. Consider alternative resources like StatefulSets or DaemonSets.
# input:
# selector:
# - type: kubernetes
package user.kubernetes.no_deployment_allowed

import rego.v1

deny contains res if {
input.kind == "Deployment"
res := result.new(
sprintf("Found deployment '%s' but deployments are not allowed", [input.metadata.name]),
input.metadata,
)
}
Loading