Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
93545b1
Initial commit.
sfc-gh-ext-simba-vb Jul 18, 2025
2964494
code checkin.
sfc-gh-ext-simba-vb Jul 21, 2025
2422cbb
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Jul 21, 2025
e831e1e
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Jul 22, 2025
5b250e1
check style issue.
sfc-gh-ext-simba-vb Jul 22, 2025
30a5b40
fixed the test failure.
sfc-gh-ext-simba-vb Jul 22, 2025
af5abc0
error msg printing.
sfc-gh-ext-simba-vb Jul 22, 2025
a0f6eeb
test failure fix.
sfc-gh-ext-simba-vb Jul 22, 2025
41c7c22
SOP added for testing
sfc-gh-ext-simba-vb Jul 22, 2025
31e7254
toml file changes.
sfc-gh-ext-simba-vb Jul 22, 2025
637c4b7
Updated tests.
sfc-gh-ext-simba-vb Jul 22, 2025
5c3023c
test fix
sfc-gh-ext-simba-vb Jul 22, 2025
9938021
converted to parameterized JUnit test.
sfc-gh-ext-simba-vb Jul 23, 2025
b478cdf
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Jul 24, 2025
56805b0
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Jul 25, 2025
af63bd8
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Jul 30, 2025
c32a8f2
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Jul 31, 2025
a362f4d
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Aug 6, 2025
674c6f5
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Aug 12, 2025
d48db6c
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Aug 12, 2025
a34f1fb
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Aug 13, 2025
1c53874
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Aug 14, 2025
f8ae72a
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Aug 20, 2025
ce50293
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Aug 21, 2025
37e7ba1
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Aug 26, 2025
0f67733
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Aug 28, 2025
b0aeece
Review comments implemented.
sfc-gh-ext-simba-vb Aug 29, 2025
c51dcc6
Merge branch 'SNOW-2161718-JDBC-fix-permission-check-for-toml-config'…
sfc-gh-ext-simba-vb Aug 29, 2025
ef020a4
review comments implemented 2
sfc-gh-ext-simba-vb Aug 29, 2025
5a36f6a
Changed the tonl file creation logic.
sfc-gh-ext-simba-vb Aug 29, 2025
f74f61c
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Sep 3, 2025
248691f
Merge branch 'master' of https://github.com/snowflakedb/snowflake-jdb…
sfc-gh-ext-simba-vb Sep 4, 2025
acf6f4c
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Sep 4, 2025
6261f93
Merge branch 'SNOW-2161718-JDBC-fix-permission-check-for-toml-config'…
sfc-gh-ext-simba-vb Sep 4, 2025
bc0c2f4
Review comments implemented
sfc-gh-ext-simba-vb Sep 5, 2025
a3d8638
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Sep 8, 2025
5e6fdc0
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Sep 15, 2025
cb0b81e
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Sep 16, 2025
2b10d23
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Sep 16, 2025
1aff90e
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Sep 22, 2025
76fd52e
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Sep 22, 2025
37b11d0
Merge branch 'master' into SNOW-2161718-JDBC-fix-permission-check-for…
sfc-gh-ext-simba-vb Sep 23, 2025
b07bc4f
Description.md altered.
sfc-gh-ext-simba-vb Sep 25, 2025
4a387da
Merge branch 'master' of https://github.com/snowflakedb/snowflake-jdb…
sfc-gh-ext-simba-vb Sep 25, 2025
ea56cf7
Merge branch 'master' of https://github.com/snowflakedb/snowflake-jdb…
sfc-gh-ext-simba-vb Sep 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
- Added HTTP 307 & 308 retries in case of internal IP redirects
- Make PAT creation return `ResultSet` when using `execute` method
- Renamed CRL_REVOCATION_CHECK_MODE to CERT_REVOCATION_CHECK_MODE in CLIENT_ENVIRONMENT metrics
- Test coverage for multistatement jdbc.
- Test coverage for multistatement jdbc.
- Fixed permission check for .toml config file.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
import java.nio.file.attribute.PosixFilePermission;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import net.snowflake.client.core.SnowflakeJdbcInternalApi;
import net.snowflake.client.jdbc.SnowflakeSQLException;
import net.snowflake.client.log.SFLogger;
Expand All @@ -37,6 +39,11 @@ public class SFConnectionConfigParser {
public static final String SNOWFLAKE_TOKEN_FILE_PATH = "/snowflake/session/token";
public static final String SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION =
"SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION";
public static final String SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE =
"SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE";

private static final List<PosixFilePermission> REQUIRED_PERMISSIONS =
Arrays.asList(PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_READ);

public static ConnectionParameters buildConnectionParameters() throws SnowflakeSQLException {
String defaultConnectionName =
Expand Down Expand Up @@ -115,23 +122,68 @@ private static Map<String, Map> readParametersMap(Path configFilePath)
}
}

