auth0-deploy-cli 9.0.0-beta.1 → 9.0.0-beta.2

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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [9.0.0-beta.2] - 2026-08-11
11
+
12
+ ### Fixed
13
+
14
+ - Enforce path traversal protection for actions, rules, hooks, and databases. [#1448]
15
+
10
16
  ## [9.0.0-beta.1] - 2026-07-02
11
17
 
12
18
  ### Security
@@ -1861,7 +1867,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1861
1867
  [#1411]: https://github.com/auth0/auth0-deploy-cli/issues/1411
1862
1868
  [#1412]: https://github.com/auth0/auth0-deploy-cli/issues/1412
1863
1869
  [#1418]: https://github.com/auth0/auth0-deploy-cli/issues/1418
1864
- [Unreleased]: https://github.com/auth0/auth0-deploy-cli/compare/v9.0.0-beta.1...HEAD
1870
+ [#1448]: https://github.com/auth0/auth0-deploy-cli/issues/1448
1871
+ [Unreleased]: https://github.com/auth0/auth0-deploy-cli/compare/v9.0.0-beta.2...HEAD
1872
+ [9.0.0-beta.2]: https://github.com/auth0/auth0-deploy-cli/compare/v9.0.0-beta.1...v9.0.0-beta.2
1865
1873
  [9.0.0-beta.1]: https://github.com/auth0/auth0-deploy-cli/compare/v8.39.0...v9.0.0-beta.1
1866
1874
  [8.39.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.38.0...v8.39.0
1867
1875
  [8.38.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.37.0...v8.38.0
@@ -20,12 +20,18 @@ function parse(context) {
20
20
  disableKeywordReplacement: context.disableKeywordReplacement,
21
21
  }),
22
22
  };
23
- const moduleFolder = path_1.default.join(tools_1.constants.ACTION_MODULES_DIRECTORY, `${module.name}`);
24
23
  if (module.code) {
25
- // The `module.code` can be a file path. It needs to be loaded.
26
- // It can be a relative path, so we need to handle both cases.
27
- const unixPath = module.code.replace(/[\\/]+/g, '/').replace(/^([a-zA-Z]+:|\.\/)/, '');
28
- module.code = context.loadFile(unixPath, moduleFolder);
24
+ const normalizedCode = module.code.replace(/\\/g, '/');
25
+ const configRoot = path_1.default.resolve(context.filePath);
26
+ const resolvedPath = path_1.default.resolve(context.filePath, normalizedCode);
27
+ if (!resolvedPath.startsWith(configRoot + path_1.default.sep)) {
28
+ throw new Error(`Path "${module.code}" resolves to "${resolvedPath}" which is outside the config directory "${configRoot}". ` +
29
+ `Move the file inside your config directory.`);
30
+ }
31
+ module.code = (0, tools_1.loadFileAndReplaceKeywords)(resolvedPath, {
32
+ mappings: context.mappings,
33
+ disableKeywordReplacement: context.disableKeywordReplacement,
34
+ });
29
35
  }
30
36
  return module;
31
37
  });
@@ -22,11 +22,18 @@ function parse(context) {
22
22
  disableKeywordReplacement: context.disableKeywordReplacement,
23
23
  }),
24
24
  };
25
- const actionFolder = path_1.default.join(tools_1.constants.ACTIONS_DIRECTORY, `${action.name}`);
26
25
  if (action.code) {
27
- // Convert `action.code` path to Unix-style path by replacing backslashes and multiple slashes with a single forward slash, and remove leading drive letters or './'.
28
- const unixPath = action.code.replace(/[\\/]+/g, '/').replace(/^([a-zA-Z]+:|\.\/)/, '');
29
- action.code = context.loadFile(unixPath, actionFolder);
26
+ const normalizedCode = action.code.replace(/\\/g, '/');
27
+ const configRoot = path_1.default.resolve(context.filePath);
28
+ const resolvedPath = path_1.default.resolve(context.filePath, normalizedCode);
29
+ if (!resolvedPath.startsWith(configRoot + path_1.default.sep)) {
30
+ throw new Error(`Path "${action.code}" resolves to "${resolvedPath}" which is outside the config directory "${configRoot}". ` +
31
+ `Move the file inside your config directory.`);
32
+ }
33
+ action.code = (0, tools_1.loadFileAndReplaceKeywords)(resolvedPath, {
34
+ mappings: context.mappings,
35
+ disableKeywordReplacement: context.disableKeywordReplacement,
36
+ });
30
37
  }
31
38
  return action;
32
39
  });
@@ -41,9 +41,10 @@ function getDatabase(folder, configRoot, mappingOpts) {
41
41
  }
42
42
  else {
43
43
  const resolvedBase = path_1.default.resolve(configRoot);
44
- const toLoad = path_1.default.resolve(folder, script);
44
+ const toLoad = path_1.default.resolve(folder, script.replace(/\\/g, '/'));
45
45
  if (!toLoad.startsWith(resolvedBase + path_1.default.sep)) {
46
- throw new Error(`File reference "${script}" in database custom script "${name}" must be relative to the config directory. Absolute paths and paths outside the config root are not supported.`);
46
+ throw new Error(`Path "${script}" resolves to "${toLoad}" which is outside the config directory "${resolvedBase}". ` +
47
+ `Move the file inside your config directory.`);
47
48
  }
48
49
  database.options.customScripts[name] = (0, tools_1.loadFileAndReplaceKeywords)(toLoad, mappingOpts);
49
50
  }
