Static application security testing, or SAST, is most useful when it is close to the way your team actually writes code. That is where Semgrep becomes valuable. It can scan source code quickly, fit into pull requests and build pipelines, and let you express security checks in a readable rule format rather than a heavyweight abstract syntax tree query. For many UK SMEs, that makes it a practical way to catch recurring mistakes before they reach production.
Semgrep is not a replacement for threat modelling, code review, dependency scanning, or dynamic testing. It is one control in a broader secure development workflow. Used well, it helps you detect insecure patterns early, especially where your own codebase has conventions, frameworks, or business logic that generic rulesets do not understand. That is often the point where custom rules add real value.
Key takeaways
- Start with narrow, high-value patterns that reflect your own codebase and secure coding standards.
- Treat Semgrep rules like code by versioning, testing, reviewing, and assigning ownership.
- Use custom rules to catch organisation-specific framework misuse, unsafe sinks, and recurring anti-patterns.
- Run Semgrep in pull requests and pipelines so developers get feedback before code is merged.
- Tune rules against real fixtures to reduce false positives and avoid brittle patterns.
What Semgrep is used for in a secure development workflow
In a modern pipeline, Semgrep usually sits alongside software composition analysis, secret scanning, and targeted dynamic testing. SAST looks at code without running it, which makes it useful for spotting risky patterns such as unsafe deserialisation, weak cryptography usage, insecure framework calls, and missing validation paths. It is especially effective when the issue can be recognised from the shape of the code rather than from runtime behaviour.
For example, if your engineering team has already standardised on secure input handling, you can use Semgrep to check that those patterns are actually followed. That complements guidance such as input validation, encoding, and output sanitisation techniques, because the rule can look for places where the expected validation helper is missing or bypassed.
Custom rules are most useful when default rulesets are too generic. A built-in rule may know that a hard-coded password is bad, but it will not know your internal wrapper for database access, your approved deserialisation library, or the exact pattern your developers use when they accidentally disable a framework safeguard. In those cases, a custom rule can encode local knowledge and reduce blind spots.
That is also why Semgrep fits well into secure development programmes that already use threat modelling and secure coding standards. If you have mapped common abuse cases, the rules can become a technical enforcement layer for those decisions. If you have not yet done that, start there first, because rules are easier to write when the risk is already defined.
How Semgrep rules are structured
A Semgrep rule is usually written in YAML. At a minimum, it needs an identifier, a short message, a severity, the languages it applies to, and one or more patterns. The basic structure is simple enough for developers to read without specialist tooling knowledge, which is one reason Semgrep is popular in engineering teams.
A typical rule file includes fields such as id, message, severity, languages, and patterns. Metadata fields such as metadata can help you record ownership, references to internal standards, or the reason the rule exists. That matters operationally, because a rule without context is hard to maintain once the original author has moved on.
Semgrep patterns use metavariables, which are placeholders that match code fragments. For example, a metavariable might represent a function name, an argument, or a variable. This lets you write rules that are flexible enough to catch variants of the same issue without hard-coding one exact line. The trade-off is that the broader the pattern, the more likely you are to generate noise.
There are several pattern operators available, including pattern matching, pattern-either, pattern-inside, and pattern-not. In practice, these let you express context. You can say, for example, that a risky call is only a finding when it appears outside a known safe wrapper, or that a dangerous sink should only be flagged when it is fed by a particular source. That is where Semgrep starts to feel more like a security engineering tool than a simple grep replacement.
How to write a first custom rule safely
The safest way to begin is to target one narrow, well-defined code pattern. Do not try to model an entire vulnerability class in a single rule. Start with a specific anti-pattern that your team already recognises, such as direct use of a risky API, a forbidden framework call, or a local convention that should never appear in production code.
For example, if your team has agreed that certain deserialisation methods are not allowed in application code, write a rule that detects those methods and nothing else. Keep the first version intentionally narrow. You can widen it later if the initial signal is too weak. This approach is much easier to manage than starting broad and trying to suppress hundreds of false positives.
Readability matters. A rule should be understandable by the developer who receives the finding and by the engineer who has to maintain it six months later. Use clear rule names, short messages, and comments where needed. If a rule depends on a specific framework behaviour, explain that in the metadata or in the message so that the finding is actionable rather than cryptic.
A practical development pattern is to create the rule in a local repository, test it against a small sample of known-good and known-bad code, and only then move it into the shared ruleset. That keeps experimentation away from production pipelines until you are confident the rule behaves as expected.
Using Semgrep examples and the Registry as a starting point
Semgrep’s Registry and published examples are useful starting points because they show common rule patterns and idioms. You should adapt them to your own codebase rather than copying them blindly. A rule that works well for one framework version or one language style may not be accurate in your environment.
When you adapt a published rule, check three things: whether the language matches, whether the framework assumptions still hold, and whether the rule is too broad for your codebase. A rule written for a public example application may assume a coding style that does not exist in your team. If you import it unchanged, you may end up with a noisy rule that developers quickly ignore.
There is also a governance choice to make. Local rules are best for organisation-specific patterns and internal conventions. Registry rules are useful for common issues and for keeping pace with known vulnerability classes. Shared repositories are helpful when several teams need the same rule set and you want a single source of truth. In many SMEs, the right answer is a mix of all three.
If you are already using other rule-based security content, such as building SIEM detections using Sigma rules, the operational lesson is similar: start from a known pattern, tune it to your environment, and keep ownership clear. The mechanics differ, but the discipline is the same.
Custom rule design for common application risks
Custom Semgrep rules are particularly useful for recurring application risks that appear in your own stack. One common area is insecure input handling. If your codebase has a pattern where request data is passed directly into a query builder, shell command, template engine, or file path operation, a custom rule can flag that path before it becomes an injection issue.
Another useful area is secret exposure. You may already use secret scanning for obvious tokens, but Semgrep can also detect code patterns that increase the chance of leakage, such as logging sensitive objects, printing environment variables, or constructing configuration objects in unsafe ways. This is especially helpful where secrets are wrapped in application-specific classes rather than stored as plain strings.
Unsafe deserialisation is another good candidate for custom rules because the risky call often depends on the language and framework. In Java, Python, JavaScript, or PHP, the dangerous method may be different, but the security concern is the same: untrusted data being turned into executable or structured objects without proper controls. A custom rule can focus on the exact library calls your team uses.
Framework misuse is often where custom rules pay for themselves. For example, a team may know that a particular authentication middleware must be enabled, or that a specific CSRF setting must remain on. A rule can check for the absence of a required configuration or the presence of a dangerous override. That is not a generic vulnerability pattern, but it is still a security control worth automating.
These rules work best when they are tied to the secure coding guidance your developers already follow. If you have documented standards for input handling, authentication, or configuration management, use those as the source of truth. That keeps the rule set aligned with engineering practice rather than becoming a separate security-only artefact.
Testing and tuning custom rules
Testing is where custom rules become reliable. A rule that looks elegant in YAML is not useful if it flags the wrong code or misses the real issue. Build a small fixture set with examples that should match and examples that should not. Include edge cases, because those are often where false positives and false negatives appear.
Semgrep supports testing workflows that let you validate expected matches against sample code. Use that capability to create regression tests for your rules. If a rule is meant to catch a specific dangerous call, add a positive example and several negative examples. When the rule changes later, the tests will show whether you have accidentally widened or narrowed the behaviour.
False positives are usually the first problem. If a rule fires too often, developers will either suppress it or stop trusting the pipeline. Reduce noise by adding context, narrowing the scope, or excluding known-safe wrappers. False negatives are the opposite problem: the rule looks clean but misses real issues. That usually means the pattern is too specific or the rule does not account for the way your code is actually written.
One useful tuning technique is to run the rule in warning mode first, then review the findings with the developers who own the code. They can tell you whether the rule reflects a real risk or whether it is catching an intentional pattern. This feedback loop is especially important in SMEs, where the people writing the code are often the same people maintaining the rules.
Working with language-specific rules
Semgrep is strongest when you respect language and framework conventions. A rule that works well in Python may need a different shape in JavaScript or Java because the syntax and idioms differ. The goal is not to write one universal rule, but to write a rule that matches how the language is used in your environment.
In Java, you may need to account for method chaining, builder patterns, and framework annotations. In JavaScript and TypeScript, you may need to handle asynchronous code, callback patterns, and object destructuring. In Python, you may need to account for dynamic typing and helper functions that wrap risky operations. In each case, the rule should reflect the language’s normal style rather than forcing an artificial structure.
Framework conventions matter just as much. A Spring application, a Node.js service, and a Django app will express security controls differently. If your team uses a framework-specific safe helper, encode that helper into the rule so that the finding points to the unsafe exception rather than the approved path. That keeps the signal useful.
For mixed-language estates, it is often better to maintain a small number of language-specific rules than one over-general rule. That makes review easier and reduces the chance that a change in one stack breaks the behaviour in another. It also helps when you want to assign ownership to the right engineering team.
Integrating custom Semgrep rules into CI/CD
Custom rules are most effective when they run automatically in pull requests and build pipelines. That gives developers feedback while the code is still cheap to change. A common pattern is to run a fast Semgrep job on changed files in the pull request, then run a fuller scan on the main branch or nightly build.
In GitHub Actions, GitLab CI, or Azure DevOps, the implementation is usually straightforward: install Semgrep, point it at your rules directory, and fail the job only for agreed severity levels. For example, you might treat high-severity findings as blocking and medium-severity findings as informational until the rule has matured. That avoids turning the pipeline into a blunt instrument.
It is also sensible to separate baseline findings from new findings. If you introduce Semgrep into an existing codebase, you may already have a backlog of issues. Instead of blocking all builds immediately, capture the current state as a baseline and only fail on newly introduced matches. That makes adoption more realistic for SMEs with limited engineering capacity.
Developer feedback loops matter here. Findings should be easy to understand, easy to reproduce, and easy to fix. If a rule points to a line of code but does not explain the security concern, it will create friction. If it explains the issue and links to the internal standard it supports, it becomes part of the development workflow rather than an external gate.
Operational considerations for rule governance
Custom rules need ownership. Put them in version control, review them like code, and assign a maintainer or team responsible for changes. A rule set that is not governed will drift as frameworks change, and stale rules are a common source of noise.
Track why each rule exists. A short metadata note can record the risk, the affected language, the internal policy or standard it supports, and the date it was last reviewed. That is useful when you are trying to understand whether a rule still reflects current practice or whether the codebase has moved on.
It is also worth tracking coverage against your secure coding standards. If your engineering standard says that all user input must be validated before reaching a sink, ask whether you have rules that check the main sink types in your stack. If not, that is a gap. Semgrep can help you turn a written standard into something testable.
This is where Semgrep supports broader secure software work rather than replacing it. It can reinforce the controls described in your secure development programme, and it can complement supply chain assurance work such as implementing software supply chain assurance controls by checking the code you actually ship, not just the dependencies you consume.
Common mistakes when building custom SAST rules
The most common mistake is making the rule too broad. Broad rules create noisy findings, and noisy findings are usually ignored. If you are trying to catch a risky API, make sure the rule is specific enough to distinguish the dangerous use from the safe one.
The second mistake is making the rule too brittle. A brittle rule only matches one exact syntax form, so a harmless refactor breaks the detection. Use metavariables and context operators to make the rule resilient to normal code changes. The aim is to detect the pattern, not the formatting.
A third mistake is writing rules without a clear remediation path. If developers cannot tell what to change, they will spend time guessing. The message should explain the risk and, where possible, point to the approved alternative. That is especially important when the rule is enforcing an internal standard rather than a well-known vulnerability class.
Finally, do not treat rule creation as a one-off task. Rules should be reviewed, tested, and retired when they are no longer relevant. A mature ruleset is curated, not just accumulated.
How to use Semgrep as part of a broader secure software programme
Semgrep works best when it is connected to the rest of your secure development lifecycle. Threat modelling can tell you which code paths matter most. Secure coding standards can tell you what good looks like. Semgrep can then enforce those expectations in the pipeline. That makes it a practical control rather than a standalone tool.
If you already use secure design reviews, use the output from those reviews to prioritise rule writing. If a design review identifies a risky trust boundary, write a rule that checks the implementation of that boundary. If a recurring defect appears in incident reviews or code audits, turn it into a rule so the same mistake is less likely to reappear.
Over time, the best use of Semgrep is not just finding issues, but improving engineering habits. When developers see the same class of issue caught early, they start to avoid it in the first place. That is the real value of custom SAST rules: they encode organisational learning into the delivery process.
If you want to strengthen that programme further, it can help to align the rules with the way your teams already work, rather than asking them to adopt a separate security process. That usually means small, targeted rules, clear ownership, and a steady review cycle.
For teams that want help shaping a rule strategy, integrating Semgrep into CI/CD, or aligning it with an ISO 27001-oriented secure development process, a pragmatic review can save time and reduce noise. Speak to a consultant if you would like to discuss the right starting point for your environment.
Frequently asked questions
How do I use Semgrep?
Install Semgrep, point it at a rules file or rules directory, and run it against your source tree or changed files in CI. Start with a small set of rules, review the findings with developers, and then expand coverage once the output is reliable.
What is the difference between a built-in Semgrep rule and a custom rule?
A built-in rule comes from Semgrep’s published content and covers common security patterns, while a custom rule is written for your own codebase, frameworks, and internal standards. Custom rules are better when you need to detect organisation-specific misuse or enforce local secure coding conventions.


Comments are closed