From: aribeiro Date: Mon, 15 Feb 2021 17:24:11 +0000 (+0000) Subject: Fix Security Vulnerabilities X-Git-Tag: 1.8.4~12 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=7010ea90e14305837a30764db8a5e4bc1338e378;p=sdc.git Fix Security Vulnerabilities Issue-ID: SDC-3500 Signed-off-by: aribeiro Change-Id: I3fa2ed2bc3a170d8256fbc91c98bbfbaf5c0a403 --- diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/LifecycleServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/LifecycleServlet.java index f13621ff10..87c890de1b 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/LifecycleServlet.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/LifecycleServlet.java @@ -33,6 +33,18 @@ import io.swagger.v3.oas.annotations.servers.Server; import io.swagger.v3.oas.annotations.servers.Servers; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tags; +import java.io.IOException; +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; import org.openecomp.sdc.be.components.impl.aaf.AafPermission; import org.openecomp.sdc.be.components.impl.aaf.PermissionAllowed; import org.openecomp.sdc.be.components.lifecycle.LifecycleBusinessLogic; @@ -54,22 +66,10 @@ import org.openecomp.sdc.common.log.elements.LoggerSupportability; import org.openecomp.sdc.common.log.enums.LoggerSupportabilityActions; import org.openecomp.sdc.common.log.enums.StatusCode; import org.openecomp.sdc.common.log.wrappers.Logger; +import org.openecomp.sdc.common.util.ValidationUtils; import org.openecomp.sdc.exception.ResponseFormat; import org.springframework.stereotype.Controller; -import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.io.IOException; - @Loggable(prepend = true, value = Loggable.DEBUG, trim = false) @Path("/v1/catalog") @Tags({@Tag(name = "SDC Internal APIs")}) @@ -101,9 +101,8 @@ public class LifecycleServlet extends BeGenericServlet { @ApiResponse(responseCode = "409", description = "Resource already exist")}) @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) public Response changeResourceState( - @Parameter( - description = "LifecycleChangeInfo - relevant for checkin, failCertification, cancelCertification", - required = false) String jsonChangeInfo, + @Parameter(description = "LifecycleChangeInfo - relevant for checkin, failCertification, cancelCertification") + String jsonChangeInfo, @Parameter(description = "validValues: resources / services / products", schema = @Schema(allowableValues = {ComponentTypeEnum.RESOURCE_PARAM_NAME, ComponentTypeEnum.SERVICE_PARAM_NAME, ComponentTypeEnum.PRODUCT_PARAM_NAME})) @PathParam( @@ -116,7 +115,6 @@ public class LifecycleServlet extends BeGenericServlet { @Context final HttpServletRequest request, @Parameter(description = "id of user initiating the operation") @HeaderParam( value = Constants.USER_ID_HEADER) String userId) throws IOException { - String url = request.getMethod() + " " + request.getRequestURI(); log.debug("Start handle request of {}", url); loggerSupportability.log(LoggerSupportabilityActions.CHANGELIFECYCLESTATE, StatusCode.STARTED,"Starting to change lifecycle state to " + lifecycleTransition + " by user " + userId); @@ -143,7 +141,9 @@ public class LifecycleServlet extends BeGenericServlet { try { if (jsonChangeInfo != null && !jsonChangeInfo.isEmpty()) { ObjectMapper mapper = new ObjectMapper(); - changeInfo = new LifecycleChangeInfoWithAction(mapper.readValue(jsonChangeInfo, LifecycleChangeInfoBase.class).getUserRemarks()); + changeInfo = new LifecycleChangeInfoWithAction(mapper + .readValue(ValidationUtils.sanitizeInputString(jsonChangeInfo), LifecycleChangeInfoBase.class) + .getUserRemarks()); } } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourcesServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourcesServlet.java index 168a70aad2..43fa3786ec 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourcesServlet.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourcesServlet.java @@ -33,6 +33,25 @@ import io.swagger.v3.oas.annotations.servers.Servers; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tags; import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import javax.inject.Inject; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; import org.apache.http.HttpStatus; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; @@ -40,7 +59,6 @@ import org.json.JSONException; import org.json.JSONObject; import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic; import org.openecomp.sdc.be.components.impl.CsarValidationUtils; -import org.openecomp.sdc.be.components.impl.ElementBusinessLogic; import org.openecomp.sdc.be.components.impl.ImportUtils; import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic; import org.openecomp.sdc.be.components.impl.ResourceImportManager; @@ -67,30 +85,11 @@ import org.openecomp.sdc.common.log.elements.LoggerSupportability; import org.openecomp.sdc.common.log.enums.LoggerSupportabilityActions; import org.openecomp.sdc.common.log.enums.StatusCode; import org.openecomp.sdc.common.log.wrappers.Logger; +import org.openecomp.sdc.common.util.ValidationUtils; import org.openecomp.sdc.common.zip.exception.ZipException; import org.openecomp.sdc.exception.ResponseFormat; import org.springframework.stereotype.Controller; -import javax.inject.Inject; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.io.IOException; -import java.util.List; -import java.util.Map; - @Loggable(prepend = true, value = Loggable.DEBUG, trim = false) @Path("/v1/catalog") @Tags({@Tag(name = "SDC Internal APIs")}) @@ -582,12 +581,12 @@ public class ResourcesServlet extends AbstractValidationsServlet { try { Either eitherResource = - resourceBusinessLogic.getLatestResourceFromCsarUuid(csarUUID, user); + resourceBusinessLogic.getLatestResourceFromCsarUuid(ValidationUtils.sanitizeInputString(csarUUID), user); // validate response if (eitherResource.isRight()) { log.debug("failed to get resource from csarUuid : {}", csarUUID); - response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), eitherResource.right().value()); + response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT), eitherResource.right().value()); } else { Object representation = RepresentationUtils.toRepresentation(eitherResource.left().value()); response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), representation); diff --git a/catalog-ui/src/app/models/components/component.ts b/catalog-ui/src/app/models/components/component.ts index 1d48151be8..f787142460 100644 --- a/catalog-ui/src/app/models/components/component.ts +++ b/catalog-ui/src/app/models/components/component.ts @@ -247,7 +247,7 @@ export abstract class Component implements IComponent { let onError = (error:any):void => { deferred.reject(error); }; - this.componentService.changeLifecycleState(this, state, JSON.stringify(commentObj)).then(onSuccess, onError); + this.componentService.changeLifecycleState(this, state, commentObj).then(onSuccess, onError); return deferred.promise; }; diff --git a/catalog-ui/src/app/services/components/component-service.ts b/catalog-ui/src/app/services/components/component-service.ts index f22562f439..47eec26a77 100644 --- a/catalog-ui/src/app/services/components/component-service.ts +++ b/catalog-ui/src/app/services/components/component-service.ts @@ -19,8 +19,25 @@ */ 'use strict'; import * as _ from "lodash"; -import {ArtifactModel, IFileDownload, InstancesInputsPropertiesMap, InputModel, IValidate, RelationshipModel, PropertyModel, Component, ComponentInstance, - AttributeModel, IAppConfigurtaion, Resource, Module, DisplayModule, ArtifactGroupModel, InputsAndProperties} from "app/models"; +import { + ArtifactModel, + IFileDownload, + InstancesInputsPropertiesMap, + InputModel, + IValidate, + RelationshipModel, + PropertyModel, + Component, + ComponentInstance, + AttributeModel, + IAppConfigurtaion, + Resource, + Module, + DisplayModule, + ArtifactGroupModel, + InputsAndProperties, + AsdcComment +} from "app/models"; import {ComponentInstanceFactory, CommonUtils} from "app/utils"; import {SharingService} from "app/services-ng2"; import {ComponentMetadata} from "../../models/component-metadata"; @@ -29,7 +46,7 @@ export interface IComponentService { getComponent(id:string); updateComponent(component:Component):ng.IPromise; - changeLifecycleState(component:Component, state:string, userRemarks:any):ng.IPromise ; + changeLifecycleState(component:Component, state:string, userRemarks:AsdcComment):ng.IPromise ; validateName(newName:string, subtype?:string):ng.IPromise; createComponent(component:Component):ng.IPromise; //importComponent @@ -233,15 +250,28 @@ export class ComponentService implements IComponentService { return deferred.promise; }; - public changeLifecycleState = (component:Component, state:string, userRemarks:any):ng.IPromise => { + public changeLifecycleState = (component:Component, state:string, commentObj:AsdcComment):ng.IPromise => { let deferred = this.$q.defer(); - this.restangular.one(component.uniqueId).one(state).customPOST(userRemarks).then((response:ComponentMetadata) => { - this.sharingService.addUuidValue(response.uniqueId, response.uuid); - let component:ComponentMetadata = new ComponentMetadata().deserialize(response); - deferred.resolve(component); - }, (err)=> { - deferred.reject(err); - }); + let headerObj = {}; + if (commentObj.userRemarks) { + headerObj = this.getHeaderMd5(commentObj); + this.restangular.one(component.uniqueId).one(state).customPOST(JSON.stringify(commentObj), '', {}, headerObj) + .then((response:ComponentMetadata) => { + this.sharingService.addUuidValue(response.uniqueId, response.uuid); + let component:ComponentMetadata = new ComponentMetadata().deserialize(response); + deferred.resolve(component); + }, (err)=> { + deferred.reject(err); + }); + } else { + this.restangular.one(component.uniqueId).one(state).customPOST().then((response:ComponentMetadata) => { + this.sharingService.addUuidValue(response.uniqueId, response.uuid); + let component:ComponentMetadata = new ComponentMetadata().deserialize(response); + deferred.resolve(component); + }, (err)=> { + deferred.reject(err); + }); + } return deferred.promise; }; diff --git a/catalog-ui/src/app/utils/validation-utils.ts b/catalog-ui/src/app/utils/validation-utils.ts index b7e43f79ba..bcb49d8b89 100644 --- a/catalog-ui/src/app/utils/validation-utils.ts +++ b/catalog-ui/src/app/utils/validation-utils.ts @@ -64,7 +64,10 @@ export class ValidationUtils { if (!text) { return null; } - return text.replace(/\s+/g, ' ').replace(/%[A-Fa-f0-9]{2}/g, '').trim(); + return text.replace(/\s+/g, ' ').replace(/%[A-Fa-f0-9]{2}/g, '') + .replace(/&/g, "&").replace(/>/g, ">") + .replace(/ { diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/ValidationUtils.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/ValidationUtils.java index 375f041e81..1a9cb26a0f 100644 --- a/common-app-api/src/main/java/org/openecomp/sdc/common/util/ValidationUtils.java +++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/ValidationUtils.java @@ -585,4 +585,15 @@ public class ValidationUtils { public static boolean validateForwardingPathNamePattern(String forwardingPathName) { return FORWARDING_PATH_NAME_PATTERN.matcher(forwardingPathName).matches(); } + + public static String sanitizeInputString(String input) { + if (StringUtils.isNotEmpty(input)) { + input = ValidationUtils.removeNoneUtf8Chars(input); + input = ValidationUtils.removeHtmlTags(input); + input = ValidationUtils.normaliseWhitespace(input); + input = ValidationUtils.stripOctets(input); + } + return input; + } + } diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java index 073400fd68..b393153ece 100644 --- a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java @@ -1,6 +1,7 @@ /* * Copyright © 2016-2018 European Support Limited * Copyright © 2021 Nokia + * Copyright © 2021 Nordix Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +33,7 @@ import java.util.Optional; import javax.activation.DataHandler; import javax.inject.Named; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import org.apache.commons.lang3.tuple.Pair; import org.apache.cxf.jaxrs.ext.multipart.Attachment; import org.openecomp.sdc.activitylog.ActivityLogManager; @@ -39,6 +41,7 @@ import org.openecomp.sdc.activitylog.ActivityLogManagerFactory; import org.openecomp.sdc.activitylog.dao.type.ActivityLogEntity; import org.openecomp.sdc.activitylog.dao.type.ActivityType; import org.openecomp.sdc.common.errors.Messages; +import org.openecomp.sdc.common.util.ValidationUtils; import org.openecomp.sdc.common.utils.SdcCommon; import org.openecomp.sdc.datatypes.error.ErrorLevel; import org.openecomp.sdc.datatypes.error.ErrorMessage; @@ -100,13 +103,13 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate final Attachment fileToUpload, final String user) { final byte[] fileToUploadBytes = fileToUpload.getObject(byte[].class); final DataHandler dataHandler = fileToUpload.getDataHandler(); - final String filename = dataHandler.getName(); + final String filename = ValidationUtils.sanitizeInputString(dataHandler.getName()); final OnboardingPackageProcessor onboardingPackageProcessor = new OnboardingPackageProcessor(filename, fileToUploadBytes); if (onboardingPackageProcessor.hasErrors()) { final UploadFileResponseDto uploadFileResponseDto = buildUploadResponseWithError(onboardingPackageProcessor.getErrorMessages().toArray(new ErrorMessage[0])); - return Response.ok(uploadFileResponseDto).build(); + return Response.status(Status.NOT_ACCEPTABLE).entity(uploadFileResponseDto).build(); } final OnboardPackageInfo onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null); @@ -117,7 +120,8 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate return Response.ok(uploadFileResponseDto).build(); } - final VspDetails vspDetails = new VspDetails(vspId, new Version(versionId)); + final VspDetails vspDetails = new VspDetails(ValidationUtils.sanitizeInputString(vspId), + new Version(ValidationUtils.sanitizeInputString(versionId))); return processOnboardPackage(onboardPackageInfo, vspDetails); } diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java index dec6342cb8..41891dea74 100644 --- a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java @@ -22,16 +22,21 @@ package org.openecomp.sdcrests.vsp.rest.services; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.MockitoAnnotations.initMocks; import static org.mockito.Mockito.when; import java.io.IOException; +import java.net.URL; import java.util.Arrays; +import java.util.Objects; import java.util.Optional; import java.util.UUID; import javax.activation.DataHandler; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.cxf.jaxrs.ext.multipart.Attachment; import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition; @@ -135,32 +140,46 @@ public class OrchestrationTemplateCandidateImplTest { @Test public void uploadSignedTest() { - Response response = orchestrationTemplateCandidate.upload("1", "1", mockAttachment("filename.zip"), "1"); - assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + Response response = orchestrationTemplateCandidate + .upload("1", "1", mockAttachment("filename.zip", this.getClass().getResource("/files/sample-signed.zip")), + "1"); + assertEquals(Status.OK.getStatusCode(), response.getStatus()); + assertTrue(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty()); } @Test - public void uploadNotSignedTest(){ - Response response = orchestrationTemplateCandidate.upload("1", "1", mockAttachment("filename.csar"), "1"); - assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + public void uploadNotSignedTest() { + Response response = orchestrationTemplateCandidate.upload("1", "1", + mockAttachment("filename.csar", this.getClass().getResource("/files/sample-not-signed.csar")), "1"); + assertEquals(Status.OK.getStatusCode(), response.getStatus()); + assertTrue(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty()); } - private Attachment mockAttachment(final String fileName) { + private Attachment mockAttachment(final String fileName, final URL fileToUpload) { final Attachment attachment = Mockito.mock(Attachment.class); when(attachment.getContentDisposition()).thenReturn(new ContentDisposition("test")); final DataHandler dataHandler = Mockito.mock(DataHandler.class); when(dataHandler.getName()).thenReturn(fileName); when(attachment.getDataHandler()).thenReturn(dataHandler); - final byte[] bytes = "upload package Test".getBytes(); + byte[] bytes = "upload package Test".getBytes(); + if (Objects.nonNull(fileToUpload)) { + try { + bytes = IOUtils.toByteArray(fileToUpload); + } catch (final IOException e) { + logger.error("unexpected exception", e); + Assert.fail("Not able to convert file to byte array"); + } + } when(attachment.getObject(ArgumentMatchers.any())).thenReturn(bytes); return attachment; } @Test public void uploadSignNotValidTest() { - Response response = orchestrationTemplateCandidate.upload("1", "1", mockAttachment("filename.zip"), "1"); - assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); - assertFalse(((UploadFileResponseDto)response.getEntity()).getErrors().isEmpty()); + Response response = orchestrationTemplateCandidate + .upload("1", "1", mockAttachment("filename.zip", null), "1"); + assertEquals(Status.NOT_ACCEPTABLE.getStatusCode(), response.getStatus()); + assertFalse(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty()); } @Test diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/files/sample-not-signed.csar b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/files/sample-not-signed.csar new file mode 100644 index 0000000000..e4e60b26d1 Binary files /dev/null and b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/files/sample-not-signed.csar differ diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/files/sample-signed.zip b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/files/sample-signed.zip new file mode 100644 index 0000000000..fecb45aaaf Binary files /dev/null and b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/files/sample-signed.zip differ