72316f280459efb12e79e455190026a86495e018
[policy/distribution.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2018 Ericsson. All rights reserved.
4  *  Copyright (C) 2019 Nordix Foundation.
5  *  Modifications Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved.
6  *  Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  * SPDX-License-Identifier: Apache-2.0
21  * ============LICENSE_END=========================================================
22  */
23
24 package org.onap.policy.distribution.reception.decoding.policy.file;
25
26 import java.io.IOException;
27 import java.nio.file.Path;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Enumeration;
31 import java.util.zip.ZipEntry;
32 import java.util.zip.ZipFile;
33 import org.onap.policy.common.parameters.ParameterService;
34 import org.onap.policy.common.utils.coder.CoderException;
35 import org.onap.policy.common.utils.coder.StandardCoder;
36 import org.onap.policy.common.utils.coder.StandardYamlCoder;
37 import org.onap.policy.distribution.model.Csar;
38 import org.onap.policy.distribution.model.PolicyInput;
39 import org.onap.policy.distribution.reception.decoding.PolicyDecoder;
40 import org.onap.policy.distribution.reception.decoding.PolicyDecodingException;
41 import org.onap.policy.models.tosca.authorative.concepts.ToscaEntity;
42 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
43
44 /**
45  * This class extracts policy files from a CSAR file.
46  *
47  * @author Ram Krishna Verma (ram.krishna.verma@ericsson.com)
48  */
49 public class PolicyDecoderFileInCsarToPolicy implements PolicyDecoder<Csar, ToscaEntity> {
50
51     private PolicyDecoderFileInCsarToPolicyParameterGroup decoderParameters;
52     private StandardCoder coder;
53     private StandardYamlCoder yamlCoder;
54     private static final long MAX_FILE_SIZE = 512L * 1024;
55
56     /**
57      * {@inheritDoc}.
58      */
59     @Override
60     public void configure(final String parameterGroupName) {
61         decoderParameters = ParameterService.get(parameterGroupName);
62         coder = new StandardCoder();
63         yamlCoder = new StandardYamlCoder();
64     }
65
66     /**
67      * {@inheritDoc}.
68      */
69     @Override
70     public boolean canHandle(final PolicyInput policyInput) {
71         return policyInput.getClass().isAssignableFrom(Csar.class);
72     }
73
74     /**
75      * {@inheritDoc}.
76      */
77     @Override
78     public Collection<ToscaEntity> decode(final Csar csar) throws PolicyDecodingException {
79         final Collection<ToscaEntity> policyList = new ArrayList<>();
80
81         try (var zipFile = new ZipFile(csar.getCsarFilePath())) {
82             final Enumeration<? extends ZipEntry> entries = zipFile.entries();
83             while (entries.hasMoreElements()) {
84                 //
85                 // Sonar will flag this as a Security Hotspot
86                 // "Expanding archive files is security-sensitive"
87                 // isZipEntryValid ensures the file being read exists in the archive
88                 //
89                 final ZipEntry entry = entries.nextElement(); // NOSONAR
90                 if (isZipEntryValid(entry.getName(), csar.getCsarFilePath(), entry.getSize())) {
91                     final ToscaServiceTemplate policy =
92                             decodeFile(zipFile, entry);
93                     policyList.add(policy);
94                 }
95             }
96         } catch (final IOException | CoderException exp) {
97             throw new PolicyDecodingException("Failed decoding the policy", exp);
98         }
99
100         return policyList;
101     }
102
103     /**
104      * Method to filter out Policy type and Policy files. In addition,
105      * ensures validation of entries in the Zipfile. Attempts to solve path
106      * injection java security issues.
107      *
108      * @param entryName name of the ZipEntry to check
109      * @param csarPath Absolute path to the csar the ZipEntry is in
110      * @param entrySize size of the ZipEntry
111      * @return true if no injection detected, and it is a policy type  or policy file.
112      * @throws PolicyDecodingException if the file size is too large
113      */
114     private boolean isZipEntryValid(String entryName, String csarPath, long entrySize) throws PolicyDecodingException {
115         //
116         // We only care about policy types and policies
117         //
118         if (entryName.contains(decoderParameters.getPolicyTypeFileName())
119                 || entryName.contains(decoderParameters.getPolicyFileName())) {
120             //
121             // Check file size
122             //
123             if (entrySize > MAX_FILE_SIZE) {
124                 throw new PolicyDecodingException("Zip entry for " + entryName + " is too large " + entrySize);
125             }
126             //
127             // Now ensure that there is no path injection
128             //
129             var path = Path.of(csarPath, entryName).normalize();
130             //
131             // Throw an exception if path is outside the csar
132             //
133             if (! path.startsWith(csarPath)) {
134                 throw new PolicyDecodingException("Potential path injection for zip entry " + entryName);
135             }
136             return true;
137         }
138
139         return false;
140     }
141
142     /**
143      * Method to decode either a json or yaml file into an object.
144      *
145      * @param zipFile the zip file
146      * @param entry the entry to read in the zip file.
147      * @return the decoded ToscaServiceTemplate object.
148      * @throws CoderException IOException if the file decoding fails.
149      */
150     private ToscaServiceTemplate decodeFile(ZipFile zipFile, final ZipEntry entry) throws IOException, CoderException {
151         ToscaServiceTemplate policy = null;
152         if (entry.getName().endsWith(".json")) {
153             policy = coder.decode(zipFile.getInputStream(entry), ToscaServiceTemplate.class);
154         } else if (entry.getName().endsWith(".yaml")) {
155             policy = yamlCoder.decode(zipFile.getInputStream(entry), ToscaServiceTemplate.class);
156         }
157         return policy;
158     }
159 }