From e7922f79bc8da0b8ef96a080e463141bb5e79694 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Mon, 26 Jan 2026 16:44:09 +0100 Subject: pkg/aflow: add helper for tool testing Add simple codeeditor tests to test testing. --- pkg/aflow/tool/codeeditor/codeeditor.go | 15 +++++- pkg/aflow/tool/codeeditor/codeeditor_test.go | 79 ++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 pkg/aflow/tool/codeeditor/codeeditor_test.go (limited to 'pkg/aflow/tool/codeeditor') diff --git a/pkg/aflow/tool/codeeditor/codeeditor.go b/pkg/aflow/tool/codeeditor/codeeditor.go index f67abbd69..ce2d7afb7 100644 --- a/pkg/aflow/tool/codeeditor/codeeditor.go +++ b/pkg/aflow/tool/codeeditor/codeeditor.go @@ -4,7 +4,11 @@ package codeeditor import ( + "path/filepath" + "strings" + "github.com/google/syzkaller/pkg/aflow" + "github.com/google/syzkaller/pkg/osutil" ) var Tool = aflow.NewFuncTool("codeeditor", codeeditor, ` @@ -26,7 +30,16 @@ type args struct { } func codeeditor(ctx *aflow.Context, state state, args args) (struct{}, error) { - // TODO: check that the SourceFile is not escaping. + if strings.Contains(filepath.Clean(args.SourceFile), "..") { + return struct{}{}, aflow.BadCallError("SourceFile %q is outside of the source tree", args.SourceFile) + } + file := filepath.Join(state.KernelScratchSrc, args.SourceFile) + if !osutil.IsExist(file) { + return struct{}{}, aflow.BadCallError("SourceFile %q does not exist", args.SourceFile) + } + if strings.TrimSpace(args.CurrentCode) == "" { + return struct{}{}, aflow.BadCallError("CurrentCode snippet is empty") + } // If SourceFile is incorrect, or CurrentCode is not matched, return aflow.BadCallError // with an explanation. Say that it needs to increase context if CurrentCode is not matched. // Try to do as fuzzy match for CurrentCode as possible (strip line numbers, diff --git a/pkg/aflow/tool/codeeditor/codeeditor_test.go b/pkg/aflow/tool/codeeditor/codeeditor_test.go new file mode 100644 index 000000000..4ba556f1b --- /dev/null +++ b/pkg/aflow/tool/codeeditor/codeeditor_test.go @@ -0,0 +1,79 @@ +// Copyright 2026 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package codeeditor + +import ( + "path/filepath" + "testing" + + "github.com/google/syzkaller/pkg/aflow" + "github.com/google/syzkaller/pkg/osutil" + "github.com/stretchr/testify/require" +) + +func TestCodeeditorEscapingPath(t *testing.T) { + aflow.TestTool(t, Tool, + state{ + KernelScratchSrc: "whatever", + }, + args{ + SourceFile: "../../passwd", + }, + struct{}{}, + `SourceFile "../../passwd" is outside of the source tree`, + ) +} + +func TestCodeeditorMissingPath(t *testing.T) { + aflow.TestTool(t, Tool, + state{ + KernelScratchSrc: t.TempDir(), + }, + args{ + SourceFile: "missing-file", + }, + struct{}{}, + `SourceFile "missing-file" does not exist`, + ) +} + +func TestCodeeditorEmptyCurrentCode(t *testing.T) { + dir := writeTestFile(t, "foo", "data") + aflow.TestTool(t, Tool, + state{ + KernelScratchSrc: dir, + }, + args{ + SourceFile: "foo", + }, + struct{}{}, + `CurrentCode snippet is empty`, + ) +} + +func writeTestFile(t *testing.T, filename, data string) string { + dir := t.TempDir() + if err := osutil.WriteFile(filepath.Join(dir, filename), []byte(data)); err != nil { + t.Fatal(err) + } + return dir +} + +func Fuzz(f *testing.F) { + dir := f.TempDir() + const filename = "src.c" + fullFilename := filepath.Join(dir, filename) + f.Fuzz(func(t *testing.T, fileData []byte, curCode, newCode string) { + require.NoError(t, osutil.WriteFile(fullFilename, fileData)) + aflow.FuzzTool(t, Tool, + state{ + KernelScratchSrc: dir, + }, + args{ + SourceFile: filename, + CurrentCode: curCode, + NewCode: newCode, + }) + }) +} -- cgit mrf-deployment