forked from Velocidex/velociraptor
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bugfix: Make filestore accessor case insensitive. (Velocidex#3645)
The filestore accessor is most commonly used in remapping for postprocessing (for example https://docs.velociraptor.app/blog/2022/2022-08-04-post-processing/#automating-the-remapping). This usually comes from Windows systems and so it assumes case insensitivity. This PR enables case insensitivity by default.
- Loading branch information
Showing
3 changed files
with
178 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package file_store | ||
|
||
import ( | ||
"errors" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/suite" | ||
config_proto "www.velocidex.com/golang/velociraptor/config/proto" | ||
"www.velocidex.com/golang/velociraptor/file_store" | ||
"www.velocidex.com/golang/velociraptor/file_store/directory" | ||
"www.velocidex.com/golang/velociraptor/file_store/path_specs" | ||
"www.velocidex.com/golang/velociraptor/file_store/test_utils" | ||
"www.velocidex.com/golang/velociraptor/utils" | ||
"www.velocidex.com/golang/velociraptor/vtesting/assert" | ||
) | ||
|
||
type testCase struct { | ||
checked string | ||
err error | ||
} | ||
|
||
var ( | ||
files = []string{ | ||
"fs:/Windows/System32/notepad.exe", | ||
"fs:/Windows/System32/NotePad2.exe", | ||
|
||
// Filestores allow data to be stored in a "directory" | ||
"fs:/Windows/System32", | ||
} | ||
|
||
checked_files = []testCase{ | ||
{checked: "fs:/WinDowS/SySteM32/NotePad.exe"}, | ||
{checked: "fs:/Windows/System32/NotePad2.exe"}, | ||
|
||
// Filestores allow data to be stored in a "directory" | ||
{checked: "fs:/windows/system32"}, | ||
|
||
{checked: "fs:/Windows/System32/DoesNotExist.exe", | ||
err: utils.NotFoundError}, | ||
} | ||
) | ||
|
||
type FSAccessorTest struct { | ||
test_utils.TestSuite | ||
|
||
config_obj *config_proto.Config | ||
file_store *directory.DirectoryFileStore | ||
} | ||
|
||
func (self *FSAccessorTest) TestCaseInsensitive() { | ||
accessor := NewFileStoreFileSystemAccessor(self.ConfigObj) | ||
|
||
file_store_factory := file_store.GetFileStore(self.ConfigObj) | ||
|
||
buf := make([]byte, 100) | ||
|
||
// Create some files with data | ||
for _, f := range files { | ||
pathspec, err := accessor.ParsePath(f) | ||
assert.NoError(self.T(), err) | ||
|
||
fullpath := path_specs.FromGenericComponentList(pathspec.Components) | ||
w, err := file_store_factory.WriteFile(fullpath) | ||
assert.NoError(self.T(), err) | ||
w.Write([]byte("hello")) | ||
w.Close() | ||
|
||
// Use the accessor to open a file directly. | ||
fd, err := accessor.Open(f) | ||
assert.NoError(self.T(), err) | ||
|
||
n, err := fd.Read(buf) | ||
assert.NoError(self.T(), err) | ||
assert.Equal(self.T(), n, 5) | ||
assert.Equal(self.T(), string(buf[:n]), "hello") | ||
} | ||
|
||
for _, testcase := range checked_files { | ||
// Now open the same file with the wrong casing. | ||
fd, err := accessor.Open(testcase.checked) | ||
if testcase.err != nil { | ||
assert.True(self.T(), errors.Is(err, testcase.err)) | ||
continue | ||
} | ||
assert.NoError(self.T(), err) | ||
|
||
n, err := fd.Read(buf) | ||
assert.NoError(self.T(), err) | ||
assert.Equal(self.T(), n, 5) | ||
assert.Equal(self.T(), string(buf[:n]), "hello") | ||
} | ||
|
||
} | ||
|
||
func TestFileStoreAccessor(t *testing.T) { | ||
suite.Run(t, &FSAccessorTest{}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package file_store | ||
|
||
import ( | ||
"strings" | ||
|
||
"www.velocidex.com/golang/velociraptor/file_store/api" | ||
"www.velocidex.com/golang/velociraptor/utils" | ||
) | ||
|
||
// Correct the filename to its correct casing. | ||
func getCorrectCase( | ||
file_store api.FileStore, | ||
filename api.FSPathSpec) (api.FSPathSpec, error) { | ||
|
||
// File is exactly fine. | ||
_, err := file_store.StatFile(filename) | ||
if err == nil { | ||
return filename, nil | ||
} | ||
|
||
// File is not found, try to find the correct casing for the base | ||
// component. | ||
basename := filename.Base() | ||
dirname := filename.Dir() | ||
|
||
// For non root directories we need to look at the parents | ||
if len(dirname.Components()) > 0 { | ||
_, err := file_store.StatFile(dirname) | ||
if err != nil { | ||
// The parent directory can not be directly opened - it is | ||
// possible that the parent directory casing is incorrect too. | ||
dirname, err = getCorrectCase(file_store, dirname) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
} | ||
|
||
// Try again with the correct case. It should work this time. | ||
entries, err := file_store.ListDirectory(dirname) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
for _, e := range entries { | ||
if strings.EqualFold(e.Name(), basename) { | ||
return dirname.AddChild(e.Name()), nil | ||
} | ||
} | ||
|
||
return nil, utils.NotFoundError | ||
} |