private static void verifyFilePermissionSecure(Path configFilePath)
static void verifyFilePermissionSecure(Path configFilePath)
throws IOException, SnowflakeSQLException {
final String fileName = "connections.toml";
if (!isWindows()) {
PosixFileAttributeView posixFileAttributeView =
Files.getFileAttributeView(configFilePath, PosixFileAttributeView.class);
if (!posixFileAttributeView.readAttributes().permissions().stream()
.allMatch(
o ->
Arrays.asList(PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_READ)
.contains(o))) {
logger.error(
"Reading from file %s is not safe because file permissions are different than read/write for user",
configFilePath);
throw new SnowflakeSQLException(
String.format(
"Reading from file %s is not safe because file permissions are different than read/write for user",
configFilePath));
if (configFilePath.getFileName().toString().equals(fileName)) {
boolean shouldSkipWarningForReadPermissions =
convertSystemGetEnvToBooleanValue(
SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE, false);
PosixFileAttributeView posixFileAttributeView =
Files.getFileAttributeView(configFilePath, PosixFileAttributeView.class);
Set<PosixFilePermission> permissions =
posixFileAttributeView.readAttributes().permissions();

if (!shouldSkipWarningForReadPermissions) {
boolean groupRead = permissions.contains(PosixFilePermission.GROUP_READ);
boolean othersRead = permissions.contains(PosixFilePermission.OTHERS_READ);
// Warning if readable by group/others (must be 600 or stricter)
if (groupRead || othersRead) {
logger.warn(
"File %s is readable by group or others. Permissions should be 600 or stricter for maximum security.",
configFilePath);
}
}

boolean groupWrite = permissions.contains(PosixFilePermission.GROUP_WRITE);
boolean othersWrite = permissions.contains(PosixFilePermission.OTHERS_WRITE);
// Error if writable by group/others (must be 644 or stricter)
if (groupWrite || othersWrite) {
logger.error(
"File %s is writable by group or others. Permissions must be 644 or stricter.",
configFilePath);
throw new SnowflakeSQLException(
String.format(
"File %s is writable by group or others. Permissions must be 644 or stricter.",
configFilePath));
}

// Error if executable by anyone
boolean ownerExec = permissions.contains(PosixFilePermission.OWNER_EXECUTE);
boolean groupExec = permissions.contains(PosixFilePermission.GROUP_EXECUTE);
boolean othersExec = permissions.contains(PosixFilePermission.OTHERS_EXECUTE);
// Executable permission is not allowed
if (ownerExec || groupExec || othersExec) {
logger.error(
"File %s is executable. Executable permission is not allowed.", configFilePath);
throw new SnowflakeSQLException(
String.format(
"File %s is executable. Executable permission is not allowed.", configFilePath));
}
} else {
PosixFileAttributeView posixFileAttributeView =
Files.getFileAttributeView(configFilePath, PosixFileAttributeView.class);
if (!posixFileAttributeView.readAttributes().permissions().stream()
.allMatch(o -> REQUIRED_PERMISSIONS.contains(o))) {
logger.error(
"Reading from file %s is not safe because file permissions are different than read/write for user",
configFilePath);
throw new SnowflakeSQLException(
String.format(
"Reading from file %s is not safe because file permissions are different than read/write for user",
configFilePath));
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package net.snowflake.client.config;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.snowflake.client.annotations.DontRunOnWindows;
import net.snowflake.client.jdbc.SnowflakeSQLException;
import net.snowflake.client.jdbc.SnowflakeUtil;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

class SFConnectionConfigParserPermissionTest {

private Path createTempFileWithPermissions(Set<PosixFilePermission> perms) throws Exception {
// Create a unique temporary directory
Path tempDir = Files.createTempDirectory("snowflake");

// Inside it, create a file named "connections.toml"
Path tomlFile = tempDir.resolve("connections.toml");
Files.createFile(tomlFile);

// Apply the given POSIX permissions
Files.setPosixFilePermissions(tomlFile, perms);

// Mark both the file and the directory for deletion on JVM exit
tomlFile.toFile().deleteOnExit();
tempDir.toFile().deleteOnExit();

return tomlFile;
}

static List<Object[]> permissionTestCases() {
return Arrays.asList(
new Object[][] {
{ // Group write
new HashSet<>(
Arrays.asList(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.GROUP_WRITE)),
true,
"writable by group or others"
},
{ // Others write
new HashSet<>(
Arrays.asList(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.OTHERS_WRITE)),
true,
"writable by group or others"
},
{ // Owner execute
new HashSet<>(
Arrays.asList(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.OWNER_EXECUTE)),
true,
"executable"
},
{ // Group execute
new HashSet<>(
Arrays.asList(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.GROUP_EXECUTE)),
true,
"executable"
},
{ // Others execute
new HashSet<>(
Arrays.asList(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.OTHERS_EXECUTE)),
true,
"executable"
},
{ // Owner read/write only
new HashSet<>(
Arrays.asList(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE)),
false,
null
}
});
}

@ParameterizedTest
@MethodSource("permissionTestCases")
@DontRunOnWindows
void testFilePermissionScenarios(
Set<PosixFilePermission> perms, boolean shouldThrow, String expectedMsg) throws Exception {
Path tempFile = createTempFileWithPermissions(perms);
try {
if (shouldThrow) {
Exception ex =
assertThrows(
SnowflakeSQLException.class,
() -> SFConnectionConfigParser.verifyFilePermissionSecure(tempFile));
assertTrue(ex.getMessage().contains(expectedMsg));
} else {
assertDoesNotThrow(() -> SFConnectionConfigParser.verifyFilePermissionSecure(tempFile));
}
} finally {
Files.deleteIfExists(tempFile);
}
}

static List<Object[]> skipReadWarningTestCases() {
return Arrays.asList(
new Object[][] {
{
new HashSet<>(
Arrays.asList(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.GROUP_READ,
PosixFilePermission.OTHERS_READ))
}
});
}

@ParameterizedTest
@MethodSource("skipReadWarningTestCases")
@DontRunOnWindows
void testSkipWarningForReadPermissionsEnvVar(Set<PosixFilePermission> perms) throws Exception {
Path tempFile = createTempFileWithPermissions(perms);
SnowflakeUtil.systemSetEnv("SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE", "true");
try {
assertDoesNotThrow(() -> SFConnectionConfigParser.verifyFilePermissionSecure(tempFile));
} finally {
Files.deleteIfExists(tempFile);
SnowflakeUtil.systemSetEnv("SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE", null);
}
}
}
Loading