View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.util;
20  
21  import java.util.Collections;
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import org.eclipse.aether.artifact.Artifact;
26  import org.eclipse.aether.metadata.Metadata;
27  
28  import static java.util.Objects.requireNonNull;
29  
30  /**
31   * A reusable utility class for file paths.
32   *
33   * @since 2.0.13
34   */
35  public final class PathUtils {
36      private PathUtils() {
37          // hide constructor
38      }
39  
40      private static final Map<String, String> ILLEGAL_PATH_SEGMENT_REPLACEMENTS;
41  
42      static {
43          HashMap<String, String> illegalPathSegmentReplacements = new HashMap<>();
44          illegalPathSegmentReplacements.put("\\", "-BACKSLASH-");
45          illegalPathSegmentReplacements.put("/", "-SLASH-");
46          illegalPathSegmentReplacements.put(":", "-COLON-");
47          illegalPathSegmentReplacements.put("\"", "-QUOTE-");
48          illegalPathSegmentReplacements.put("<", "-LT-");
49          illegalPathSegmentReplacements.put(">", "-GT-");
50          illegalPathSegmentReplacements.put("|", "-PIPE-");
51          illegalPathSegmentReplacements.put("?", "-QMARK-");
52          illegalPathSegmentReplacements.put("*", "-ASTERISK-");
53          ILLEGAL_PATH_SEGMENT_REPLACEMENTS = Collections.unmodifiableMap(illegalPathSegmentReplacements);
54      }
55  
56      /**
57       * Method that makes sure that passed in string is valid "path segment" string. It achieves it by potentially
58       * changing it, replacing illegal characters in it with legal ones.
59       * <p>
60       * Note: this method considers empty string as "valid path segment", it is caller duty to ensure empty string
61       * is not used as path segment alone.
62       * <p>
63       * This method is simplistic on purpose, and if frequently used, best if results are cached (per session)
64       */
65      public static String stringToPathSegment(String string) {
66          requireNonNull(string);
67          StringBuilder result = new StringBuilder(string);
68          for (Map.Entry<String, String> entry : ILLEGAL_PATH_SEGMENT_REPLACEMENTS.entrySet()) {
69              String illegal = entry.getKey();
70              int pos = result.indexOf(illegal);
71              while (pos >= 0) {
72                  result.replace(pos, pos + illegal.length(), entry.getValue());
73                  pos = result.indexOf(illegal);
74              }
75          }
76          return result.toString();
77      }
78  
79      /**
80       * Validates that a coordinate component does not contain path traversal sequences
81       * or path separator characters that could cause the composed path to escape
82       * the local repository directory.
83       *
84       * @since 2.0.21
85       */
86      public static void validatePathComponent(String value, String label) {
87          if (value != null && !value.isEmpty()) {
88              // Important: "equals .." and not "contains ..", as if escape attempted, it will contain path separators
89              // OTOH: version "1.." is valid version string!
90              if (value.equals("..") || value.contains("/") || value.contains("\\")) {
91                  throw new IllegalArgumentException(
92                          "Invalid " + label + ": must not contain '..', '/' or '\\': " + value);
93              }
94          }
95      }
96  
97      /**
98       * Validates all coordinate components of an {@link Artifact}.
99       *
100      * @see #validatePathComponent(String, String)
101      * @since 2.0.21
102      */
103     public static void validateArtifactComponents(Artifact artifact) {
104         validatePathComponent(artifact.getGroupId(), "groupId");
105         validatePathComponent(artifact.getArtifactId(), "artifactId");
106         validatePathComponent(artifact.getVersion(), "version");
107         validatePathComponent(artifact.getBaseVersion(), "baseVersion");
108         validatePathComponent(artifact.getClassifier(), "classifier");
109         validatePathComponent(artifact.getExtension(), "extension");
110     }
111 
112     /**
113      * Validates all coordinate components of a {@link Metadata}.
114      *
115      * @see #validatePathComponent(String, String)
116      * @since 2.0.21
117      */
118     public static void validateMetadataComponents(Metadata metadata) {
119         validatePathComponent(metadata.getGroupId(), "groupId");
120         validatePathComponent(metadata.getArtifactId(), "artifactId");
121         validatePathComponent(metadata.getVersion(), "version");
122         // note: type may contain string like ".meta/prefixes.txt"!
123     }
124 }