Skip to content

Commit f2fa090

Browse files
committed
Add support for tfsec evidence integration
1 parent 0a094f2 commit f2fa090

4 files changed

Lines changed: 230 additions & 0 deletions

File tree

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: "tfsec evidence Integration example"
2+
3+
on:
4+
workflow_dispatch:
5+
6+
permissions:
7+
id-token: write
8+
contents: read
9+
10+
jobs:
11+
package-terraform-with-tfsec-evidence:
12+
runs-on: ubuntu-latest
13+
env:
14+
ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE: true
15+
steps:
16+
# Build and publish the packages to JFrog Artifactory
17+
- name: Setup jfrog cli
18+
uses: jfrog/setup-jfrog-cli@v4
19+
env:
20+
JF_URL: ${{ vars.ARTIFACTORY_URL }}
21+
JF_ACCESS_TOKEN: ${{ secrets.ARTIFACTORY_ACCESS_TOKEN }}
22+
- uses: actions/checkout@v4
23+
with:
24+
sparse-checkout: |
25+
examples/tfsec/**
26+
sparse-checkout-cone-mode: false
27+
- name: Publish to JFrog Artifactory
28+
run: |
29+
jf tfc --repo-deploy tf-local \
30+
--server-id-deploy setup-jfrog-cli-server
31+
jf tf p --namespace example \
32+
--provider aws \
33+
--tag v0.0.${{ github.run_number }} \
34+
--build-name my-tf-build \
35+
--build-number ${{ github.run_number }}
36+
jf rt bp my-tf-build ${{ github.run_number }}
37+
38+
# Run tfsec to scan Terraform code for security issues
39+
- name: Run tfsec
40+
uses: aquasecurity/tfsec-action@v1.0.0
41+
with:
42+
additional_args: --format json --out tfsec.json
43+
soft_fail: true
44+
45+
# This is an optional step to generate a custom markdown report
46+
- name: Generate optional custom markdown report
47+
if: env.ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE == 'true'
48+
run: |
49+
pwd
50+
ls -al
51+
python ./examples/tfsec/tfsec_json_to_markdown_helper.py tfsec.json
52+
53+
# Attaching the evidence to associated package
54+
- name: Attach evidence using jfrog cli
55+
run: |
56+
jf evd create \
57+
--build-name my-tf-build \
58+
--build-number ${{ github.run_number }} \
59+
--key "${{ secrets.PRIVATE_KEY }}" \
60+
--key-alias "${{ vars.EVIDENCE_KEY_ALIAS }}" \
61+
--predicate ./tfsec.json \
62+
--predicate-type http://aquasec.com/tfsec/security-scan \
63+
${{ env.ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE == 'true' && '--markdown "tfsec.md"' || '' }}

examples/tfsec/README.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# TFSec Security Scan Evidence Example
2+
3+
This example demonstrates how to automate TFSec security scanning for Terraform code and attach the scan results as
4+
signed evidence to the package in JFrog Artifactory using GitHub Actions and JFrog CLI.
5+
6+
## Overview
7+
8+
The workflow scans Terraform code with TFSec for security issues, publishes the package to Artifactory, and
9+
attaches the TFSec scan results as evidence to the package. This enables traceability and compliance for security
10+
scanning in your CI/CD pipeline.
11+
12+
## Prerequisites
13+
14+
- JFrog CLI 2.65.0 or above (installed automatically in the workflow)
15+
- Artifactory configured as a repository
16+
- The following GitHub repository variables:
17+
- `ARTIFACTORY_URL` (Artifactory base URL)
18+
- `EVIDENCE_KEY_ALIAS` (Key alias for signing evidence)
19+
- The following GitHub repository secrets:
20+
- `ARTIFACTORY_ACCESS_TOKEN` (Artifactory access token)
21+
- `PRIVATE_KEY` (Private key for signing evidence)
22+
23+
## Environment Variables Used
24+
25+
- `ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE` - Whether to attach a custom markdown report to the evidence
26+
27+
## Workflow Steps
28+
29+
1. **Install JFrog CLI**
30+
- Installs the JFrog CLI using the official GitHub Action.
31+
2. **Checkout Repository**
32+
- Checks out the source code for the Terraform package.
33+
3. **Publish Terraform Package to Artifactory**
34+
- Publishes the Terraform package to Artifactory using JFrog CLI.
35+
4. **Run TFSec Security Scan**
36+
- Scans the Terraform code for security issues using TFSec and outputs the results in JSON format.
37+
5. **Generate Custom Markdown For TFSec Results**
38+
- (Optional) Converts the TFSec JSON scan results to markdown format for better readability using a Python script
39+
with a predefined static markdown template.
40+
6. **Attach TFSec Evidence Using JFrog CLI**
41+
- Attaches the TFSec scan results as signed evidence to the Terraform package in Artifactory.
42+
43+
```mermaid
44+
graph TD
45+
A[Workflow Dispatch Trigger] --> B[Setup JFrog CLI]
46+
B --> C[Checkout Repository]
47+
C --> D[Publish Terraform Package to Artifactory]
48+
D --> E[Run TFSec Security Scan]
49+
E --> F{Attach Optional Custom Markdown Report?}
50+
F -->|Yes| G[Generate Custom Markdown Report]
51+
F -->|No| H[Skip Markdown Report]
52+
G --> I[Attach Evidence Using JFrog CLI]
53+
H --> I[Attach Evidence Using JFrog CLI]
54+
```
55+
56+
## Example Usage
57+
58+
You can trigger the workflow manually from the GitHub Actions tab. The workflow will:
59+
60+
- Scan the Terraform code
61+
- Publish the package to Artifactory
62+
- Attach the TFSec scan results as evidence
63+
64+
## Key Commands Used
65+
66+
- **Publish Terraform Package:**
67+
```bash
68+
jf tfc --repo-deploy tf-local \
69+
--server-id-deploy setup-jfrog-cli-server
70+
jf tf p --namespace example \
71+
--provider aws \
72+
--tag v0.0.${{ github.run_number }} \
73+
--build-name my-tf-build \
74+
--build-number ${{ github.run_number }}
75+
jf rt bp my-tf-build ${{ github.run_number }}
76+
```
77+
- **Run TFSec Scan:**
78+
```yaml
79+
uses: aquasecurity/tfsec-action@v1.0.0
80+
with:
81+
additional_args: --format json --out tfsec.json
82+
soft_fail: true
83+
```
84+
- **Attach Evidence:**
85+
```bash
86+
jf evd create \
87+
--build-name my-tf-build \
88+
--build-number ${{ github.run_number }} \
89+
--key "${{ secrets.PRIVATE_KEY }}" \
90+
--key-alias "${{ vars.EVIDENCE_KEY_ALIAS }}" \
91+
--predicate ./tfsec.json \
92+
--predicate-type http://aquasec.com/tfsec/security-scan \
93+
${{ env.ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE == 'true' && '--markdown "tfsec.md"' || '' }}
94+
```
95+
96+
## References
97+
98+
- [TFSec Documentation](https://aquasecurity.github.io/tfsec/)
99+
- [JFrog Evidence Management](https://jfrog.com/help/r/jfrog-artifactory-documentation/evidence-management)
100+
- [JFrog CLI Documentation](https://jfrog.com/getcli/)

examples/tfsec/module/main.tf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
resource "aws_vpc" "main" {
2+
cidr_block = var.vpc_cidr
3+
enable_dns_hostnames = true
4+
tags = {
5+
name = "main"
6+
}
7+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import json
2+
import os
3+
import sys
4+
5+
def generate_readme(json_file_path, output_file_path):
6+
try:
7+
# Read the JSON file
8+
with open(json_file_path, 'r') as json_file:
9+
data = json.load(json_file)
10+
11+
# Extract results
12+
results = data.get("results", [])
13+
# Generate markdown content
14+
markdown_content = f"""
15+
# Detected Vulnerabilities by tfsec
16+
17+
"""
18+
for result in results:
19+
markdown_content += f"## Issue: {result.get('description', 'No description')}\n\n"
20+
markdown_content += f"### Impact\n{result.get('impact', 'No impact information')}\n\n"
21+
markdown_content += "### Links\n"
22+
for link in result.get('links', []):
23+
markdown_content += f"- [{link}]({link})\n"
24+
markdown_content += "\n"
25+
markdown_content += "### Location\n"
26+
location = result.get('location', {})
27+
markdown_content += f"- **File:** {location.get('filename', 'Unknown file')}\n"
28+
markdown_content += f"- **Start Line:** {location.get('start_line', 'Unknown start line')}\n"
29+
markdown_content += f"- **End Line:** {location.get('end_line', 'Unknown end line')}\n\n"
30+
markdown_content += "### Details\n"
31+
markdown_content += f"- **Long ID:** `{result.get('long_id', 'Unknown long ID')}`\n"
32+
markdown_content += f"- **Resolution:** {result.get('resolution', 'No resolution provided')}\n"
33+
markdown_content += f"- **Resource:** `{result.get('resource', 'Unknown resource')}`\n"
34+
markdown_content += f"- **Rule Description:** {result.get('rule_description', 'No rule description')}\n"
35+
markdown_content += f"- **Rule ID:** `{result.get('rule_id', 'Unknown rule ID')}`\n"
36+
markdown_content += f"- **Rule Provider:** `{result.get('rule_provider', 'Unknown rule provider')}`\n"
37+
markdown_content += f"- **Rule Service:** `{result.get('rule_service', 'Unknown rule service')}`\n"
38+
markdown_content += f"- **Severity:** `{result.get('severity', 'Unknown severity')}`\n"
39+
markdown_content += f"- **Status:** `{result.get('status', 'Unknown status')}`\n"
40+
markdown_content += f"- **Warning:** `{result.get('warning', 'Unknown warning')}`\n\n"
41+
42+
# Write to the README file
43+
with open(output_file_path, 'w') as output_file:
44+
output_file.write(markdown_content)
45+
46+
print(f"README file generated successfully at {output_file_path}")
47+
48+
except Exception as e:
49+
print(f"An error occurred: {e}")
50+
51+
if __name__ == "__main__":
52+
if len(sys.argv) != 2:
53+
print("Usage: python tfsec_json_to_markdown_helper.py <input_file>")
54+
sys.exit(1)
55+
# Define paths
56+
json_file_path = sys.argv[1]
57+
output_file_path = "tfsec.md" # Adjust path as needed
58+
59+
# Generate README
60+
generate_readme(json_file_path, output_file_path)

0 commit comments

Comments
 (0)