Semgrep 之规则语法(三)

现在终于到我们的重头戏了,学习 Semgrep 的规则语法。 Semgrep 的规则包含了一些字段,其中有些字段是必填字段、可选字段,每个字段代表的意义不同

一、必填字段

字段名 类型 描述
id string id必须唯一, 描述字符,例如使用no-unused-variable
message string 一般描述命中此规则的原因以及如何解决该问题单元格
severity string INFO、WARNING、ERROR之一
languages array 语言的扩展标签,例如.py, .go等,具体可查看附录一
pattern* string 查找匹配此表达式的代码
patterns* array 多个模式的逻辑与
patterns* array 多个模式的逻辑或
pattern-regex* string 在多行模式下查找与此PCRE兼容模式匹配的代码

pattern、patterns、pattern-either、pattern-regex在规则里仅需要其中之一即可

二、可选字段

字段 类型 描述
options object 启用/禁用某些匹配功能的选项对象
fix object 简单的搜索和替换,自动修复功能
metadata object 任意用户提供的数据;将数据附加到规则而不影响 Semgrep 的行为
paths object 运行此规则时要包含或排除的路径

pattern-inside 字段必须位于 patterns or pattern-either 字段下方。

字段 类型 描述
pattern-inside string 保留此模式内的发现

以下可选字段必须位于 patterns 字段下方。

字段 类型 描述
metavariable-regex map 通过Python的re模块搜索元变量;正则表达式匹配未锚定
metavariable-pattern map 将元变量与模式公式匹配
metavariable-comparison map 将元变量与基本Python 表达式进行比较
pattern-not string 逻辑非 – 删除匹配此表达式的结果
pattern-not-inside string 保留不属于此模式的发现
pattern-not-regex string 在多行模式下使用PCRE兼容模式过滤结果

三、操作方式

下面将介绍每个字段的使用方式

3.1 pattern

pattern 运算符查找与其表达式匹配的代码。可以是基本表达式如 $X == $X 或者查找不想要的函数调用,例如 hashlib.md5(...)Semgrep 之规则语法(三)

3.2 patterns

运算符 patterns 是对一个或多个子模式执行逻辑与运算。这对于将多个 pattern 结合在一起非常有用,所有 pattern 都必须为 true。Semgrep 之规则语法(三)

注意,在运算符中声明子 pattern 的顺序,对 patterns 最终结果没有影响。patterns 运算符始终以相同的方式进行评估。

3.3 pattern-either

运算符 pattern-either 对一个或多个子模式执行逻辑或运算。对于在任何可能为 true 的情况下将多个模式链接在一起很有用。

下面的规则是查找 Python 标准库函数 hashlib.md5hashlib.sha1. 这两个散列函数被认为是不安全的。Semgrep 之规则语法(三)

3.4  pattern-regex

pattern-regex 运算符在文件中搜索给定 PCRE 模式匹配的子字符串。这对于将现有的正则表达式代码搜索功能迁移到 Semgrep 很有用。PCRE “Perl-Compatible Regular Expressions” 是一个功能齐全的正则表达式库,它当然与 Perl 广泛兼容,也与 Python、JavaScript、Go、Ruby 和 Java 各自的正则表达式库兼容。该模式以多行模式编译,即除了输入的开头 ^,和 $ 结尾之外。还将分别在行的开头和结尾匹配(自 Semgrep 0.95.0 起)。

PCRE 仅支持有限数量的 Unicode 字符属性。例如,p{Egyptian_Hieroglyphs} 支持但 p{Bidi_Control} 不支持。

pattern-regex 运算符可以与其他模式运算符组合:Semgrep 之规则语法(三)

也可以用作独立的顶级运算符:Semgrep 之规则语法(三)

