@naturalpay/sdk 1.6.4 → 1.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,8 +43,8 @@ function normalizeClientOptions(options) {
43
43
  const headers = (0, headers_js_1.mergeHeaders)({
44
44
  "X-Fern-Language": "JavaScript",
45
45
  "X-Fern-SDK-Name": "@naturalpay/sdk",
46
- "X-Fern-SDK-Version": "1.6.4",
47
- "User-Agent": "@naturalpay/sdk/1.6.4",
46
+ "X-Fern-SDK-Version": "1.6.5",
47
+ "User-Agent": "@naturalpay/sdk/1.6.5",
48
48
  "X-Fern-Runtime": core.RUNTIME.type,
49
49
  "X-Fern-Runtime-Version": core.RUNTIME.version,
50
50
  "X-Instance-ID": options === null || options === void 0 ? void 0 : options.instanceId,
@@ -22,6 +22,7 @@ const getResponseBody_js_1 = require("./getResponseBody.js");
22
22
  const Headers_js_1 = require("./Headers.js");
23
23
  const makeRequest_js_1 = require("./makeRequest.js");
24
24
  const RawResponse_js_1 = require("./RawResponse.js");
25
+ const redactUrl_js_1 = require("./redactUrl.js");
25
26
  const requestWithRetries_js_1 = require("./requestWithRetries.js");
26
27
  const SENSITIVE_HEADERS = new Set([
27
28
  "authorization",
@@ -53,100 +54,16 @@ function redactHeaders(headers) {
53
54
  }
54
55
  return filtered;
55
56
  }
56
- const SENSITIVE_QUERY_PARAMS = new Set([
57
- "api_key",
58
- "api-key",
59
- "apikey",
60
- "token",
61
- "access_token",
62
- "access-token",
63
- "auth_token",
64
- "auth-token",
65
- "password",
66
- "passwd",
67
- "secret",
68
- "api_secret",
69
- "api-secret",
70
- "apisecret",
71
- "key",
72
- "session",
73
- "session_id",
74
- "session-id",
75
- ]);
76
57
  function redactQueryParameters(queryParameters) {
77
58
  if (queryParameters == null) {
78
59
  return undefined;
79
60
  }
80
61
  const redacted = {};
81
62
  for (const [key, value] of Object.entries(queryParameters)) {
82
- redacted[key] = SENSITIVE_QUERY_PARAMS.has(key.toLowerCase()) ? "[REDACTED]" : value;
63
+ redacted[key] = redactUrl_js_1.SENSITIVE_QUERY_PARAMS.has(key.toLowerCase()) ? "[REDACTED]" : value;
83
64
  }
84
65
  return redacted;
85
66
  }
86
- function redactUrl(url) {
87
- const protocolIndex = url.indexOf("://");
88
- if (protocolIndex === -1)
89
- return url;
90
- const afterProtocol = protocolIndex + 3;
91
- // Find the first delimiter that marks the end of the authority section
92
- const pathStart = url.indexOf("/", afterProtocol);
93
- let queryStart = url.indexOf("?", afterProtocol);
94
- let fragmentStart = url.indexOf("#", afterProtocol);
95
- const firstDelimiter = Math.min(pathStart === -1 ? url.length : pathStart, queryStart === -1 ? url.length : queryStart, fragmentStart === -1 ? url.length : fragmentStart);
96
- // Find the LAST @ before the delimiter (handles multiple @ in credentials)
97
- let atIndex = -1;
98
- for (let i = afterProtocol; i < firstDelimiter; i++) {
99
- if (url[i] === "@") {
100
- atIndex = i;
101
- }
102
- }
103
- if (atIndex !== -1) {
104
- url = `${url.slice(0, afterProtocol)}[REDACTED]@${url.slice(atIndex + 1)}`;
105
- }
106
- // Recalculate queryStart since url might have changed
107
- queryStart = url.indexOf("?");
108
- if (queryStart === -1)
109
- return url;
110
- fragmentStart = url.indexOf("#", queryStart);
111
- const queryEnd = fragmentStart !== -1 ? fragmentStart : url.length;
112
- const queryString = url.slice(queryStart + 1, queryEnd);
113
- if (queryString.length === 0)
114
- return url;
115
- // FAST PATH: Quick check if any sensitive keywords present
116
- // Using indexOf is faster than regex for simple substring matching
117
- const lower = queryString.toLowerCase();
118
- const hasSensitive = lower.includes("token") ||
119
- lower.includes("key") ||
120
- lower.includes("password") ||
121
- lower.includes("passwd") ||
122
- lower.includes("secret") ||
123
- lower.includes("session") ||
124
- lower.includes("auth");
125
- if (!hasSensitive) {
126
- return url;
127
- }
128
- // SLOW PATH: Parse and redact
129
- const redactedParams = [];
130
- const params = queryString.split("&");
131
- for (const param of params) {
132
- const equalIndex = param.indexOf("=");
133
- if (equalIndex === -1) {
134
- redactedParams.push(param);
135
- continue;
136
- }
137
- const key = param.slice(0, equalIndex);
138
- let shouldRedact = SENSITIVE_QUERY_PARAMS.has(key.toLowerCase());
139
- if (!shouldRedact && key.includes("%")) {
140
- try {
141
- const decodedKey = decodeURIComponent(key);
142
- shouldRedact = SENSITIVE_QUERY_PARAMS.has(decodedKey.toLowerCase());
143
- }
144
- catch (_a) { }
145
- }
146
- redactedParams.push(shouldRedact ? `${key}=[REDACTED]` : param);
147
- }
148
- return url.slice(0, queryStart + 1) + redactedParams.join("&") + url.slice(queryEnd);
149
- }
150
67
  function getHeaders(args) {
151
68
  return __awaiter(this, void 0, void 0, function* () {
152
69
  var _a;
@@ -198,7 +115,7 @@ function fetcherImpl(args) {
198
115
  if (logger.isDebug()) {
199
116
  const metadata = {
200
117
  method: args.method,
201
- url: redactUrl(url),
118
+ url: (0, redactUrl_js_1.redactUrl)(url),
202
119
  headers: redactHeaders(headers),
203
120
  queryParameters: redactQueryParameters(args.queryParameters),
204
121
  hasBody: requestBody != null,
@@ -213,7 +130,7 @@ function fetcherImpl(args) {
213
130
  if (logger.isDebug()) {
214
131
  const metadata = {
215
132
  method: args.method,
216
- url: redactUrl(url),
133
+ url: (0, redactUrl_js_1.redactUrl)(url),
217
134
  statusCode: response.status,
218
135
  responseHeaders: redactHeaders(response.headers),
219
136
  };
@@ -231,7 +148,7 @@ function fetcherImpl(args) {
231
148
  if (logger.isError()) {
232
149
  const metadata = {
233
150
  method: args.method,
234
- url: redactUrl(url),
151
+ url: (0, redactUrl_js_1.redactUrl)(url),
235
152
  statusCode: response.status,
236
153
  responseHeaders: redactHeaders(Object.fromEntries(response.headers.entries())),
237
154
  };
@@ -253,7 +170,7 @@ function fetcherImpl(args) {
253
170
  if (logger.isError()) {
254
171
  const metadata = {
255
172
  method: args.method,
256
- url: redactUrl(url),
173
+ url: (0, redactUrl_js_1.redactUrl)(url),
257
174
  };
258
175
  logger.error("HTTP request was aborted", metadata);
259
176
  }
@@ -271,7 +188,7 @@ function fetcherImpl(args) {
271
188
  if (logger.isError()) {
272
189
  const metadata = {
273
190
  method: args.method,
274
- url: redactUrl(url),
191
+ url: (0, redactUrl_js_1.redactUrl)(url),
275
192
  timeoutMs: args.timeoutMs,
276
193
  };
277
194
  logger.error("HTTP request timed out", metadata);
@@ -289,7 +206,7 @@ function fetcherImpl(args) {
289
206
  if (logger.isError()) {
290
207
  const metadata = {
291
208
  method: args.method,
292
- url: redactUrl(url),
209
+ url: (0, redactUrl_js_1.redactUrl)(url),
293
210
  errorMessage: error.message,
294
211
  };
295
212
  logger.error("HTTP request failed with error", metadata);
@@ -307,7 +224,7 @@ function fetcherImpl(args) {
307
224
  if (logger.isError()) {
308
225
  const metadata = {
309
226
  method: args.method,
310
- url: redactUrl(url),
227
+ url: (0, redactUrl_js_1.redactUrl)(url),
311
228
  error: (0, json_js_1.toJson)(error),
312
229
  };
313
230
  logger.error("HTTP request failed with unknown error", metadata);
@@ -15,6 +15,7 @@ const join_js_1 = require("../url/join.js");
15
15
  const EndpointSupplier_js_1 = require("./EndpointSupplier.js");
16
16
  const getFetchFn_js_1 = require("./getFetchFn.js");
17
17
  const makeRequest_js_1 = require("./makeRequest.js");
18
+ const redactUrl_js_1 = require("./redactUrl.js");
18
19
  const requestWithRetries_js_1 = require("./requestWithRetries.js");
19
20
  const Supplier_js_1 = require("./Supplier.js");
20
21
  /**
@@ -79,8 +80,10 @@ function makePassthroughRequest(input, init, clientOptions, requestOptions) {
79
80
  }
80
81
  }
81
82
  }
82
- // Apply auth headers
83
- if (clientOptions.getAuthHeaders != null) {
83
+ // Apply auth headers, but only when the resolved URL targets the configured base URL.
84
+ // This prevents the SDK's credentials from leaking to an unrelated host when a caller
85
+ // passes an absolute cross-origin URL into the passthrough fetch escape hatch.
86
+ if (clientOptions.getAuthHeaders != null && targetsBaseUrl(fullUrl, baseUrl)) {
84
87
  const authHeaders = yield clientOptions.getAuthHeaders();
85
88
  for (const [key, value] of Object.entries(authHeaders)) {
86
89
  mergedHeaders[key.toLowerCase()] = value;
@@ -115,7 +118,7 @@ function makePassthroughRequest(input, init, clientOptions, requestOptions) {
115
118
  if (logger.isDebug()) {
116
119
  logger.debug("Making passthrough HTTP request", {
117
120
  method,
118
- url: fullUrl,
121
+ url: (0, redactUrl_js_1.redactUrl)(fullUrl),
119
122
  hasBody: body != null,
120
123
  });
121
124
  }
@@ -126,10 +129,29 @@ function makePassthroughRequest(input, init, clientOptions, requestOptions) {
126
129
  if (logger.isDebug()) {
127
130
  logger.debug("Passthrough HTTP request completed", {
128
131
  method,
129
- url: fullUrl,
132
+ url: (0, redactUrl_js_1.redactUrl)(fullUrl),
130
133
  statusCode: response.status,
131
134
  });
132
135
  }
133
136
  return response;
134
137
  });
135
138
  }
139
+ /**
140
+ * Returns true when the resolved request URL points at the same origin as the
141
+ * configured base URL. Relative paths are always joined onto the base URL, so
142
+ * they resolve to the base origin and return true. Absolute URLs only match when
143
+ * their origin equals the base origin. When there is no base URL to compare
144
+ * against, or either value is not a parseable absolute URL, this returns false so
145
+ * auth headers are not attached.
146
+ */
147
+ function targetsBaseUrl(fullUrl, baseUrl) {
148
+ if (baseUrl == null) {
149
+ return false;
150
+ }
151
+ try {
152
+ return new URL(fullUrl).origin === new URL(baseUrl).origin;
153
+ }
154
+ catch (_a) {
155
+ return false;
156
+ }
157
+ }
@@ -0,0 +1,2 @@
1
+ export declare const SENSITIVE_QUERY_PARAMS: Set<string>;
2
+ export declare function redactUrl(url: string): string;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SENSITIVE_QUERY_PARAMS = void 0;
4
+ exports.redactUrl = redactUrl;
5
+ exports.SENSITIVE_QUERY_PARAMS = new Set([
6
+ "api_key",
7
+ "api-key",
8
+ "apikey",
9
+ "token",
10
+ "access_token",
11
+ "access-token",
12
+ "auth_token",
13
+ "auth-token",
14
+ "password",
15
+ "passwd",
16
+ "secret",
17
+ "api_secret",
18
+ "api-secret",
19
+ "apisecret",
20
+ "key",
21
+ "session",
22
+ "session_id",
23
+ "session-id",
24
+ ]);
25
+ function redactUrl(url) {
26
+ const protocolIndex = url.indexOf("://");
27
+ if (protocolIndex === -1)
28
+ return url;
29
+ const afterProtocol = protocolIndex + 3;
30
+ // Find the first delimiter that marks the end of the authority section
31
+ const pathStart = url.indexOf("/", afterProtocol);
32
+ let queryStart = url.indexOf("?", afterProtocol);
33
+ let fragmentStart = url.indexOf("#", afterProtocol);
34
+ const firstDelimiter = Math.min(pathStart === -1 ? url.length : pathStart, queryStart === -1 ? url.length : queryStart, fragmentStart === -1 ? url.length : fragmentStart);
35
+ // Find the LAST @ before the delimiter (handles multiple @ in credentials)
36
+ let atIndex = -1;
37
+ for (let i = afterProtocol; i < firstDelimiter; i++) {
38
+ if (url[i] === "@") {
39
+ atIndex = i;
40
+ }
41
+ }
42
+ if (atIndex !== -1) {
43
+ url = `${url.slice(0, afterProtocol)}[REDACTED]@${url.slice(atIndex + 1)}`;
44
+ }
45
+ // Recalculate queryStart since url might have changed
46
+ queryStart = url.indexOf("?");
47
+ if (queryStart === -1)
48
+ return url;
49
+ fragmentStart = url.indexOf("#", queryStart);
50
+ const queryEnd = fragmentStart !== -1 ? fragmentStart : url.length;
51
+ const queryString = url.slice(queryStart + 1, queryEnd);
52
+ if (queryString.length === 0)
53
+ return url;
54
+ // FAST PATH: Quick check if any sensitive keywords present
55
+ // Using indexOf is faster than regex for simple substring matching
56
+ const lower = queryString.toLowerCase();
57
+ const hasSensitive = lower.includes("token") ||
58
+ lower.includes("key") ||
59
+ lower.includes("password") ||
60
+ lower.includes("passwd") ||
61
+ lower.includes("secret") ||
62
+ lower.includes("session") ||
63
+ lower.includes("auth");
64
+ if (!hasSensitive) {
65
+ return url;
66
+ }
67
+ // SLOW PATH: Parse and redact
68
+ const redactedParams = [];
69
+ const params = queryString.split("&");
70
+ for (const param of params) {
71
+ const equalIndex = param.indexOf("=");
72
+ if (equalIndex === -1) {
73
+ redactedParams.push(param);
74
+ continue;
75
+ }
76
+ const key = param.slice(0, equalIndex);
77
+ let shouldRedact = exports.SENSITIVE_QUERY_PARAMS.has(key.toLowerCase());
78
+ if (!shouldRedact && key.includes("%")) {
79
+ try {
80
+ const decodedKey = decodeURIComponent(key);
81
+ shouldRedact = exports.SENSITIVE_QUERY_PARAMS.has(decodedKey.toLowerCase());
82
+ }
83
+ catch (_a) { }
84
+ }
85
+ redactedParams.push(shouldRedact ? `${key}=[REDACTED]` : param);
86
+ }
87
+ return url.slice(0, queryStart + 1) + redactedParams.join("&") + url.slice(queryEnd);
88
+ }
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "1.6.4";
1
+ export declare const SDK_VERSION = "1.6.5";
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SDK_VERSION = void 0;
4
- exports.SDK_VERSION = "1.6.4";
4
+ exports.SDK_VERSION = "1.6.5";
@@ -6,8 +6,8 @@ export function normalizeClientOptions(options) {
6
6
  const headers = mergeHeaders({
7
7
  "X-Fern-Language": "JavaScript",
8
8
  "X-Fern-SDK-Name": "@naturalpay/sdk",
9
- "X-Fern-SDK-Version": "1.6.4",
10
- "User-Agent": "@naturalpay/sdk/1.6.4",
9
+ "X-Fern-SDK-Version": "1.6.5",
10
+ "User-Agent": "@naturalpay/sdk/1.6.5",
11
11
  "X-Fern-Runtime": core.RUNTIME.type,
12
12
  "X-Fern-Runtime-Version": core.RUNTIME.version,
13
13
  "X-Instance-ID": options === null || options === void 0 ? void 0 : options.instanceId,
@@ -18,6 +18,7 @@ import { getResponseBody } from "./getResponseBody.mjs";
18
18
  import { Headers } from "./Headers.mjs";
19
19
  import { makeRequest } from "./makeRequest.mjs";
20
20
  import { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.mjs";
21
+ import { redactUrl, SENSITIVE_QUERY_PARAMS } from "./redactUrl.mjs";
21
22
  import { requestWithRetries } from "./requestWithRetries.mjs";
22
23
  const SENSITIVE_HEADERS = new Set([
23
24
  "authorization",
@@ -49,26 +50,6 @@ function redactHeaders(headers) {
49
50
  }
50
51
  return filtered;
51
52
  }
52
- const SENSITIVE_QUERY_PARAMS = new Set([
53
- "api_key",
54
- "api-key",
55
- "apikey",
56
- "token",
57
- "access_token",
58
- "access-token",
59
- "auth_token",
60
- "auth-token",
61
- "password",
62
- "passwd",
63
- "secret",
64
- "api_secret",
65
- "api-secret",
66
- "apisecret",
67
- "key",
68
- "session",
69
- "session_id",
70
- "session-id",
71
- ]);
72
53
  function redactQueryParameters(queryParameters) {
73
54
  if (queryParameters == null) {
74
55
  return undefined;
@@ -79,70 +60,6 @@ function redactQueryParameters(queryParameters) {
79
60
  }
80
61
  return redacted;
81
62
  }
82
- function redactUrl(url) {
83
- const protocolIndex = url.indexOf("://");
84
- if (protocolIndex === -1)
85
- return url;
86
- const afterProtocol = protocolIndex + 3;
87
- // Find the first delimiter that marks the end of the authority section
88
- const pathStart = url.indexOf("/", afterProtocol);
89
- let queryStart = url.indexOf("?", afterProtocol);
90
- let fragmentStart = url.indexOf("#", afterProtocol);
91
- const firstDelimiter = Math.min(pathStart === -1 ? url.length : pathStart, queryStart === -1 ? url.length : queryStart, fragmentStart === -1 ? url.length : fragmentStart);
92
- // Find the LAST @ before the delimiter (handles multiple @ in credentials)
93
- let atIndex = -1;
94
- for (let i = afterProtocol; i < firstDelimiter; i++) {
95
- if (url[i] === "@") {
96
- atIndex = i;
97
- }
98
- }
99
- if (atIndex !== -1) {
100
- url = `${url.slice(0, afterProtocol)}[REDACTED]@${url.slice(atIndex + 1)}`;
101
- }
102
- // Recalculate queryStart since url might have changed
103
- queryStart = url.indexOf("?");
104
- if (queryStart === -1)
105
- return url;
106
- fragmentStart = url.indexOf("#", queryStart);
107
- const queryEnd = fragmentStart !== -1 ? fragmentStart : url.length;
108
- const queryString = url.slice(queryStart + 1, queryEnd);
109
- if (queryString.length === 0)
110
- return url;
111
- // FAST PATH: Quick check if any sensitive keywords present
112
- // Using indexOf is faster than regex for simple substring matching
113
- const lower = queryString.toLowerCase();
114
- const hasSensitive = lower.includes("token") ||
115
- lower.includes("key") ||
116
- lower.includes("password") ||
117
- lower.includes("passwd") ||
118
- lower.includes("secret") ||
119
- lower.includes("session") ||
120
- lower.includes("auth");
121
- if (!hasSensitive) {
122
- return url;
123
- }
124
- // SLOW PATH: Parse and redact
125
- const redactedParams = [];
126
- const params = queryString.split("&");
127
- for (const param of params) {
128
- const equalIndex = param.indexOf("=");
129
- if (equalIndex === -1) {
130
- redactedParams.push(param);
131
- continue;
132
- }
133
- const key = param.slice(0, equalIndex);
134
- let shouldRedact = SENSITIVE_QUERY_PARAMS.has(key.toLowerCase());
135
- if (!shouldRedact && key.includes("%")) {
136
- try {
137
- const decodedKey = decodeURIComponent(key);
138
- shouldRedact = SENSITIVE_QUERY_PARAMS.has(decodedKey.toLowerCase());
139
- }
140
- catch (_a) { }
141
- }
142
- redactedParams.push(shouldRedact ? `${key}=[REDACTED]` : param);
143
- }
144
- return url.slice(0, queryStart + 1) + redactedParams.join("&") + url.slice(queryEnd);
145
- }
146
63
  function getHeaders(args) {
147
64
  return __awaiter(this, void 0, void 0, function* () {
148
65
  var _a;
@@ -12,6 +12,7 @@ import { join } from "../url/join.mjs";
12
12
  import { EndpointSupplier } from "./EndpointSupplier.mjs";
13
13
  import { getFetchFn } from "./getFetchFn.mjs";
14
14
  import { makeRequest } from "./makeRequest.mjs";
15
+ import { redactUrl } from "./redactUrl.mjs";
15
16
  import { requestWithRetries } from "./requestWithRetries.mjs";
16
17
  import { Supplier } from "./Supplier.mjs";
17
18
  /**
@@ -76,8 +77,10 @@ export function makePassthroughRequest(input, init, clientOptions, requestOption
76
77
  }
77
78
  }
78
79
  }
79
- // Apply auth headers
80
- if (clientOptions.getAuthHeaders != null) {
80
+ // Apply auth headers, but only when the resolved URL targets the configured base URL.
81
+ // This prevents the SDK's credentials from leaking to an unrelated host when a caller
82
+ // passes an absolute cross-origin URL into the passthrough fetch escape hatch.
83
+ if (clientOptions.getAuthHeaders != null && targetsBaseUrl(fullUrl, baseUrl)) {
81
84
  const authHeaders = yield clientOptions.getAuthHeaders();
82
85
  for (const [key, value] of Object.entries(authHeaders)) {
83
86
  mergedHeaders[key.toLowerCase()] = value;
@@ -112,7 +115,7 @@ export function makePassthroughRequest(input, init, clientOptions, requestOption
112
115
  if (logger.isDebug()) {
113
116
  logger.debug("Making passthrough HTTP request", {
114
117
  method,
115
- url: fullUrl,
118
+ url: redactUrl(fullUrl),
116
119
  hasBody: body != null,
117
120
  });
118
121
  }
@@ -123,10 +126,29 @@ export function makePassthroughRequest(input, init, clientOptions, requestOption
123
126
  if (logger.isDebug()) {
124
127
  logger.debug("Passthrough HTTP request completed", {
125
128
  method,
126
- url: fullUrl,
129
+ url: redactUrl(fullUrl),
127
130
  statusCode: response.status,
128
131
  });
129
132
  }
130
133
  return response;
131
134
  });
132
135
  }
136
+ /**
137
+ * Returns true when the resolved request URL points at the same origin as the
138
+ * configured base URL. Relative paths are always joined onto the base URL, so
139
+ * they resolve to the base origin and return true. Absolute URLs only match when
140
+ * their origin equals the base origin. When there is no base URL to compare
141
+ * against, or either value is not a parseable absolute URL, this returns false so
142
+ * auth headers are not attached.
143
+ */
144
+ function targetsBaseUrl(fullUrl, baseUrl) {
145
+ if (baseUrl == null) {
146
+ return false;
147
+ }
148
+ try {
149
+ return new URL(fullUrl).origin === new URL(baseUrl).origin;
150
+ }
151
+ catch (_a) {
152
+ return false;
153
+ }
154
+ }
@@ -0,0 +1,2 @@
1
+ export declare const SENSITIVE_QUERY_PARAMS: Set<string>;
2
+ export declare function redactUrl(url: string): string;
@@ -0,0 +1,84 @@
1
+ export const SENSITIVE_QUERY_PARAMS = new Set([
2
+ "api_key",
3
+ "api-key",
4
+ "apikey",
5
+ "token",
6
+ "access_token",
7
+ "access-token",
8
+ "auth_token",
9
+ "auth-token",
10
+ "password",
11
+ "passwd",
12
+ "secret",
13
+ "api_secret",
14
+ "api-secret",
15
+ "apisecret",
16
+ "key",
17
+ "session",
18
+ "session_id",
19
+ "session-id",
20
+ ]);
21
+ export function redactUrl(url) {
22
+ const protocolIndex = url.indexOf("://");
23
+ if (protocolIndex === -1)
24
+ return url;
25
+ const afterProtocol = protocolIndex + 3;
26
+ // Find the first delimiter that marks the end of the authority section
27
+ const pathStart = url.indexOf("/", afterProtocol);
28
+ let queryStart = url.indexOf("?", afterProtocol);
29
+ let fragmentStart = url.indexOf("#", afterProtocol);
30
+ const firstDelimiter = Math.min(pathStart === -1 ? url.length : pathStart, queryStart === -1 ? url.length : queryStart, fragmentStart === -1 ? url.length : fragmentStart);
31
+ // Find the LAST @ before the delimiter (handles multiple @ in credentials)
32
+ let atIndex = -1;
33
+ for (let i = afterProtocol; i < firstDelimiter; i++) {
34
+ if (url[i] === "@") {
35
+ atIndex = i;
36
+ }
37
+ }
38
+ if (atIndex !== -1) {
39
+ url = `${url.slice(0, afterProtocol)}[REDACTED]@${url.slice(atIndex + 1)}`;
40
+ }
41
+ // Recalculate queryStart since url might have changed
42
+ queryStart = url.indexOf("?");
43
+ if (queryStart === -1)
44
+ return url;
45
+ fragmentStart = url.indexOf("#", queryStart);
46
+ const queryEnd = fragmentStart !== -1 ? fragmentStart : url.length;
47
+ const queryString = url.slice(queryStart + 1, queryEnd);
48
+ if (queryString.length === 0)
49
+ return url;
50
+ // FAST PATH: Quick check if any sensitive keywords present
51
+ // Using indexOf is faster than regex for simple substring matching
52
+ const lower = queryString.toLowerCase();
53
+ const hasSensitive = lower.includes("token") ||
54
+ lower.includes("key") ||
55
+ lower.includes("password") ||
56
+ lower.includes("passwd") ||
57
+ lower.includes("secret") ||
58
+ lower.includes("session") ||
59
+ lower.includes("auth");
60
+ if (!hasSensitive) {
61
+ return url;
62
+ }
63
+ // SLOW PATH: Parse and redact
64
+ const redactedParams = [];
65
+ const params = queryString.split("&");
66
+ for (const param of params) {
67
+ const equalIndex = param.indexOf("=");
68
+ if (equalIndex === -1) {
69
+ redactedParams.push(param);
70
+ continue;
71
+ }
72
+ const key = param.slice(0, equalIndex);
73
+ let shouldRedact = SENSITIVE_QUERY_PARAMS.has(key.toLowerCase());
74
+ if (!shouldRedact && key.includes("%")) {
75
+ try {
76
+ const decodedKey = decodeURIComponent(key);
77
+ shouldRedact = SENSITIVE_QUERY_PARAMS.has(decodedKey.toLowerCase());
78
+ }
79
+ catch (_a) { }
80
+ }
81
+ redactedParams.push(shouldRedact ? `${key}=[REDACTED]` : param);
82
+ }
83
+ return url.slice(0, queryStart + 1) + redactedParams.join("&") + url.slice(queryEnd);
84
+ }
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "1.6.4";
1
+ export declare const SDK_VERSION = "1.6.5";
@@ -1 +1 @@
1
- export const SDK_VERSION = "1.6.4";
1
+ export const SDK_VERSION = "1.6.5";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalpay/sdk",
3
- "version": "1.6.4",
3
+ "version": "1.6.5",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",