自定义规则集

  • 引入于 13.5 版本。
  • 添加了对透传链的支持。在版本 14.6 中,扩展为包括额外的传递类型的 filegiturl
  • 对覆盖规则的支持添加于 14.8 版本。

您可以通过在被扫描的仓库中定义规则集配置文件,来自定义我们的 SAST 分析器的行为。自定义有两种:

禁用预定义规则

您可以禁用任何 SAST 分析器的预定义规则。禁用的规则不会出现在流水线安全页面或漏洞报告中。

禁用规则具有追溯效力。分析器继续扫描漏洞,但 gl-sast-report.json 产物中省略了发现。

有关如何配置的信息,请参阅 Schema示例部分。

覆盖预定义规则

可以为任何 SAST 分析器覆盖预定义规则的某些属性。这在使 SAST 适应您现有的工作流程或工具时非常有用。例如,您可能希望根据组织策略覆盖漏洞的严重性,或者选择不同的消息显示在漏洞报告中。

有关如何配置的信息,请参阅 Schema示例部分。

合成自定义配置

您可以完全替换某些 SAST 分析器的预定义规则:

  • nodejs-scan - 您可以将默认的 njsscan 配置文件替换为您自己的。
  • semgrep - 您可以用自己的规则集替换极狐GitLab 维护的规则集。

透传配置在运行时组成透传链,并进行评估后生成完整的配置,然后针对新配置执行底层扫描器。

有多种透传类型允许您以不同的方式提供配置,例如使用提交到您的仓库的文件,或在规则集配置文件中内联。您还可以选择如何处理链中的后续传递;他们可以覆盖或附加到以前的配置。

有关如何配置的信息,请参阅 Schema示例部分。

创建配置文件

创建规则集配置文件:

  1. 在项目的根目录下创建一个 .gitlab 目录(如果不存在的话)。
  2. .gitlab 目录中创建一个名为 sast-ruleset.toml 的文件。

Schema

顶层部分

顶层部分包含定义为 TOML 表的一个或多个配置。

设置 描述
[$analyzer] 声明分析器的配置部分。该名称遵循 SAST 分析器列表中定义的蛇形名称。

配置示例:

[semgrep]
...

避免创建修改现有规则和合成自定义规则集的配置部分,因为后者完全取代了预定义规则。

[$analyzer] 配置部分

[$analyzer] 部分允许您自定义分析器的行为。有效属性因您进行的配置类型而异。

设置 适用于 描述
[[$analyzer.ruleset]] 预定义规则 定义对现有规则的修改。
interpolate 所有 如果设置为 true,则可以在配置中使用 $VAR 来评估环境变量。请谨慎使用此功能,以免泄露 secret 或令牌。(默认值:false
description 透传 自定义规则集的描述。
targetdir 透传 应保留最终配置的目录。如果为空,将创建一个具有随机名称的目录。该目录最多可包含 100MB 的文件。
validate 透传 如果设置为 true,则验证每个透传的内容。验证适用于 yamlxmljsontoml 内容。正确的验证器是根据 [[$analyzer.passthrough]] 部分的 target 参数中使用的扩展来识别的。(默认值:false
timeout 透传 在超时之前评估透传链所花费的最长时间。超时不能超过 300 秒。(默认值:60)

interpolate

caution为降低泄露 secrets 的风险,请谨慎使用此功能。

下面的示例显示了使用 $GITURL 环境变量访问私有仓库的配置。该变量包含用户名和令牌(例如 https://user:token@url),因此它们未明确存储在配置文件中。

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

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

[[$analyzer.ruleset]] 部分

[[$analyzer.ruleset]] 部分针对并修改单个预定义规则。您可以为每个分析器定义一个或多个。

设置 描述
disable 是否应禁用规则。(默认值:false
[$analyzer.ruleset.identifier] 选择要修改的预定义规则。
[$analyzer.ruleset.override] 定义规则的覆盖。

配置示例:

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

[$analyzer.ruleset.identifier] 部分

[$analyzer.ruleset.identifier] 部分定义了您希望修改的预定义规则的标识符。

设置 描述
type 预定义规则使用的标识符类型。
value 预定义规则使用的标识符的值。

您可以通过查看分析器生成的 gl-sast-report.json 来查找 typevalue 的正确值。 您可以从分析器的 CI 作业中下载此文件作为作业产物。

例如,下面的代码片段显示了来自具有三个标识符的 semgrep 规则的发现。JSON 对象中的 typevalue 键对应于您应在该部分中提供的值。

...
  "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"
        }
      ]
    }
    ...
  ]
...

配置示例:

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

[$analyzer.ruleset.override] 部分

[$analyzer.ruleset.override] 部分允许您覆盖预定义规则的属性。

设置 描述
description 议题的详细描述。
message (已废弃)议题的详细描述。
name 规则的名称。
severity 规则的严重性。有效选项是:CriticalHighMediumLowUnknownInfo
note虽然 message 由分析器填写,但它已被弃用,取而代之的是 namedescription

