|
1 | 1 | import { describe, expect, test } from "bun:test" |
2 | | -import { normalizeConfig } from "@altimateai/drivers" |
| 2 | +import { normalizeConfig, sanitizeConnectionString } from "@altimateai/drivers" |
3 | 3 | import { isSensitiveField } from "../../src/altimate/native/connections/credential-store" |
4 | 4 |
|
5 | 5 | // --------------------------------------------------------------------------- |
@@ -947,3 +947,133 @@ describe("normalizeConfig — ClickHouse", () => { |
947 | 947 | expect(result.ssl_key).toBeUndefined() |
948 | 948 | }) |
949 | 949 | }) |
| 950 | + |
| 951 | +// --------------------------------------------------------------------------- |
| 952 | +// sanitizeConnectionString — special character encoding |
| 953 | +// --------------------------------------------------------------------------- |
| 954 | + |
| 955 | +describe("sanitizeConnectionString", () => { |
| 956 | + test("encodes @ in password", () => { |
| 957 | + const input = "postgresql://testuser:t@st@localhost:5432/testdb" |
| 958 | + const result = sanitizeConnectionString(input) |
| 959 | + expect(result).toBe("postgresql://testuser:t%40st@localhost:5432/testdb") |
| 960 | + }) |
| 961 | + |
| 962 | + test("encodes # in password", () => { |
| 963 | + const input = "postgresql://testuser:test#val@localhost:5432/testdb" |
| 964 | + const result = sanitizeConnectionString(input) |
| 965 | + expect(result).toBe("postgresql://testuser:test%23val@localhost:5432/testdb") |
| 966 | + }) |
| 967 | + |
| 968 | + test("encodes : in password", () => { |
| 969 | + const input = "postgresql://testuser:test:val@localhost:5432/testdb" |
| 970 | + const result = sanitizeConnectionString(input) |
| 971 | + expect(result).toBe("postgresql://testuser:test%3Aval@localhost:5432/testdb") |
| 972 | + }) |
| 973 | + |
| 974 | + test("encodes multiple special characters", () => { |
| 975 | + const input = "postgresql://testuser:t@st#v:al@localhost:5432/testdb" |
| 976 | + const result = sanitizeConnectionString(input) |
| 977 | + expect(result).toBe("postgresql://testuser:t%40st%23v%3Aal@localhost:5432/testdb") |
| 978 | + }) |
| 979 | + |
| 980 | + test("encodes / in password", () => { |
| 981 | + const input = "postgresql://testuser:test/val@localhost:5432/testdb" |
| 982 | + const result = sanitizeConnectionString(input) |
| 983 | + expect(result).toBe("postgresql://testuser:test%2Fval@localhost:5432/testdb") |
| 984 | + }) |
| 985 | + |
| 986 | + test("encodes ? in password", () => { |
| 987 | + const input = "postgresql://testuser:test?val@localhost:5432/testdb" |
| 988 | + const result = sanitizeConnectionString(input) |
| 989 | + expect(result).toBe("postgresql://testuser:test%3Fval@localhost:5432/testdb") |
| 990 | + }) |
| 991 | + |
| 992 | + test("handles malformed percent sequence in username gracefully", () => { |
| 993 | + const input = "postgresql://bad%ZZuser:t@st@localhost:5432/testdb" |
| 994 | + const result = sanitizeConnectionString(input) |
| 995 | + // Should not throw — falls back to encoding the raw username |
| 996 | + expect(result).toContain("@localhost:5432/testdb") |
| 997 | + }) |
| 998 | + |
| 999 | + test("leaves already-encoded passwords untouched", () => { |
| 1000 | + const input = "postgresql://testuser:t%40st%23val@localhost:5432/testdb" |
| 1001 | + expect(sanitizeConnectionString(input)).toBe(input) |
| 1002 | + }) |
| 1003 | + |
| 1004 | + test("leaves passwords without special characters untouched", () => { |
| 1005 | + const input = "postgresql://testuser:simpletestval@localhost:5432/testdb" |
| 1006 | + expect(sanitizeConnectionString(input)).toBe(input) |
| 1007 | + }) |
| 1008 | + |
| 1009 | + test("leaves non-URI strings untouched", () => { |
| 1010 | + const input = "host=localhost dbname=mydb" |
| 1011 | + expect(sanitizeConnectionString(input)).toBe(input) |
| 1012 | + }) |
| 1013 | + |
| 1014 | + test("handles mongodb scheme", () => { |
| 1015 | + const input = "mongodb://testuser:t@st@localhost:27017/testdb" |
| 1016 | + const result = sanitizeConnectionString(input) |
| 1017 | + expect(result).toBe("mongodb://testuser:t%40st@localhost:27017/testdb") |
| 1018 | + }) |
| 1019 | + |
| 1020 | + test("handles mongodb+srv scheme", () => { |
| 1021 | + const input = "mongodb+srv://testuser:t@st@cluster.example.com/testdb" |
| 1022 | + const result = sanitizeConnectionString(input) |
| 1023 | + expect(result).toBe("mongodb+srv://testuser:t%40st@cluster.example.com/testdb") |
| 1024 | + }) |
| 1025 | + |
| 1026 | + test("leaves URIs without password untouched", () => { |
| 1027 | + const input = "postgresql://testuser@localhost:5432/testdb" |
| 1028 | + expect(sanitizeConnectionString(input)).toBe(input) |
| 1029 | + }) |
| 1030 | +}) |
| 1031 | + |
| 1032 | +// --------------------------------------------------------------------------- |
| 1033 | +// normalizeConfig — connection_string sanitization integration |
| 1034 | +// --------------------------------------------------------------------------- |
| 1035 | + |
| 1036 | +describe("normalizeConfig — connection_string sanitization", () => { |
| 1037 | + test("sanitizes connection_string with special chars in password", () => { |
| 1038 | + const result = normalizeConfig({ |
| 1039 | + type: "postgres", |
| 1040 | + connection_string: "postgresql://testuser:t@st#val@localhost:5432/testdb", |
| 1041 | + }) |
| 1042 | + expect(result.connection_string).toBe( |
| 1043 | + "postgresql://testuser:t%40st%23val@localhost:5432/testdb", |
| 1044 | + ) |
| 1045 | + }) |
| 1046 | + |
| 1047 | + test("sanitizes connectionString alias with special chars", () => { |
| 1048 | + const result = normalizeConfig({ |
| 1049 | + type: "postgres", |
| 1050 | + connectionString: "postgresql://testuser:t@st@localhost:5432/testdb", |
| 1051 | + }) |
| 1052 | + // alias resolved to connection_string, then sanitized |
| 1053 | + expect(result.connection_string).toBe( |
| 1054 | + "postgresql://testuser:t%40st@localhost:5432/testdb", |
| 1055 | + ) |
| 1056 | + expect(result.connectionString).toBeUndefined() |
| 1057 | + }) |
| 1058 | + |
| 1059 | + test("does not alter connection_string without special chars", () => { |
| 1060 | + const result = normalizeConfig({ |
| 1061 | + type: "redshift", |
| 1062 | + connection_string: "postgresql://testuser:testval@localhost:5439/testdb", |
| 1063 | + }) |
| 1064 | + expect(result.connection_string).toBe( |
| 1065 | + "postgresql://testuser:testval@localhost:5439/testdb", |
| 1066 | + ) |
| 1067 | + }) |
| 1068 | + |
| 1069 | + test("does not alter config without connection_string", () => { |
| 1070 | + const result = normalizeConfig({ |
| 1071 | + type: "postgres", |
| 1072 | + host: "localhost", |
| 1073 | + password: "t@st#val", |
| 1074 | + }) |
| 1075 | + // Individual fields are NOT URI-encoded — drivers handle them natively |
| 1076 | + expect(result.password).toBe("t@st#val") |
| 1077 | + expect(result.connection_string).toBeUndefined() |
| 1078 | + }) |
| 1079 | +}) |
0 commit comments