我们经常发布文档更新,此页面的翻译可能仍在进行中。有关最新信息,请访问英文文档。如果此页面上的翻译有问题,请告诉我们

对代码扫描的 SARIF 支持

要在 GitHub 上的仓库中显示第三方静态分析工具的结果,您需要将结果存储在 SARIF 文件中,以支持用于代码扫描的 SARIF 2.1.0 JSON 架构的特定子集。 如果使用默认 CodeQL 静态分析引擎,结果将自动显示于您在 GitHub 上的仓库中。

本文内容

注意:代码扫描 目前处于测试阶段,可能会更改。 要申请访问测试版,请加入等待列表

关于 SARIF 支持

SARIF(数据分析结果交换格式)是定义输出文件格式的 OASIS 标准。 SARIF 标准用于简化静态分析工具分享其结果的方式。 代码扫描 支持 SARIF 2.1.0 JSON 架构的子集。

要从第三方静态代码分析引擎上传 SARIF 文件,需确保上传的文件使用 SARIF 2.1.0 版本。 GitHub 将剖析 SARIF 文件,并在代码扫描过程中使用仓库中的结果显示警报。 更多信息请参阅“将 SARIF 文件上传到 GitHub”。 有关 SARIF 2.1.0 JSON 架构的更多信息,请参阅 sarif-schema-2.1.0.json

如果您使用的是 GitHub 的语义代码分析引擎 CodeQL,则代码分析输出将自动使用支持的 SARIF 2.1.0 子集。

GitHub 使用 SARIF 文件中的属性来显示警报。 例如,shortDescriptionfullDescription 出现在 代码扫描 警报的顶部。 location 允许 GitHub 在代码文件中显示注释。 更多信息请参阅“管理来自 代码扫描 的警报”。

如果您是 SARIF 的新用户,想了解更多信息,请参阅 Microsoft 的SARIF 教程库。

使用指纹防止重复警报

每当 GitHub 操作 工作流程运行新代码扫描时,都会处理每个运行的结果并将警报添加到仓库中。 为防止出现针对同一问题的重复警报,代码扫描 使用指纹匹配各个运行的结果,使它们只会出现在所选分支的最新运行中出现一次。

GitHub 使用 OASIS 标准中的 partialFingerprints 属性来检测两个结果在逻辑上是否相同。 更多信息请参阅 OASIS 文档中的 "partialFingerprints property" 条目。

如果您的 SARIF 文件不包含 partialFingerprints,则当您使用 GitHub 操作 上传 SARIF 文件时,将计算 partialFingerprints 字段。 更多信息请参阅“将 SARIF 文件上传到 GitHub”。

支持的 SARIF 输出文件属性

如果您使用 CodeQL 以外的代码分析引擎,则可以查看受支持的 SARIF 属性来优化您的分析结果在 GitHub 中的显示方式。

任何有效的 SARIF 2.1.0 输出文件都可以上传,但 代码扫描 只使用以下受支持的属性。

sarifLog 对象

名称描述
$schema必需。2.1.0 版 SARIFJSON 架构的 URI。 例如,https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json
version必选。 代码扫描 只支持 SARIF 版本 2.1.0
runs[]必选。 SARIF 文件包含一个或多个运行的数组。 每个运行代表分析工具的一次运行。 有关 run 的更多信息,请参阅 run 对象

run 对象

代码扫描 使用 run 对象按工具过滤结果并提供关于结果来源的信息。 run 对象包含 tool.driver 工具组件对象,该对象包含有关生成结果的工具的信息。 每个 run 只能获得一个分析工具的结果。

