summaryrefslogtreecommitdiff
path: root/doc/user/application_security/sast/customize_rulesets.md
blob: 919a3565d882120122757114a417be2fd6f0f597 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
---
stage: Secure
group: Static Analysis
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---

# Customize rulesets **(ULTIMATE)**

> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235382) in GitLab 13.5.
> - [Added](https://gitlab.com/gitlab-org/gitlab/-/issues/339614) support for
>   passthrough chains. Expanded to include additional passthrough types of `file`, `git`, and `url` in GitLab 14.6.
> - [Added](https://gitlab.com/gitlab-org/gitlab/-/issues/235359) support for overriding rules in GitLab 14.8.

You can customize the default scanning rules provided by our SAST analyzers.
Ruleset customization supports the following that can be used
simultaneously:

- [Disabling predefined rules](#disable-predefined-analyzer-rules). Available for all analyzers.
- [Overriding predefined rules](#override-predefined-analyzer-rules). Available for all analyzers.
- Modifying the default behavior of a given analyzer by [synthesizing and passing a custom configuration](#synthesize-a-custom-configuration). Available for only `nodejs-scan`, `gosec`, and `semgrep`.

To customize the default scanning rules, create a file containing custom rules. These rules
are passed through to the analyzer's underlying scanner tools.

To create a custom ruleset:

1. Create a `.gitlab` directory at the root of your project, if one doesn't already exist.
1. Create a custom ruleset file named `sast-ruleset.toml` in the `.gitlab` directory.

## Disable predefined analyzer rules

To disable analyzer rules:

1. Set the `disabled` flag to `true` in the context of a `ruleset` section

1. In one or more `ruleset.identifier` sub sections, list the rules that you want disabled. Every `ruleset.identifier` section has:

- a `type` field, to name the predefined rule identifier that the targeted analyzer uses.
- a `value` field, to name the rule to be disabled.

### Example: Disable predefined rules of SAST analyzers

In the following example, the disabled rules are assigned to `eslint`
and `sobelow` by matching the `type` and `value` of identifiers:

```toml
[eslint]
  [[eslint.ruleset]]
    disable = true
    [eslint.ruleset.identifier]
      type = "eslint_rule_id"
      value = "security/detect-object-injection"

  [[eslint.ruleset]]
    disable = true
    [eslint.ruleset.identifier]
      type = "cwe"
      value = "185"

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

Those vulnerabilities containing the provided type and value are now disabled, meaning
they won't be displayed in Merge Request nor the Vulnerability Report.

## Override predefined analyzer rules

To override analyzer rules:

1. In one or more `ruleset.identifier` subsections, list the rules that you want to override. Every `ruleset.identifier` section has:

   - a `type` field, to name the predefined rule identifier that the targeted analyzer uses.
   - a `value` field, to name the rule to be overridden.

1. In the `ruleset.override` context of a `ruleset` section,
   provide the keys to override. Any combination of keys can be
   overridden. Valid keys are:

   - description
   - message
   - name
   - severity (valid options are: Critical, High, Medium, Low, Unknown, Info)

### Example: Override predefined rules of SAST analyzers

Before adding a ruleset, we verify which vulnerability will be overwritten by viewing the [`gl-sast-report.json`](index.md#reports-json-format):

```json
"identifiers": [
        {
          "type": "gosec_rule_id",
          "name": "Gosec Rule ID G307",
          "value": "G307"
        },
        {
          "type": "CWE",
          "name": "CWE-703",
          "value": "703",
          "url": "https://cwe.mitre.org/data/definitions/703.html"
        }
      ]
```

In the following example, rules from `gosec` are matched by the `type`
and `value` of identifiers and then overridden:

```toml
[gosec]
  [[gosec.ruleset]]
    [gosec.ruleset.identifier]
        type = "CWE"
        value = "703"
    [gosec.ruleset.override]
      severity = "Critical"
```

If a vulnerability is found with a type `CWE` with a value of `703` then
the vulnerability severity is overwritten to `Critical`.

## Synthesize a custom configuration

To create a custom configuration, you can use passthrough chains.

A passthrough is a single step in a passthrough chain. The passthrough is evaluated
in a sequence to incrementally build a configuration. The configuration is then
passed to the target analyzer.

A configuration section for an analyzer has the following
parameters:

| Parameter     | Explanation |
| ------------- | ------ |
| `description` | Description about the analyzer configuration section. |
| `targetdir`   | The `targetdir` parameter defines the directory where the final configuration is located. If `targetdir` is empty, the analyzer uses a random directory. The maximum size of `targetdir` is 100MB. |
| `validate`    | If set to `true`, the target files for passthroughs (`raw`, `file` and `url`) are validated. The validation works for `yaml`, `xml`, `json` and `toml` files. The proper validator is identified based on the extension of the target file. By default, `validate` is set to `false`. |
| `interpolate` | If set to `true`, environment variable interpolation is enabled so that the configuration uses secrets/tokens. We advise using this feature with caution to not leak any secrets. By default, `interpolate` is set to `false`. |
| `timeout`     | The total `timeout` for the evaluation of a passthrough chain is set to 60 seconds. If `timeout` is not set, the default timeout is 60 seconds. The timeout cannot exceed 300 seconds. |

A configuration section can include one or more passthrough sections. The maximum number of passthrough sections is 20.
There are several types of passthroughs:

| Type   | Description |
| ------ | ------ |
| `file` | Use a file that is already available in the Git repository. |
| `raw`  | Provide the configuration inline. |
| `git`  | Pull the configuration from a remote Git repository. |
| `url`  | Fetch the analyzer configuration through HTTP. |

If multiple passthrough sections are defined in a passthrough chain, their
position in the chain defines the order in which they are evaluated.

- Passthroughs listed later in the chain sequence have a higher precedence.
- Passthroughs with a higher precedence overwrite (default) and append data
  yielded by previous passthroughs. This is useful for cases where you need to
  use or modify an existing configuration.

Configure a passthrough these parameters:

| Parameter     | Explanation |
| ------------ | ----------- |
| `type`       | One of `file`, `raw`, `git` or `url`. |
| `target`     | The target file that contains the data written by the passthrough evaluation. If no value is provided, a random target file is generated. |
| `mode`       | `overwrite`: if `target` exists, overwrites the file; `append`: append to file instead. The default is `overwrite`. |
| `ref`        | This option only applies to the `git` passthrough type and contains the name of the branch or the SHA to be used. |
| `subdir`     | This option only applies to the `git` passthrough type and can be used to only consider a certain subdirectory of the source Git repository. |
| `value`      | For the `file` `url` and `git` types, `value` defines the source location of the file/Git repository; for the `raw` type, `value` carries the raw content to be passed through. |
| `validator`  | Can be used to explicitly invoke validators (`xml`, `yaml`, `json`, `toml`) on the target files after the application of a passthrough. Per default, no validator is set. |

The amount of data generated by a single passthrough is limited to 1MB.

## Passthrough configuration examples

### Raw passthrough for nodejs-scan

Define a custom analyzer configuration. In this example, customized rules are
defined for the `nodejs-scan` scanner:

```toml
[nodejs-scan]
  description = 'custom ruleset for nodejs-scan'

  [[nodejs-scan.passthrough]]
    type  = "raw"
    value = '''
- nodejs-extensions:
  - .js

  template-extensions:
  - .new
  - .hbs
  - ''

  ignore-filenames:
- skip.js

  ignore-paths:
  - __MACOSX
  - skip_dir
  - node_modules

  ignore-extensions:
  - .hbs

  ignore-rules:
  - regex_injection_dos
  - pug_jade_template
  - express_xss

'''
```

### File passthrough for Gosec

Provide the name of the file containing a custom analyzer configuration. In
this example, customized rules for the `gosec` scanner are contained in the
file `gosec-config.json`:

```toml
[gosec]
  description = 'custom ruleset for gosec'

  [[gosec.passthrough]]
    type  = "file"
    value = "gosec-config.json"
```

### Passthrough chain for Semgrep

In the below example, we generate a custom configuration under the `/sgrules`
target directory with a total `timeout` of 60 seconds.

Several passthrouh types generate a configuration for the target analyzer:

- Two `git` passthrough sections pull the head of branch
  `refs/remotes/origin/test` from the `myrules` Git repository, and revision
  `97f7686` from the `sast-rules` Git repository. From the `sast-rules` Git
  repository, only data from the `go` subdirectory is considered.
  - The `sast-rules` entry has a higher precedence because it appears later in
    the configuration.
  - If there is a filename collision between files in both repositories, files
    from the `sast` repository overwrite files from the `myrules` repository,
    as `sast-rules` has higher precedence.
- The `raw` entry creates a file named `insecure.yml` under `/sgrules`. The
  full path is `/sgrules/insecure.yml`.
- The `url` entry fetches a configuration made available through a URL and
  stores it in the `/sgrules/gosec.yml` file.

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

```toml
[semgrep]
  description = 'semgrep custom rules configuration'
  targetdir = "/sgrules"
  timeout = 60

  [[semgrep.passthrough]]
    type  = "git"
    value = "https://gitlab.com/user/myrules.git"
    ref = "refs/remotes/origin/test"

  [[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"
```

### Interpolation

The code snippet below shows an example configuration that uses an environment
variable `$GITURL` to access a private repositories with a Git URL. The variable contains
a username and token in the `value` field (for example `https://user:token@url`).
It does not explicitly store credentials in the configuration file. To reduce the risk of leaking secrets through created paths and files, use this feature with caution.

```toml
[semgrep]
  description = 'semgrep custom rules configuration'
  targetdir = "/sgrules"
  interpolate = true

  [[semgrep.passthrough]]
    type  = "git"
    value = "$GITURL"
    ref = "refs/remotes/origin/main"
```

### Configure the append mode for passthroughs

To append data to previous passthroughs, use the `append` mode for the
passthrough types `file`, `url`, and `raw`.

Passthroughs in `override` mode overwrite files
created when preceding passthroughs in the chain find a naming
collision. If `mode` is set to `append`, a passthrough appends data to the
files created by its predecessors instead of overwriting.

In the below Semgrep configuration,`/sgrules/insecure.yml` assembles two passthroughs. The rules are:

- `insecure`
- `secret`

These rules add a search pattern to the analyzer and extends Semgrep capabilities.

For passthrough chains we recommend that you enable validation. To enable validation,
you can either:

- set `validate` to `true`

- set a passthrough `validator` to `xml`, `json`, `yaml`, or `toml`.

```toml
[semgrep]
  description = 'semgrep custom rules configuration'
  targetdir = "/sgrules"
  validate = true

  [[semgrep.passthrough]]
    type  = "raw"
    target = "insecure.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 = "insecure.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
    cwe: "..."
  severity: "ERROR"
  languages:
  - "go"
"""
```