Skip to content

Commit 89d91e8

Browse files
SNOW-1706569: Fix file permissions (#1089)
1 parent cab672d commit 89d91e8

23 files changed

+699
-186
lines changed

Snowflake.Data.Tests/IntegrationTests/MaxLobSizeIT.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Data.Common;
1212
using System.IO;
1313
using System.Linq;
14+
using Snowflake.Data.Core.Tools;
1415

1516
namespace Snowflake.Data.Tests.IntegrationTests
1617
{
@@ -301,7 +302,11 @@ void PrepareTest()
301302
t_inputFilePath = Path.GetTempPath() + t_fileName;
302303

303304
var data = $"{t_colData[0]},{t_colData[1]},{t_colData[2]}";
304-
File.WriteAllText(t_inputFilePath, data);
305+
using (var stream = FileOperations.Instance.Create(t_inputFilePath))
306+
using (var writer = new StreamWriter(stream))
307+
{
308+
writer.Write(data);
309+
}
305310

306311
t_outputFilePath = $@"{s_outputDirectory}/{t_fileName}";
307312
t_filesToDelete.Add(t_inputFilePath);

Snowflake.Data.Tests/IntegrationTests/SFPutGetTest.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Data.Common;
88
using System.IO.Compression;
99
using System.Text;
10+
using Snowflake.Data.Core.Tools;
1011
using Snowflake.Data.Tests.Util;
1112

1213
namespace Snowflake.Data.Tests.IntegrationTests
@@ -807,7 +808,12 @@ private static void PrepareFileData(string file)
807808
var rawDataRow = string.Join(",", s_colData) + "\n";
808809
var rawData = string.Concat(Enumerable.Repeat(rawDataRow, NumberOfRows));
809810

810-
File.WriteAllText(file, rawData);
811+
812+
using (var stream = FileOperations.Instance.Create(file))
813+
using (var writer = new StreamWriter(stream))
814+
{
815+
writer.Write(rawData);
816+
}
811817
t_filesToDelete.Add(file);
812818
}
813819

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using Mono.Unix;
2+
using Snowflake.Data.Core.Tools;
3+
4+
namespace Snowflake.Data.Tests.Mock
5+
{
6+
internal class MockUnixOperations : UnixOperations
7+
{
8+
public long CurrentUserId { get; set; } = 0;
9+
public long FileOwnerId { get; set; } = 0;
10+
public long DirectoryOwnerId { get; set; } = 0;
11+
public bool DirectoryPermissionsReturn { get; set; } = false;
12+
public bool FilePermissionsReturn { get; set; } = false;
13+
14+
public override bool CheckDirectoryHasAnyOfPermissions(string path, FileAccessPermissions permissions)
15+
{
16+
return DirectoryPermissionsReturn;
17+
}
18+
19+
public override bool CheckFileHasAnyOfPermissions(string path, FileAccessPermissions permissions)
20+
{
21+
return FilePermissionsReturn;
22+
}
23+
24+
public override long GetCurrentUserId()
25+
{
26+
return CurrentUserId;
27+
}
28+
29+
public override long GetOwnerIdOfDirectory(string path)
30+
{
31+
return DirectoryOwnerId;
32+
}
33+
34+
public override long GetOwnerIdOfFile(string path)
35+
{
36+
return FileOwnerId;
37+
}
38+
}
39+
}

Snowflake.Data.Tests/UnitTests/CredentialManager/SFCredentialManagerFileImplTest.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System.IO;
77
using System.Security;
88
using Mono.Unix;
9-
using Mono.Unix.Native;
109
using Moq;
1110
using NUnit.Framework;
1211
using Snowflake.Data.Core.CredentialManager.Infrastructure;
@@ -66,8 +65,8 @@ public void TestThatThrowsErrorWhenCacheFailToCreateCacheFile()
6665
.Returns(false);
6766
t_unixOperations
6867
.Setup(u => u.CreateFileWithPermissions(s_customJsonPath,
69-
FilePermissions.S_IRUSR | FilePermissions.S_IWUSR))
70-
.Returns(-1);
68+
FileAccessPermissions.UserRead | FileAccessPermissions.UserWrite))
69+
.Throws<IOException>();
7170
t_environmentOperations
7271
.Setup(e => e.GetEnvironmentVariable(SFCredentialManagerFileStorage.CredentialCacheDirectoryEnvironmentName))
7372
.Returns(CustomJsonDir);
@@ -83,6 +82,9 @@ public void TestThatThrowsErrorWhenCacheFailToCreateCacheFile()
8382
t_directoryOperations
8483
.Setup(d => d.GetDirectoryInfo(s_customLockPath))
8584
.Returns(new DirectoryInformation(false, null));
85+
t_unixOperations
86+
.Setup(u => u.CreateDirectoryWithPermissionsMkdir(s_customLockPath, FileAccessPermissions.UserRead))
87+
.Returns(0);
8688
_credentialManager = new SFCredentialManagerFileImpl(t_fileOperations.Object, t_directoryOperations.Object, t_unixOperations.Object, t_environmentOperations.Object);
8789

