- 测试
- 操作系统
- 网络安全
【免费下载链接】syzkaller
syzkaller is an unsupervised coverage-guided kernel fuzzer
syzkaller仓库通过 Go modules 的 vendor 机制内置了github.com/chavacava/garif这个 Go 包,用于创建和操作 SARIF(Static Analysis Results Interchange Format)日志。本文以仓库中的 garif README 为主线,结合 vendored 源码(constructors.go、decorators.go、models.go、io.go、enums.go)完整讲解:SARIF 格式是什么、garif 的“构造器 + 装饰器”API 设计、各模型结构的必选字段约束,以及如何把它接入真实工具链(本仓库中的真实消费者是 vendored 的revive静态分析器的 SARIF formatter,见 sarif.go)。读完本文,你可以直接在自己的项目里用 garif 写出符合 SARIF 2.1.0 规范的分析报告 JSON,并理解其每一个字段背后的 schema 语义。
1. SARIF 是什么,garif 解决什么问题
SARIF 全称Static Analysis Results Interchange Format,是由 OASIS 组织定义并推广的一种基于 JSON 的静态分析工具输出标准格式。garif 的 README 明确说明当前支持的版本是SARIF v2.1.0。
它的定位是“交换格式”:静态分析工具(linter、代码扫描器、编译器告警器等)各自有私有的报告格式,而 IDE、CI 平台、代码评审系统等消费方如果统一按 SARIF 解析,就不需要为每种工具写专门的适配器。这正是 OASIS 推动该标准的核心动机——让分析结果在不同工程系统之间可移植。
garif 包的设计哲学在 doc.go 的包注释中写得很清楚:
// Package garif defines all the GO structures required to model a SARIF log file. // These structures were created using the JSON-schema sarif-schema-2.1.0.json of SARIF logfiles // ... // The package provides constructors for all structures (see constructors.go) These constructors // ensure that the returned structure instantiation is valid with respect to the JSON schema and // should be used in place of plain structure instantiation. // The root structure is LogFile. // // The package provides utility decorators for the most commonly used structures (see decorators.go)可以归纳出三条关键设计原则:
- 结构体直接映射 SARIF 2.1.0 JSON Schema:models.go 中约 60 个结构体一一对应 schema 中的对象(
LogFile、Run、Result、Location……),字段上的jsontag 严格使用 schema 定义的属性名,绝大多数带omitempty。 - 推荐用
New...构造器而非直接&Struct{}:schema 中若干字段是必填的(例如Run.tool、Result.message、ReportingDescriptor.id),直接零值实例化再序列化会得到不合法的 SARIF。构造器负责预置这些必选字段,保证“出厂即合法”。 - 用
With...装饰器降低常用场景的样板代码:给Location设置行号列号、给Run追加 result、给ReportingDescriptor设置 help URI 等操作都有链式方法。
2. 包结构与文件职责
vendored 包位于 vendor/github.com/chavacava/garif/,共 5 个 Go 源文件,职责清晰:
| 文件 | 职责 |
|---|---|
| doc.go | 包注释:说明结构来源(sarif-schema-2.1.0.json)、构造器与装饰器的用法约定,根结构为LogFile |
| models.go | 全部 SARIF 模型的 Go 结构体定义(约 60 个),含每个字段的 schema 语义注释 |
| constructors.go | 所有结构的New...构造器,预置必填字段 |
| decorators.go | With...链式装饰器方法 |
| io.go | LogFile的 JSON 序列化输出(紧凑 / 缩进两种) |
| enums.go | ResultKind、ResultLevel两组带命名空间的枚举常量 |
这种“schema 全量映射 + 少量高层糖”的组织方式意味着:garif 不隐藏 SARIF 的任何能力,README 第一句话即强调 “The package provides access to every element of the SARIF model, therefore you are free to manipulate it at every detail”——构造器和装饰器只是快捷入口,遇到冷门字段时直接操作结构体字段即可。
3. 构造器(New...)全量清单
constructors.go 为每个模型提供New...函数。下面按 SARIF 的逻辑分组列出(签名以源码为准),其中加粗参数是 schema 必填项,由构造器保证非空:
日志文件与运行(顶层骨架)
NewLogFile(runs []*Run, version LogFileVersion) *LogFile— 根结构,预置runs与version(schema 中两者均必填)NewRun(tool *Tool) *Run— 预置必填的ToolNewTool(driver *ToolComponent) *Tool— 预置必填的DriverNewToolComponent(name string) *ToolComponent— 预置必填的NameNewDriver(name string) *ToolComponent—NewToolComponent的别名NewLogFile的版本参数类型LogFileVersion定义为string,目前仅一个常量Version210 = "2.1.0"
规则与描述符
NewReportingDescriptor(id string) *ReportingDescriptor— 预置必填的IdNewRule(id string) *ReportingDescriptor—NewReportingDescriptor的别名NewReportingConfiguration() *ReportingConfigurationNewReportingDescriptorReference() *ReportingDescriptorReferenceNewReportingDescriptorRelationship(target *ReportingDescriptorReference) *ReportingDescriptorRelationship— 预置必填的TargetNewConfigurationOverride(configuration *ReportingConfiguration, descriptor *ReportingDescriptorReference) *ConfigurationOverride
结果与消息
NewMessage() *Message/NewMessageFromText(text string) *MessageNewMultiformatMessageString(text string) *MultiformatMessageString— 预置必填的TextNewResult(message *Message) *Result— 预置必填的MessageNewResultProvenance() *ResultProvenanceNewNotification(message *Message) *NotificationNewException() *ExceptionNewSuppression(kind string) *Suppression— 预置必填的KindNewFix(artifactChanges ...*ArtifactChange) *FixNewArtifactChange(location *ArtifactLocation, replacements ...*Replacement) *ArtifactChangeNewReplacement(deletedRegion *Region) *Replacement
位置与区域
NewLocation() *LocationNewLocationRelationship(target int) *LocationRelationship— 预置必填的TargetNewPhysicalLocation() *PhysicalLocation/NewArtifactLocation() *ArtifactLocationNewLogicalLocation() *LogicalLocation/NewRectangle() *Rectangle/NewRegion() *RegionNewAddress() *Address/NewSpecialLocations() *SpecialLocationsNewAttachment(location *ArtifactLocation) *Attachment
代码流、图与栈
NewCodeFlow(threadFlows ...*ThreadFlow) *CodeFlowNewThreadFlow(locations []*ThreadFlowLocation) *ThreadFlow/NewThreadFlowLocation() *ThreadFlowLocationNewGraph() *Graph/NewGraphTraversal() *GraphTraversalNewNode(id string) *Node/NewEdge(id, sourceNodeId, targetNodeId string) *Edge/NewEdgeTraversal(edgeId string) *EdgeTraversalNewStack(frames ...*StackFrame) *Stack/NewStackFrame() *StackFrame
工件、调用与环境
NewArtifact() *Artifact/NewArtifactContent() *ArtifactContentNewInvocation(executionSuccessful bool) *Invocation— 预置必填的ExecutionSuccessfulNewVersionControlDetails(repositoryUri string) *VersionControlDetails— 预置必填的RepositoryUriNewWebRequest() *WebRequest/NewWebResponse() *WebResponse
外部属性文件与转换
NewExternalProperties() *ExternalPropertiesNewExternalPropertyFileReference() *ExternalPropertyFileReference/NewExternalPropertyFileReferences() *ExternalPropertyFileReferencesNewConversion(tool *Tool) *Conversion— 预置必填的ToolNewTranslationMetadata(name string) *TranslationMetadataNewPropertyBag() *PropertyBag(PropertyBag类型即map[string]interface{},见 models.go)NewRunAutomationDetails() *RunAutomationDetails
注意NewSpecialLocations在源码中注释简写为 “New creates a valid”(constructors.go),这是上游包的一处注释小瑕疵,不影响功能。
4. 装饰器(With...)方法详解
decorators.go 只有 9 个方法,但覆盖了日常生成 SARIF 最高频的诉求:
4.1 Location:一次调用定位到“文件 + 行 + 列”
// WithLineColumn sets a physical location with the given line and column func (l *Location) WithLineColumn(line, column int) *Location // WithURI sets a physical location with the given URI func (l *Location) WithURI(uri string) *Location从源码看,WithLineColumn会在PhysicalLocation == nil时自动创建,并新建一个Region设置StartLine/StartColumn;WithURI则自动创建PhysicalLocation.ArtifactLocation并写入Uri。两者都可链式使用,这是把“文件路径 + 行列号”翻译成 SARIFlocation.physicalLocation的最短路径。
4.2 Run:批量登记工件与结果
// WithArtifactsURIs adds the given URI as artifacts of this Run func (r *Run) WithArtifactsURIs(uris ...string) *Run // WithResult adds a result to this Run func (r *Run) WithResult(ruleId string, message string, uri string, line int, column int) *RunWithArtifactsURIs接受可变参数 URI,逐个包装为Artifact{Location: {Uri}}追加到run.artifacts(对应 schema 中 run 级别的工件缓存数组)。WithResult是最常用的一步到位 API:内部依次执行NewMessage()→NewResult(msg)→NewLocation().WithURI(uri).WithLineColumn(line, column)→ 设置result.RuleId→ 追加到run.Results,等价于手写了 6~8 行结构体拼装代码。
4.3 ReportingDescriptor / ToolComponent / PropertyBag
func (r *ReportingDescriptor) WithHelpUri(uri string) *ReportingDescriptor func (r *ReportingDescriptor) WithProperties(key string, value interface{}) *ReportingDescriptor func (t *ToolComponent) WithInformationUri(uri string) *ToolComponent func (t *ToolComponent) WithRules(rules ...*ReportingDescriptor) *ToolComponent func (b PropertyBag) WithKeyValue(key string, value interface{}) PropertyBagWithProperties内部自动初始化PropertyBag再写入键值对;WithRules是覆盖式设置(overwrites),追加规则需要自行append(见第 6 节 revive 的实际用法)。
5. 数据模型骨架:从 LogFile 到 Result 的必填链路
SARIF 2.1.0 的层级是日志文件 → 运行 → 工具 → 规则 / 结果。对应到 models.go 中的结构定义,必填字段(JSON tag 无omitempty)构成一条清晰的“合法链路”:
LogFile (runs, version 必填) └─ Run (tool 必填) ├─ Tool (driver 必填) │ └─ ToolComponent (name 必填) │ └─ Rules []*ReportingDescriptor (id 必填) ├─ Results []*Result (message 必填) │ ├─ RuleId / Rule / Level / Kind │ └─ Locations []*Location │ └─ PhysicalLocation │ ├─ ArtifactLocation (uri) │ └─ Region (startLine/startColumn/...) ├─ Artifacts []*Artifact(工件缓存,artifacts[].location 提供 uri) └─ VersionControlProvenance []*VersionControlDetails (repositoryUri 必填)几个值得注意的模型细节:
Region(models.go)同时支持字节偏移(ByteOffset/ByteLength)与字符行列(StartLine/StartColumn/EndLine/EndColumn/CharOffset),并可用Snippet *ArtifactContent内联该区域的源码片段,方便消费端直接展示上下文。Message支持Text(纯文本)与Markdown两种载体,外加Arguments数组用于占位符替换;MultiformatMessageString则用于规则描述等多格式消息。Result承载了 SARIF 的高级能力:CodeFlows(代码流)、GraphTraversals/Graphs(图遍历,如调用图污点路径)、Fixes(修复建议,通过ArtifactChange+Replacement描述“删除某区域、插入新内容”)、Fingerprints/CorrelationGuid(跨运行去重)、Suppression(抑制声明)、Stacks等,均带omitempty按需出现。Run还有OriginalUriBaseIds、SpecialLocations、RedactionTokens、Invocations、Conversion等字段,分别服务于相对 URI 基准、路径展示基址、敏感信息脱敏、运行环境记录与格式转换元数据。
5.1 枚举常量
enums.go 定义了两组“命名空间化”的字符串枚举:
| 类型 | 常量 | JSON 值 |
|---|---|---|
ResultKind | ResultKind_Pass/ResultKind_Open/ResultKind_Informational/ResultKind_NotApplicable/ResultKind_Review/ResultKind_Fail | pass/open/informational/notApplicable/review/fail |
ResultLevel | ResultLevel_Warning/ResultLevel_Error/ResultLevel_Note/ResultLevel_None | warning/error/note/none |
命名采用类型名_值名的 Go 风格(如ResultKind_Fail),避免裸pass、fail等标识符污染包命名空间;底层仍是 string 类型,可以直接赋值给Result.Kind/Result.Level(后者在 models.go 中类型即为ResultLevel,前者为ResultKind)。
6. 输出层:Write 与 PrettyWrite
io.go 只有两个方法,序列化基于标准库encoding/json,行为完全由结构体的 JSON tag 决定:
// Write writes the JSON func (l *LogFile) Write(w io.Writer) error { marshal, err := json.Marshal(l) ... } // PrettyWrite writes indented JSON func (l *LogFile) PrettyWrite(w io.Writer) error { marshal, err := json.MarshalIndent(l, "", " ") ... }Write输出紧凑单行 JSON,适合 CI 机器间传递;PrettyWrite输出 2 空格缩进的 JSON,适合人工阅读与日志落盘。
这里也解释了“为什么要用构造器”:由于序列化没有任何额外校验或修补逻辑,合法性完全由你构建对象时保证。比如跳过NewLogFile直接写&garif.LogFile{},序列化后runs与version会因omitempty之外的零值行为缺失,产出非法 SARIF——这正是 doc.go 建议“should be used in place of plain structure instantiation”的原因。
7. 完整可运行示例:复现 README 的 SARIF 样例
README(README.md)用一段代码演示了如何构造出 Microsoft SARIF 教程中的经典示例:ESLint 报告一条no-unused-vars结果。原 README 中import to ...一行不是合法 Go 语法,下面按实际 import 语句修正后给出完整可运行程序(假设模块已依赖github.com/chavacava/garif v0.1.0,与 go.sum/vendor/modules.txt 中记录的版本一致):
package main import ( "os" "github.com/chavacava/garif" ) func main() { // 1. 定义一条规则(ReportingDescriptor,id 必填) rule := garif.NewRule("no-unused-vars"). WithHelpUri("https://eslint.org/docs/rules/no-unused-vars"). WithProperties("category", "Variables") // 2. 声明分析工具 driver(ToolComponent,name 必填) driver := garif.NewDriver("ESLint"). WithInformationUri("https://eslint.org"). WithRules(rule) // 3. 创建 run 并登记被分析的工件 URI run := garif.NewRun(garif.NewTool(driver)). WithArtifactsURIs("file:///C:/dev/sarif/sarif-tutorials/samples/Introduction/simple-example.js") // 4. 追加一条结果:规则ID + 消息 + 文件 + 行:列 run.WithResult(rule.Id, "'x' is assigned a value but never used.", "file:///C:/dev/sarif/sarif-tutorials/samples/Introduction/simple-example.js", 1, 5) // 5. 组装 LogFile(version 必填,当前仅支持 2.1.0)并输出 logFile := garif.NewLogFile([]*garif.Run{run}, garif.Version210) logFile.PrettyWrite(os.Stdout) }逐步对照源码验证其生成结构:
NewRule("no-unused-vars")得到ReportingDescriptor{Id: "no-unused-vars"};WithHelpUri填helpUri;WithProperties("category","Variables")在properties中写入{"category":"Variables"}。NewDriver("ESLint")得到ToolComponent{Name:"ESLint"};WithInformationUri填informationUri;WithRules把上一步的规则放入rules数组。NewRun(garif.NewTool(driver))完成run.tool.driver必填链路;WithArtifactsURIs在run.artifacts中登记一个{location:{uri:...}}工件。run.WithResult(...)在run.results中追加一条result,其message.text、ruleId、locations[0].physicalLocation.artifactLocation.uri与locations[0].physicalLocation.region.startLine/startColumn分别对应实参中的消息、rule.Id、文件 URI、行 1 列 5。NewLogFile(..., garif.Version210)预置runs与version: "2.1.0";PrettyWrite输出 2 空格缩进的合法 SARIF JSON。
输出的 JSON 顶层形如:
{ "runs": [ { "artifacts": [ { "location": { "uri": "file:///C:/dev/sarif/..." } } ], "results": [ { "locations": [ { "physicalLocation": { "artifactLocation": { "uri": "file:///C:/dev/sarif/..." }, "region": { "startColumn": 5, "startLine": 1 } } } ], "message": { "text": "'x' is assigned a value but never used." }, "ruleId": "no-unused-vars" } ], "tool": { "driver": { "informationUri": "https://eslint.org", "name": "ESLint", "rules": [ { "helpUri": "https://eslint.org/docs/rules/no-unused-vars", "id": "no-unused-vars", "properties": { "category": "Variables" } } ] } } } ], "version": "2.1.0" }8. 真实消费案例:revive 的 SARIF formatter
在 syzkaller 仓库中,garif 的实际调用方是 vendored 的 Go 静态分析工具revive的 SARIF formatter(formatter/sarif.go),它也是这个包诞生的原始动机(README “Why this package?” 一节即说明作者为给 revive 添加 SARIF 输出格式器而创建了 garif)。该 formatter 展示了把“分析器内部结果”系统性转换为 SARIF 的完整套路:
(1)初始化 run 日志:以 revive 为 driver,规则文档站点为 informationUri:
run := garif.NewRun(garif.NewTool(garif.NewDriver("revive").WithInformationUri(reviveSite))) log := garif.NewLogFile([]*garif.Run{run}, garif.Version210)(2)从配置展开规则:遍历 lint 配置中的每条规则,用garif.NewRule(name).WithHelpUri(...)生成描述符,并通过WithProperties("severity", ...)/WithProperties("arguments", ...)把规则严重级与参数写入properties;此处因为WithRules是覆盖式语义,代码采用直接append(driver.Rules, rule)的方式累加规则。
(3)逐条转换 failure 为 result:
result := garif.NewResult(garif.NewMessageFromText(failure.Failure)) location := garif.NewLocation().WithURI(filename).WithLineColumn(line, column) result.Locations = append(result.Locations, location) result.RuleId = failure.RuleName result.Level = garif.ResultLevel(l.rules[failure.RuleName].Severity)注意两个实现细节:行号列号经过positiveOrZero归一化,以符合 SARIF 2.1.0 对line/column非负的要求;level来自 lint 配置的 severity 字符串直接转ResultLevel。
(4)输出:所有 failure 处理完后调用sarifLog.PrettyWrite(buf)得到最终 JSON 字符串返回给 revive 的输出管线。
这套“driver + rules 元数据 → 逐条 result → PrettyWrite”的模式,可以原样套用到任何自研静态检查工具上。
9. 在 syzkaller 仓库中的依赖关系与使用前提
- 依赖声明:go.mod 中
github.com/chavacava/garif v0.1.0 // indirect,表明 syzkaller 主程序本身不直接 import 它,而是经由mgechev/revive间接引入,源码被完整固化在 vendor 目录下供离线构建。 - 由于包已通过
go mod vendor固化,阅读与引用时应以 vendor 内源码为最终依据(本文所有行号与签名均以此为准)。 - 适用前提与限制:garif 当前仅支持SARIF 2.1.0(
Version210是唯一导出的版本常量);序列化不内建 schema 校验,字段合法性依赖构造器约定;PropertyBag是开放键值对(map[string]interface{}),可用于存放各工具自定义扩展属性,消费端需自行约定。
10. 小结
garif 以“全量 schema 映射 + 构造器保证必填 + 装饰器压缩样板”的方式,把 SARIF 2.1.0 的完整对象模型搬进了 Go:models.go 给出每个对象的字段语义,constructors.go 给出合法化入口,decorators.go 覆盖最高频操作,io.go 一行Write/PrettyWrite完成序列化。在 syzkaller 的 vendor 树中,它由revive的 SARIF formatter 消费,提供了一个从“linter 内部结果”到“标准 SARIF 日志”的完整可参照实现;对于需要输出静态分析结果的自研工具,直接复用第 7、8 节的骨架即可产出可被通用 SARIF 消费方解析的报告。
- 测试
- 操作系统
- 网络安全
【免费下载链接】syzkaller
syzkaller is an unsupervised coverage-guided kernel fuzzer
相关推荐
在 awesome-copilot 中使用 /acreadiness-assess 评估仓库 AI 就绪度:AgentRC 9 支柱成熟度模型与静态报告生成实战
在 awesome copilot 中使用 /acreadiness assess 评估仓库 AI 就绪度:AgentRC 9 支柱成熟度模型与静态报告生成实战
文档知识库AI 技能/插件Bandit SARIF 格式化器:将 Python 安全扫描结果输出为标准 SARIF 2.1.0 报告
Bandit SARIF 格式化器:将 Python 安全扫描结果输出为标准 SARIF 2.1.0 报告 Bandit 是用于在 Python 代码中查找常见
SAST应用安全Material Shell源码静态代码分析报告:HTML生成
Material Shell源码静态代码分析报告:HTML生成 引言 Material Shell是一个为Linux打造的现代桌面界面,旨在改善用户体验并摆脱传
桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考