名称Description
tool.driver.name必需。分析工具的名称。 代码扫描 在 GitHub 上显示名称,以允许您按工具过滤结果。
tool.driver.version可选。分析工具的版本。 代码扫描 使用版本号来跟踪何时可能因工具版本的变更而不是所分析代码的变更而导致了结果变化。 如果 SARIF 文件包含 semanticVersion 字段,则 代码扫描 不使用 version
tool.driver.semanticVersion可选。以语义版本 2.0 格式指定的分析工具版本。 代码扫描 使用版本号来跟踪何时可能因工具版本的变更而不是所分析代码的变更而导致了结果变化。 如果 SARIF 文件包含 semanticVersion 字段,则 代码扫描 不使用 version。 更多信息请参阅语义版本文档中的“语义版本 2.0.0”。
tool.driver.rules[]必需。用于表示规则的 reportingDescriptor 对象数组。 分析工具使用规则来查找所分析代码中的问题。 更多信息请参阅 reportingDescriptor 对象
results[]必需。分析工具的结果。 代码扫描 在 GitHub 上显示结果。 更多信息请参阅 result 对象

reportingDescriptor 对象

名称描述
id必需。规则的唯一标识符。 id 是从 SARIF 文件的其他部分引用的,可能被 代码扫描 用于在 GitHub 上显示 URL。
name可选。规则的名称。 代码扫描 显示名称,以允许按 GitHub 上的规则过滤结果。
shortDescription.text必需。规则的简要说明。 代码扫描 在 GitHub 上的相关结果旁边显示简短说明。
fullDescription.text必需。规则的说明。 代码扫描 在 GitHub 上的相关结果旁边显示完整说明。 最大字符数限制为 1000。
defaultConfiguration.level可选。规则的默认严重级别。 代码扫描 使用严重级别帮助您了解结果对于给定规则的严重程度。 此值可用 result 对象中的 level 属性进行覆盖。 更多信息请参阅 result 对象。 默认值:warning
help.text可选。使用文本格式的规则文档。 代码扫描 在相关结果旁边显示此帮助文档。
help.markdown推荐。使用 Markdown 格式的规则文档。 代码扫描 在相关结果旁边显示此帮助文档。 Markdown 帮助优先于 help.text
properties.tags[]可选。字符串数组。 代码扫描 使用 tags 允许您在 GitHub 上过滤结果。 例如,可以过滤带标记 security 的所有结果。
properties.precision推荐。一个字符串,表示此规则指示的结果为真的频率。 例如,如果已知某项规则的误报率较高,则其准确性应为 low。 代码扫描 在 GitHub 上按准确性对结果进行排序,使具有最高 level 和最高 precision 的结果显示在最前面。 可以是以下值之一:very-highhighmediumlowunknown

result 对象

名称描述
ruleId可选。规则的唯一标识符 (reportingDescriptor.id)。 更多信息请参阅 reportingDescriptor 对象。 代码扫描 使用规则标识符在 GitHub 上按规则过滤结果。
ruleIndex可选。工具组件 rules 数组中相关规则(reportingDescriptor 对象)的索引。 更多信息请参阅 run 对象
rule可选。用于定位此结果的规则 (reportingdescriptor) 的引用。 更多信息请参阅 reportingDescriptor 对象
level可选。结果的严重程度。 此级别覆盖规则定义的默认严重程度。 代码扫描 使用级别在 GitHub 上按严重程度过滤结果。
message.text必选。描述结果的消息。 代码扫描 显示消息文本作为结果的标题。 当可见空间有限时,仅显示消息的第一句。
locations[]必选。检测到结果的位置集。 应只包含一个位置,除非只能通过在每个指定位置进行更改来更正问题。 注:代码扫描 至少需要一个位置才能显示结果。 代码扫描 将使用此属性来决定要用结果注释哪个文件。 仅使用此数组的第一个值。 所有其他值都被忽略。
partialFingerprints必选。用于跟踪结果的唯一标识的一组字符串。 代码扫描 使用 partialFingerprints 准确地识别在提交和分支之间相同的结果。 代码扫描 将尝试使用 partialFingerprints(如果存在)。 如果您使用 upload-action 上传第三方 SARIF 文件,该操作将为您创建 partialFingerprints(如果它们未包含在 SARIF 文件中)。 更多信息请参阅“使用指纹防止重复警报”。 注:代码扫描 只使用 primaryLocationLineHash
codeFlows[].threadFlows[].locations[]可选。threadFlow 对象的 location 对象数组,它描述程序通过执行线程的进度。 codeFlow 对象描述用于检测结果的代码执行模式。 如果提供了代码流,代码扫描 将在 GitHub 上扩展代码流以获取相关结果。 更多信息请参阅 location 对象
relatedLocations[]与此结果相关的一组位置。 当相关位置嵌入在结果消息中时,代码扫描 将链接到这些位置。 更多信息请参阅 location 对象
suppressions[].state可选。state 设置为 accepted 时,代码扫描 将在 GitHub 上将结果的状态更新为 Closed

