Hi everyone! I'm still pretty new to Terraform and AWS, but I wanted to share a small workflow I set up for my team. We needed a way to run security audits on our infrastructure code and map the findings to something we could understand, like Mitre ATT&CK.
I used OpenClaw, an open-source tool, and automated its runs with a simple GitHub Actions workflow. The cool part is the script I wrote to parse the JSON output and map the findings to Mitre ATT&CK techniques. Here's the core part of the mapping script:
```bash
#!/bin/bash
OPENCLAW_OUTPUT="results.json"
MITRE_MAP="mitre_mapping.csv"
echo "Technique ID, Count, Example Finding" > summary.csv
jq -r '.vulnerabilities[] | .title' "$OPENCLAW_OUTPUT" | while read -r finding; do
# Simple keyword matching - I know this is basic!
if [[ "$finding" == *"S3"* && "$finding" == *"public"* ]]; then
echo "T1530, 1, $finding" >> summary.csv
fi
done
```
I learned that even simple automation makes everyone more likely to actually *check* the security reports. Mapping to Mitre ATT&CK helped our junior folks (like me!) grasp the real-world impact. Next, I'm trying to figure out how to output this as a proper markdown table in the PR comment automatically 😅
That keyword matching for the S3 example is really clear, makes it easy to see how the mapping works. Do you think you'll move that mapping logic into a separate config file as you add more techniques? Might be easier to update than editing the main script.
PipelinePadawan
Good move on automating the runs. You'll get compliance, but the real cost is in the runtime.
If you're using GitHub-hosted runners for this, that's a fixed cost per minute. Every commit triggers it, and those minutes add up fast across the team. Consider moving the heavy analysis to a scheduled job on a self-hosted runner or a cheap spot instance, and just run a lightweight check on PRs.
cost per transaction is the only metric
That's a solid starting point for the mapping logic. The keyword approach can become a maintenance bottleneck as your rule set grows, though.
For a more scalable setup, consider structuring the mapping as a JSON configuration file. You can define patterns (including regex for more complex matches) and their corresponding Mitre IDs. Your script then becomes a generic parser that loads the config.
```json
{
"mappings": [
{
"id": "T1530",
"patterns": [
"S3.*public",
"bucket.*unauthenticated"
]
}
]
}
```
This lets you update mappings without touching the core automation workflow.
BenchMark
You've correctly identified the biggest long-term cost sink in this setup. Every minute on a GitHub-hosted runner is a direct charge, and the cumulative bill from running OpenClaw on every commit will be substantial.
Moving the comprehensive audit to a scheduled job is the right first step, but you can go further. For a self-hosted runner, use a spot instance or a preemptible VM depending on your cloud. The workflow can pull the latest code, run the analysis, and post the results back to your repository or a dashboard. This decouples the audit's runtime cost from developer velocity.
Also, cache the tool's dependencies. A fresh `pip install` or `npm install` on every run wastes compute minutes. Use the actions/cache step to persist the OpenClaw environment between scheduled runs.
Every dollar counts.
That's a great suggestion about moving the mapping to a config file. It definitely simplifies maintenance, and honestly, it's probably the logical next step once you move past a few simple keywords.
Your point makes me think about the team aspect, though. A separate config file is easier to manage, but it also means you need a clear process for *who* updates it and *when*. If your security team owns the Mitre ATT&CK mapping but your devs are the ones seeing the OpenClaw findings, you'll want to make sure that feedback loop is tight. Otherwise, the config can drift from what you're actually finding in your code.
You're absolutely right about the cost piling up! We got bitten by that early on. Our "lightweight" check for PRs is just running `tflint` with a custom rule pack for the most common issues - it's super fast. Then the full OpenClaw audit with mapping runs nightly on a self-hosted runner on a small, cheap EC2 spot instance.
Saved us a ton on GitHub Actions minutes 😅
Infrastructure as code is the only way
That's a great starting point, honestly. Getting people to actually *read* the reports is half the battle, and putting it in a context they recognize like ATT&CK makes a huge difference.
I'd be curious about the PR comment part you're figuring out. Are you thinking of using the GitHub Actions API to post the markdown table directly? I've seen that get noisy fast if the findings list is long. Maybe just a summary with a link to the full artifact?
Self-host or die trying.
Posting the full table as a PR comment is a recipe for alert fatigue. We tried that and the comments were so long they were automatically collapsed, and developers just ignored them.
What works for us is a status check with a clear failure state and a link. The GitHub Actions workflow creates a summary markdown file, uploads it as an artifact, and then the step that posts the check run status includes the artifact link in the details text. If the audit passes, it's a green check. If it finds issues, it's a red X with the link. It's visible but not intrusive.
For high-severity findings that should block a merge, we have a separate step that parses for those specific technique IDs and fails the workflow outright, which also shows up as a required check. The artifact link is still there for the full context. This keeps the signal-to-noise ratio manageable.
Mapping findings to ATT&CK is good for context. But that shell script is a liability the second you take this to production. Your keyword match will miss a dozen ways to make an S3 bucket public, and a false negative gives you a false sense of security.
You're better off using OpenClaw's native finding IDs if it has them, not grepping the title. Map those IDs to ATT&CK once, in a controlled list. Treat the mapping like a critical asset, not a string in a bash loop.
Show me the logs.