From d1404626f1e84ea0d68e72ba1815072a2d997554 Mon Sep 17 00:00:00 2001 From: Keepers Date: Mon, 6 Mar 2023 13:28:10 -0700 Subject: [PATCH] avoid downloading known malware (#2702) If graph flags an item as malware, avoid attempting to download it. Only applies to drive items. --- #### Does this PR need a docs update or release note? - [x] :white_check_mark: Yes, it's included #### Type of change - [x] :sunflower: Feature #### Issue(s) * #2701 #### Test Plan - [x] :muscle: Manual - [x] :zap: Unit test --- CHANGELOG.md | 2 + .../connector/onedrive/collections.go | 8 ++ .../connector/onedrive/collections_test.go | 104 +++++++++++++++++- 3 files changed, 112 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 708cacb13..72e55c84c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Show owner information when doing backup list in json format +- Onedrive files that are flagged as malware get skipped during backup. ### Fixed - Corso-generated .meta files and permissions no longer appear in the backup details. @@ -36,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Nested attachments are currently not restored due to an [issue](https://github.com/microsoft/kiota-serialization-json-go/issues/61) discovered in the Graph APIs - Breaking changes to Exchange Calendar backups. - The debugging env variable CORSO_URL_LOGGING causes exchange get requests to fail. +- Onedrive files that are flagged as Malware consistently fail during backup. ## [v0.3.0] (alpha) - 2023-2-07 diff --git a/src/internal/connector/onedrive/collections.go b/src/internal/connector/onedrive/collections.go index 2899e83fc..c9bb5c407 100644 --- a/src/internal/connector/onedrive/collections.go +++ b/src/internal/connector/onedrive/collections.go @@ -608,6 +608,14 @@ func (c *Collections) UpdateCollections( break } + if item.GetMalware() != nil { + // TODO: track the item as skipped; logging alone might + // slice out the data from tracking. + // https://learn.microsoft.com/en-us/graph/api/resources/malware?view=graph-rest-1.0 + logger.Ctx(ctx).Infow("malware detected", "malware_description", ptr.Val(item.GetMalware().GetDescription())) + continue + } + var ( itemID = ptr.Val(item.GetId()) ictx = clues.Add(ctx, "update_item_id", itemID) diff --git a/src/internal/connector/onedrive/collections_test.go b/src/internal/connector/onedrive/collections_test.go index 954bffc11..1ca8614b8 100644 --- a/src/internal/connector/onedrive/collections_test.go +++ b/src/internal/connector/onedrive/collections_test.go @@ -724,6 +724,34 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { }, expectedExcludes: map[string]struct{}{}, }, + { + testCase: "1 root file, 1 folder, 1 package, 1 good file, 1 malware", + items: []models.DriveItemable{ + driveRootItem("root"), + driveItem("fileInRoot", "fileInRoot", testBaseDrivePath, "root", true, false, false), + driveItem("folder", "folder", testBaseDrivePath, "root", false, true, false), + driveItem("package", "package", testBaseDrivePath, "root", false, false, true), + driveItem("goodFile", "goodFile", testBaseDrivePath+folder, "folder", true, false, false), + malwareItem("malwareFile", "malwareFile", testBaseDrivePath+folder, "folder", true, false, false), + }, + inputFolderMap: map[string]string{}, + scope: anyFolder, + expect: assert.NoError, + expectedCollectionIDs: map[string]statePath{ + "root": expectedStatePath(data.NotMovedState, ""), + "folder": expectedStatePath(data.NewState, folder), + "package": expectedStatePath(data.NewState, pkg), + }, + expectedItemCount: 4, + expectedFileCount: 2, + expectedContainerCount: 3, + expectedMetadataPaths: map[string]string{ + "root": expectedPath(""), + "folder": expectedPath("/folder"), + "package": expectedPath("/package"), + }, + expectedExcludes: getDelList("fileInRoot", "goodFile"), + }, } for _, tt := range tests { @@ -1743,6 +1771,50 @@ func (suite *OneDriveCollectionsSuite) TestGet() { expectedDelList: map[string]struct{}{}, doNotMergeItems: true, }, + { + name: "OneDrive Two Item Pages with Malware", + drives: []models.Driveable{drive1}, + items: map[string][]deltaPagerResult{ + driveID1: { + { + items: []models.DriveItemable{ + driveRootItem("root"), + driveItem("folder", "folder", driveBasePath1, "root", false, true, false), + driveItem("file", "file", driveBasePath1+"/folder", "folder", true, false, false), + malwareItem("malware", "malware", driveBasePath1+"/folder", "folder", true, false, false), + }, + nextLink: &next, + }, + { + items: []models.DriveItemable{ + driveRootItem("root"), + driveItem("folder", "folder", driveBasePath1, "root", false, true, false), + driveItem("file2", "file2", driveBasePath1+"/folder", "folder", true, false, false), + malwareItem("malware2", "malware2", driveBasePath1+"/folder", "folder", true, false, false), + }, + deltaLink: &delta, + }, + }, + }, + errCheck: assert.NoError, + prevFolderPaths: map[string]map[string]string{ + driveID1: {}, + }, + expectedCollections: map[string]map[data.CollectionState][]string{ + rootFolderPath1: {data.NewState: {}}, + folderPath1: {data.NewState: {"folder", "file", "file2"}}, + }, + expectedDeltaURLs: map[string]string{ + driveID1: delta, + }, + expectedFolderPaths: map[string]map[string]string{ + driveID1: { + "root": rootFolderPath1, + "folder": folderPath1, + }, + }, + expectedDelList: getDelList("file", "file2"), + }, } for _, test := range table { suite.Run(test.name, func() { @@ -1886,13 +1958,13 @@ func (suite *OneDriveCollectionsSuite) TestGet() { } } -func driveItem( +func coreItem( id string, name string, parentPath string, parentID string, isFile, isFolder, isPackage bool, -) models.DriveItemable { +) *models.DriveItem { item := models.NewDriveItem() item.SetName(&name) item.SetId(&id) @@ -1914,6 +1986,34 @@ func driveItem( return item } +func driveItem( + id string, + name string, + parentPath string, + parentID string, + isFile, isFolder, isPackage bool, +) models.DriveItemable { + return coreItem(id, name, parentPath, parentID, isFile, isFolder, isPackage) +} + +func malwareItem( + id string, + name string, + parentPath string, + parentID string, + isFile, isFolder, isPackage bool, +) models.DriveItemable { + c := coreItem(id, name, parentPath, parentID, isFile, isFolder, isPackage) + + mal := models.NewMalware() + malStr := "test malware" + mal.SetDescription(&malStr) + + c.SetMalware(mal) + + return c +} + func driveRootItem(id string) models.DriveItemable { name := "root" item := models.NewDriveItem()