配置示例:

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

[[$analyzer.passthrough]] 部分

note目前仅 nodejs-scansemgrep 分析器支持。

[[$analyzer.passthrough]] 部分允许您为分析器合成自定义配置。每个分析器最多可以定义 20 个。透传传递被组合成一个透传链,该链评估为一个完整的配置,该配置替换了分析器的预定义规则。

透传按顺序评估。链中稍后列出的传递具有更高的优先级,并且可以覆盖或附加到先前传递产生的数据(取决于 mode)。对于需要使用或修改现有配置的情况很有用。

单次透传生成的数据量限制为 1MB。

设置 适用于 描述
type 所有 filerawgiturl 之一。
target 所有 包含透传评估写入的数据的目标文件。如果为空,则使用随机文件名。
mode 所有 如果 overwrite,则覆盖 target 文件。如果使用 append,新内容将附加到 target 文件。请注意,git 类型仅支持 overwrite。(默认:overwrite
ref type = "git" 包含要提取的分支或 SHA 的名称。使用分支名称时,请以 refs/heads/<branch> 的形式指定,而不是 refs/remotes/<remote_name>/<branch>
subdir type = "git" 用于选择 Git 仓库的一个子目录作为配置源。
value 所有 对于 fileurlgit 类型,定义文件或 Git 仓库的位置。对于 raw 类型,包含内联配置。
validator 所有 用于在评估透传后在目标文件上显式调用验证器(xmlyamljsontoml)。

透传类型

类型 描述
file 使用 Git 仓库中存在的文件。
raw 提供内联配置。
git 从远端 Git 仓库中拉取配置。
url 使用 HTTP 获取配置。
caution当使用带有 YAML 片段的 raw 透传传递时,建议将 sast-ruleset.toml 文件中的所有缩进格式化为空格。YAML 规范要求制表符上有空格,除非相应地表示缩进,否则分析器将无法解析您的自定义规则集。

示例

禁用 SAST 分析器的预定义规则

使用以下自定义规则集配置,报告中将省略以下规则:

  • semgrep 规则的 semgrep_idgosec.G106-1cwe322
  • sobelow 规则带有 sql_injectionsobelow_rule_id
  • flawfinder 规则使用 memcpyflawfinder_func_name
[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"

覆盖 SAST 分析器的预定义规则

使用以下自定义规则集配置,使用类型为 CWE 且值为 322semgrep 发现的漏洞的严重性将被覆盖为 Critical

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

使用 nodejs-scan 的原始透传合成自定义配置

使用以下自定义规则集配置,nodejs-scan 分析器的预定义行为将替换为自定义配置。

用于 value 的语法遵循 njsscan 配置格式

[nodejs-scan]
  description = "My 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
'''

使用 semgrep 的文件透传合成自定义配置

使用以下自定义规则集配置,semgrep 分析器的预定义规则集将替换为包含在被扫描仓库中名为 my-semgrep-rules.yaml 的文件中的自定义规则集。

# 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"

使用 semgrep 的透传链合成自定义配置

使用以下自定义规则集配置,semgrep 分析器的预定义规则集将替换为通过评估四个透传链生成的自定义规则集。每次透传都会生成一个文件,该文件将写入容器内的 /sgrules 目录。设置了 60 秒的 timeout,以防任何 Git 远端没有响应。

此示例中演示了不同的透传类型:

  • 两次 git 透传,第一次从 myrules Git 仓库拉取 refs/heads/test,第二次从 sast-rules 仓库拉取修订版 97f7686,并且只考虑 go 子目录中的文件。
    • sast-rules 条目具有更高的优先级,因为它出现在配置的后面。
    • 如果两次检出之间存在文件名冲突,则来自 sast-rules 仓库的文件将覆盖来自 myrules 仓库的文件。
  • 一个 raw 透传,它将 value 写入 /sgrules/insecure.yml
  • 一个 url 透传,它获取托管在 URL 上的配置并将其写入 /sgrules/gosec.yml

之后,使用位于 /sgrules 下的最终配置调用 Semgrep。

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

  [[semgrep.passthrough]]
    type  = "git"
    value = "https://gitlab.com/user/myrules.git"
    ref = "refs/heads/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"

配置链中透传模式

您可以选择如何处理链中透传之间发生的文件名冲突。默认覆盖同名的现有文件,但您可以选择 mode = append 来将后面文件的内容附加到前面的文件上。

您只能对 fileurlraw 透传类型使用 append 模式。

通过以下自定义规则集配置,两个 raw 透传用于迭代组装 /sgrules/my-rules.yml 文件,然后将其作为规则集提供给 Semgrep。每个透传都会将一个规则附加到规则集中。根据 Semgrep 规则语法,第一个透传负责初始化顶层 rules 对象。

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