Skip to content
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
63 changes: 63 additions & 0 deletions .github/workflows/tfsec-evidence-example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: "tfsec evidence Integration example"

on:
workflow_dispatch:

permissions:
id-token: write
contents: read

jobs:
package-terraform-with-tfsec-evidence:
runs-on: ubuntu-latest
env:
ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE: true
steps:
# Build and publish the packages to JFrog Artifactory
- name: Setup jfrog cli
uses: jfrog/setup-jfrog-cli@v4
env:
JF_URL: ${{ vars.ARTIFACTORY_URL }}
JF_ACCESS_TOKEN: ${{ secrets.ARTIFACTORY_ACCESS_TOKEN }}
- uses: actions/checkout@v4
with:
sparse-checkout: |
examples/tfsec/**
sparse-checkout-cone-mode: false
- name: Publish to JFrog Artifactory
run: |
jf tfc --repo-deploy tf-local \
--server-id-deploy setup-jfrog-cli-server
jf tf p --namespace example \
--provider aws \
--tag v0.0.${{ github.run_number }} \
--build-name my-tf-build \
--build-number ${{ github.run_number }}
jf rt bp my-tf-build ${{ github.run_number }}

# Run tfsec to scan Terraform code for security issues
- name: Run tfsec
uses: aquasecurity/tfsec-action@v1.0.0
with:
additional_args: --format json --out tfsec.json
soft_fail: true

# This is an optional step to generate a custom markdown report
- name: Generate optional custom markdown report
if: env.ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE == 'true'
run: |
pwd
ls -al
python ./examples/tfsec/tfsec_json_to_markdown_helper.py tfsec.json

# Attaching the evidence to associated package
- name: Attach evidence using jfrog cli
run: |
jf evd create \
--build-name my-tf-build \
--build-number ${{ github.run_number }} \
--key "${{ secrets.PRIVATE_KEY }}" \
--key-alias "${{ vars.EVIDENCE_KEY_ALIAS }}" \
--predicate ./tfsec.json \
--predicate-type http://aquasec.com/tfsec/security-scan \
${{ env.ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE == 'true' && '--markdown "tfsec.md"' || '' }}
86 changes: 86 additions & 0 deletions examples/tfsec/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# TFSec Security Scan Evidence Example

This example demonstrates how to automate TFSec security scanning for Terraform code and attach the scan results as
signed evidence to the package in JFrog Artifactory using GitHub Actions and JFrog CLI.

## Overview

The workflow scans Terraform code with TFSec for security issues, publishes the package to Artifactory, and
attaches the TFSec scan results as evidence to the package. This enables traceability and compliance for security
scanning in your CI/CD pipeline.

## Prerequisites

- JFrog CLI 2.65.0 or above (installed automatically in the workflow)
- Artifactory configured as a repository
- The following GitHub repository variables:
- `ARTIFACTORY_URL` (Artifactory base URL)
- `EVIDENCE_KEY_ALIAS` (Key alias for signing evidence)
- The following GitHub repository secrets:
- `ARTIFACTORY_ACCESS_TOKEN` (Artifactory access token)
- `PRIVATE_KEY` (Private key for signing evidence)

## Environment Variables Used

- `ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE` - Whether to attach a custom markdown report to the evidence

## Workflow

```mermaid
graph TD
A[Workflow Dispatch Trigger] --> B[Setup JFrog CLI]
B --> C[Checkout Repository]
C --> D[Publish Terraform Package to Artifactory]
D --> E[Run TFSec Security Scan]
E --> F{Attach Optional Custom Markdown Report?}
F -->|Yes| G[Generate Custom Markdown Report]
F -->|No| H[Skip Markdown Report]
G --> I[Attach Evidence Using JFrog CLI]
H --> I[Attach Evidence Using JFrog CLI]
```

## Example Usage

You can trigger the workflow manually from the GitHub Actions tab. The workflow will:

- Scan the Terraform code
- Publish the package to Artifactory
- Attach the TFSec scan results as evidence

## Key Commands Used

- **Publish Terraform Package:**
```bash
jf tfc --repo-deploy tf-local \
--server-id-deploy setup-jfrog-cli-server
jf tf p --namespace example \
--provider aws \
--tag v0.0.${{ github.run_number }} \
--build-name my-tf-build \
--build-number ${{ github.run_number }}
jf rt bp my-tf-build ${{ github.run_number }}
```
- **Run TFSec Scan:**
```yaml
uses: aquasecurity/tfsec-action@v1.0.0
with:
additional_args: --format json --out tfsec.json
soft_fail: true
```
- **Attach Evidence:**
```bash
jf evd create \
--build-name my-tf-build \
--build-number ${{ github.run_number }} \
--key "${{ secrets.PRIVATE_KEY }}" \
--key-alias "${{ vars.EVIDENCE_KEY_ALIAS }}" \
--predicate ./tfsec.json \
--predicate-type http://aquasec.com/tfsec/security-scan \
${{ env.ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE == 'true' && '--markdown "tfsec.md"' || '' }}
```

## References

- [TFSec Documentation](https://aquasecurity.github.io/tfsec/)
- [JFrog Evidence Management](https://jfrog.com/help/r/jfrog-artifactory-documentation/evidence-management)
- [JFrog CLI Documentation](https://jfrog.com/getcli/)
7 changes: 7 additions & 0 deletions examples/tfsec/module/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
tags = {
name = "main"
}
}
60 changes: 60 additions & 0 deletions examples/tfsec/tfsec_json_to_markdown_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import json
import os
import sys

def generate_readme(json_file_path, output_file_path):
try:
# Read the JSON file
with open(json_file_path, 'r') as json_file:
data = json.load(json_file)

# Extract results
results = data.get("results", [])
# Generate markdown content
markdown_content = f"""
# Detected Vulnerabilities by tfsec

"""
for result in results:
markdown_content += f"## Issue: {result.get('description', 'No description')}\n\n"
markdown_content += f"### Impact\n{result.get('impact', 'No impact information')}\n\n"
markdown_content += "### Links\n"
for link in result.get('links', []):
markdown_content += f"- [{link}]({link})\n"
markdown_content += "\n"
markdown_content += "### Location\n"
location = result.get('location', {})
markdown_content += f"- **File:** {location.get('filename', 'Unknown file')}\n"
markdown_content += f"- **Start Line:** {location.get('start_line', 'Unknown start line')}\n"
markdown_content += f"- **End Line:** {location.get('end_line', 'Unknown end line')}\n\n"
markdown_content += "### Details\n"
markdown_content += f"- **Long ID:** `{result.get('long_id', 'Unknown long ID')}`\n"
markdown_content += f"- **Resolution:** {result.get('resolution', 'No resolution provided')}\n"
markdown_content += f"- **Resource:** `{result.get('resource', 'Unknown resource')}`\n"
markdown_content += f"- **Rule Description:** {result.get('rule_description', 'No rule description')}\n"
markdown_content += f"- **Rule ID:** `{result.get('rule_id', 'Unknown rule ID')}`\n"
markdown_content += f"- **Rule Provider:** `{result.get('rule_provider', 'Unknown rule provider')}`\n"
markdown_content += f"- **Rule Service:** `{result.get('rule_service', 'Unknown rule service')}`\n"
markdown_content += f"- **Severity:** `{result.get('severity', 'Unknown severity')}`\n"
markdown_content += f"- **Status:** `{result.get('status', 'Unknown status')}`\n"
markdown_content += f"- **Warning:** `{result.get('warning', 'Unknown warning')}`\n\n"

# Write to the README file
with open(output_file_path, 'w') as output_file:
output_file.write(markdown_content)

print(f"README file generated successfully at {output_file_path}")

except Exception as e:
print(f"An error occurred: {e}")

if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python tfsec_json_to_markdown_helper.py <input_file>")
sys.exit(1)
# Define paths
json_file_path = sys.argv[1]
output_file_path = "tfsec.md" # Adjust path as needed

# Generate README
generate_readme(json_file_path, output_file_path)