diff --git a/CHANGELOG.md b/CHANGELOG.md index d83518f488..a59bc351ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Fixed +- Upgraded dependencies + ## [2.8.3] - 2018-08-01 ### Fixed diff --git a/build.gradle b/build.gradle index d03dc91c84..0be33a08e8 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,33 @@ import java.util.concurrent.TimeUnit +buildscript { + ext { + springBootVersion = '1.5.15.RELEASE' + } + + repositories { + mavenCentral() + maven { + url "https://plugins.gradle.org/m2/" + } + } + + dependencies { + classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}" + classpath 'org.yaml:snakeyaml:1.21' + } +} + apply plugin: 'java' apply plugin: 'groovy' apply plugin: 'eclipse' apply plugin: 'application' -apply plugin: 'spring-boot' -apply plugin: "jacoco" +apply plugin: 'jacoco' apply plugin: 'findbugs' apply plugin: 'checkstyle' apply plugin: 'project-report' +apply plugin: 'org.springframework.boot' +apply plugin: 'io.spring.dependency-management' group 'org.zalando' sourceCompatibility = 1.8 @@ -19,9 +38,6 @@ springBoot { layout = "ZIP" } -def dockerGroup = "aruha" -def dockerApplicationName = "nakadi" - repositories { mavenCentral() maven { url 'https://jitpack.io' } @@ -45,25 +61,6 @@ sourceSets { } } -buildscript { - ext { - springBootVersion = '1.5.3.RELEASE' - springFrameworkVersion = '4.3.8.RELEASE' - } - - repositories { - mavenCentral() - maven { - url "https://plugins.gradle.org/m2/" - } - } - - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:1.2.5.RELEASE" - classpath 'org.yaml:snakeyaml:1.17' - } -} - jar { baseName = 'nakadi' } @@ -84,55 +81,62 @@ findbugs { dependencies { ext { - dropwizardVersion = '3.1.2' + dropwizardVersion = '3.1.3' curatorVersion = '4.0.1' zookeeperVersion = '3.4.10' } + // Override spring-boot BOM versions + ext['json.version'] = '20180130' + ext['json-path'] = '2.4.0' + ext['jsonassert'] = '1.5.0' + ext['postgresql.version'] = '42.2.2' // spring - compile("org.springframework.boot:spring-boot-starter-web:$springBootVersion") { + compile("org.springframework.boot:spring-boot-starter-web") { exclude module: 'logback-classic' exclude module: 'log4j-over-slf4j' exclude module: 'spring-boot-starter-tomcat' } - compile "org.springframework:spring-context:$springFrameworkVersion" - compile "org.springframework:spring-web:$springFrameworkVersion" - compile "org.springframework:spring-webmvc:$springFrameworkVersion" - compile "org.springframework.boot:spring-boot-test:$springBootVersion" - compile "org.springframework.boot:spring-boot-starter-jetty:$springBootVersion" + compile "org.springframework:spring-context" + compile "org.springframework:spring-web" + compile "org.springframework:spring-webmvc" + compile "org.springframework.boot:spring-boot-starter-jetty" // oauth - compile 'org.springframework.security.oauth:spring-security-oauth2:2.1.0.RELEASE' + compile('org.springframework.security.oauth:spring-security-oauth2') { + exclude module: 'spring-beans' + exclude module: 'spring-core' + exclude module: 'spring-context' + } compile('org.springframework.boot:spring-boot-starter-security') { exclude module: "logback-classic" } // actuator - compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" - compile 'org.zalando.zmon:zmon-actuator:0.9.7' + compile "org.springframework.boot:spring-boot-starter-actuator" + compile 'org.zalando.zmon:zmon-actuator:0.9.8' // storage - compile "org.springframework.boot:spring-boot-starter-jdbc:$springBootVersion" - compile 'org.postgresql:postgresql:42.1.1' + compile "org.springframework.boot:spring-boot-starter-jdbc" + compile 'org.postgresql:postgresql' - compile 'org.springframework.cloud:spring-cloud-starter-hystrix:1.3.0.RELEASE' + compile 'org.springframework.cloud:spring-cloud-starter-hystrix:1.4.5.RELEASE' // misc - compile 'org.apache.httpcomponents:httpclient:4.5.3' - compile('org.zalando.stups:stups-spring-oauth2-server:1.0.19') { + compile 'org.apache.httpcomponents:httpclient' + compile('org.zalando.stups:stups-spring-oauth2-server:1.0.22') { exclude module: "httpclient" } compile 'org.zalando:jackson-datatype-problem:0.5.0' compile 'org.zalando:problem:0.5.0' compile 'org.zalando:problem-spring-web:0.5.0' - compile 'com.google.guava:guava:19.0' - compile 'javax.ws.rs:javax.ws.rs-api:2.0.1' - compile 'org.slf4j:slf4j-log4j12:1.7.25' + compile 'com.google.guava:guava:25.1-jre' + compile 'org.slf4j:slf4j-log4j12' compile "io.dropwizard.metrics:metrics-core:$dropwizardVersion" compile "com.ryantenney.metrics:metrics-spring:$dropwizardVersion" compile "io.dropwizard.metrics:metrics-servlets:$dropwizardVersion" compile "io.dropwizard.metrics:metrics-jvm:$dropwizardVersion" - compile 'org.apache.commons:commons-lang3:3.5' + compile 'org.apache.commons:commons-lang3:3.7' compile 'org.zalando:nakadi-plugin-api:2.0.0' compile 'org.echocat.jomon:runtime:1.6.3' @@ -147,33 +151,38 @@ dependencies { compile "org.apache.zookeeper:zookeeper:$zookeeperVersion" // json - compile('com.github.everit-org.json-schema:org.everit.json.schema:6cb8ae440b0bc34030d7bea85ac609d980d741fb') { + compile('com.github.everit-org.json-schema:org.everit.json.schema:1.8.0') { exclude module: "json" } - compile('com.fasterxml.jackson.datatype:jackson-datatype-json-org:2.8.8') { + compile("com.fasterxml.jackson.datatype:jackson-datatype-json-org") { exclude module: "json" } - compile 'com.fasterxml.jackson.datatype:jackson-datatype-joda:2.8.8' - compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' + compile "com.fasterxml.jackson.core:jackson-annotations" + compile "com.fasterxml.jackson.core:jackson-core" + compile "com.fasterxml.jackson.core:jackson-databind" + compile "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml" + compile "com.fasterxml.jackson.datatype:jackson-datatype-jdk8" + compile "com.fasterxml.jackson.datatype:jackson-datatype-joda" + compile "com.fasterxml.jackson.module:jackson-module-afterburner" compile 'org.zalando:twintip-spring-web:1.1.0' - compile 'org.json:json:20171018' + compile 'org.json:json:20180130' // tests testCompile 'org.hamcrest:hamcrest-all:1.3' testCompile('junit:junit:4.12') { exclude module: "hamcrest-core" } - testCompile "org.springframework:spring-test:$springFrameworkVersion" + testCompile "org.springframework:spring-test" testCompile 'org.springframework.boot:spring-boot-test' testCompile 'org.springframework.boot:spring-boot-starter-test' - testCompile 'org.skyscreamer:jsonassert:1.5.0' + testCompile 'org.skyscreamer:jsonassert' testCompile 'uk.co.datumedge:hamcrest-json:0.2' testCompile 'org.mockito:mockito-all:1.10.19' testCompile('com.jayway.restassured:rest-assured:2.9.0') { exclude module: "hamcrest-core" exclude module: "hamcrest-library" } - testCompile 'com.jayway.jsonpath:json-path:2.2.0' + testCompile 'com.jayway.jsonpath:json-path' testRuntime 'org.pegdown:pegdown:1.6.0' } // end::dependencies[] diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 3baa851b28..91ca28c8b8 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f9147d507d..7dc503f149 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Tue Jun 20 11:33:10 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/gradlew b/gradlew index 27309d9231..cccdd3d517 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f6d5974e72..e95643d6a2 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -49,7 +49,6 @@ goto fail @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/src/main/java/org/zalando/nakadi/config/SecurityConfiguration.java b/src/main/java/org/zalando/nakadi/config/SecurityConfiguration.java index b57de03e68..81b10df382 100644 --- a/src/main/java/org/zalando/nakadi/config/SecurityConfiguration.java +++ b/src/main/java/org/zalando/nakadi/config/SecurityConfiguration.java @@ -4,6 +4,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpOutputMessage; import org.springframework.http.converter.HttpMessageConverter; @@ -19,13 +20,22 @@ import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler; import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +import org.springframework.security.web.firewall.FirewalledRequest; +import org.springframework.security.web.firewall.HttpFirewall; +import org.springframework.security.web.firewall.RequestRejectedException; +import org.springframework.security.web.firewall.StrictHttpFirewall; import org.zalando.stups.oauth2.spring.security.expression.ExtendedOAuth2WebSecurityExpressionHandler; +import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Response; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import static org.springframework.http.HttpMethod.DELETE; import static org.springframework.http.HttpMethod.GET; @@ -189,5 +199,141 @@ public String getDetail() { } } + // TODO: REMOVE IT AFTER EVERYONE HAS NORMALIZED THEIR URLS + @Bean + public HttpFirewall allowUrlEncodedSlashHttpFirewall() { + return new AllowForwardSlashesStrictHttpFirewall(); + } + + // TODO: REMOVE IT AFTER EVERYONE HAS NORMALIZED THEIR URLS + private static class AllowForwardSlashesStrictHttpFirewall extends StrictHttpFirewall { + + private static final String ENCODED_PERCENT = "%25"; + private static final String PERCENT = "%"; + private static final List FORBIDDEN_ENCODED_PERIOD = + Collections.unmodifiableList(Arrays.asList("%2e", "%2E")); + private Set encodedUrlBlacklist = new HashSet<>(); + private Set decodedUrlBlacklist = new HashSet<>(); + + AllowForwardSlashesStrictHttpFirewall() { + super(); + this.encodedUrlBlacklist.add(ENCODED_PERCENT); + this.encodedUrlBlacklist.addAll(FORBIDDEN_ENCODED_PERIOD); + this.decodedUrlBlacklist.add(PERCENT); + } + + private static boolean containsOnlyPrintableAsciiCharacters(final String uri) { + final int length = uri.length(); + for (int i = 0; i < length; i++) { + final char c = uri.charAt(i); + if (c < '\u0020' || c > '\u007e') { + return false; + } + } + + return true; + } + + private static boolean encodedUrlContains(final HttpServletRequest request, final String value) { + if (valueContains(request.getContextPath(), value)) { + return true; + } + return valueContains(request.getRequestURI(), value); + } + + private static boolean decodedUrlContains(final HttpServletRequest request, final String value) { + if (valueContains(request.getServletPath(), value)) { + return true; + } + if (valueContains(request.getPathInfo(), value)) { + return true; + } + return false; + } + + private static boolean valueContains(final String value, final String contains) { + return value != null && value.contains(contains); + } + + @Override + public FirewalledRequest getFirewalledRequest(final HttpServletRequest request) + throws RequestRejectedException { + rejectedBlacklistedUrls(request); + + if (!isNormalized(request)) { + throw new RequestRejectedException("The request was rejected because the URL was not normalized."); + } + + final String requestUri = request.getRequestURI(); + if (!containsOnlyPrintableAsciiCharacters(requestUri)) { + throw new RequestRejectedException("The requestURI was rejected because it can only " + + "contain printable ASCII characters."); + } + return new FirewalledRequest(request) { + @Override + public void reset() { + } + }; + } + + private static boolean isNormalized(final HttpServletRequest request) { + if (!isNormalized(request.getRequestURI())) { + return false; + } + if (!isNormalized(request.getContextPath())) { + return false; + } + if (!isNormalized(request.getServletPath())) { + return false; + } + if (!isNormalized(request.getPathInfo())) { + return false; + } + return true; + } + + private static boolean isNormalized(final String path) { + if (path == null) { + return true; + } + + // ONLY THIS PART IS REMOVED, ALL OTHER CODE IS THE SAME AS IN StrictHttpFirewall + // if (path.indexOf("//") > -1) { + // return false; + // } + + for (int j = path.length(); j > 0;) { + final int i = path.lastIndexOf('/', j - 1); + final int gap = j - i; + + if (gap == 2 && path.charAt(i + 1) == '.') { + // ".", "/./" or "/." + return false; + } else if (gap == 3 && path.charAt(i + 1) == '.' && path.charAt(i + 2) == '.') { + return false; + } + + j = i; + } + + return true; + } + + private void rejectedBlacklistedUrls(final HttpServletRequest request) { + for (final String forbidden : this.encodedUrlBlacklist) { + if (encodedUrlContains(request, forbidden)) { + throw new RequestRejectedException("The request was rejected because the URL contained " + + "a potentially malicious String \"" + forbidden + "\""); + } + } + for (final String forbidden : this.decodedUrlBlacklist) { + if (decodedUrlContains(request, forbidden)) { + throw new RequestRejectedException("The request was rejected because the URL contained " + + "a potentially malicious String \"" + forbidden + "\""); + } + } + } + + } }