# tfsec

## What is TFSec?

* TFSec is an [open-source project from Aqua Security](https://aquasecurity.github.io/tfsec/v1.28.4/)&#x20;
* However, the team is consolidating tfsec into their other solution, [Trivy](https://github.com/aquasecurity/trivy) which offers more comprehensive capabilities&#x20;

## Installation

* `brew install tfsec`
* Other methods can be found in the [official docs](https://aquasecurity.github.io/tfsec/v1.28.4/guides/installation/)

## Scanning

### Local Scanning

* With tfsec installed, we can easily run it to scan a directory of terraform code
* `tfsec /terraform-directory`

```hcl
# Partial output of results

Result #1 HIGH Bucket does not encrypt data with a customer managed key. 
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  s3-cis-req.tf:3-15
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    3  ┌ resource "aws_s3_bucket_server_side_encryption_configuration" "s3-encryption-config" {
    4  │   bucket = aws_s3_bucket.s3-bucket-1.bucket # defines bucket name
    5  │ 
    6  │   rule {
    7  │     apply_server_side_encryption_by_default {
    8  │       sse_algorithm = var.sse-algorithm # defines encryption type
    9  │     }
   10  │   }
   11  └ 
   ..  
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
          ID aws-s3-encryption-customer-key
      Impact Using AWS managed keys does not allow for fine grained control
  Resolution Enable encryption using customer managed keys

  More Information
  - https://aquasecurity.github.io/tfsec/v1.28.4/checks/aws/s3/encryption-customer-key/
  - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket#enable-default-server-side-encryption
  
  [SNIP]

  results
  ──────────────────────────────────────────
  passed               9
  ignored              0
  critical             0
  high                 7
  medium               3
  low                  1

  9 passed, 11 potential problem(s) detected.
```

### CI Job

tfsec can be added as a job to a CI pipeline. For example, the code below can be used for GitLab. Just add it to `.gitlab-ci.yml`.&#x20;

```yaml
# example leveraging the docker image

stages:
  - iac_scanning
  - prep
  - plan
  - deploy
  
code_scanning:
  stage: iac_scanning
  image:
    name: aquasec/tfsec-ci:latest
  script: tfsec . -f markdown --out $CI_PROJECT_DIR/tfsec_findings.md
  allow_failure: true
  artifacts:
    when: always
    expire_in: 1 day
    paths:
      - tfsec_findings.md
```

```yaml
# example downloading the tool

stages:
  - iac_scanning
  - prep
  - plan
  - deploy

code_scanning:
  stage: iac_scanning
  image:
    name: ubuntu:latest
  script:
    - apt-get update && apt-get install -y curl
    - curl -s https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash
    - tfsec . -f markdown --out $CI_PROJECT_DIR/tfsec_findings.md
  allow_failure: true
  artifacts:
    when: always
    expire_in: 1 day
    paths:
      - tfsec_findings.md
```

## Creating Exceptions

We can easily make exceptions by adding this line of code directly above the resource. Hint, if you use the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=tfsec.tfsec), you can just click a button to do this for you. When the scan runs again, this finding will be ignored.

```hcl
#tfsec:ignore:aws-s3-encryption-customer-key
resource "aws_s3_bucket_server_side_encryption_configuration" "tpcrc" {
```

Additionally, we can set an expiration date to suppress the finding until after the date e.g., Mar 2, 2024.&#x20;

```bash
#tfsec:ignore:aws-s3-encryption-customer-key:exp:2024-03-02
resource "aws_s3_bucket_server_side_encryption_configuration" "tpcrc" {
```

## Config File

The tfsec config file enables us to modify a rule's severity level or exclude a rule from a scan completely. This is useful when a rule is not applicable or the severity should be lowered for a dev environment vs a prod environment.&#x20;

### Overriding the Severity

This rule below is being modified from the original "HIGH" finding to a "LOW" finding.&#x20;

{% code overflow="wrap" %}

```json
# tfsec.json
{
    "severity_overrides": {
        "AWS001": "ERROR",
        "aws-s3-encryption-customer-key": "LOW"
    }
}
```

{% endcode %}

The custom config file can be leveraged during a scan. The next time this finding is discovered, it will be reported with a LOW severity.

```bash
tfsec --config-file tfsec.json
```

### Excluding a Rule from a Scan

Sometimes you may want to exclude a rule from a scan completely.

```json
#tfsec.json
{
    "severity_overrides": {
        "AWS001": "ERROR",
        "aws-s3-encryption-customer-key": "LOW"
    },
    "exclude": [
        "AWS002",
        "aws-s3-enable-bucket-encryption"
    ]
}
```

Again, leveraging the custom config file will result in the rule `aws-s3-enable-bucket-encryption` being excluded from the findings.

## Custom tfsec Rules

tfsec supports custom rules written in JSON, YAML, or Rego. Custom rules are useful when you want to enforce security policies specific to your organization or when out-of-the-box rules don't meet your requirements.

### JSON Format

This rule ensures an S3 bucket has a business owner tag.

```json
# _tfchecks.json

{
    "checks": [
        {
            "code": "No Business Owner Tag - CUS001",
            "description": "Custom check to ensure buckets are tagged with a business owner.",
            "impact": "By not having a business owner tag, it is difficult to determine who is responsible for the bucket.",
            "resolution": "Add the businessOwner tag.",
            "requiredTypes": [
                "resource"
            ],
            "requiredLabels": [
                "aws_s3_bucket"
            ],
            "severity": "CRITICAL",
            "matchSpec": {
                "name": "tags",
                "action": "contains",
                "value": "businessOwner"
            },
            "errorMessage": "The required businessOwner tag was missing."
        }
    ]
}
```

The custom rule can be leveraged during a scan. This assumes the terraform code and custom rule are in the same directory but this doesn't have to be true.&#x20;

```bash
tfsec --custom-check-dir .
```

### YAML Format

The same [rule as above](#json-format) but in YAML.

```yaml
---
checks:
  - code: No Business Owner Tag - CUS001
    description: Custom check to ensure buckets are tagged with a business owner.
    impact: By not having a business owner tag, it is difficult to determine who is responsible for the bucket.
    resolution: Add the businessOwner tag.
    requiredTypes:
      - resource
    requiredLabels:
      - aws_s3_bucket
    severity: CRITICAL
    matchSpec:
      name: tags
      action: contains
      value: businessOwner
    errorMessage: The required businessOwner tag was missing.
```

### Rego Format

tfsec allows for writing policies in Rego format but not all arguments are supported. In this case, we cannot write a tfsec rego policy that checks for tags. Under the hood, tfsec is utilizing the open-source project [defsec](https://github.com/aquasecurity/defsec) and tags are not supported for an S3 bucket. You can view what is [supported here](https://github.com/aquasecurity/defsec/blob/v0.94.1/pkg/providers/aws/s3/bucket.go). Additionally, you can see all the values and their current configuration by running this command, `tfsec --print-rego-input | jq '.aws.s3.buckets[0]'` which will return an output similar to below.

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FDks22fheOtOBQVwpaEzL%2Fimage.png?alt=media&#x26;token=fcbbb708-5592-42c4-9629-abeadbe3ea76" alt=""><figcaption></figcaption></figure>

An example of a supported check would be a Rego policy that checks S3 bucket versioning is enabled.

```rego
package custom.aws.s3.versioning_enabled

deny[res] {
    bucket := input.aws.s3.buckets[_]
    bucket.versioning.enabled.value == false
    msg := "Bucket should have versioning enabled."
    res := result.new(msg, bucket.versioning.enabled)
}
```

## Useful Resources

{% embed url="<https://aquasecurity.github.io/tfsec/latest/>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.techwithtyler.dev/devsecops/iac-scanning/tfsec.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