8890
// act
@@ -104,7 +106,7 @@ public void TestThatThrowsErrorWhenCacheFileCanBeAccessedByOthers()
104106
try
105107
{
106108
DirectoryOperations.Instance.CreateDirectory(tempDirectory);
107-
UnixOperations.Instance.CreateFileWithPermissions(Path.Combine(tempDirectory, SFCredentialManagerFileStorage.CredentialCacheFileName), FilePermissions.ALLPERMS);
109+
UnixOperations.Instance.CreateFileWithPermissions(Path.Combine(tempDirectory, SFCredentialManagerFileStorage.CredentialCacheFileName), FileAccessPermissions.AllPermissions);
108110

109111
// act
110112
var thrown = Assert.Throws<SecurityException>(() => _credentialManager.SaveCredentials("key", "token"));
@@ -124,8 +126,7 @@ public void TestThatJsonFileIsCheckedIfAlreadyExists()
124126
// arrange
125127
t_unixOperations
126128
.Setup(u => u.CreateFileWithPermissions(s_customJsonPath,
127-
FilePermissions.S_IRUSR | FilePermissions.S_IWUSR))
128-
.Returns(0);
129+
FileAccessPermissions.UserRead | FileAccessPermissions.UserWrite));
129130
t_unixOperations
130131
.Setup(u => u.GetFilePermissions(s_customJsonPath))
131132
.Returns(FileAccessPermissions.UserRead | FileAccessPermissions.UserWrite);
@@ -184,7 +185,7 @@ public void TestWritingIsUnavailableIfFailedToCreateDirLock()
184185
.Setup(u => u.GetCurrentUserId())
185186
.Returns(UserId);
186187
t_unixOperations
187-
.Setup(u => u.CreateDirectoryWithPermissions(s_customLockPath, SFCredentialManagerFileImpl.CredentialCacheLockDirPermissions))
188+
.Setup(u => u.CreateDirectoryWithPermissionsMkdir(s_customLockPath, SFCredentialManagerFileImpl.CredentialCacheLockDirPermissions))
188189
.Returns(-1);
189190
_credentialManager = new SFCredentialManagerFileImpl(t_fileOperations.Object, t_directoryOperations.Object, t_unixOperations.Object, t_environmentOperations.Object);
190191

@@ -210,7 +211,7 @@ public void TestReadingIsUnavailableIfFailedToCreateDirLock()
210211
.Returns(false)
211212
.Returns(true);
212213
t_unixOperations
213-
.Setup(u => u.CreateDirectoryWithPermissions(s_customLockPath, SFCredentialManagerFileImpl.CredentialCacheLockDirPermissions))
214+
.Setup(u => u.CreateDirectoryWithPermissionsMkdir(s_customLockPath, SFCredentialManagerFileImpl.CredentialCacheLockDirPermissions))
214215
.Returns(-1);
215216
t_directoryOperations
216217
.Setup(d => d.GetParentDirectoryInfo(CustomJsonDir))

Snowflake.Data.Tests/UnitTests/Logger/EasyLoggingStarterTest.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System.IO;
77
using System.Runtime.InteropServices;
88
using Mono.Unix;
9-
using Mono.Unix.Native;
109
using Moq;
1110
using NUnit.Framework;
1211
using Snowflake.Data.Configuration;
@@ -157,7 +156,7 @@ public void TestThatDoesNotFailWhenLogDirectoryPermissionIsNot700()
157156

158157
// assert
159158
t_unixOperations.Verify(u => u.CreateDirectoryWithPermissions(s_expectedLogPath,
160-
FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR), Times.Never);
159+
FileAccessPermissions.UserReadWriteExecute), Times.Never);
161160
}
162161

163162
[Test]
@@ -172,9 +171,12 @@ public void TestFailIfDirectoryCreationFails()
172171
t_easyLoggingProvider
173172
.Setup(provider => provider.ProvideConfig(ConfigPath))
174173
.Returns(s_configWithErrorLevel);
174+
t_directoryOperations
175+
.Setup(d => d.CreateDirectory(s_expectedLogPath))
176+
.Throws(() => new Exception("Unable to create directory"));
175177
t_unixOperations
176-
.Setup(u => u.CreateDirectoryWithPermissions(s_expectedLogPath, FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR))
177-
.Returns((int)Errno.EPERM);
178+
.Setup(u => u.CreateDirectoryWithPermissions(s_expectedLogPath, FileAccessPermissions.UserReadWriteExecute))
179+
.Throws(() => new Exception("Unable to create directory"));
178180