location 对象

编程构件中的位置,例如仓库中的文件或在构建过程中生成的文件。

名称描述
location.id可选。用于在单个结果对象中区分此位置与所有其他位置的唯一标识符。
location.physicalLocation必选。标识构件和区域。 更多信息请参阅 physicalLocation
location.message.text可选。与位置相关的消息。

physicalLocation 对象

名称描述
artifactLocation.uri必选。表示构件位置的 URI,通常是仓库中或在构建期间生成的文件。 如果 URI 是相对的,它应相对于正在分析的 GitHub 仓库的根目录。 例如,main.js 或 src/script.js 相对于仓库的根目录。 如果 URI 是绝对的,则 代码扫描 可使用 URI 检出构件并匹配仓库中的文件。 例如,https://github.com/github/example/blob/00/src/promiseUtils.js
region.startLine必选。区域中第一个字符的行号。
region.startColumn必选。区域中第一个字符的列编号。
region.endLine必选。区域中最后一个字符的行号。
region.endColumn必选。区域结束后字符的列编号。

SARIF 输出文件示例

这些示例 SARIF 输出文件显示支持的属性和示例值。

具有最少必需属性的示例

此 SARIF 输出文件的示例值显示了 代码扫描 结果正常运行所需的最少属性。 如果您删除任何属性或不包含值,此数据将无法正确显示或在 GitHub 上同步。

{
    "$schema" : "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
    "version" : "2.1.0",
    "runs" :
    [
    {
        "tool" : {
        "driver" : {
            "name" : "Tool Name"
        }
        },
        "results" : [ {
        "message" : {
            "text" : "Result text. This result does not have a rule associated."
        },
        "locations" : [ {
            "physicalLocation" : {
            "artifactLocation" : {
                "uri" : "src/build.cmd"
            },
            "region" : {
                "startLine" : 2,
                "startColumn" : 7,
                "endColumn" : 10
            }
            }
        } ],
        "partialFingerprints" : {
            "primaryLocationLineHash" : "39fa2ee980eb94b0:1"
        }
        }]
    }       
    ]
}

显示所有支持的 SARIF 属性的示例

此 SARIF 输出文件的示例值显示了 代码扫描 的所有受支持 SARIF 属性。

