Hey everyone, I've been spending more time writing custom Semgrep rules for our security reviews, especially around AWS infrastructure-as-code. One of the biggest recurring issues we catch is hardcoded secrets, like AWS access keys, getting committed. While Semgrep has great built-in rules, I wanted to share a few custom ones I wrote that are a bit more tailored to our Terraform and CloudFormation setups.
These rules look for patterns beyond just the obvious `AKIA` strings. They also check for likely variable names and specific resource attributes where keys shouldn't be hardcoded.
Here's an example rule that looks for hardcoded keys in Terraform `aws` provider blocks and common variable defaults:
```yaml
rules:
- id: terraform-hardcoded-aws-access-key
message: Hardcoded AWS access key ID detected in Terraform.
languages:
- hcl
severity: ERROR
patterns:
- pattern: |
provider "aws" {
...,
access_key = "$KEY",
...
}
- pattern: |
variable "aws_access_key" {
...,
default = "$KEY"
}
paths:
include:
- "*.tf"
metavariable-regex:
metavariable: $KEY
regex: '(AKIA|ASIA)[A-Z0-9]{16}'
```
Another useful one catches them in CloudFormation `AWS::IAM::User` properties or `AWS::SecretsManager::Secret` string values. The key is combining the regex with the specific YAML/JSON paths.
I've found layering these with the generic secret detectors really cuts down on false positives. A few benefits of this approach:
* It catches keys in the specific places our teams tend to accidentally hardcode them.
* The rules run in our pre-commit hooks and CI pipeline, giving fast feedback.
* We've extended a few to also flag potential secret ARNs that look like they're pointing to a hardcoded value instead of a dynamic reference.
Has anyone else built similar custom rules? I'm curious if you've found other sneaky places where keys tend to hide, or if you've integrated these findings into your observability dashboards.
-- Amy
Cloud cost nerd. No, I don't use Reserved Instances.
Custom rules are clever, but this is treating the symptom, not the disease. You're building a better mousetrap for a rodent problem you invited into your house by hardcoding credentials in the first place.
Your example rule scans provider blocks, but what about module sources? Or local-exec provisioners pulling from scripts? The developer who pastes a key into a 'secret' variable named `environment_specific_token` will sail right past your regex. I've seen more keys leaked through 'temporary' workarounds in CI/CD scripts than in actual Terraform.
The real cost isn't the detection - it's the cultural expectation that static analysis will catch everything. You've now added another tool the team has to maintain, with its own false positives and tuning cycles. Did you factor the hours spent writing and tweaking these YAML files against just enforcing a vault-first pattern with a pre-commit hook that blocks any string matching the AKIA pattern, full stop?
Test the migration.
Look, your rule is catching keys in provider blocks and variable defaults. That's fine as far as it goes. But you're missing the point user330 was making. You patched one specific crack in the wall. What about all the other plaster? A determined engineer will just move the secret to a `local_file` data source, a heredoc in a null_resource, or a comment they think is clever. Static analysis is a compliance checkbox, not a security strategy. The real fix is removing the ability to commit keys at all via pre-commit hooks and environment enforcement. Otherwise, you're just playing whack-a-mole with your own team.
Trust but verify
I get where you're coming from, and the cultural point is huge - you can't scan your way into security. But I've found these custom rules are actually a bridge to that culture shift.
When a junior dev gets a Semgrep finding in their PR that says "Hey, this looks like a secret in your local-exec script," it becomes a concrete teachable moment. Way more effective than a blanket pre-commit rejection they'll just try to circumvent. It's like showing them the smoke detector is working.
That said, you're right about the maintenance trap. I spent an afternoon last week chasing a false positive on a variable named `kiam_pod_identity`. The trick is to keep these rules simple and focused on the most common, high-risk patterns we actually see. If I'm spending more than an hour a month tweaking them, the process is broken. The goal is to eventually replace them with stricter guardrails, not maintain a forever-ruleset.
cost first, then scale