179181
// act
180182
var thrown = Assert.Throws<Exception>(() => t_easyLoggerStarter.Init(ConfigPath));
@@ -208,7 +210,7 @@ public void TestThatConfiguresEasyLoggingOnlyOnceWhenInitializedWithConfigPath()
208210
else
209211
{
210212
t_unixOperations.Verify(u => u.CreateDirectoryWithPermissions(s_expectedLogPath,
211-
FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR), Times.Once);
213+
FileAccessPermissions.UserReadWriteExecute), Times.Once);
212214
}
213215
t_easyLoggerManager.Verify(manager => manager.ReconfigureEasyLogging(EasyLoggingLogLevel.Error, s_expectedLogPath), Times.Once);
214216

@@ -241,7 +243,7 @@ public void TestThatConfiguresEasyLoggingOnlyOnceForInitializationsWithoutConfig
241243
else
242244
{
243245
t_unixOperations.Verify(u => u.CreateDirectoryWithPermissions(s_expectedLogPath,
244-
FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR), Times.Once);
246+
FileAccessPermissions.UserReadWriteExecute), Times.Once);
245247
}
246248
t_easyLoggerManager.Verify(manager => manager.ReconfigureEasyLogging(EasyLoggingLogLevel.Error, s_expectedLogPath), Times.Once);
247249
}
@@ -268,7 +270,7 @@ public void TestThatReconfiguresEasyLoggingWithConfigPathIfNotGivenForTheFirstTi
268270
else
269271
{
270272
t_unixOperations.Verify(u => u.CreateDirectoryWithPermissions(s_expectedLogPath,
271-
FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR), Times.Once);
273+
FileAccessPermissions.UserReadWriteExecute), Times.Once);
272274
}
273275
t_easyLoggerManager.Verify(manager => manager.ReconfigureEasyLogging(EasyLoggingLogLevel.Error, s_expectedLogPath), Times.Once);
274276

