---
title: Static Code Analysis (SAST) Custom Rules
description: >-
  Write Static Analysis Custom Rules to check the security and quality of your
  code.
breadcrumbs: >-
  Docs > Datadog Security > Code Security > Static Code Analysis (SAST) > Static
  Code Analysis (SAST) Custom Rules
---

# Static Code Analysis (SAST) Custom Rules

{% callout %}
# Important note for users on the following Datadog sites: app.ddog-gov.com

{% alert level="danger" %}
This product is not supported for your selected [Datadog site](https://docs.datadoghq.com/getting_started/site.md). ().
{% /alert %}

{% /callout %}

{% callout %}
# Important note for users on the following Datadog sites: app.ddog-gov.com



{% alert level="warning" %}
Code Security is not available for the  site.
{% /alert %}


{% /callout %}

[Datadog Static Code Analysis](https://app.datadoghq.com/ci/code-analysis) lets you define static analysis rules as custom rules. You can share these custom rules within your organization.

## Rule organization{% #rule-organization %}

SAST rules are organized within rulesets. A ruleset is a collection of rules. There are no constraints on how rules are organized within a ruleset. For example, some users might want to have rulesets for a specific language and others for a category.

A ruleset must have a unique name with only letters, numbers, and hyphens (`-`). Examples of valid ruleset names are `python-security`, `cobra-team-checks`, or `my-company-security-checks`.

## Write rules with the VS Code extension{% #write-rules-with-the-vs-code-extension %}

The [Datadog VS Code Extension](https://marketplace.visualstudio.com/items?itemName=Datadog.datadog-vscode) includes the DDSA Rule Editor for interactively writing and testing custom rules.

### Prerequisites{% #prerequisites %}

Install the [Datadog VS Code Extension](https://marketplace.visualstudio.com/items?itemName=Datadog.datadog-vscode) from the VS Code marketplace and keep it up to date.

### Open the editor{% #open-the-editor %}

**To create a new rule**, open the command palette (**Cmd+Shift+P** / **Ctrl+Shift+P**) and run:

```
Datadog: New DDSA Rule
```

**To edit an existing rule**, right-click any `.yaml` rule file in the explorer and select **Datadog Code Security > Open in DDSA Rule Editor**.

The template repository includes a `.ddsainclude` file that automatically opens matching rule YAML files in the editor.

### Editor panels{% #editor-panels %}

The editor has four panels:

| Panel        | Description                   |
| ------------ | ----------------------------- |
| Top-left     | Tree-sitter query             |
| Top-right    | Code sample and scan results  |
| Bottom-left  | JavaScript rule logic         |
| Bottom-right | AST viewer and console output |

### Test your rule{% #test-your-rule %}

As you write, violations appear as squiggly underlines in the code sample panel in real-time. To run your rule against an entire directory, switch to the **Scan results** tab in the top-right panel.

### Rule metadata{% #rule-metadata %}

The collapsible sidebar lets you set rule metadata such as name, description, category, severity, and language before exporting.

### Save your rule{% #save-your-rule %}

- **Cmd/Ctrl+S**: Save to the current file
- **Shift+Cmd/Ctrl+S**: Save as a new file
- **Export button**: Validates required fields and saves as a YAML file

## Manage rules with the template repository{% #manage-rules-with-the-template-repository %}

As an alternative to managing custom rules in Datadog, the [datadog-custom-rules-template](https://github.com/DataDog/datadog-custom-rules-template) GitHub repository provides a Git-based workflow for managing custom rules as code. It works alongside the VS Code extension: write and test rules in the extension, then push to `main` to sync them to Datadog automatically.

### Get started{% #get-started %}

1. Click **Use this template** on the [datadog-custom-rules-template](https://github.com/DataDog/datadog-custom-rules-template) repository to create your own copy.
1. Add your Datadog credentials as GitHub secrets. See Authentication.
1. Rename `rulesets/my-custom-rules/` or add new ruleset directories under `rulesets/`.
1. Push to `main`. The GitHub Action uploads your rules automatically.

### Authentication{% #authentication %}

1. In your GitHub repository, go to **Settings → Secrets and variables → Actions**.
1. Add three secrets:
   - `DD_API_KEY` — your Datadog API key
   - `DD_APP_KEY` — your Datadog Application key
   - `DD_SITE` — your [Datadog site](https://docs.datadoghq.com/getting_started/site.md) hostname (for example, `datadoghq.com`, `datadoghq.eu`, or `us3.datadoghq.com`)

### How sync works{% #how-sync-works %}

On every push to `main`, the GitHub Action runs `upload.py`, which:

- **Creates** rulesets and rules that are new in the repository
- **Updates** rulesets and rules whose content has changed
- **Deletes** rulesets and rules that have been removed the repository

Only changed rules trigger API calls—unchanged rules are skipped.

To run a sync manually, in to your GitHub repository, go to the **Actions** tab, select **Upload Custom Rules**, and click **Run workflow**.

### Rule file format{% #rule-file-format %}

Each ruleset directory contains a `ruleset.yaml` and a separate `.yaml` file for each rule.

**`ruleset.yaml`**

```yaml
name: my-org-custom-rules
short_description: One-line summary
description: Longer description of what this ruleset covers.
```

**Rule file**

```yaml
name: your-rule-name
short_description: One-line summary
description: Detailed description.
category: BEST_PRACTICES     # SECURITY | BEST_PRACTICES | CODE_STYLE | ERROR_PRONE | PERFORMANCE
severity: ERROR              # ERROR | WARNING | NOTICE | INFO
language: JAVASCRIPT
tree_sitter_query: <TREE_SITTER_QUERY>    # See Anatomy of a custom rule
code: |-                                  # See Anatomy of a custom rule
  function visit(node, filename, code) { <RULE_LOGIC> }
tests: []
is_published: false          # Set to true when the rule is ready for scans
```

## Anatomy of a custom rule{% #anatomy-of-a-custom-rule %}

A custom rule is composed of three main components:

- A **tree-sitter query** that captures what AST elements to check.
- **JavaScript code** that process the AST elements reports violations.
- **Test code** to test the rule.

### Tree-sitter query{% #tree-sitter-query %}

Custom rules use [tree-sitter queries](https://tree-sitter.github.io/tree-sitter/using-parsers/queries/index.html) to query the code abstract syntax tree (AST) and retrieve elements to analyze. Elements of the AST are captured by the query using the `@` operator.

All captured nodes from the tree-sitter query are injected in the JavaScript code and further processed to produce violations.

### JavaScript code{% #javascript-code %}

The JavaScript code is defined in a `visit` function. This function is triggered at each match of the tree-sitter query. If a tree-sitter query captures a function call and the analyzed code contains 10 function calls, the `visit` function is called 10 times and each invocation has the capture of each occurrence.

The `visit` function has the signature `visit(node, path, code)`:

- `node` is the tree-sitter context being matched.
- `path` is the path under analysis (convenient for filtering violation on path or filename).
- `code` is the code under analysis.

To get a captured node, use the `captures` attribute of the first argument of the `visit` function. For example, the code below retrieves the `functionName` from a tree-sitter query. Each element contains the following attributes:

- `cstType`: the tree-sitter type of the node.
- `start`: start position of the node. The position contains `line` and `col` attributes.
- `end`: end position of the node. The position contains `line` and `col` attributes.
- `text`: the content of the node.

{% alert level="danger" %}
`line` and `col` attributes start at 1. Any result with `line` or `col` set to 0 is ignored.
{% /alert %}

```javascript
function visit(node, filename, code) {
  const functionNameNode = node.captures["functionName"];
  console.log("cst type");
  console.log(functionNameNode.cstType);
  console.log("start line");
  console.log(functionNameNode.start.line);
}
```

The analyzer includes a few helper functions to help you write rules:

- `buildError(startLine, startCol, endLine, endCol, message, severity, category)` builds an error.
  - `severity` is one of the following: `ERROR`, `WARNING`, `NOTICE` and `INFO`.
  - `category` is one of the following: `BEST_PRACTICES`, `CODE_STYLE`, `ERROR_PRONE`, `PERFORMANCE` and `SECURITY`.
- `addError(error)` reports an error.
- `ddsa.getParent(node)` returns the given node's parent, or `undefined` if the node is the root node.
- `ddsa.getChildren(node)` returns an array of the given node's children, or an empty array if the node is a leaf node.

### Rule examples{% #rule-examples %}

All Datadog default rules are available in [Code Security](https://app.datadoghq.com/ci/code-analysis/static-analysis/default-rulesets). You can easily analyze and copy them to create your own custom rules.