单 ( ') 和双 ( ") 引号在 YAML 语法中的行为不同。当使用 pattern-regex 与反斜杠 一起使用时,通常首选单引号。

如果正则表达式使用组,元变量 $1,$2 等将绑定到捕获组的内容。Semgrep 之规则语法(三)

3.5  pattern-not-regex

pattern-not-regex 运算符在多行模式下使用 PCRE 正则表达式过滤结果。当与正则表达式规则结合使用时,非常有用。它提供了一种简单的方法来过滤结果,而无需使用 negative lookahead(零宽度负预测先行断言)。pattern-not-regex 也可以与常规 pattern 子句一起使用。

pattern-not-regex 运算符的语法与 pattern-regex 很像,但是该运算符将过滤与提供的正则表达式有任何重叠的结果。例如,如果您使用 pattern-regex 检测 Foo==1.1.1 并且它也检测 Foo-Bar==3.0.8Bar-Foo==3.0.8,你可以使用 pattern-not-regex 过滤不需要的发现。Semgrep 之规则语法(三)

3.6  focus-metavariable

focus-metavariable 将焦点放大在与元变量匹配的代码区域上。例如,要查找使用类型是 bad 的所有函数参数,可以编写以下模式:

pattern: |
  def $FUNC(..., $ARG : bad, ...):
    ...

这个规则是有效的,但它会匹配整个函数定义。这是不可取的。如果定义跨越数百行,都将会匹配到。特别是,如果你正在使用 Semgrep 应用程序并且您已对由此模式生成的结果进行分类,如果您对函数的定义进行任何更改,相同的结果将再次显示为新结果!

需要指定只对特定元变量匹配的代码感兴趣,例如示例 $ARG 中,使用 focus-metavariable.Semgrep 之规则语法(三)注意,focus-metavariable: $ARG 这与 pattern: $ARG 不一样。 使用 pattern: $ARG 会找到使用该参数的用法,x 这不是我们想要的!(pattern: $ARG 不匹配形式参数声明,因为在这个上下文中 $ARG 只匹配表达式。)Semgrep 之规则语法(三)

简而言之,focus-metavariable: $X 它本身不是一个 pattern,它不执行任何匹配,它只将匹配重点放在已经 $X 被其他模式绑定的代码上。而 pattern: $X 将与代码匹配(在这种情况下,$X 仅匹配表达式)

3.7 metavariable-regex

metavariable-regex 运算符在元变量中搜索 PCRE 正则表达式。这对于根据元变量的值过滤结果很有用。它需要 metavariable和regex,并且可以与其他模式运算符结合使用。Semgrep 之规则语法(三)

正则表达式匹配是未锚定的。对于锚定匹配,A 用于字符串开始锚定、Z 字符串结尾锚定。下面示例,使用与上面相同的表达式但指定锚定,将找不到匹配项:Semgrep 之规则语法(三)

metavariable-regex 在用于搜索字符串文字时,需要在正则表达式中包含引号。

3.8 metavariable-pattern

metavariable-pattern 运算符将元变量与模式公式匹配。这对于根据元变量的值过滤结果很有用。它需要 metavariable 的 key,并且需要是 patternpatternspattern-either pattern-regex 其中一个。该运算符可以嵌套以及与其他运算符组合。

例如,它可用于过滤掉与某些条件不匹配的匹配项:Semgrep 之规则语法(三)

还可以结合使用 pattern-eitherSemgrep 之规则语法(三)

3.9 metavariable-comparison

metavariable-comparison 运算符将元变量与基本的 Python 表达式进行比较。这对于根据元变量的数值过滤结果很有用。

metavariable-comparison 运算符是一个需要 metavariablecomparison key 的映射。它可以与其他模式运算符结合使用:Semgrep 之规则语法(三)

这将捕获到 set_port(443),而不是 set_port(8080)

比较表达式支持简单的算术运算以及与布尔运算符的组合用以允许更复杂的匹配。这对于检查元变量是否可以被特定值整除特别有用,例如强制特定值是偶数还是奇数:Semgrep 之规则语法(三)

在前面的示例的基础上,会捕获到 set_port(80) 但不会再捕获 set_port(443)set_port(8080)

comparison key 使用以下命令接受 Python 表达式:

  • 布尔、字符串、整数和浮点文字。
  • 布尔运算符 not, or, 和 and.
  • 算术运算符 +, -, *, /, 和 %.
  • 比较运算符 ==, !=, <, <=, >, 和 >=.
  • int() 将字符串转换为整数的函数。
  • str() 将数字转换为字符串的函数。
  • 判断是否在列表中存在,in 运算符。
  • re.match() 匹配正则表达式的函数(没有可选 flags 参数)。

你可以使用 Semgrep 元变量,例如 $MVAR,Semgrep 评估检测如下:

  • 如果 $MVAR 绑定到文字,则该文字是分配给的值 $MVAR
  • 如果 $MVAR 绑定到作为常量的代码变量,并且启用了常量传播(默认情况下),那么该常量就是分配给 $MVAR.
  • 否则,绑定到 的代码将 $MVAR 保持未评估,并且可以使用 str() 函数获取其字符串表示形式,如 str($MVAR). 例如,如果 $MVAR 绑定到代码变量 x,则 str($MVAR) 计算为字符串字面量 "x"

3.10 pattern-not

pattern-not和pattern 是相反的。它找到与其表达式不匹配的代码。这对于消除常见的误报很有用。Semgrep 之规则语法(三)

3.11 pattern-inside

pattern-inside 运算符保留其表达式中的匹配结果。这对于在其他代码段(如函数或 if 块)中查找代码很有用。Semgrep 之规则语法(三)

3.12 pattern-not-inside

pattern-not-inside 运算符保留不存在于其表达式中的匹配结果。它和 pattern-inside 是相反的。这对于查找缺少相应清理操作(如断开、关闭或关闭)的代码很有用、对于查找不在缓解问题的代码中的有问题的代码也很有用。Semgrep 之规则语法(三)

上述规则会查找已打开但从未关闭的文件,这可能会导致资源耗尽。它寻找 open(...) 且后续没有 close() 的模式。

需要确保在 open和close 调用中使用相同的变量名 $F。省略号运算符允许将任何参数传递 open 调用。该规则忽略了如何被调用或调用发生了什么——它只需要确保 close 被调用。

四、元变量匹配

对于逻辑 与 ( patterns) 和逻辑 或( pattern-either) 运算符,元变量匹配的操作方式不同。所有子运算符的行为都是一致的:pattern, pattern-not, pattern-regex, pattern-inside, pattern-not-inside

4.1 逻辑与中的元变量

使用运算符执行逻辑与运算时,元变量值必须在子模式中相同 patterns

例子:

rules:
  - id: function-args-to-open
    patterns:
      - pattern-inside: |
          def $F($X):
              ...
      - pattern: open($X)
    message: "Function argument passed to open() builtin"
    languages: [python]
    severity: ERROR

此规则与以下代码匹配:

def foo(path):
    open(path)

示例规则与此代码不匹配:

def foo(path):
    open(something_else)

4.2 逻辑或中的元变量

元变量匹配不影响逻辑或 pattern-either 运算符的匹配。

例子:

rules:
  - id: insecure-function-call
    pattern-either:
      - pattern: insecure_func1($X)
      - pattern: insecure_func2($X)
    message: "Insecure function use"
    languages: [python]
    severity: ERROR

上述规则匹配以下两个示例:

insecure_func1(something)
insecure_func2(something)

insecure_func1(something)
insecure_func2(something_else)

4.3 复杂的元变量

如果父项是逻辑 与,元变量匹配仍会影响后续的逻 或。

例子:

patterns:
  - pattern-inside: |
      def $F($X):
        ...
  - pattern-either:
      - pattern: bar($X)
      - pattern: baz($X)

上述规则匹配以下两个示例:

def foo(something):
    bar(something)

def foo(something):
    baz(something)

示例规则与此代码不匹配:

def foo(something):
    bar(something_else)

五、其他功能

5.1 fix

fix key 允许为每个匹配建议自动修复。运行 semgrep--autofix 将更改应用到文件。

例子:

rules:
  - id: use-dict-get
    patterns:
      - pattern: $DICT[$KEY]
    fix: $DICT.get($KEY)
    message: "Use `.get()` method to avoid a KeyNotFound error"
    languages: [python]
    severity: ERROR

5.2 metadata

记录有关规则的额外信息,例如相关的 CVE 或编写规则的安全工程师的姓名,这时可以使用 metadata: key。

例子:

rules:
  - id: eqeq-is-bad
    patterns:
      - [...]
    message: "useless comparison operation `$X == $X` or `$X != $X`"
    metadata:
      cve: CVE-2077-1234
      discovered-by: Ikwa L'equale

5.3 Paths

5.3.1 排除路径

要忽略特定文件的特定规则,paths: 使用一个或多个过滤器设置键。

例子:

rules:
  - id: eqeq-is-bad
    pattern: $X == $X
    paths:
      exclude:
        - "*.jinja2"
        - "*_test.go"
        - "project/tests"
        - project/static/*.js

当使用 调用时 semgrep -f rule.yaml project/,上述规则将在 内部的文件上运行 project/,但不会返回任何结果:

  • 任何带有 .jinja2 文件扩展名的文件
  • 任何名称以 结尾的文件 _test.go,例如 project/backend/server_test.go
  • 里面的任何文件 project/tests 或其子目录
  • 任何匹配 project/static/*.js glob 模式的文件

5.3.2 指定路径

相反,要针特定文件运行规则 paths: 使用以下一个或多个过滤器设置键:

rules:
  - id: eqeq-is-bad
    pattern: $X == $X
    paths:
      include:
        - "*_test.go"
        - "project/server"
        - "project/schemata"
        - "project/static/*.js"
        - "tests/**/*.js"

当使用 调用时 semgrep -f rule.yaml project/,此规则将在 内部的文件上运行 project/,但结果将仅返回:

  • 名称以 结尾的文件 _test.go,例如 project/backend/server_test.go
  • project/server,project/schemata 或其子目录中的文件
  • 匹配 project/static/*.js glob 模式的文件
  • .js 扩展名的所有文件,测试文件夹内的任意深度

如果正在为规则编写测试,则还需要将任何测试文件或目录添加到包含的路径中。

当同时包含过滤器和排除过滤器时,排除过滤器优先。

例子:

paths:
  include: "project/schemata"
  exclude: "*_internal.py"

上述规则返回结果来自 /schemata/scan.py,但不会来自 project/schemata/scan_internal.py

六、编写规则

当到这一步的时候,相信你已经对 Semgrep 的规则语法比较熟悉了,下面写一个属于自己的规则。

需求时,当检测 Python 语言进行 csv 文件导出的时候,即使用 csv.writer 的时候,判断是否使用 defusedcsv.writer,如果未使用 defusedcsv.writer,可能会有 CSV 注入,这时我们把 csv 替换成 defusedcsv,对于 CSV 注入漏洞感兴趣的可以参考该文章 CSV 注入原理和预防

rules:
  - id: use-defusedcsv
    patterns:
      - pattern: csv.writer(...)
      - pattern-not: defusedcsv.writer(...)
    message: >-
      Detected the generation of a CSV file using the built-in `csv` module.
      If user data is used to generate the data in this file, it is possible that
      an attacker could inject a formula when the CSV is imported into a spreadsheet
      application that runs an attacker script, which could steal data from the importing
      user or, at worst, install malware on the user's computer. `defusedcsv` is a
      drop-in replacement with the same API that will attempt to mitigate formula
      injection attempts. You can use `defusedcsv` instead of `csv` to safely generate CSVs.
    metadata:
      cwe: "CWE-1236: Improper Neutralization of Formula Elements in a CSV File"
      owasp: 
      - "A01:2017 - Injection"
      - "A03:2021 - Injection"
      references:
        - https://github.com/raphaelm/defusedcsv
        - https://owasp.org/www-community/attacks/CSV_Injection
        - https://web.archive.org/web/20220516052229/https://www.contextis.com/us/blog/comma-separated-vulnerabilities
      category: security
      technology:
        - python
      confidence: LOW
    fix-regex:
      regex: csv
      replacement: defusedcsv
    languages: [python]
    severity: INFO

附录 1、各语言的扩展和标签

语言 扩展 标签
Bash .sh bash
C .c c
C++ .cpp, .h cpp
C# .cs csharp, cs, C#
Generic
generic
Go .go go, golang
Hack .h, .hack hack
HTML .htm, .html html
Java .java java
JavaScript .js, .jsx js, jsx, javascript
JSON .json json, JSON, Json
JSX .js, .jsx js, jsx, javascript
Kotlin .kt, .kts, .ktm kotlin
Lua .lua lua
OCaml .ml, .mli ocaml, ml
PHP .php php
Python .py, .pyi python, python2, python3, py
R .r, .rda, rds r
Ruby .rb ruby, rb
Rust .rs rust, Rust, rs
Scala .scala, .sc scala
Solidity .sol solidity
Terraform .tf hcl
TypeScript .ts, .tsx ts, tsx, typescript
TSX .ts, .tsx ts, tsx, typescript
YAML .yaml yaml


原文始发于微信公众号(洋洋自语):Semgrep 之规则语法(三)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/272917.html

(1)
明月予我的头像明月予我bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!