Snowflake.Data.Tests/UnitTests/SFAzureClientTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ public void TestDownloadFile(HttpStatusCode httpStatusCode, ResultStatus expecte
303303
.Returns<string>((blobName) =>
304304
{
305305
var mockBlobClient = new Mock<BlobClient>();
306-
mockBlobClient.Setup(client => client.DownloadTo(It.IsAny<string>()))
306+
mockBlobClient.Setup(client => client.DownloadTo(It.IsAny<Stream>()))
307307
.Returns(() =>
308308
{
309309
if (key == HttpStatusCode.OK.ToString())
@@ -350,7 +350,7 @@ public async Task TestDownloadFileAsync(HttpStatusCode httpStatusCode, ResultSta
350350
.Returns<string>((blobName) =>
351351
{
352352
var mockBlobClient = new Mock<BlobClient>();
353-
mockBlobClient.Setup(client => client.DownloadToAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
353+
mockBlobClient.Setup(client => client.DownloadToAsync(It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
354354
.Returns(async () =>
355355
{
356356
if (key == HttpStatusCode.OK.ToString())

Snowflake.Data.Tests/UnitTests/SFBindUploaderTest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
namespace Snowflake.Data.Tests.UnitTests
1010
{
1111
[TestFixture]
12+
[SetCulture("en-US")]
1213
class SFBindUploaderTest
1314
{
1415
private readonly SFBindUploader _bindUploader = new SFBindUploader(null, "test");
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using Mono.Unix;
4+
using Mono.Unix.Native;
5+
using NUnit.Framework;
6+
using Snowflake.Data.Core.Tools;
7+
using Snowflake.Data.Tests.Mock;
8+
9+
namespace Snowflake.Data.Tests.UnitTests.Tools
10+
{
11+
[TestFixture, NonParallelizable]
12+
public class DirectoryOperationsTest
13+
{
14+
private static DirectoryOperations s_directoryOperations;
15+
private static readonly string s_relativeWorkingDirectory = $"directory_operations_test_{Path.GetRandomFileName()}";
16+
private static readonly string s_workingDirectory = Path.Combine(TempUtil.GetTempPath(), s_relativeWorkingDirectory);
17+
private static readonly string s_dirName = "testdir";
18+
private static readonly string s_dirAbsolutePath = Path.Combine(s_workingDirectory, s_dirName);
19+
20+
[SetUp]
21+
public static void Before()
22+
{
23+
if (!Directory.Exists(s_workingDirectory))
24+
{
25+
Directory.CreateDirectory(s_workingDirectory);
26+
}
27+
28+
s_directoryOperations = new DirectoryOperations();
29+
}
30+
31+
[TearDown]
32+
public static void After()
33+
{
34+
Directory.Delete(s_workingDirectory, true);
35+
}
36+
37+
[Test]
38+
[Platform("Win")]
39+
public void TestDirectoryIsSafeOnWindows()
40+
{
41+
// arrange
42+
var absoluteFilePath = Path.Combine(s_workingDirectory, s_dirName);
43+
Directory.CreateDirectory(absoluteFilePath);
44+
45+
// act and assert
46+
Assert.IsTrue(s_directoryOperations.IsDirectorySafe(absoluteFilePath));
47+
}
48+
49+
[Test]
50+
[Platform(Exclude = "Win")]
51+
public void TestDirectoryIsNotSafeOnNotWindowsWhenPermissionsAreTooBroad(
52+
[ValueSource(nameof(InsecurePermissions))]
53+
FileAccessPermissions permissions)
54+
{
55+
// arrange
56+
Syscall.mkdir(s_dirAbsolutePath, (FilePermissions)permissions);
57+
58+
// act and assert
59+
Assert.IsFalse(s_directoryOperations.IsDirectorySafe(s_dirAbsolutePath));
60+
}
61+
62+
[Test]
63+
public void TestShouldCreateDirectoryWithSafePermissions()
64+
{
65+
// act
66+
s_directoryOperations.CreateDirectory(s_dirAbsolutePath);
67+
68+
// assert
69+
Assert.IsTrue(Directory.Exists(s_dirAbsolutePath));
70+
Assert.IsTrue(s_directoryOperations.IsDirectorySafe(s_dirAbsolutePath));
71+
}
72+
73+
[Test]
74+
[Platform(Exclude = "Win")]
75+
public void TestOwnerIsCurrentUser()
76+
{
77+
// arrange
78+
var mockUnixOperations = new MockUnixOperations{ CurrentUserId = 1, DirectoryOwnerId = 1};
79+
var dirOps = new DirectoryOperations(mockUnixOperations);
80+
81+
// act and assert
82+
Assert.IsTrue(dirOps.IsDirectoryOwnedByCurrentUser(s_dirAbsolutePath));
83+
}
84+
85+
[Test]
86+
[Platform(Exclude = "Win")]
87+
public void TestOwnerIsNotCurrentUser()
88+
{
89+
// arrange
90+
var mockUnixOperations = new MockUnixOperations{ CurrentUserId = 1, DirectoryOwnerId = 2};
91+
var dirOps = new DirectoryOperations(mockUnixOperations);
92+
93+
// act and assert
94+
Assert.IsFalse(dirOps.IsDirectoryOwnedByCurrentUser(s_dirAbsolutePath));
95+
}
96+
97+
[Test]
98+
[Platform(Exclude = "Win")]
99+
public void TestDirectoryIsNotSecureWhenNotOwnedByCurrentUser()
100+
{
101+
// arrange
102+
var mockUnixOperations = new MockUnixOperations{ CurrentUserId = 1, DirectoryOwnerId = 2};
103+
var dirOps = new DirectoryOperations(mockUnixOperations);
104+
105+
// act and assert
106+
Assert.IsFalse(dirOps.IsDirectorySafe(s_dirAbsolutePath));
107+
}
108+
109+
// User permissions are required for all of the tests to be able to access directory information
110+
public static IEnumerable<FileAccessPermissions> InsecurePermissions()
111+
{
112+
yield return FileAccessPermissions.UserReadWriteExecute | FileAccessPermissions.GroupRead;
113+
yield return FileAccessPermissions.UserReadWriteExecute | FileAccessPermissions.GroupRead | FileAccessPermissions.GroupWrite;
114+
yield return FileAccessPermissions.UserReadWriteExecute | FileAccessPermissions.GroupExecute;
115+
yield return FileAccessPermissions.UserReadWriteExecute | FileAccessPermissions.GroupReadWriteExecute;
116+
yield return FileAccessPermissions.UserReadWriteExecute | FileAccessPermissions.OtherRead;
117+
yield return FileAccessPermissions.UserReadWriteExecute | FileAccessPermissions.OtherRead | FileAccessPermissions.OtherWrite;
118+
yield return FileAccessPermissions.UserReadWriteExecute | FileAccessPermissions.OtherExecute;
119+
yield return FileAccessPermissions.UserReadWriteExecute | FileAccessPermissions.OtherReadWriteExecute;
120+
yield return FileAccessPermissions.UserReadWriteExecute | FileAccessPermissions.AllPermissions;
121+
}
122+
}
123+
}

0 commit comments

Comments
 (0)