Customize rulesets

  • Tier: Ultimate
  • Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated

Each SAST analyzer supports different levels of customization through the ruleset configuration file. The Semgrep-based SAST analyzer and GitLab Advanced SAST analyzer have a default ruleset.

Ruleset glossary

Rule
An individual security check or detection pattern that scans for specific vulnerabilities.
Ruleset
A collection of rules and their configuration, defined in the sast-ruleset.toml file.
Passthrough

A passthrough is a configuration source that pulls ruleset customizations from a file, Git repository, URL, or inline configuration. You can combine multiple passthroughs into a chain, where each one can overwrite or append to the previous configuration.

Rule customization options

SAST rulesets come with default rules, but every organization has different security requirements. You can customize these rulesets by disabling rules, overriding their metadata, or replacing or adding rules.

The table below shows which customization options are available for each analyzer type.

CustomizationGitLab Advanced SASTGitLab SemgrepOther analyzers
Disable default rulescheck-smcheck-smcheck-sm
Override metadata of default rulescheck-smcheck-smcheck-sm
Replace or add to default rulesSupports modifying the behavior of default non-taint, structural rules and the application of file and raw passthroughs. Other passthrough types are ignored.Supports full passthroughs.No

Disable default rules

You can disable default rules for any SAST analyzer. For example, you might want to exclude a specific rule based on organizational policy.

Override metadata of default rules

You can override certain attributes of default rules for any SAST analyzer. For example, you might want to override the severity of a vulnerability based on organizational policy, or choose a different message to display in the vulnerability report.

Replace or add to the default rules

You can replace or add to the default rules of the Semgrep-based SAST analyzer and GitLab Advanced SAST analyzer. By default, defining a custom ruleset replaces the default ruleset. To add to the default ruleset you must set keepdefaultrules to true in your ruleset configuration file.

Effects of ruleset customization

The following table describes what happens when you customize SAST rulesets:

ActionScan behaviorPipeline security tabVulnerability report
Disable a ruleAnalyzers still scan for the vulnerability but the rule’s results are removed after the scan completes. GitLab Advanced SAST excludes disabled rules from the initial scan.Findings detected by the rule before it was disabled no longer appear after the next pipeline runs.Vulnerabilities detected by the rule before it was disabled are marked as No longer detected.
Override metadataNo change to scan behavior.Metadata of findings detected by the rule before it was overridden are updated after the next pipeline runs.Metadata of vulnerabilities detected by the rule before it was overridden are updated.
Replace default rulesetThe default ruleset is not used by analyzers that support custom rulesets.Findings detected by rules in the default ruleset before it was replaced no longer appear after the next pipeline runs.Vulnerabilities detected by rules in the default ruleset are marked as No longer detected.

Configuration methods

You can provide your ruleset customizations in the following ways:

Local ruleset file
Define your customizations in a sast-ruleset.toml file committed to your repository. This approach keeps your ruleset configuration under version control alongside your code.
Remote ruleset file
Specify a remote location (Git repository, URL, or other source) where your ruleset configuration is hosted. This approach lets you manage rulesets centrally and reuse them across multiple projects.

A local .gitlab/sast-ruleset.toml file takes precedence over a remote ruleset file.

You provide your customizations by using passthroughs, which are configuration sources that can be combined into a ruleset.

Local ruleset file

To create the local ruleset configuration file:

  1. Create a .gitlab directory at the root of your project, if one doesn’t already exist.
  2. Create a file named sast-ruleset.toml in the .gitlab directory.
  3. Add your custom ruleset to the sast-ruleset.toml file.
  4. Commit the local ruleset configuration file to the repository.

For more details on customizing the ruleset, see schema and examples.

Remote ruleset file

You can set a CI/CD variable to use a ruleset configuration file that’s stored outside of the current repository. This can help you apply the same rules across multiple projects.

The SAST_RULESET_GIT_REFERENCE variable uses a format similar to Git URLs for specifying a project URI, optional authentication, and optional Git SHA. The variable uses the following format:

[<AUTH_USER>[:<AUTH_PASSWORD>]@]<PROJECT_PATH>[@<GIT_SHA>]

