diff --git a/internal/io/ioutil.go b/internal/io/ioutil.go new file mode 100644 index 000000000..4a83ccd62 --- /dev/null +++ b/internal/io/ioutil.go @@ -0,0 +1,13 @@ +// Copyright 2022 Juan Pablo Tosso and the OWASP Coraza contributors +// SPDX-License-Identifier: Apache-2.0 + +//go:build !windows + +package io + +import ( + "io/fs" +) + +// FSReadFile wraps fs.ReadFile supporting embedio on windows +var FSReadFile = fs.ReadFile diff --git a/internal/io/ioutil_test.go b/internal/io/ioutil_test.go new file mode 100644 index 000000000..e68b3084d --- /dev/null +++ b/internal/io/ioutil_test.go @@ -0,0 +1,72 @@ +// Copyright 2022 Juan Pablo Tosso and the OWASP Coraza contributors +// SPDX-License-Identifier: Apache-2.0 + +package io + +import ( + "embed" + "io/fs" + "os" + "path/filepath" + "runtime" + "testing" +) + +//go:embed testdata +var testdata embed.FS + +func TestFSReadFile(t *testing.T) { + testdir, err := os.MkdirTemp(t.TempDir(), "testdata") + if err != nil { + t.Fatal(err) + } + err = os.Mkdir(filepath.Join(testdir, "subdir"), os.ModePerm) + if err != nil { + t.Fatal(err) + } + err = os.WriteFile(filepath.Join(testdir, "subdir", "testfile.txt"), []byte("Hello World\n"), os.ModePerm) + if err != nil { + t.Fatal(err) + } + + realFS, err := fs.Sub(os.DirFS(testdir), ".") + if err != nil { + t.Fatal(err) + } + + tests := []struct { + name string + path string + fail bool + fs fs.FS + }{ + {name: "embed/unix path", path: "testdata/subdir/testfile.txt", fail: false, fs: testdata}, + {name: "embed/windows path", path: "testdata\\subdir\\testfile.txt", fail: runtime.GOOS != "windows", fs: testdata}, + {name: "embed/invalid", path: "testdata/subdir/notexist", fail: true, fs: testdata}, + {name: "real/unix path", path: "testdata/subdir/testfile.txt", fail: false, fs: realFS}, + {name: "real/windows path", path: "testdata\\subdir\\testfile.txt", fail: runtime.GOOS != "windows", fs: realFS}, + {name: "real/invalid", path: "testdata/subdir/notexist", fail: true, fs: realFS}, + } + + for _, next := range tests { + test := next + t.Run(test.name, func(t *testing.T) { + data, err := FSReadFile(testdata, test.path) + if test.fail { + if err == nil { + t.Fatal("expected an error but it is nil") + } + if data != nil { + t.Fatal("expected data to be nil") + } + } else { + if err != nil { + t.Fatal(err) + } + if string(data) != "Hello World\n" { + t.Fatal("unexpected output: \"", string(data), "\"") + } + } + }) + } +} diff --git a/internal/io/ioutil_windows.go b/internal/io/ioutil_windows.go new file mode 100644 index 000000000..720cccf42 --- /dev/null +++ b/internal/io/ioutil_windows.go @@ -0,0 +1,15 @@ +// Copyright 2022 Juan Pablo Tosso and the OWASP Coraza contributors +// SPDX-License-Identifier: Apache-2.0 + +package io + +import ( + "io/fs" + "path/filepath" + "strings" +) + +// FSReadFile wraps fs.ReadFile supporting embedio on windows +func FSReadFile(fsys fs.FS, name string) ([]byte, error) { + return fs.ReadFile(fsys, strings.ReplaceAll(name, string(filepath.Separator), "/")) +} diff --git a/internal/io/testdata/subdir/testfile.txt b/internal/io/testdata/subdir/testfile.txt new file mode 100644 index 000000000..557db03de --- /dev/null +++ b/internal/io/testdata/subdir/testfile.txt @@ -0,0 +1 @@ +Hello World diff --git a/internal/operators/from_file.go b/internal/operators/from_file.go index 4d011c1af..f60736891 100644 --- a/internal/operators/from_file.go +++ b/internal/operators/from_file.go @@ -7,14 +7,16 @@ import ( "errors" "io/fs" "os" - "path" + "path/filepath" + + "github.com/corazawaf/coraza/v3/internal/io" ) var errEmptyDirs = errors.New("empty dirs") -func loadFromFile(filepath string, dirs []string, root fs.FS) ([]byte, error) { - if path.IsAbs(filepath) { - return fs.ReadFile(root, filepath) +func loadFromFile(filename string, dirs []string, root fs.FS) ([]byte, error) { + if filepath.IsAbs(filename) { + return io.FSReadFile(root, filename) } if len(dirs) == 0 { @@ -30,8 +32,8 @@ func loadFromFile(filepath string, dirs []string, root fs.FS) ([]byte, error) { ) for _, p := range dirs { - absFilepath := path.Join(p, filepath) - content, err = fs.ReadFile(root, absFilepath) + absFilepath := filepath.Join(p, filename) + content, err = io.FSReadFile(root, absFilepath) if err != nil { if os.IsNotExist(err) { continue diff --git a/internal/seclang/parser.go b/internal/seclang/parser.go index 2532d9eaf..d578e9edd 100644 --- a/internal/seclang/parser.go +++ b/internal/seclang/parser.go @@ -56,7 +56,7 @@ func (p *Parser) FromFile(profilePath string) error { p.currentFile = profilePath lastDir := p.currentDir p.currentDir = filepath.Dir(profilePath) - file, err := fs.ReadFile(p.root, profilePath) + file, err := io.FSReadFile(p.root, profilePath) if err != nil { // we don't use defer for this as tinygo does not seem to like it p.currentDir = originalDir