Snyk custom rules let you define and enforce your own security policies beyond Snyk’s built-in checks.

Let’s see this in action. Imagine you have a strict policy against using eval() in JavaScript, as it’s a common source of injection vulnerabilities. Snyk’s default rules might not catch every instance, especially if it’s wrapped in a helper function.

Here’s how you’d write a custom rule to tackle this. First, you need a snyk-rules.yml file in your repository’s root.

version: 1.0.0
name: My Custom Security Rules
rules:
  - id: JS_NO_EVAL
    language: javascript
    severity: HIGH
    message: "The use of 'eval()' is forbidden due to security risks. Consider safer alternatives like JSON.parse() or function constructors."
    pattern: |
      eval(...)

In this rule:

  • id: A unique identifier for your rule.
  • language: Specifies which language this rule applies to.
  • severity: Sets the impact level (LOW, MEDIUM, HIGH, CRITICAL).
  • message: The human-readable explanation shown when the rule is violated.
  • pattern: This is the core. It’s a Snyk-specific query language that describes the code structure you want to find. Here, eval(...) will match any call to the eval function, regardless of the arguments passed to it.

To run this, you’d typically integrate Snyk into your CI/CD pipeline. When Snyk scans your code, it will execute these custom rules alongside its built-in ones.

For example, if you have a file utils.js with:

function runCode(codeString) {
  return eval(codeString);
}

runCode("console.log('hello')");

Snyk would report a finding like this:

High severity vulnerability found in utils.js
JS_NO_EVAL: The use of 'eval()' is forbidden due to security risks. Consider safer alternatives like JSON.parse() or function constructors.

The power here is in the pattern. Snyk’s pattern language is based on Abstract Syntax Trees (ASTs). This means you’re not just searching for text strings; you’re describing the structure of the code. For more complex scenarios, you can use more sophisticated AST selectors. For instance, to find eval calls that are directly assigned to a variable, you might use something like:

version: 1.0.0
name: My Custom Security Rules
rules:
  - id: JS_EVAL_ASSIGNMENT
    language: javascript
    severity: HIGH
    message: "Direct assignment of 'eval()' calls is a critical security risk."
    pattern: |
      variable_declaration[declarator.init.callee.name = "eval"]

This rule looks for a variable_declaration where the initializer (init) is a function call (callee) whose name is specifically eval. This allows for highly precise targeting of code patterns.

You can also define rules for other languages Snyk supports, like Python, Java, or Go, using their respective AST structures. For example, a Python rule to disallow os.system:

version: 1.0.0
name: My Custom Security Rules
rules:
  - id: PY_NO_OS_SYSTEM
    language: python
    severity: CRITICAL
    message: "The use of 'os.system()' is highly discouraged due to command injection vulnerabilities."
    pattern: |
      call[func.name = "system"][func.value.name = "os"]

This rule targets a call where the function name (func.name) is system and it belongs to an object named os (func.value.name = "os").

The real magic happens when you start chaining conditions or looking for specific argument types. For instance, if you want to ensure that any call to fetch in JavaScript must have a credentials option set to 'include', you could write:

version: 1.0.0
name: My Custom Security Rules
rules:
  - id: JS_FETCH_CREDENTIALS_REQUIRED
    language: javascript
    severity: MEDIUM
    message: "All 'fetch' calls must explicitly include credentials for security compliance."
    pattern: |
      call[callee.name = "fetch"][arguments.properties.find(p => p.key.name = "credentials" && p.value.value != "include")]

This rule checks for fetch calls where the arguments object (assuming it’s an object literal for options) has a property named credentials, and that property’s value is not "include". This is where you see the AST querying power – it’s not just about finding the function, but inspecting its arguments and their properties.

What most people miss is that Snyk’s pattern language supports a rich set of operators and ways to traverse the AST. You can look for specific node types, check property values, iterate over array elements, and even use regular expressions within string comparisons. This allows you to build incredibly granular and context-aware security checks that perfectly match your organization’s unique threat model and development practices. For example, you can look for specific string literals being passed as arguments to sensitive functions, or check if a certain configuration setting is present in an object literal.

Once you’ve defined your custom rules, Snyk will automatically pick them up on the next scan, integrating them seamlessly into your security workflow.

The next hurdle you’ll likely encounter is managing a large and growing set of custom rules, which can become challenging to maintain and debug.

Want structured learning?

Take the full Snyk course →