diff --git a/CHANGELOG.md b/CHANGELOG.md index c7d2431db0..a8193a6b58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.29.1] - 2022-01-17 +### Fixed +- Path traversal vulnerability + ## [2.29.0] - 2022-01-07 ### Added - CSS variables for plugins ([#1910](https://github.com/scm-manager/scm-manager/pull/1910)) @@ -900,3 +904,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [2.27.4]: https://scm-manager.org/download/2.27.4 [2.28.0]: https://scm-manager.org/download/2.28.0 [2.29.0]: https://scm-manager.org/download/2.29.0 +[2.29.1]: https://scm-manager.org/download/2.29.1 + diff --git a/gradle.properties b/gradle.properties index 39de41d92e..aa4c973c2c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,5 +22,5 @@ # SOFTWARE. # group = sonia.scm -version = 2.29.1-SNAPSHOT +version = 2.29.2-SNAPSHOT org.gradle.jvmargs=-Xmx1024M diff --git a/lerna.json b/lerna.json index e032a8590e..700a2bf407 100644 --- a/lerna.json +++ b/lerna.json @@ -5,5 +5,5 @@ ], "npmClient": "yarn", "useWorkspaces": true, - "version": "2.29.1-SNAPSHOT" + "version": "2.29.2-SNAPSHOT" } diff --git a/scm-plugins/scm-git-plugin/package.json b/scm-plugins/scm-git-plugin/package.json index fcf75f0c00..f35868cde3 100644 --- a/scm-plugins/scm-git-plugin/package.json +++ b/scm-plugins/scm-git-plugin/package.json @@ -1,7 +1,7 @@ { "name": "@scm-manager/scm-git-plugin", "private": true, - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "license": "MIT", "main": "./src/main/js/index.ts", "scripts": { @@ -11,7 +11,7 @@ "typecheck": "tsc" }, "dependencies": { - "@scm-manager/ui-plugins": "^2.29.1-SNAPSHOT" + "@scm-manager/ui-plugins": "^2.29.2-SNAPSHOT" }, "devDependencies": { "@scm-manager/babel-preset": "^2.12.0", diff --git a/scm-plugins/scm-hg-plugin/package.json b/scm-plugins/scm-hg-plugin/package.json index a57f3b8c2c..768d447d11 100644 --- a/scm-plugins/scm-hg-plugin/package.json +++ b/scm-plugins/scm-hg-plugin/package.json @@ -1,7 +1,7 @@ { "name": "@scm-manager/scm-hg-plugin", "private": true, - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "license": "MIT", "main": "./src/main/js/index.ts", "scripts": { @@ -10,7 +10,7 @@ "typecheck": "tsc" }, "dependencies": { - "@scm-manager/ui-plugins": "^2.29.1-SNAPSHOT" + "@scm-manager/ui-plugins": "^2.29.2-SNAPSHOT" }, "devDependencies": { "@scm-manager/babel-preset": "^2.12.0", diff --git a/scm-plugins/scm-legacy-plugin/package.json b/scm-plugins/scm-legacy-plugin/package.json index 3225c2ebd1..c98255d91a 100644 --- a/scm-plugins/scm-legacy-plugin/package.json +++ b/scm-plugins/scm-legacy-plugin/package.json @@ -1,7 +1,7 @@ { "name": "@scm-manager/scm-legacy-plugin", "private": true, - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "license": "MIT", "main": "./src/main/js/index.tsx", "scripts": { @@ -10,7 +10,7 @@ "typecheck": "tsc" }, "dependencies": { - "@scm-manager/ui-plugins": "^2.29.1-SNAPSHOT" + "@scm-manager/ui-plugins": "^2.29.2-SNAPSHOT" }, "devDependencies": { "@scm-manager/babel-preset": "^2.12.0", diff --git a/scm-plugins/scm-svn-plugin/package.json b/scm-plugins/scm-svn-plugin/package.json index 5b21c230b3..600a7711a7 100644 --- a/scm-plugins/scm-svn-plugin/package.json +++ b/scm-plugins/scm-svn-plugin/package.json @@ -1,7 +1,7 @@ { "name": "@scm-manager/scm-svn-plugin", "private": true, - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "license": "MIT", "main": "./src/main/js/index.ts", "scripts": { @@ -10,7 +10,7 @@ "typecheck": "tsc" }, "dependencies": { - "@scm-manager/ui-plugins": "^2.29.1-SNAPSHOT" + "@scm-manager/ui-plugins": "^2.29.2-SNAPSHOT" }, "devDependencies": { "@scm-manager/babel-preset": "^2.12.0", diff --git a/scm-ui/e2e-tests/package.json b/scm-ui/e2e-tests/package.json index 59b12b3296..d6e9e3758a 100644 --- a/scm-ui/e2e-tests/package.json +++ b/scm-ui/e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "@scm-manager/e2e-tests", - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "description": "End to end Tests for SCM-Manager", "main": "index.js", "author": "Eduard Heimbuch ", diff --git a/scm-ui/ui-api/package.json b/scm-ui/ui-api/package.json index ad18de740e..7c2302d407 100644 --- a/scm-ui/ui-api/package.json +++ b/scm-ui/ui-api/package.json @@ -1,6 +1,6 @@ { "name": "@scm-manager/ui-api", - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "description": "React hook api for the SCM-Manager backend", "main": "src/index.ts", "files": [ @@ -25,7 +25,7 @@ "react-test-renderer": "^17.0.1" }, "dependencies": { - "@scm-manager/ui-types": "^2.29.1-SNAPSHOT", + "@scm-manager/ui-types": "^2.29.2-SNAPSHOT", "fetch-mock-jest": "^1.5.1", "gitdiff-parser": "^0.1.2", "query-string": "6.14.1", diff --git a/scm-ui/ui-components/package.json b/scm-ui/ui-components/package.json index 2c3a61c74d..c3e96f5f2a 100644 --- a/scm-ui/ui-components/package.json +++ b/scm-ui/ui-components/package.json @@ -1,6 +1,6 @@ { "name": "@scm-manager/ui-components", - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "description": "UI Components for SCM-Manager and its plugins", "main": "src/index.ts", "files": [ @@ -25,7 +25,7 @@ "@scm-manager/jest-preset": "^2.13.0", "@scm-manager/prettier-config": "^2.10.1", "@scm-manager/tsconfig": "^2.12.0", - "@scm-manager/ui-tests": "^2.29.1-SNAPSHOT", + "@scm-manager/ui-tests": "^2.29.2-SNAPSHOT", "@storybook/addon-actions": "^6.3.12", "@storybook/addon-storyshots": "^6.3.12", "@storybook/builder-webpack5": "^6.3.12", @@ -65,9 +65,9 @@ "worker-plugin": "^3.2.0" }, "dependencies": { - "@scm-manager/ui-api": "^2.29.1-SNAPSHOT", - "@scm-manager/ui-extensions": "^2.29.1-SNAPSHOT", - "@scm-manager/ui-types": "^2.29.1-SNAPSHOT", + "@scm-manager/ui-api": "^2.29.2-SNAPSHOT", + "@scm-manager/ui-extensions": "^2.29.2-SNAPSHOT", + "@scm-manager/ui-types": "^2.29.2-SNAPSHOT", "classnames": "^2.2.6", "date-fns": "^2.4.1", "deepmerge": "^4.2.2", diff --git a/scm-ui/ui-extensions/package.json b/scm-ui/ui-extensions/package.json index ce80e3c0e5..1c5a03b5e2 100644 --- a/scm-ui/ui-extensions/package.json +++ b/scm-ui/ui-extensions/package.json @@ -1,6 +1,6 @@ { "name": "@scm-manager/ui-extensions", - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "main": "src/index.ts", "license": "MIT", "private": false, @@ -10,7 +10,7 @@ "test": "jest" }, "dependencies": { - "@scm-manager/ui-types": "^2.29.1-SNAPSHOT", + "@scm-manager/ui-types": "^2.29.2-SNAPSHOT", "react": "^17.0.1" }, "devDependencies": { diff --git a/scm-ui/ui-plugins/package.json b/scm-ui/ui-plugins/package.json index 9b01886963..84169965f3 100644 --- a/scm-ui/ui-plugins/package.json +++ b/scm-ui/ui-plugins/package.json @@ -1,13 +1,13 @@ { "name": "@scm-manager/ui-plugins", - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "license": "MIT", "bin": { "ui-plugins": "./bin/ui-plugins.js" }, "dependencies": { - "@scm-manager/ui-components": "^2.29.1-SNAPSHOT", - "@scm-manager/ui-extensions": "^2.29.1-SNAPSHOT", + "@scm-manager/ui-components": "^2.29.2-SNAPSHOT", + "@scm-manager/ui-extensions": "^2.29.2-SNAPSHOT", "classnames": "^2.2.6", "query-string": "6.14.1", "react": "^17.0.1", @@ -25,9 +25,9 @@ "@scm-manager/plugin-scripts": "^1.2.2", "@scm-manager/prettier-config": "^2.10.1", "@scm-manager/tsconfig": "^2.12.0", - "@scm-manager/ui-scripts": "^2.29.1-SNAPSHOT", - "@scm-manager/ui-tests": "^2.29.1-SNAPSHOT", - "@scm-manager/ui-types": "^2.29.1-SNAPSHOT", + "@scm-manager/ui-scripts": "^2.29.2-SNAPSHOT", + "@scm-manager/ui-tests": "^2.29.2-SNAPSHOT", + "@scm-manager/ui-types": "^2.29.2-SNAPSHOT", "@types/classnames": "^2.2.9", "@types/enzyme": "^3.10.3", "@types/fetch-mock": "^7.3.1", diff --git a/scm-ui/ui-polyfill/package.json b/scm-ui/ui-polyfill/package.json index 8c6cc32f51..1e6f619ad7 100644 --- a/scm-ui/ui-polyfill/package.json +++ b/scm-ui/ui-polyfill/package.json @@ -1,6 +1,6 @@ { "name": "@scm-manager/ui-polyfill", - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "description": "Polyfills for SCM-Manager UI", "main": "src/index.js", "author": "Sebastian Sdorra ", diff --git a/scm-ui/ui-scripts/package.json b/scm-ui/ui-scripts/package.json index bab7913438..d15650bf2d 100644 --- a/scm-ui/ui-scripts/package.json +++ b/scm-ui/ui-scripts/package.json @@ -1,6 +1,6 @@ { "name": "@scm-manager/ui-scripts", - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "description": "Build scripts for SCM-Manager", "main": "src/index.js", "author": "Sebastian Sdorra ", diff --git a/scm-ui/ui-styles/package.json b/scm-ui/ui-styles/package.json index 95f3015aff..6aba4dc901 100644 --- a/scm-ui/ui-styles/package.json +++ b/scm-ui/ui-styles/package.json @@ -1,6 +1,6 @@ { "name": "@scm-manager/ui-styles", - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "description": "Styles for SCM-Manager", "main": "src/scm.scss", "license": "MIT", diff --git a/scm-ui/ui-tests/package.json b/scm-ui/ui-tests/package.json index da2f95afe6..ebd1cb2c73 100644 --- a/scm-ui/ui-tests/package.json +++ b/scm-ui/ui-tests/package.json @@ -1,6 +1,6 @@ { "name": "@scm-manager/ui-tests", - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "description": "UI-Tests helpers", "author": "Sebastian Sdorra ", "license": "MIT", diff --git a/scm-ui/ui-types/package.json b/scm-ui/ui-types/package.json index 8e0b65b81f..583edd66b8 100644 --- a/scm-ui/ui-types/package.json +++ b/scm-ui/ui-types/package.json @@ -1,6 +1,6 @@ { "name": "@scm-manager/ui-types", - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "description": "Typescript types for SCM-Manager related Objects", "main": "src/index.ts", "files": [ diff --git a/scm-ui/ui-webapp/package.json b/scm-ui/ui-webapp/package.json index f6938f47df..df8f391742 100644 --- a/scm-ui/ui-webapp/package.json +++ b/scm-ui/ui-webapp/package.json @@ -1,11 +1,11 @@ { "name": "@scm-manager/ui-webapp", - "version": "2.29.1-SNAPSHOT", + "version": "2.29.2-SNAPSHOT", "private": true, "dependencies": { - "@scm-manager/ui-api": "^2.29.1-SNAPSHOT", - "@scm-manager/ui-components": "^2.29.1-SNAPSHOT", - "@scm-manager/ui-extensions": "^2.29.1-SNAPSHOT", + "@scm-manager/ui-api": "^2.29.2-SNAPSHOT", + "@scm-manager/ui-components": "^2.29.2-SNAPSHOT", + "@scm-manager/ui-extensions": "^2.29.2-SNAPSHOT", "classnames": "^2.2.5", "history": "^4.10.1", "i18next": "^19.6.0", @@ -30,7 +30,7 @@ }, "devDependencies": { "@scm-manager/jest-preset": "^2.13.0", - "@scm-manager/ui-tests": "^2.29.1-SNAPSHOT", + "@scm-manager/ui-tests": "^2.29.2-SNAPSHOT", "@types/classnames": "^2.2.9", "@types/enzyme": "^3.10.3", "@types/fetch-mock": "^7.3.1", diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PathWebResourceLoader.java b/scm-webapp/src/main/java/sonia/scm/plugin/PathWebResourceLoader.java index 8dbd29ef03..fd8889f5b7 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PathWebResourceLoader.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PathWebResourceLoader.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.plugin; import org.slf4j.Logger; @@ -38,37 +38,32 @@ import java.nio.file.Path; * @author Sebastian Sdorra * @since 2.0.0 */ -public class PathWebResourceLoader implements WebResourceLoader -{ +public class PathWebResourceLoader implements WebResourceLoader { private static final String SEPARATOR = "/"; + private final Path directory; + /** * the logger for PathWebResourceLoader */ - private static final Logger LOG = - LoggerFactory.getLogger(PathWebResourceLoader.class); + private static final Logger LOG = LoggerFactory.getLogger(PathWebResourceLoader.class); - public PathWebResourceLoader(Path directory) - { - this.directory = directory; + public PathWebResourceLoader(Path directory) { + this.directory = directory.toAbsolutePath().normalize(); } @Override public URL getResource(String path) { URL resource = null; - Path file = directory.resolve(filePath(path)); + Path file = directory.resolve(filePath(path)).toAbsolutePath().normalize(); - if (Files.exists(file) && ! Files.isDirectory(file)) - { + if (isValidPath(file)) { LOG.trace("found path {} at {}", path, file); - try - { + try { resource = file.toUri().toURL(); - } - catch (MalformedURLException ex) - { + } catch (MalformedURLException ex) { LOG.error("could not transform path to url", ex); } } else { @@ -78,6 +73,12 @@ public class PathWebResourceLoader implements WebResourceLoader return resource; } + private boolean isValidPath(Path file) { + return Files.exists(file) + && !Files.isDirectory(file) + && file.startsWith(directory); + } + private String filePath(String path) { if (path.startsWith(SEPARATOR)) { return path.substring(1); @@ -85,8 +86,4 @@ public class PathWebResourceLoader implements WebResourceLoader return path; } - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private final Path directory; } diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PathWebResourceLoaderTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PathWebResourceLoaderTest.java index ce9ca8d497..b8075bd2f5 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PathWebResourceLoaderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PathWebResourceLoaderTest.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- @@ -42,11 +42,10 @@ import java.net.URL; * * @author Sebastian Sdorra */ -public class PathWebResourceLoaderTest extends WebResourceLoaderTestBase -{ +public class PathWebResourceLoaderTest extends WebResourceLoaderTestBase { @Test - public void testGetNullForDirectories() throws IOException { + public void shouldReturnNullForDirectories() throws IOException { File directory = temp.newFolder(); assertTrue(new File(directory, "awesome").mkdir()); @@ -56,7 +55,7 @@ public class PathWebResourceLoaderTest extends WebResourceLoaderTestBase @Test - public void testGetResource() throws IOException { + public void shouldReturnResource() throws IOException { File directory = temp.newFolder(); URL url = file(directory, "myresource").toURI().toURL(); @@ -68,4 +67,36 @@ public class PathWebResourceLoaderTest extends WebResourceLoaderTestBase assertNull(resourceLoader.getResource("other")); } + @Test + public void shouldNotReturnPathsWithAbsolutePath() throws IOException { + File base = temp.newFolder(); + + File one = new File(base, "one"); + assertTrue(one.mkdirs()); + File two = new File(base, "two"); + assertTrue(two.mkdirs()); + + File secret = new File(two, "secret"); + assertTrue(secret.createNewFile()); + + WebResourceLoader resourceLoader = new PathWebResourceLoader(one.toPath()); + assertNull(resourceLoader.getResource(secret.getAbsolutePath())); + assertNull(resourceLoader.getResource("/" + secret.getAbsolutePath())); + } + + @Test + public void shouldNotReturnPathsWithPathTraversal() throws IOException { + File base = temp.newFolder(); + + File one = new File(base, "one"); + assertTrue(one.mkdirs()); + File two = new File(base, "two"); + assertTrue(two.mkdirs()); + + File secret = new File(two, "secret"); + assertTrue(secret.createNewFile()); + + WebResourceLoader resourceLoader = new PathWebResourceLoader(one.toPath()); + assertNull(resourceLoader.getResource("../two/secret")); + } }