Skip to content
Notifications
Clear all

Guide: converting old grep scripts into Semgrep patterns

4 Posts
4 Users
0 Reactions
1 Views
(@briana)
Estimable Member
Joined: 1 week ago
Posts: 106
Topic starter   [#20693]

Hey everyone! 👋 I've been knee-deep in migrating our old security and code quality checks from a pile of `grep` and `awk` scripts over to Semgrep over the last few months. It's been quite the journey, and I thought I'd share some of my learnings, especially around translating those old, sometimes brittle, regex patterns into something more robust and maintainable.

If you're sitting on a treasure trove of scripts like these:

```bash
# Old grep for finding potential hardcoded secrets (simplified example)
grep -r -E "(password|secret|key)s*=s*['"][^'"]+['"]" --include="*.py" .
```

You can absolutely bring that logic into Semgrep, but the real power is in moving *beyond* just line-based matching. Here’s how I approach the conversion.

### Step 1: Start Simple, Just Capture the Pattern
First, directly translate the regex into a Semgrep pattern. Don't worry about the fancy stuff yet.

```yaml
rules:
- id: simple-hardcoded-credential
patterns:
- pattern: |
$KEY = "..."
message: Found a hardcoded string assignment to a variable that looks like a key.
languages: [python]
severity: WARNING
```
This is a start, but it's not much better than grep. The `$KEY` metavariable is too generic.

### Step 2: Refine with Metavariables and `metavariable-regex`
This is where Semgrep starts to shine. You can use `metavariable-regex` to apply a regex *only* to what a metavariable has already captured, which is much more precise.

```yaml
rules:
- id: refined-hardcoded-secret
patterns:
- pattern: |
$KEY = $VALUE
- metavariable-regex:
metavariable: $KEY
regex: (password|secret|api_key|token)
- metavariable-regex:
metavariable: $VALUE
regex: '.+'
message: Found a hardcoded credential assigned to a sensitive-looking variable name.
languages: [python]
severity: ERROR
```
Now we're matching structure *and* content. The regexes are confined to specific parts of the AST, reducing false positives.

### Step 3: Leverage `pattern-either` for Variations
Those old `grep -E` scripts often had multiple branches. Use `pattern-either` to group them.

```yaml
rules:
- id: multi-flavor-deprecated-function
patterns:
- pattern-either:
- pattern: oldMethod(...)
- pattern: deprecatedFunc(...)
- pattern: legacyCall(...)
message: Found a call to a deprecated function from our grep script "check_deprecated.sh".
languages: [java]
severity: WARNING
```

### Common Pitfalls & Tips:
* **Don't try to do it all in one rule.** Break down complex old scripts into 2-3 focused Semgrep rules. It's easier to test and maintain.
* **Remember `pattern-inside`.** A huge advantage over grep is you can scope a search. Need to find something only within a function? Use `pattern-inside` to narrow it down.
* **You'll probably simplify.** Many of my old regexes were convoluted to avoid false positives. With Semgrep's AST awareness, I often found I could use a simpler regex combined with the code structure.
* **Test, test, test.** Run your new rules against the same codebase your old scripts scanned. Compare the outputs. You'll catch missed edge cases and see the false positives drop.

The migration effort has been totally worth it for us. The performance gain on large codebases is noticeable, and having the rules in a declarative YAML format is a maintenance dream compared to those sprawling shell scripts. Happy to dive deeper into any specific conversion challenges you all are facing!

β€”B


Backup first.


   
Quote
(@crm_hopper_alt)
Estimable Member
Joined: 2 months ago
Posts: 100
 

That's the key insight right there, that initial pattern is just grep with a YAML wrapper. The real pain starts when you try to make it actually understand code structure. Your example `$KEY = "..."` will also flag `api_key = "public_example"`, which is probably fine. But try to add a check that the value isn't just a short placeholder or a dummy value, and you're suddenly down a metavariable-regex rabbit hole.

Semgrep's promise is moving beyond line-matching, but for those of us coming from decades of regex-fu, the shift to patterns that understand assignment vs comparison can feel like learning to walk again. And the error messages when you get the syntax wrong? Not exactly helpful sometimes.


been there, migrated that


   
ReplyQuote
(@amyc)
Estimable Member
Joined: 1 week ago
Posts: 86
 

You've hit on the exact mental hurdle. That shift from thinking in lines to thinking in AST nodes trips up even the most seasoned regex wranglers.

The error messages are a known pain point, especially when you're deep in a metavariable-regex hole. I've found the Semgrep Playground's pattern debugger is the only way to stay sane sometimes. It helps you see what the rule actually matches before you commit.

And you're right, catching `api_key = "public_example"` is fine at first, but then you want to exclude common false positives like "test" or "demo". That's where you really feel the growing pains of the new paradigm.



   
ReplyQuote
(@cloud_ops_amy_2)
Estimable Member
Joined: 5 months ago
Posts: 96
 

Absolutely. That metavariable-regex rabbit hole is where the real work lives. For the placeholder value problem, I've had better luck using the `metavariable-pattern` operator with a separate, focused rule to filter out false positives, rather than trying to cram all the logic into one regex.

It does feel like learning a new language. The error messages got better in the 1.0 CLI release, but you still need the Playground to visualize the AST. Once it clicks, you can finally match `password = "..."` but not `if password == "..."`, which was nearly impossible in grep without a ton of context lines.


terraform and chill


   
ReplyQuote