{
  "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
  "version": "2.1.0",
  "runs": [
    {
      "tool": {
        "driver": {
          "name": "Tool Name",
          "semanticVersion": "2.0.0",
          "rules": [
            {
              "id": "js/unused-local-variable",
              "name": "js/unused-local-variable",
              "shortDescription": {
                "text": "Unused variable, import, function or class"
              },
              "fullDescription": {
                "text": "Unused variables, imports, functions or classes may be a symptom of a bug and should be examined carefully."
              },
              "defaultConfiguration": {
                "level": "note"
              },
              "properties": {
                "tags": [
                  "maintainability"
                ],
                "precision": "very-high"
              }
            },
            {
              "id": "js/inconsistent-use-of-new",
              "name": "js/inconsistent-use-of-new",
              "shortDescription": {
                "text": "Inconsistent use of 'new'"
              },
              "fullDescription": {
                "text": "If a function is intended to be a constructor, it should always be invoked with 'new'. Otherwise, it should always be invoked as a normal function, that is, without 'new'."
              },
              "defaultConfiguration": null,
              "properties": {
                "tags": [
                  "reliability",
                  "correctness",
                  "language-features"
                ],
                "precision": "very-high"
              }
            }
          ]
        }
      },
      "results": [
        {
          "ruleId": "js/unused-local-variable",
          "ruleIndex": 0,
          "message": {
            "text": "Unused variable foo."
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "main.js",
                  "uriBaseId": "%SRCROOT%",
                  "index": 0
                },
                "region": {
                  "startLine": 2,
                  "startColumn": 7,
                  "endColumn": 10
                }
              }
            }
          ],
          "partialFingerprints": {
            "primaryLocationLineHash": "39fa2ee980eb94b0:1",
            "primaryLocationStartColumnFingerprint": "4"
          }
        },
        {
          "ruleId": "js/inconsistent-use-of-new",
          "ruleIndex": 1,
          "message": {
            "text": "Function resolvingPromise is sometimes invoked as a constructor (for example [here](1)), and sometimes as a normal function (for example [here](2))."
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "https://github.com/github/example/blob/0000000000000000000000000000000000000000/src/promiseUtils.js",
                  "index": 1
                },
                "region": {
                  "startLine": 2
                }
              }
            }
          ],
          "partialFingerprints": {
            "primaryLocationLineHash": "5061c3315a741b7d:1",
            "primaryLocationStartColumnFingerprint": "7"
          },
          "relatedLocations": [
            {
              "id": 1,
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "src/ParseObject.js",
                  "uriBaseId": "%SRCROOT%",
                  "index": 3
                },
                "region": {
                  "startLine": 2281,
                  "startColumn": 33,
                  "endColumn": 55
                }
              },
              "message": {
                "text": "here"
              }
            },
            {
              "id": 2,
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "src/LiveQueryClient.js",
                  "uriBaseId": "%SRCROOT%",
                  "index": 2
                },
                "region": {
                  "startLine": 166
                }
              },
              "message": {
                "text": "here"
              }
            }
          ]
        },
        {
          "message": {
            "text": "Specifying both [ruleIndex](1) and [ruleID](2) might lead to inconsistencies."
          },
          "level": "error",
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "full.sarif",
                  "uriBaseId": "%SRCROOT%",
                  "index": 0
                },
                "region": {
                  "startLine": 54,
                  "startColumn": 10,
                  "endLine": 55,
                  "endColumn": 25
                }
              }
            }
          ],
          "relatedLocations": [
            {
              "id": 1,
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "full.sarif"
                },
                "region": {
                  "startLine": 81,
                  "startColumn": 10,
                  "endColumn": 18
                }
              },
              "message": {
                "text": "here"
              }
            },
            {
              "id": 2,
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "full.sarif"
                },
                "region": {
                  "startLine": 82,
                  "startColumn": 10,
                  "endColumn": 21
                }
              },
              "message": {
                "text": "here"
              }
            }
          ],
          "codeFlows": [
            {
              "threadFlows": [
                {
                  "locations": [
                    {
                      "location": {
                        "physicalLocation": {
                          "region": {
                            "startLine": 11,
                            "endLine": 29,
                            "startColumn": 10,
                            "endColumn": 18
                          },
                          "artifactLocation": {
                            "uriBaseId": "%SRCROOT%",
                            "uri": "full.sarif"
                          }
                        },
                        "message": {
                          "text": "Rule has index 0"
                        }
                      }
                    },
                    {
                      "location": {
                        "physicalLocation": {
                          "region": {
                            "endColumn": 47,
                            "startColumn": 12,
                            "startLine": 12
                          },
                          "artifactLocation": {
                            "uriBaseId": "%SRCROOT%",
                            "uri": "full.sarif"
                          }
                        }
                      }
                    }
                  ]
                }
              ]
            }
          ],
          "partialFingerprints": {
            "primaryLocationLineHash": "ABC:2"
          }
        }
      ],
      "newlineSequences": [
        "\r\n",
        "\n",
        "",
        "
"
      ],
      "columnKind": "utf16CodeUnits"
    }
  ]
}

问问别人

找不到要找的内容?

联系我们