summaryrefslogtreecommitdiffstats
path: root/common-app-api
diff options
context:
space:
mode:
authorvasraz <vasyl.razinkov@est.tech>2022-10-14 13:35:39 +0100
committerMichael Morris <michael.morris@est.tech>2022-10-18 08:27:16 +0000
commitddb9d5a7637b382be9ac7a96ad023a983c41c342 (patch)
tree4e551d6ce4348aed56f42b021bbe4fcfccc3cd15 /common-app-api
parentccab3629426bdc6a87ca6102db3fdb23d4419b3e (diff)
Fix security risk 'Improper Input Validation'
Signed-off-by: Vasyl Razinkov <vasyl.razinkov@est.tech> Change-Id: I6a52148aec3b567db43ec57109214e52d106f73c Issue-ID: SDC-4189
Diffstat (limited to 'common-app-api')
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/be/config/Configuration.java1
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/filters/DataValidatorFilterAbstract.java158
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/filters/RequestWrapper.java107
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/servlets/BasicServlet.java2
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/DataValidator.java57
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/NoHtml.java47
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/NoHtmlValidator.java37
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/SecureString.java36
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/NotAllowedSpecialCharsException.java38
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/fe/config/Configuration.java1
10 files changed, 483 insertions, 1 deletions
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/config/Configuration.java b/common-app-api/src/main/java/org/openecomp/sdc/be/config/Configuration.java
index da849f385c..f2bad701df 100644
--- a/common-app-api/src/main/java/org/openecomp/sdc/be/config/Configuration.java
+++ b/common-app-api/src/main/java/org/openecomp/sdc/be/config/Configuration.java
@@ -147,6 +147,7 @@ public class Configuration extends BasicConfiguration {
private ExternalCsarStore externalCsarStore;
private CsarFormat csarFormat;
private String componentInstanceCounterDelimiter;
+ private String dataValidatorFilterExcludedUrls; // Comma separated list of excluded URLs by the DataValidatorFilter
private String permittedAncestors; // Space separated list of permitted ancestors
@SuppressWarnings("unchecked")
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/filters/DataValidatorFilterAbstract.java b/common-app-api/src/main/java/org/openecomp/sdc/common/filters/DataValidatorFilterAbstract.java
new file mode 100644
index 0000000000..44c0cbb791
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/filters/DataValidatorFilterAbstract.java
@@ -0,0 +1,158 @@
+/*
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2022 Nordix Foundation. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.filters;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.HttpMethod;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.openecomp.sdc.common.util.DataValidator;
+import org.openecomp.sdc.common.util.SecureString;
+import org.openecomp.sdc.exception.NotAllowedSpecialCharsException;
+
+/**
+ * Provides mechanism to filter request according to {@link DataValidator} and {@code dataValidatorFilterExcludedUrlsList}.
+ */
+public abstract class DataValidatorFilterAbstract implements Filter {
+
+ protected static final String DATA_VALIDATOR_FILTER_EXCLUDED_URLS = "dataValidatorFilterExcludedUrls";
+ protected static final String ERROR_SPECIAL_CHARACTERS_NOT_ALLOWED = "Error: HTML elements not permitted in field values.";
+ private DataValidator dataValidator;
+
+ @Override
+ public void init(final FilterConfig filterConfig) throws ServletException {
+ dataValidator = new DataValidator();
+ }
+
+ @Override
+ public void destroy() {
+ dataValidator = null;
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, final ServletResponse response, final FilterChain chain)
+ throws IOException, ServletException {
+ if (isExcluded(((HttpServletRequest) request).getRequestURI()) || !isPostOrPut(((HttpServletRequest) request).getMethod())) {
+ chain.doFilter(request, response);
+ } else {
+ if (!skipCheckBody((HttpServletRequest) request)) {
+ request = new RequestWrapper((HttpServletRequest) request);
+ }
+ if (isValid((HttpServletRequest) request)) {
+ chain.doFilter(request, response);
+ } else {
+ throw new NotAllowedSpecialCharsException();
+ }
+ }
+ }
+
+ private boolean isPostOrPut(final String method) {
+ return method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT);
+ }
+
+ private boolean isExcluded(final String path) {
+ final List<String> dataValidatorFilterExcludedUrlsList = getDataValidatorFilterExcludedUrls();
+ return CollectionUtils.isNotEmpty(dataValidatorFilterExcludedUrlsList)
+ && dataValidatorFilterExcludedUrlsList.stream().anyMatch(s -> path.trim().contains(s.trim()));
+ }
+
+ protected abstract List<String> getDataValidatorFilterExcludedUrls();
+
+ private boolean skipCheckBody(final HttpServletRequest requestWrapper) {
+ final String contentType = requestWrapper.getContentType();
+ return StringUtils.isNotEmpty(contentType) && contentType.contains("multipart/form-data");
+ }
+
+ private boolean isValid(final HttpServletRequest request) {
+ final boolean skipCheckBody = skipCheckBody(request);
+ return (skipCheckBody || checkBody((RequestWrapper) request))
+ && checkHeaders(request)
+ && checkCookies(request)
+ && checkParameters(request)
+ && checkQuery(request);
+ }
+
+ private boolean checkParameters(final HttpServletRequest httpRequest) {
+ final Iterator<String> parameterNamesIterator = httpRequest.getParameterNames().asIterator();
+ while (parameterNamesIterator.hasNext()) {
+ final String parameterName = parameterNamesIterator.next();
+ final String parameter = httpRequest.getParameter(parameterName);
+ if (!dataValidator.isValid(new SecureString(parameter))) {
+ return false;
+ }
+ final String[] parameterValues = httpRequest.getParameterValues(parameterName);
+ if (parameterValues != null) {
+ for (final String parameterValue : parameterValues) {
+ if (!dataValidator.isValid(new SecureString(parameterValue))) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean checkHeaders(final HttpServletRequest httpRequest) {
+ final Iterator<String> headerNamesIterator = httpRequest.getHeaderNames().asIterator();
+ while (headerNamesIterator.hasNext()) {
+ final String headerName = headerNamesIterator.next();
+ final String header = httpRequest.getHeader(headerName);
+ if (!dataValidator.isValid(new SecureString(header))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean checkCookies(final HttpServletRequest httpRequest) {
+ final Cookie[] cookies = httpRequest.getCookies();
+ if (cookies != null) {
+ for (final Cookie cookie : cookies) {
+ if (!dataValidator.isValid(new SecureString(cookie.getValue()))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean checkQuery(final HttpServletRequest httpRequest) {
+ final String queryString = httpRequest.getQueryString();
+ return StringUtils.isEmpty(queryString) || dataValidator.isValid(new SecureString(queryString));
+ }
+
+ private boolean checkBody(final RequestWrapper httpRequest) {
+ final String body = httpRequest.getBody();
+ return StringUtils.isEmpty(body) || dataValidator.isValid(new SecureString(body));
+ }
+
+}
+
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/filters/RequestWrapper.java b/common-app-api/src/main/java/org/openecomp/sdc/common/filters/RequestWrapper.java
new file mode 100644
index 0000000000..79ef42d230
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/filters/RequestWrapper.java
@@ -0,0 +1,107 @@
+/*
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2022 Nordix Foundation. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.filters;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import lombok.Getter;
+import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
+import org.openecomp.sdc.common.log.wrappers.Logger;
+
+/**
+ * Provides mechanism to wrap request's InputStream and read it more than once.
+ */
+public class RequestWrapper extends HttpServletRequestWrapper {
+
+ private static final Logger LOGGER = Logger.getLogger(RequestWrapper.class);
+
+ @Getter
+ private final String body;
+
+ public RequestWrapper(final HttpServletRequest request) throws IOException {
+ //So that other request method behave just like before
+ super(request);
+
+ final StringBuilder stringBuilder = new StringBuilder();
+ try (final InputStream inputStream = request.getInputStream();
+ final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
+ final char[] charBuffer = new char[128];
+ int bytesRead;
+ while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
+ stringBuilder.append(charBuffer, 0, bytesRead);
+ }
+ } catch (IOException ex) {
+ LOGGER.warn(EcompLoggerErrorCode.UNKNOWN_ERROR, RequestWrapper.class.getName(), "Failed to read InputStream from request", ex);
+ throw ex;
+ }
+ //Store request body content in 'body' variable
+ body = stringBuilder.toString();
+ }
+
+ @Override
+ public String getParameter(final String name) {
+ if (body.contains(name) && super.getParameter(name) == null) {
+ final String[] split = body.split("&");
+ return Arrays.stream(split).filter(s -> s.contains(name)).findFirst().get().replace(name + '=', "");
+ } else {
+ return super.getParameter(name);
+ }
+ }
+
+ @Override
+ public ServletInputStream getInputStream() {
+ final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
+ return new ServletInputStream() {
+ @Override
+ public boolean isFinished() {
+ return false;
+ }
+
+ @Override
+ public boolean isReady() {
+ return true;
+ }
+
+ @Override
+ public void setReadListener(final ReadListener readListener) {
+ // Nothing to override
+ }
+
+ public int read() {
+ return byteArrayInputStream.read();
+ }
+ };
+ }
+
+ @Override
+ public BufferedReader getReader() {
+ return new BufferedReader(new InputStreamReader(this.getInputStream()));
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/servlets/BasicServlet.java b/common-app-api/src/main/java/org/openecomp/sdc/common/servlets/BasicServlet.java
index 658f0955b2..baee9464fa 100644
--- a/common-app-api/src/main/java/org/openecomp/sdc/common/servlets/BasicServlet.java
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/servlets/BasicServlet.java
@@ -24,5 +24,5 @@ import com.google.gson.GsonBuilder;
public abstract class BasicServlet {
- protected Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ protected final Gson gson = new GsonBuilder().setPrettyPrinting().create();
}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/DataValidator.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/DataValidator.java
new file mode 100644
index 0000000000..58b4a87997
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/DataValidator.java
@@ -0,0 +1,57 @@
+/*
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2022 Nordix Foundation. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import java.util.Set;
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+/**
+ * Provides mechanism to validate input for Constraint Violations. For more info see {@link ValidatorFactory}, {@link ConstraintViolation},
+ * {@link Validator}.
+ */
+public class DataValidator {
+
+ private final ValidatorFactory validatorFactory;
+
+ public DataValidator() {
+ validatorFactory = Validation.buildDefaultValidatorFactory();
+ }
+
+ private <E> Set<ConstraintViolation<E>> getConstraintViolations(E classToValid) {
+ final Validator validator = validatorFactory.getValidator();
+ return validator.validate(classToValid);
+ }
+
+ /**
+ * Validates input for Constraint Violations
+ *
+ * @param classToValid - class to validate
+ * @param <E>
+ * @return true if input is valid, false if not
+ */
+ public <E> boolean isValid(E classToValid) {
+ return getConstraintViolations(classToValid).isEmpty();
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/NoHtml.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/NoHtml.java
new file mode 100644
index 0000000000..f1e9d6dbfd
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/NoHtml.java
@@ -0,0 +1,47 @@
+/*
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2022 Nordix Foundation. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+/**
+ * Provides mechanism to annotate METHOD and/or FIELD to be validated by {@link NoHtmlValidator}.
+ */
+@Documented
+@Constraint(validatedBy = NoHtmlValidator.class)
+@Target({METHOD, FIELD})
+@Retention(RUNTIME)
+public @interface NoHtml {
+
+ String message() default "Unsafe html content";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/NoHtmlValidator.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/NoHtmlValidator.java
new file mode 100644
index 0000000000..38d4e7d79b
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/NoHtmlValidator.java
@@ -0,0 +1,37 @@
+/*
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2022 Nordix Foundation. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import org.jsoup.Jsoup;
+import org.jsoup.safety.Safelist;
+
+/**
+ * Provides mechanism to check if code annotated with {@link NoHtml} contains no html.
+ */
+public class NoHtmlValidator implements ConstraintValidator<NoHtml, String> {
+
+ @Override
+ public boolean isValid(String value, ConstraintValidatorContext ctx) {
+ return value == null || Jsoup.isValid(value, Safelist.none());
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/SecureString.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/SecureString.java
new file mode 100644
index 0000000000..97afcb547e
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/SecureString.java
@@ -0,0 +1,36 @@
+/*
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2022 Nordix Foundation. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * Provides wrapper for string to be checked by {@link DataValidator}.
+ */
+@Getter
+@AllArgsConstructor
+public class SecureString {
+
+ @NoHtml
+ private String data;
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/NotAllowedSpecialCharsException.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/NotAllowedSpecialCharsException.java
new file mode 100644
index 0000000000..39a59ae7eb
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/NotAllowedSpecialCharsException.java
@@ -0,0 +1,38 @@
+/*
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2022 Nordix Foundation. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+
+import lombok.Getter;
+
+@Getter
+public class NotAllowedSpecialCharsException extends RuntimeException {
+
+ private static final String ERROR_SPECIAL_CHARACTERS_NOT_ALLOWED = "Error: HTML elements not permitted in field values.";
+ private final String errorId;
+ private final String message;
+
+ public NotAllowedSpecialCharsException() {
+ this.errorId = "NOT_PERMITTED_SPECIAL_CHARS";
+ this.message = ERROR_SPECIAL_CHARACTERS_NOT_ALLOWED;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/fe/config/Configuration.java b/common-app-api/src/main/java/org/openecomp/sdc/fe/config/Configuration.java
index 279f183324..4c97a4aa40 100644
--- a/common-app-api/src/main/java/org/openecomp/sdc/fe/config/Configuration.java
+++ b/common-app-api/src/main/java/org/openecomp/sdc/fe/config/Configuration.java
@@ -75,6 +75,7 @@ public class Configuration extends BasicConfiguration {
private List<List<String>> identificationHeaderFields;
private List<List<String>> optionalHeaderFields;
private List<String> forwardHeaderFields;
+ private String dataValidatorFilterExcludedUrls; // Comma separated list of excluded URLs by the DataValidatorFilter
private String permittedAncestors; // Space separated list of permitted ancestors
public Integer getHealthCheckSocketTimeoutInMs(int defaultVal) {