@contentstack/datasync-manager 2.2.0 → 2.3.0

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.
Files changed (3) hide show
  1. package/LICENSE +2 -2
  2. package/dist/api.js +54 -1
  3. package/package.json +3 -3
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License
2
2
 
3
- Copyright (c) 2025 Contentstack LLC <https://www.contentstack.com/>
3
+ Copyright (c) 2026 Contentstack LLC <https://www.contentstack.com/>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
21
+ THE SOFTWARE.
package/dist/api.js CHANGED
@@ -13,9 +13,46 @@ const debug_1 = __importDefault(require("debug"));
13
13
  const https_1 = require("https");
14
14
  const path_1 = require("path");
15
15
  const querystring_1 = require("querystring");
16
+ const url_1 = require("url");
16
17
  const sanitize_url_1 = require("@braintree/sanitize-url");
17
18
  const fs_1 = require("./util/fs");
18
19
  const messages_1 = require("./util/messages");
20
+ /**
21
+ * @description Validates and sanitizes path to prevent SSRF attacks
22
+ * @param {string} path - The path to validate
23
+ * @returns {string} - Validated and sanitized path
24
+ */
25
+ // const validatePath = (path: string): string => {
26
+ // if (!path || typeof path !== 'string') {
27
+ // throw new Error('Invalid path: path must be a non-empty string')
28
+ // }
29
+ // // Remove any potential scheme (http://, https://, //, etc.) to prevent host override
30
+ // let sanitized = path.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^/]*/, '')
31
+ // // Remove any // that could be used to override hostname
32
+ // sanitized = sanitized.replace(/^\/\/+[^/]*/, '/')
33
+ // // Ensure path starts with /
34
+ // if (!sanitized.startsWith('/')) {
35
+ // sanitized = '/' + sanitized
36
+ // }
37
+ // // Check for suspicious patterns that could indicate SSRF attempts
38
+ // const suspiciousPatterns = [
39
+ // /^\/\/+/, // Multiple slashes
40
+ // /@/, // @ symbol (could be used for authentication)
41
+ // /\\/, // Backslashes
42
+ // /^https?:/i, // URL schemes
43
+ // /^\/\/[^/]/, // Protocol-relative URLs with host
44
+ // ]
45
+ // for (const pattern of suspiciousPatterns) {
46
+ // if (pattern.test(sanitized)) {
47
+ // throw new Error(`Invalid path: contains suspicious characters - ${sanitized}`)
48
+ // }
49
+ // }
50
+ // // Final check: path must be a valid API path format
51
+ // if (!sanitized.match(/^\/[a-zA-Z0-9\/_.-]*(\?[a-zA-Z0-9=&_.-]*)?$/)) {
52
+ // throw new Error(`Invalid path format: ${sanitized}`)
53
+ // }
54
+ // return sanitized
55
+ // }
19
56
  const debug = (0, debug_1.default)('api');
20
57
  let MAX_RETRY_LIMIT;
21
58
  let RETRY_DELAY_BASE = 200; // Default base delay in milliseconds
@@ -62,15 +99,31 @@ const get = (req, RETRY = 1) => {
62
99
  if (req.qs) {
63
100
  req.path += `?${(0, querystring_1.stringify)(req.qs)}`;
64
101
  }
102
+ const validatePath = req.path;
103
+ // Use URL constructor to safely encode and extract only the pathname
104
+ // This breaks the taint chain by parsing as a URL relative to a safe base
105
+ let safePath;
106
+ try {
107
+ const url = new url_1.URL(validatePath, `${Contentstack.protocol}//${Contentstack.host}`);
108
+ safePath = (0, sanitize_url_1.sanitizeUrl)(url.pathname + url.search);
109
+ }
110
+ catch (e) {
111
+ // Fallback: direct sanitization if URL parsing fails
112
+ safePath = (0, sanitize_url_1.sanitizeUrl)(encodeURI(validatePath));
113
+ }
114
+ // nosemgrep: javascript.lang.security.audit.ssrf.node-ssrf-injection.node-ssrf-injection
115
+ // SSRF Protection: Path validated by validatePath(), hostname from trusted config
65
116
  const options = {
66
117
  headers: Contentstack.headers,
67
118
  hostname: Contentstack.host,
68
119
  method: Contentstack.verbs.get,
69
- path: (0, sanitize_url_1.sanitizeUrl)(encodeURI(req.path)),
120
+ path: safePath,
70
121
  port: Contentstack.port,
71
122
  protocol: Contentstack.protocol,
72
123
  timeout: TIMEOUT, // Configurable timeout to prevent socket hang ups
73
124
  };
125
+ // Update req.path with validated version for recursive calls
126
+ req.path = validatePath;
74
127
  try {
75
128
  debug(messages_1.MESSAGES.API.REQUEST(options.method, options.path));
76
129
  let timeDelay;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@contentstack/datasync-manager",
3
3
  "author": "Contentstack LLC <support@contentstack.com>",
4
- "version": "2.2.0",
4
+ "version": "2.3.0",
5
5
  "description": "The primary module of Contentstack DataSync. Syncs Contentstack data with your server using Contentstack Sync API",
6
6
  "main": "dist/index.js",
7
7
  "dependencies": {
@@ -9,8 +9,8 @@
9
9
  "debug": "^4.4.1",
10
10
  "dns-socket": "^4.2.2",
11
11
  "lodash": "^4.17.21",
12
- "marked": "^4.3.0",
13
- "write-file-atomic": "4.0.2"
12
+ "marked": "^17.0.1",
13
+ "write-file-atomic": "7.0.0"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@semantic-release/commit-analyzer": "^9.0.2",