@@ -21,7 +21,17 @@ function parse(context) {
21
21
  }),
22
22
  };
23
23
  if (hook.script) {
24
- hook.script = context.loadFile(hook.script, tools_1.constants.HOOKS_DIRECTORY);
24
+ const normalizedScript = hook.script.replace(/\\/g, '/');
25
+ const configRoot = path_1.default.resolve(context.filePath);
26
+ const resolvedPath = path_1.default.resolve(context.filePath, tools_1.constants.HOOKS_DIRECTORY, normalizedScript);
27
+ if (!resolvedPath.startsWith(configRoot + path_1.default.sep)) {
28
+ throw new Error(`Path "${hook.script}" resolves to "${resolvedPath}" which is outside the config directory "${configRoot}". ` +
29
+ `Move the file inside your config directory.`);
30
+ }
31
+ hook.script = (0, tools_1.loadFileAndReplaceKeywords)(resolvedPath, {
32
+ mappings: context.mappings,
33
+ disableKeywordReplacement: context.disableKeywordReplacement,
34
+ });
25
35
  }
26
36
  hook.name = hook.name.toLowerCase().replace(/\s/g, '-');
27
37
  return hook;
@@ -21,7 +21,17 @@ function parse(context) {
21
21
  }),
22
22
  };
23
23
  if (rule.script) {
24
- rule.script = context.loadFile(rule.script, tools_1.constants.RULES_DIRECTORY);
24
+ const normalizedScript = rule.script.replace(/\\/g, '/');
25
+ const configRoot = path_1.default.resolve(context.filePath);
26
+ const resolvedPath = path_1.default.resolve(context.filePath, tools_1.constants.RULES_DIRECTORY, normalizedScript);
27
+ if (!resolvedPath.startsWith(configRoot + path_1.default.sep)) {
28
+ throw new Error(`Path "${rule.script}" resolves to "${resolvedPath}" which is outside the config directory "${configRoot}". ` +
29
+ `Move the file inside your config directory.`);
30
+ }
31
+ rule.script = (0, tools_1.loadFileAndReplaceKeywords)(resolvedPath, {
32
+ mappings: context.mappings,
33
+ disableKeywordReplacement: context.disableKeywordReplacement,
34
+ });
25
35
  }
26
36
  return rule;
27
37
  });
@@ -68,12 +68,12 @@ class DirectoryContext {
68
68
  };
69
69
  }
70
70
  loadFile(f, folder) {
71
- const resolvedBase = path.resolve(this.filePath);
72
- const inSubfolder = path.resolve(this.filePath, folder, f);
73
- const inRoot = path.resolve(this.filePath, f);
74
- const toLoad = (0, utils_1.isFile)(inSubfolder) ? inSubfolder : inRoot;
75
- if (!toLoad.startsWith(resolvedBase + path.sep)) {
76
- throw new Error(`File reference "${f}" must be relative to the config directory. Absolute paths and paths outside the config root are not supported.`);
71
+ const configRoot = path.resolve(this.filePath);
72
+ const basePath = path.resolve(this.filePath, folder);
73
+ const toLoad = path.resolve(basePath, f.replace(/\\/g, '/'));
74
+ if (!toLoad.startsWith(configRoot + path.sep)) {
75
+ throw new Error(`Path "${f}" resolves to "${toLoad}" which is outside the config directory "${configRoot}". ` +
76
+ `Move the file inside your config directory.`);
77
77
  }
78
78
  return (0, tools_1.loadFileAndReplaceKeywords)(toLoad, {
79
79
  mappings: this.mappings,
@@ -43,10 +43,11 @@ class YAMLContext {
43
43
  })();
44
44
  }
45
45
  loadFile(f) {
46
- const resolvedBase = path_1.default.resolve(this.basePath);
47
- const toLoad = path_1.default.resolve(this.basePath, f);
48
- if (!toLoad.startsWith(resolvedBase + path_1.default.sep)) {
49
- throw new Error(`File reference "${f}" must be relative to the config directory. Absolute paths and paths outside the config root are not supported.`);
46
+ const configRoot = path_1.default.resolve(this.basePath);
47
+ const toLoad = path_1.default.resolve(this.basePath, f.replace(/\\/g, '/'));
48
+ if (!toLoad.startsWith(configRoot + path_1.default.sep)) {
49
+ throw new Error(`Path "${f}" resolves to "${toLoad}" which is outside the config directory "${configRoot}". ` +
50
+ `Move the file inside your config directory.`);
50
51
  }
51
52
  return (0, tools_1.loadFileAndReplaceKeywords)(toLoad, {
52
53
  mappings: this.mappings,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auth0-deploy-cli",
3
- "version": "9.0.0-beta.1",
3
+ "version": "9.0.0-beta.2",
4
4
  "description": "A command line tool for deploying updates to your Auth0 tenant",
5
5
  "main": "lib/index.js",
6
6
  "bin": {