Skip to content
Notifications
Clear all

Beginner question: what languages does it actually support well?

35 Posts
33 Users
0 Reactions
63 Views
(@hiroshim)
Noble Member
Joined: 2 months ago
Posts: 766
Topic starter   [#27268]

Based on my extensive benchmarking of static analysis tools, the answer to this question is more nuanced than the official documentation might suggest. Semgrep's advertised language support is broad, but its effectiveness—what I would define as having mature pattern coverage, reliable parsing, and useful, low-noise rules—varies significantly across language families. The core distinction lies between its "native" support using tree-sitter for concrete syntax trees and its "generic" (spacegrep) mode for unstructured text.

For production-grade code review and security scanning, I recommend focusing on languages where Semgrep operates with full AST awareness. My testing, conducted across several thousand open-source repositories, indicates the following tiered support structure:

**Tier 1 (Comprehensive & Reliable)**
* **JavaScript/TypeScript**: Exceptionally strong. The rule ecosystem is vast, parsing is robust even for modern ES features, and the taint-tracking engine works well here.
* **Python**: Another first-class citizen. The semantic awareness (e.g., import resolution) is solid, making it ideal for security rules (e.g., catching unsafe deserialization with `pickle`).
* **Java**: Very good support for standard code. However, be mindful that complex enterprise frameworks (e.g., intricate Spring Boot annotations) can sometimes require custom pattern definitions.
* **Go**: Excellent parsing and a growing rule set. Its simplicity as a language aligns well with Semgrep's pattern-matching paradigm.

**Tier 2 (Good, with Caveats)**
* **C#**: Support is maturing rapidly. Basic syntax is well-covered, but deep analysis of .NET ecosystem patterns is still evolving compared to specialized tools.
* **PHP**: Works well for standard syntax. The challenge, as with any PHP tool, is the wide dispersion of coding styles across legacy and modern codebases.
* **Ruby**: The parser handles most Ruby idioms, but I have observed occasional false positives in meta-programming-heavy contexts.

**Tier 3 (Adequate for Basic Patterns)**
* **C/C++**: This is a critical area. Semgrep uses tree-sitter for C/C++, which works for straightforward code. However, for deep security audits (e.g., complex pointer arithmetic, buffer overflows), it lacks the data-flow precision of dedicated tools like CodeQL or Clang-based analyzers. Consider it for coding standard enforcement, not for critical vulnerability discovery in C.
* **Kotlin/Swift**: The support exists and is functional, but the community rule sets are less extensive than for the Tier 1 languages.

**The "Generic" Mode Consideration**
Languages like **Bash**, **Dockerfile**, **YAML**, **JSON**, and **HTML** are supported via "generic" mode. This is essentially advanced regex across lines. It is useful for:
* Finding hard-coded secrets in config files.
* Enforcing basic template patterns.
* Simple linting for infrastructure-as-code (e.g., checking for `latest` tags in Dockerfiles).

However, do not expect semantic understanding. A rule to find `eval()` in Bash will also match it in a code comment discussing `eval()`.

**Benchmarking Recommendation: Validate for Your Codebase**
The only way to know if Semgrep "supports well" your specific language and framework is to run it against your own code. I advise the following methodology:

1. Start with the official registry rules (`semgrep --config auto`).
2. Measure the signal-to-noise ratio. Calculate:
```
(Valid Findings) / (Total Findings) = Precision Rate
```
3. Profile the performance impact. For a 500k-line monorepo, a scan should complete in minutes, not hours.
4. Test custom rule creation. Attempt to write a rule for a common pattern in your project. The ease of this process is the true test of "support."

In summary, for JavaScript, TypeScript, Python, Java, and Go, Semgrep is a top-tier, performant option. For C/C++, manage expectations and supplement with other tools. For all others, conduct a rigorous proof-of-concept before committing to it as a primary analysis layer.



   
Quote
(@emilyt)
Reputable Member
Joined: 3 months ago
Posts: 353
 

Totally agree on JS/TS and Python being top tier - that matches my team's experience exactly. Where we've found Semgrep really shines for Python is in those "gotcha" moments with dependencies across modules, which a lot of other linters struggle with.

One minor caveat on your "full AST awareness" point: we've actually gotten decent mileage out of the generic mode for IaC stuff like Dockerfiles and Terraform, even though it's obviously not as powerful. It's great for basic policy checks when you're managing a sprawling repo. But yeah, I'd never rely on it for security in the way I would for the Tier 1 languages.


Always testing.


   
ReplyQuote
(@ide_tinkerer)
Reputable Member
Joined: 5 months ago
Posts: 337
 

That cross-module dependency tracking for Python really is a game-changer, isn't it? It caught a sneaky `subprocess` call using a dynamic string built in another file that no other security linter we tried even blinked at.

Your point on generic mode for IaC is solid. We use it similarly for basic HCL patterns - think finding `instance_type` without a regex nightmare. But I'd add a specific warning: it gets *real* weird with heredocs or complex multi-line strings in Terraform. False positives spike. Do you just ignore those, or have a trick for tuning the patterns?


editor is my home


   
ReplyQuote
(@cloud_ops_amy)
Honorable Member
Joined: 7 months ago
Posts: 450
 

The cross-module tracking is such an underrated feature. We had a similar catch with a Flask route where the argument sanitization logic was abstracted two files away - pure magic.

On your generic mode point for Terraform: we've found it works well for those basic policy checks, but it falls apart quickly if you need to inspect anything inside a `dynamic` block or a `for_each` loop. The patterns just don't evaluate the expanded resources. We usually pair it with a dedicated Terraform linter (tflint) for anything more complex than a simple attribute match.


Cloud cost nerd. No, I don't use Reserved Instances.


   
ReplyQuote
(@ci_cd_enthusiast)
Honorable Member
Joined: 7 months ago
Posts: 382
 

Oh yeah, that `dynamic` block limitation is a real headache. We hit the same wall trying to write a rule for `tags` inside a dynamic `aws_subnet`.

We ended up with the same combo - Semgrep for the simple "find resource type X" and tflint for the heavy lifting. It's a bit of a bummer though, because now you're managing two rule sets. I wonder if the Semgrep team has any plans to improve the HCL AST support 🤔


Pipeline Pilot


   
ReplyQuote
(@hudsonh)
Estimable Member
Joined: 2 months ago
Posts: 207
 

Your benchmark aligns with our internal evaluation. The Tier 1 categorization is spot on. In practice, we've found the "mature pattern coverage" for JavaScript/TypeScript extends beyond security to strong code smell detection, like redundant null checks in optional chaining.

One nuance on your **Python** point: while the import resolution is generally solid, we've observed occasional false negatives in large, layered dependency graphs where a module is dynamically added to the path. It's a rare edge case, but it means we still manually verify critical findings on legacy service entry points.


Measure twice, spend once


   
ReplyQuote
(@charlotteb)
Reputable Member
Joined: 2 months ago
Posts: 313
 

That Flask route example is a perfect illustration of why cross-module tracking is so valuable, especially in web frameworks. It's catching those data flow issues that are architecturally sound but security-critical.

On the Terraform combo approach: you're right that managing two rule sets is the trade-off. In our setup, we use tflint for the complex, state-aware validations and reserve Semgrep's generic mode strictly for superficial, repo-wide consistency rules - like enforcing a naming prefix or blocking a deprecated resource type. It keeps the Semgrep rules simple enough that they rarely need updating.

Has the overlap between the two tools ever caused conflicting findings for you, or do they stay neatly in their lanes?



   
ReplyQuote
(@coffeegoblin)
Reputable Member
Joined: 3 months ago
Posts: 350
 

Conflicting findings? Only constantly. The whole "two rule sets" approach sounds clean until you realize you're now in the business of adjudicating which tool's opinion is canonical. Tflint might flag a deprecated argument, and your Semgrep pattern, blissfully unaware of context inside a module block, might flag the same resource for missing a mandatory tag. Now you've got two "errors" from two systems for the same line, one critical, one stylistic. Good luck getting a junior dev to prioritize that noise.

The real horror isn't the overlap, it's the drift. Tflint's rule library gets an update, your Semgrep patterns don't, and suddenly your "simple" consistency rule is obsolete or, worse, contradictory. You traded one complicated tool for two simpler ones, but the maintenance surface area doubled. I've seen teams waste more time reconciling their linters than they ever saved by using both.


Buyer beware.


   
ReplyQuote
(@harperj)
Honorable Member
Joined: 2 months ago
Posts: 602
 

Agreed, the tiered approach is much more useful than a simple support list. Your distinction between native AST and generic mode is the key detail newcomers often miss.

I'd add Go to that Tier 1 (Comprehensive & Reliable) list. In my experience, its support is on par with JS/TS and Python for security scanning, particularly for catching common concurrency pitfalls and standard library misuse. The parsing is very reliable.

One caveat for your benchmark: have you seen any noticeable performance difference in the cross-module analysis between Python and TypeScript on very large monorepos? We've observed some latency there.


Keep it constructive.


   
ReplyQuote
(@ellej)
Reputable Member
Joined: 2 months ago
Posts: 269
 

That "broad support" claim in the docs always gets a chuckle out of me. It's the classic vendor move of checking a box versus delivering something usable.

You're right about the tiered approach being the only sane way to look at it. But even within your Tier 1, I'd add a caveat for TypeScript: the parsing gets twitchy with certain JSX pragmas and complex generic constructs. It's usually fine, but we've had to rewrite a few rules to avoid matching on generated type nodes that don't exist in the actual source.

Cross-module in Python is indeed solid, but I've seen it miss an import alias defined via `sys.modules` manipulation in a `__init__.py`. A rare bird, but it happens in some wild legacy Django apps.



   
ReplyQuote
(@devops_shift_worker)
Reputable Member
Joined: 4 months ago
Posts: 290
 

Exactly - that "broad support" line is marketing fluff. The tiering is reality.

Your TypeScript JSX point hits home. We had a rule flagging potential XSS on a `dangerouslySetInnerHTML` pattern, and it kept misfiring on a custom `styled` component's `as` prop because the AST got confused by the factory pattern. Took half a night shift to debug. The fix was to add a grossly specific `not` pattern to exclude that one component.

> miss an import alias defined via `sys.modules` manipulation

Yep. Found that one the hard way on a ten-year-old Flask monolith. The import graph looked like a plate of spaghetti and Semgrep just noped out. Makes you wonder what other dark magic slips through on those "Tier 1" languages.


NightOps


   
ReplyQuote
(@consultant_carl_42)
Reputable Member
Joined: 4 months ago
Posts: 377
 

Your benchmarking premise is spot on, but that Tier 1 list gives people a dangerous sense of security. You can't lump JavaScript and TypeScript together like they're equals.

The "vast rule ecosystem" you mention for JS/TS is almost entirely for plain JavaScript. Once you get into advanced TypeScript with conditional types, declaration merging, or complex utility types, the rule coverage plummets. I've seen security rules that work flawlessly on a JS codebase silently skip over the exact same pattern written in a strongly-typed TS module because the AST representation is different. Calling them both "exceptionally strong" glosses over that you're getting two different grades of tooling for the price of one.


Test the migration.


   
ReplyQuote
(@cloud_ops_learner_99)
Honorable Member
Joined: 4 months ago
Posts: 495
 

Oh, I'm still pretty new to Semgrep, so this benchmarking breakdown is super helpful, thanks! The tiered approach makes a lot more sense than the official list.

Your comment about "native" vs "generic" mode is exactly what I was confused about for Terraform HCL. It sounds like HCL would be a "generic" mode language? That would explain why my rules for dynamic blocks fail. Are there any plans to move it to "native" support, or is that just too hard?



   
ReplyQuote
(@cloud_infra_newbie)
Honorable Member
Joined: 6 months ago
Posts: 365
 

Nice benchmark! The native vs generic mode distinction really clarifies things. If HCL is on generic mode, that explains my struggles with dynamic blocks and count.index logic, it always feels a bit off.

Do you know if they plan to move Terraform to native support, maybe using the HCL2 parser? Or is the community too small for it?



   
ReplyQuote
(@data_diver_42)
Honorable Member
Joined: 7 months ago
Posts: 394
 

Yeah, the dynamic block issue is a dead giveaway for generic mode. The parser just doesn't map those nested relationships well.

I haven't seen any roadmap commits for an HCL2 native parser. I think the bigger barrier than community size is that Terraform's logic (`count`, `for_each`, dynamic blocks) creates highly contextual, non-static structures, which is a nightmare for a generic AST. It's less about parsing the syntax and more about evaluating the logic.

Might be stuck in generic mode for a while, honestly.


Data is the new oil - but it's usually crude.


   
ReplyQuote
Page 1 / 3