A local .gitlab/sast-ruleset.toml file takes precedence over a remote ruleset file.

The following example enables SAST and uses a shared ruleset customization file. In this example, the file is committed on the default branch of example-ruleset-project at the path .gitlab/sast-ruleset.toml.

include:
  - template: Jobs/SAST.gitlab-ci.yml

variables:
  SAST_RULESET_GIT_REFERENCE: "gitlab.com/example-group/example-ruleset-project"

See specify a private remote configuration example for advanced usage.

Troubleshooting remote configuration files

If remote configuration file doesn’t seem to be applying customizations correctly, the causes can be:

  1. Your repository has a local .gitlab/sast-ruleset.toml file.
  2. There is a problem with authentication.
    • To check whether this is the cause of the problem, try referencing a configuration file from a repository location that doesn’t require authentication.

Schema

The top-level section

The top-level section contains one or more configuration sections, defined as TOML tables.

SettingDescription
[$analyzer]Declares a configuration section for an analyzer. The name follows the names defined in the list of SAST analyzers.

Configuration example:

[semgrep]
...

Avoid creating configuration sections that modify existing rules and build a custom ruleset, as the latter replaces default rules completely.

The [$analyzer] configuration section

The [$analyzer] section lets you customize the behavior of an analyzer. Valid properties differ based on the kind of configuration you’re making.

SettingApplies toDescription
[[$analyzer.ruleset]]Default rulesDefines modifications to an existing rule.
interpolateAllIf set to true, you can use $VAR in the configuration to evaluate environment variables. Use this feature with caution, so you don’t leak secrets or tokens. (Default: false)
descriptionPassthroughsDescription of the custom ruleset.
targetdirPassthroughsThe directory where the final configuration should be persisted. If empty, a directory with a random name is created. The directory can contain up to 100 MB of files. In case the SAST job is running with non-root user privileges, ensure that the user has read and write permissions for this directory.
validatePassthroughsIf set to true, the content of each passthrough is validated. The validation works for yaml, xml, json and toml content. The proper validator is identified based on the extension used in the target parameter of the [[$analyzer.passthrough]] section. (Default: false)
timeoutPassthroughsThe maximum time to spend to evaluate the passthrough chain, before timing out. The timeout cannot exceed 300 seconds. (Default: 60)
keepdefaultrulesPassthroughsIf set to true, the analyzer’s default rules are activated in conjunction with the defined passthroughs. (Default: false)

interpolate

To reduce the risk of leaking secrets, use this feature with caution.

