Skip to content
Notifications
Clear all

Semgrep onboarding - tips for getting started with custom rules

6 Posts
6 Users
0 Reactions
2 Views
(@cloud_infra_newbie)
Reputable Member
Joined: 4 months ago
Posts: 177
Topic starter   [#10041]

Hi everyone! I'm just starting to use Semgrep for scanning our Terraform code. The built-in rules are great, but I need to write a few custom ones for our AWS setup.

I'm a bit stuck on the basics of writing my own rule. For example, I want to flag any S3 bucket that doesn't have `block_public_acls` set to true. I tried writing a rule but it's not catching what I think it should.

Here's my attempt so far:

```yaml
rules:
- id: no-block-public-acls
patterns:
- pattern: |
resource "aws_s3_bucket_public_access_block" "..." {
...
}
- pattern-not: |
resource "aws_s3_bucket_public_access_block" "..." {
block_public_acls = true
}
message: S3 bucket should have block_public_acls enabled.
severity: WARNING
```

Is this the right approach? Any tips on how to structure these custom rules for Terraform? Also, what's the best way to test them locally before pushing to the registry?



   
Quote
(@carlosm)
Estimable Member
Joined: 1 week ago
Posts: 103
 

Good start, but I think your pattern is looking for the resource block itself, not the missing property. Try using a single pattern with a metavariable. Something like:

pattern: resource "aws_s3_bucket_public_access_block" $NAME { ... }
pattern-not: block_public_acls = true

This way it finds the resource and then flags if that specific line isn't there. The metavariable $NAME helps anchor it.

For testing, run `semgrep --config your-rule.yaml /path/to/test-terraform` locally. I keep a small folder of "bad" test files to verify catches. Also, watch your indentation in the YAML, it can be finicky. Happy to look at your next draft!


Keep automating!


   
ReplyQuote
(@charliep)
Reputable Member
Joined: 1 week ago
Posts: 172
 

You're overcomplicating it. The ellipsis operator ... matches anything, so your pattern-not is also matching the resource declaration itself. It'll never fire.

For a missing property, you need patterns-inside with a metavariable, not pattern-not on the whole block. Something like:

patterns:
pattern-inside: resource "aws_s3_bucket_public_access_block" $NAME { ... }
pattern-not: block_public_acls = true

And test locally with a dummy file that's definitely wrong. The registry's validation is too loose.


Your stack is too complicated.


   
ReplyQuote
(@jordanh)
Estimable Member
Joined: 1 week ago
Posts: 85
 

Hold on, you're throwing away the resource pattern entirely? If the pattern-inside anchors to the block but the pattern-not just looks for the property anywhere inside, won't it incorrectly flag a file that has the property set correctly *and* also has another, completely unrelated resource without it? The ellipsis in the pattern-inside is too permissive.

The real mischief is thinking you can catch a missing property with just a pair of simple patterns. Terraform's nested blocks and dynamic arguments make this a proper logic puzzle. You're basically trying to prove a negative, which Semgrep's patterns alone are annoyingly bad at.

Maybe we're all barking up the wrong tree and should just use a Rego rule for OPA instead. Just a thought


🤷


   
ReplyQuote
(@gracyj)
Trusted Member
Joined: 1 week ago
Posts: 61
 

Ooh, good catch about the over-permissive ellipsis! That's a classic trap. For Terraform, I often use `pattern-either` with explicit property patterns instead of a single `pattern-not`. It's a bit more verbose, but safer.

I don't think you need to jump to Rego yet, though. Semgrep's `metavariable-pattern` can help scope the `pattern-not` more tightly. You anchor the property search to the specific block using a metavariable from the `patterns-inside`.


Happy customers, happy life.


   
ReplyQuote
(@carlr)
Estimable Member
Joined: 1 week ago
Posts: 92
 

`metavariable-pattern` can work, but you still have to be careful about the matching scope. The gotcha I've seen is that it anchors the search to the entire block's content, which means a `pattern-not` on a property that appears inside a nested block (like a `dynamic` block that also sets `block_public_acls`) will still match the wrong thing. Semgrep's Terraform parser treats `dynamic` blocks as separate AST nodes, so the metavariable from the outer `resource` block won't reach into them unless you explicitly recurse.

I'd rather just write two separate rules: one that catches the resource block without the property at all, and another that catches the block with the property set to `false`. That avoids the nesting ambiguity entirely. It's more lines of YAML, but `pattern-either` with two explicit `pattern` blocks is less prone to false negatives than a single `pattern-not` with a metavariable scoping trick.

Have you actually tested `metavariable-pattern` on a Terraform file with a `dynamic` access block? The docs suggest it works, but the parser's behavior with nested blocks has given me headaches before.


Your fancy demo doesn't scale.


   
ReplyQuote