Skip to content

Commit 1e5e646

Browse files
committed
tools/syz-aflow: add command line tool for agentic workflows
syz-aflow tool can be used to invoke any agentic workflow registered with pkg/aflow. For example, to run the patching workflow use: go run ./tools/syz-aflow -input=input.json -download-bug=d8fd35fa6177afa8c92b go run ./tools/syz-aflow -input=input.json -workflow=patching-baseline -workdir=workdir
1 parent e056656 commit 1e5e646

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed

tools/syz-aflow/aflow.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Copyright 2025 syzkaller project authors. All rights reserved.
2+
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3+
4+
// syz-aflow tool can be used to invoke any agentic workflow registered with pkg/aflow.
5+
// For example, to run the patching workflow use:
6+
//
7+
// go run ./tools/syz-aflow -input=input.json -download-bug=d8fd35fa6177afa8c92b
8+
// go run ./tools/syz-aflow -input=input.json -workflow=patching -workdir=workdir
9+
package main
10+
11+
import (
12+
"context"
13+
"encoding/json"
14+
"flag"
15+
"fmt"
16+
"io"
17+
"log"
18+
"net/http"
19+
"os"
20+
"path/filepath"
21+
22+
"github.com/google/syzkaller/pkg/aflow"
23+
_ "github.com/google/syzkaller/pkg/aflow/flow"
24+
"github.com/google/syzkaller/pkg/aflow/trajectory"
25+
"github.com/google/syzkaller/pkg/osutil"
26+
"github.com/google/syzkaller/pkg/tool"
27+
)
28+
29+
func main() {
30+
var (
31+
flagFlow = flag.String("workflow", "", "workflow to execute")
32+
flagInput = flag.String("input", "", "input json file with workflow arguments")
33+
flagWorkdir = flag.String("workdir", "", "directory for kernel checkout, kernel builds, etc")
34+
flagModel = flag.String("model", aflow.DefaultModel, "use this LLM model")
35+
flagDownloadBug = flag.String("download-bug", "", "extid of a bug to download from the dashboard"+
36+
" and save into -input file")
37+
)
38+
defer tool.Init()()
39+
if *flagDownloadBug != "" {
40+
if err := downloadBug(*flagDownloadBug, *flagInput); err != nil {
41+
tool.Fail(err)
42+
}
43+
return
44+
}
45+
if *flagFlow == "" {
46+
fmt.Fprintf(os.Stderr, "syz-aflow usage:\n")
47+
flag.PrintDefaults()
48+
fmt.Fprintf(os.Stderr, "available workflows:\n")
49+
for _, flow := range aflow.Flows {
50+
fmt.Fprintf(os.Stderr, "\t%v: %v\n", flow.Name, flow.Description)
51+
}
52+
return
53+
}
54+
if err := run(context.Background(), *flagModel, *flagFlow, *flagInput, *flagWorkdir); err != nil {
55+
tool.Fail(err)
56+
}
57+
}
58+
59+
func run(ctx context.Context, model, flowName, inputFile, workdir string) error {
60+
flow := aflow.Flows[flowName]
61+
if flow == nil {
62+
return fmt.Errorf("workflow %q is not found", flowName)
63+
}
64+
inputData, err := os.ReadFile(inputFile)
65+
if err != nil {
66+
return fmt.Errorf("failed to open -input file: %w", err)
67+
}
68+
var inputs map[string]any
69+
if err := json.Unmarshal(inputData, &inputs); err != nil {
70+
return err
71+
}
72+
cache, err := aflow.NewCache(filepath.Join(workdir, "cache"), 0)
73+
if err != nil {
74+
return err
75+
}
76+
_, err = flow.Execute(ctx, model, workdir, inputs, cache, onEvent)
77+
return err
78+
}
79+
80+
func onEvent(span *trajectory.Span) error {
81+
log.Printf("%v", span)
82+
return nil
83+
}
84+
85+
func downloadBug(extID, inputFile string) error {
86+
if inputFile == "" {
87+
return fmt.Errorf("-download-bug requires -input flag")
88+
}
89+
resp, err := get(fmt.Sprintf("/bug?extid=%v&json=1", extID))
90+
if err != nil {
91+
return err
92+
}
93+
var info map[string]any
94+
if err := json.Unmarshal([]byte(resp), &info); err != nil {
95+
return err
96+
}
97+
crash := info["crashes"].([]any)[0].(map[string]any)
98+
inputs := map[string]any{
99+
"SyzkallerCommit": crash["syzkaller-commit"],
100+
}
101+
inputs["ReproSyz"], err = get(crash["syz-reproducer"].(string))
102+
if err != nil {
103+
return err
104+
}
105+
inputs["ReproC"], err = get(crash["c-reproducer"].(string))
106+
if err != nil {
107+
return err
108+
}
109+
inputs["KernelConfig"], err = get(crash["kernel-config"].(string))
110+
if err != nil {
111+
return err
112+
}
113+
data, err := json.MarshalIndent(inputs, "", "\t")
114+
if err != nil {
115+
return err
116+
}
117+
return osutil.WriteFile(inputFile, data)
118+
}
119+
120+
func get(path string) (string, error) {
121+
if path == "" {
122+
return "", nil
123+
}
124+
const host = "https://syzbot.org"
125+
resp, err := http.Get(fmt.Sprintf("%v%v", host, path))
126+
if err != nil {
127+
return "", err
128+
}
129+
defer resp.Body.Close()
130+
body, err := io.ReadAll(resp.Body)
131+
return string(body), err
132+
}

0 commit comments

Comments
 (0)