From 1021640a4cb2f8098e7815c93be594f69bbd466a Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 17 Jan 2022 15:33:42 +0100 Subject: [PATCH 1/3] Fix path traversal vulnerability --- .../scm/plugin/PathWebResourceLoader.java | 37 ++++++++--------- .../scm/plugin/PathWebResourceLoaderTest.java | 41 ++++++++++++++++--- 2 files changed, 53 insertions(+), 25 deletions(-) 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")); + } } From 913d3dbf59b55faeb9c6db7369fa578834a7cb5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 18 Jan 2022 09:24:26 +0100 Subject: [PATCH 2/3] Adjust for hotfix releases --- Jenkinsfile | 55 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index a1c0a5dc37..e4321842bc 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -23,7 +23,10 @@ pipeline { stage('Set Version') { when { - branch pattern: 'release/*', comparator: 'GLOB' + anyOf { + branch pattern: 'release/*', comparator: 'GLOB' + branch pattern: 'hotfix/*', comparator: 'GLOB' + } } steps { // read version from branch, set it and commit it @@ -35,10 +38,13 @@ pipeline { sh 'git config --replace-all "remote.origin.fetch" "+refs/heads/*:refs/remotes/origin/*"' sh 'git fetch --all' - // checkout, reset and merge - sh 'git checkout master' - sh 'git reset --hard origin/master' - sh "git merge --ff-only ${env.BRANCH_NAME}" + script { + if (isReleaseBuild()) { + // checkout, reset and merge + sh 'git reset --hard origin/master' + sh "git merge --ff-only ${env.BRANCH_NAME}" + } + } // set tag tag releaseVersion @@ -101,6 +107,7 @@ pipeline { when { anyOf { branch pattern: 'release/*', comparator: 'GLOB' + branch pattern: 'hotfix/*', comparator: 'GLOB' branch 'develop' } expression { return isBuildSuccess() } @@ -131,16 +138,20 @@ pipeline { stage('Push Tag') { when { branch pattern: 'release/*', comparator: 'GLOB' + branch pattern: 'hotfix/*', comparator: 'GLOB' expression { return isBuildSuccess() } } steps { - // push changes back to remote repository - authGit 'cesmarvin-github', 'push origin master --tags' - authGit 'cesmarvin-github', 'push origin --tags' + script { + // push changes back to remote repository + if (isReleaseBuild()) { + authGit 'cesmarvin-github', 'push origin master --tags' + } + authGit 'cesmarvin-github', 'push origin --tags' + } } } - stage('Set Next Version') { when { branch pattern: 'release/*', comparator: 'GLOB' @@ -169,6 +180,20 @@ pipeline { } } + stage('Send Merge Notification') { + when { + branch pattern: 'hotfix/*', comparator: 'GLOB' + expression { return isBuildSuccess() } + } + steps { + mail to: "scm-team@cloudogu.com", + subject: "Jenkins Job ${JOB_NAME} - Merge Hotfix Release #${env.BRANCH_NAME}!", + body: """Please, + - merge the hotfix release branch ${env.BRANCH_NAME} into master (keep versions of master, merge changelog to keep both versions), + - merge master into develop (the changelog should have no conflicts), + - if needed, increase version.""" + } + } } post { @@ -195,8 +220,16 @@ void gradle(String command) { sh "./gradlew -Duser.home=${env.WORKSPACE} ${command}" } +boolean isReleaseBuild() { + return env.BRANCH_NAME.startsWith('release/') +} + String getReleaseVersion() { - return env.BRANCH_NAME.substring("release/".length()); + if (isReleaseBuild()) { + return env.BRANCH_NAME.substring("release/".length()); + } else { + return env.BRANCH_NAME.substring("hotfix/".length()); + } } void commit(String message) { @@ -208,7 +241,7 @@ void tag(String version) { sh "git -c user.name='CES Marvin' -c user.email='cesmarvin@cloudogu.com' tag -m '${message}' ${version}" } -void isBuildSuccess() { +boolean isBuildSuccess() { return currentBuild.result == null || currentBuild.result == 'SUCCESS' } From 5be3f1c9391abb7a424fcc0dac10d7777148fa6e Mon Sep 17 00:00:00 2001 From: CES Marvin Date: Tue, 18 Jan 2022 08:48:22 +0000 Subject: [PATCH 3/3] Release version 2.20.1 --- gradle.properties | 2 +- lerna.json | 2 +- scm-plugins/scm-git-plugin/package.json | 4 ++-- scm-plugins/scm-hg-plugin/package.json | 4 ++-- scm-plugins/scm-legacy-plugin/package.json | 4 ++-- scm-plugins/scm-svn-plugin/package.json | 4 ++-- scm-ui/e2e-tests/package.json | 2 +- scm-ui/ui-api/package.json | 4 ++-- scm-ui/ui-components/package.json | 10 +++++----- scm-ui/ui-extensions/package.json | 4 ++-- scm-ui/ui-plugins/package.json | 12 ++++++------ scm-ui/ui-polyfill/package.json | 2 +- scm-ui/ui-scripts/package.json | 2 +- scm-ui/ui-styles/package.json | 2 +- scm-ui/ui-tests/package.json | 2 +- scm-ui/ui-types/package.json | 2 +- scm-ui/ui-webapp/package.json | 10 +++++----- 17 files changed, 36 insertions(+), 36 deletions(-) diff --git a/gradle.properties b/gradle.properties index 7d42352962..db1126681b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,5 +22,5 @@ # SOFTWARE. # group = sonia.scm -version = 2.20.0 +version = 2.20.1 org.gradle.jvmargs=-Xmx1024M diff --git a/lerna.json b/lerna.json index babfb9b3d2..2087d165e8 100644 --- a/lerna.json +++ b/lerna.json @@ -5,5 +5,5 @@ ], "npmClient": "yarn", "useWorkspaces": true, - "version": "2.20.0" + "version": "2.20.1" } diff --git a/scm-plugins/scm-git-plugin/package.json b/scm-plugins/scm-git-plugin/package.json index d31d2ccca1..5f4f3720a6 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.20.0", + "version": "2.20.1", "license": "MIT", "main": "./src/main/js/index.ts", "scripts": { @@ -11,7 +11,7 @@ "typecheck": "tsc" }, "dependencies": { - "@scm-manager/ui-plugins": "^2.20.0" + "@scm-manager/ui-plugins": "^2.20.1" }, "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 cd65d1774a..aeb528c8a6 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.20.0", + "version": "2.20.1", "license": "MIT", "main": "./src/main/js/index.ts", "scripts": { @@ -10,7 +10,7 @@ "typecheck": "tsc" }, "dependencies": { - "@scm-manager/ui-plugins": "^2.20.0" + "@scm-manager/ui-plugins": "^2.20.1" }, "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 bcf0ccd3fb..0014c6acd9 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.20.0", + "version": "2.20.1", "license": "MIT", "main": "./src/main/js/index.tsx", "scripts": { @@ -10,7 +10,7 @@ "typecheck": "tsc" }, "dependencies": { - "@scm-manager/ui-plugins": "^2.20.0" + "@scm-manager/ui-plugins": "^2.20.1" }, "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 ef974ba970..878bdd2953 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.20.0", + "version": "2.20.1", "license": "MIT", "main": "./src/main/js/index.ts", "scripts": { @@ -10,7 +10,7 @@ "typecheck": "tsc" }, "dependencies": { - "@scm-manager/ui-plugins": "^2.20.0" + "@scm-manager/ui-plugins": "^2.20.1" }, "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 08ee2536ad..f3ca7f7f69 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.20.0", + "version": "2.20.1", "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 0dd8629f31..39eaccfe31 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.20.0", + "version": "2.20.1", "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.20.0", + "@scm-manager/ui-types": "^2.20.1", "fetch-mock-jest": "^1.5.1", "gitdiff-parser": "^0.1.2", "query-string": "5", diff --git a/scm-ui/ui-components/package.json b/scm-ui/ui-components/package.json index 9e8b37b95a..302873b98d 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.20.0", + "version": "2.20.1", "description": "UI Components for SCM-Manager and its plugins", "main": "src/index.ts", "files": [ @@ -25,7 +25,7 @@ "@scm-manager/jest-preset": "^2.12.7", "@scm-manager/prettier-config": "^2.10.1", "@scm-manager/tsconfig": "^2.11.2", - "@scm-manager/ui-tests": "^2.20.0", + "@scm-manager/ui-tests": "^2.20.1", "@storybook/addon-actions": "^6.1.17", "@storybook/addon-storyshots": "^6.1.17", "@storybook/react": "^6.1.17", @@ -59,9 +59,9 @@ "worker-plugin": "^3.2.0" }, "dependencies": { - "@scm-manager/ui-api": "^2.20.0", - "@scm-manager/ui-extensions": "^2.20.0", - "@scm-manager/ui-types": "^2.20.0", + "@scm-manager/ui-api": "^2.20.1", + "@scm-manager/ui-extensions": "^2.20.1", + "@scm-manager/ui-types": "^2.20.1", "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 70aec11483..b9b4098338 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.20.0", + "version": "2.20.1", "main": "src/index.ts", "license": "MIT", "private": false, @@ -10,7 +10,7 @@ "test": "jest" }, "dependencies": { - "@scm-manager/ui-types": "^2.20.0", + "@scm-manager/ui-types": "^2.20.1", "react": "^17.0.1" }, "devDependencies": { diff --git a/scm-ui/ui-plugins/package.json b/scm-ui/ui-plugins/package.json index 6c7042a347..91b7cad465 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.20.0", + "version": "2.20.1", "license": "MIT", "bin": { "ui-plugins": "./bin/ui-plugins.js" }, "dependencies": { - "@scm-manager/ui-components": "^2.20.0", - "@scm-manager/ui-extensions": "^2.20.0", + "@scm-manager/ui-components": "^2.20.1", + "@scm-manager/ui-extensions": "^2.20.1", "classnames": "^2.2.6", "query-string": "^5.0.1", "react": "^17.0.1", @@ -24,9 +24,9 @@ "@scm-manager/jest-preset": "^2.12.7", "@scm-manager/prettier-config": "^2.10.1", "@scm-manager/tsconfig": "^2.11.2", - "@scm-manager/ui-scripts": "^2.20.0", - "@scm-manager/ui-tests": "^2.20.0", - "@scm-manager/ui-types": "^2.20.0", + "@scm-manager/ui-scripts": "^2.20.1", + "@scm-manager/ui-tests": "^2.20.1", + "@scm-manager/ui-types": "^2.20.1", "@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 71e77196f5..f7bd02c689 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.20.0", + "version": "2.20.1", "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 d3484c76b6..d52c416fd2 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.20.0", + "version": "2.20.1", "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 a1df281935..34e058781c 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.20.0", + "version": "2.20.1", "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 a00b3ef2fe..af3f5c5add 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.20.0", + "version": "2.20.1", "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 5eff574815..10ff5a83ff 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.20.0", + "version": "2.20.1", "description": "Flow 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 90ad02ddca..8043d5d0fb 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.20.0", + "version": "2.20.1", "private": true, "dependencies": { - "@scm-manager/ui-api": "^2.20.0", - "@scm-manager/ui-components": "^2.20.0", - "@scm-manager/ui-extensions": "^2.20.0", + "@scm-manager/ui-api": "^2.20.1", + "@scm-manager/ui-components": "^2.20.1", + "@scm-manager/ui-extensions": "^2.20.1", "classnames": "^2.2.5", "history": "^4.10.1", "i18next": "^19.6.0", @@ -29,7 +29,7 @@ }, "devDependencies": { "@scm-manager/jest-preset": "^2.12.7", - "@scm-manager/ui-tests": "^2.20.0", + "@scm-manager/ui-tests": "^2.20.1", "@types/classnames": "^2.2.9", "@types/enzyme": "^3.10.3", "@types/fetch-mock": "^7.3.1",