The example below shows a configuration that uses the $GITURL environment variable to access a private repository. The variable contains a username and token (for example https://user:token@url), so they’re not explicitly stored in the configuration file.

[semgrep]
  description = "My private Semgrep ruleset"
  interpolate = true

  [[semgrep.passthrough]]
    type  = "git"
    value = "$GITURL"
    ref = "main"

The [[$analyzer.ruleset]] section

The [[$analyzer.ruleset]] section targets and modifies a single default rule. You can define one to many of these sections per analyzer.

SettingDescription
disableWhether the rule should be disabled. (Default: false)
[$analyzer.ruleset.identifier]Selects the default rule to be modified.
[$analyzer.ruleset.override]Defines the overrides for the rule.

Configuration example:

[semgrep]
  [[semgrep.ruleset]]
    disable = true
    ...

The [$analyzer.ruleset.identifier] section

The [$analyzer.ruleset.identifier] section defines the identifiers of the default rule that you wish to modify.

SettingDescription
typeThe type of identifier used by the default rule.
valueThe value of the identifier used by the default rule.

You can look up the correct values for type and value by viewing the gl-sast-report.json produced by the analyzer. You can download this file as a job artifact from the analyzer’s CI job.

For example, the snippet below shows a finding from a semgrep rule with three identifiers. The type and value keys in the JSON object correspond to the values you should provide in this section.

...
  "vulnerabilities": [
    {
      "id": "7331a4b7093875f6eb9f6eb1755b30cc792e9fb3a08c9ce673fb0d2207d7c9c9",
      "category": "sast",
      "message": "Key Exchange without Entity Authentication",
      "description": "Audit the use of ssh.InsecureIgnoreHostKey\n",
      ...
      "identifiers": [
        {
          "type": "semgrep_id",
          "name": "gosec.G106-1",
          "value": "gosec.G106-1"
        },
        {
          "type": "cwe",
          "name": "CWE-322",
          "value": "322",
          "url": "https://cwe.mitre.org/data/definitions/322.html"
        },
        {
          "type": "gosec_rule_id",
          "name": "Gosec Rule ID G106",
          "value": "G106"
        }
      ]
    }
    ...
  ]
...

Configuration example:

[semgrep]
  [[semgrep.ruleset]]
    [semgrep.ruleset.identifier]
      type = "semgrep_id"
      value = "gosec.G106-1
    ...

The [$analyzer.ruleset.override] section

The [$analyzer.ruleset.override] section allows you to override attributes of a default rule.

SettingDescription
descriptionA detailed description of the issue.
message(Deprecated) A description of the issue.
nameThe name of the rule.
severityThe severity of the rule. Valid options are: Critical, High, Medium, Low, Unknown, Info

While message is populated by the analyzers, it has been deprecated in favor of name and description.

Configuration example:

[semgrep]
  [[semgrep.ruleset]]
    [semgrep.ruleset.override]
      severity = "Critical"
      name = "Command injection"
    ...

The [[$analyzer.passthrough]] section

Passthrough configurations are available for the Semgrep-based analyzer only.

The [[$analyzer.passthrough]] section allows you to build a custom configuration for an analyzer. You can define up to 20 of these sections per analyzer. Passthroughs are composed into a passthrough chain that evaluates into a complete configuration that replaces the default rules of the analyzer.

Passthroughs are evaluated in order. Passthroughs listed later in the chain have a higher precedence and can overwrite or append to data yielded by previous passthroughs (depending on the mode). This is useful for cases where you need to use or modify an existing configuration.

The size of the configuration generated by a single passthrough is limited to 10 MB.

SettingApplies toDescription
typeAllOne of file, raw, git or url.
targetAllThe target file to contain the data written by the passthrough evaluation. If empty, a random filename is used.
modeAllIf overwrite, the target file is overwritten. If append, new content is appended to the target file. The git type only supports overwrite. (Default: overwrite)
reftype = "git"Contains the name of the branch, tag, or the SHA to pull
subdirtype = "git"Used to select a subdirectory of the Git repository as the configuration source.
valueAllFor the file, url, and git types, defines the location of the file or Git repository. For the raw type, contains the inline configuration.
validatorAllUsed to explicitly invoke validators (xml, yaml, json, toml) on the target file after the evaluation of a passthrough.

Passthrough types

TypeDescription
fileUse a file that is present in the Git repository.
rawProvide the configuration inline.
gitPull the configuration from a remote Git repository.
urlFetch the configuration using HTTP.

When using the raw passthrough with a YAML snippet, it’s recommended to format all indentation in the sast-ruleset.toml file as spaces. The YAML specification mandates spaces over tabs, and the analyzer fails to parse your custom ruleset unless the indentation is represented accordingly.

Examples

Replace the default rules of GitLab Advanced SAST

With the following custom ruleset configuration, the default ruleset of the GitLab Advanced SAST analyzer is replaced with a custom ruleset contained in a file called my-gitlab-advanced-sast-rules.yaml in the repository being scanned.

# my-gitlab-advanced-sast-rules.yaml
---
rules:
- id: my-custom-rule
  pattern: print("Hello World")
  message: |
    Unauthorized use of Hello World.
  severity: ERROR
  languages:
  - python
[gitlab-advanced-sast]
  description = "My custom ruleset for Semgrep"

  [[gitlab-advanced-sast.passthrough]]
    type  = "file"
    value = "my-gitlab-advanced-sast-rules.yaml"

Disable default GitLab Advanced SAST rules

You can disable GitLab Advanced SAST rules or edit their metadata. The following example disables rules based on different criteria:

  • A CWE identifier, which identifies an entire class of vulnerabilities.
  • An GitLab Advanced SAST rule ID, which identifies a specific detection strategy used in GitLab Advanced SAST.
  • An associated Semgrep rule ID, which is included in GitLab Advanced SAST findings for compatibility. This additional metadata allows findings to be automatically transitioned when both analyzers create similar findings in the same location.

These identifiers are shown in the vulnerability details of each vulnerability. You can also see each identifier and its associated type in the downloadable SAST report artifact.

[gitlab-advanced-sast]
  [[gitlab-advanced-sast.ruleset]]
    disable = true
    [gitlab-advanced-sast.ruleset.identifier]
      type = "cwe"
      value = "89"

  [[gitlab-advanced-sast.ruleset]]
    disable = true
    [gitlab-advanced-sast.ruleset.identifier]
      type = "gitlab-advanced-sast_id"
      value = "java-spring-csrf-unrestricted-requestmapping-atomic"

  [[gitlab-advanced-sast.ruleset]]
    disable = true
    [gitlab-advanced-sast.ruleset.identifier]
      type = "semgrep_id"
      value = "java_cookie_rule-CookieHTTPOnly"

Disable default rules of other SAST analyzers

With the following custom ruleset configuration, the following rules are omitted from the report:

  • semgrep rules with a semgrep_id of gosec.G106-1 or a cwe of 322.
  • sobelow rules with a sobelow_rule_id of sql_injection.
  • flawfinder rules with a flawfinder_func_name of memcpy.
[semgrep]
  [[semgrep.ruleset]]
    disable = true
    [semgrep.ruleset.identifier]
      type = "semgrep_id"
      value = "gosec.G106-1"

  [[semgrep.ruleset]]
    disable = true
    [semgrep.ruleset.identifier]
      type = "cwe"
      value = "322"

[sobelow]
  [[sobelow.ruleset]]
    disable = true
    [sobelow.ruleset.identifier]
      type = "sobelow_rule_id"
      value = "sql_injection"

[flawfinder]
  [[flawfinder.ruleset]]
    disable = true
    [flawfinder.ruleset.identifier]
      type = "flawfinder_func_name"
      value = "memcpy"

Override default rule metadata

With the following custom ruleset configuration, vulnerabilities found with semgrep with a type CWE and a value 322 have their severity overridden to Critical.

[semgrep]
  [[semgrep.ruleset]]
    [semgrep.ruleset.identifier]
      type = "cwe"
      value = "322"
    [semgrep.ruleset.override]
      severity = "Critical"

Replace or add to the default rules of semgrep

With the following custom ruleset configuration, the default ruleset of the semgrep analyzer is replaced with a custom ruleset contained in a file called my-semgrep-rules.yaml in the repository being scanned.

# my-semgrep-rules.yml
---
rules:
- id: my-custom-rule
  pattern: print("Hello World")
  message: |
    Unauthorized use of Hello World.
  severity: ERROR
  languages:
  - python
[semgrep]
  description = "My custom ruleset for Semgrep"

  [[semgrep.passthrough]]
    type  = "file"
    value = "my-semgrep-rules.yml"

Build a custom configuration using a passthrough chain for semgrep

With the following custom ruleset configuration, the default ruleset of the semgrep analyzer is replaced with a custom ruleset produced by evaluating a chain of four passthroughs. Each passthrough produces a file that’s written to the /sgrules directory within the container. A timeout of 60 seconds is set in case any Git remotes are unresponsive.

Different passthrough types are demonstrated in this example:

  • Two git passthroughs, the first pulling develop branch from the myrules Git repository, and the second pulling revision 97f7686 from the sast-rules repository, and considering only files in the go subdirectory.
    • The sast-rules entry has a higher precedence because it appears later in the configuration.
    • If there’s a filename collision between the two checkouts, files from the sast-rules repository overwrite files from the myrules repository.
  • A raw passthrough, which writes its value to /sgrules/insecure.yml.
  • A url passthrough, which fetches a configuration hosted at a URL and writes it to /sgrules/gosec.yml.

Afterwards, Semgrep is invoked with the final configuration located under /sgrules.

[semgrep]
  description = "My custom ruleset for Semgrep"
  targetdir = "/sgrules"
  timeout = 60

  [[semgrep.passthrough]]
    type  = "git"
    value = "https://gitlab.com/user/myrules.git"
    ref = "develop"

  [[semgrep.passthrough]]
    type  = "git"
    value = "https://gitlab.com/gitlab-org/secure/gsoc-sast-vulnerability-rules/playground/sast-rules.git"
    ref = "97f7686db058e2141c0806a477c1e04835c4f395"
    subdir = "go"

  [[semgrep.passthrough]]
    type  = "raw"
    target = "insecure.yml"
    value = """
rules:
- id: "insecure"
  patterns:
    - pattern: "func insecure() {...}"
  message: |
    Insecure function insecure detected
  metadata:
    cwe: "CWE-200: Exposure of Sensitive Information to an Unauthorized Actor"
  severity: "ERROR"
  languages:
    - "go"
"""

  [[semgrep.passthrough]]
    type  = "url"
    value = "https://semgrep.dev/c/p/gosec"
    target = "gosec.yml"

Configure the mode for passthroughs in a chain

You can choose how to handle filename conflicts that occur between passthroughs in a chain. The default behavior is to overwrite existing files with the same name, but you can choose mode = append instead to append the content of later files onto earlier ones.

You can use the append mode for the file, url, and raw passthrough types only.

With the following custom ruleset configuration, two raw passthroughs are used to iteratively assemble the /sgrules/my-rules.yml file, which is then provided to Semgrep as the ruleset. Each passthrough appends a single rule to the ruleset. The first passthrough is responsible for initializing the top-level rules object, according to the Semgrep rule syntax.

[semgrep]
  description = "My custom ruleset for Semgrep"
  targetdir = "/sgrules"
  validate = true

  [[semgrep.passthrough]]
    type  = "raw"
    target = "my-rules.yml"
    value = """
rules:
- id: "insecure"
  patterns:
    - pattern: "func insecure() {...}"
  message: |
    Insecure function 'insecure' detected
  metadata:
    cwe: "..."
  severity: "ERROR"
  languages:
    - "go"
"""

  [[semgrep.passthrough]]
    type  = "raw"
    mode  = "append"
    target = "my-rules.yml"
    value = """
- id: "secret"
  patterns:
    - pattern-either:
        - pattern: '$MASK = "..."'
    - metavariable-regex:
        metavariable: "$MASK"
        regex: "(password|pass|passwd|pwd|secret|token)"
  message: |
    Use of hard-coded password
  metadata:
    cwe: "..."
  severity: "ERROR"
  languages:
    - "go"
"""
# /sgrules/my-rules.yml
rules:
- id: "insecure"
  patterns:
    - pattern: "func insecure() {...}"
  message: |
    Insecure function 'insecure' detected
  metadata:
    cwe: "..."
  severity: "ERROR"
  languages:
    - "go"
- id: "secret"
  patterns:
    - pattern-either:
        - pattern: '$MASK = "..."'
    - metavariable-regex:
        metavariable: "$MASK"
        regex: "(password|pass|passwd|pwd|secret|token)"
  message: |
    Use of hard-coded password
  metadata:
    cwe: "..."
  severity: "ERROR"
  languages:
    - "go"

Specify a private remote configuration

The following example enables SAST and uses a shared ruleset customization file. The file is:

  • Downloaded from a private project that requires authentication, by using a Group Access Token securely stored within a CI variable.
  • Checked out at a specific Git commit SHA instead of the default branch.

See group access tokens for how to find the username associated with a group token.

include:
  - template: Jobs/SAST.gitlab-ci.yml

variables:
  SAST_RULESET_GIT_REFERENCE: "group_2504721_bot_7c9311ffb83f2850e794d478ccee36f5:$PERSONAL_ACCESS_TOKEN@gitlab.com/example-group/example-ruleset-project@c8ea7e3ff126987fb4819cc35f2310755511c2ab"

Demo Projects

There are demonstration projects that illustrate some of these configuration options.

Many of these projects illustrate using remote rulesets to override or disable rules and are grouped together by which analyzer they are for.

There are also some video demonstrations walking through setting up remote rulesets: