View Javadoc
1   /*
2    * Copyright (C) 2018, Google LLC. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.internal.submodule;
11  
12  import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PATH;
13  import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_URL;
14  import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_SUBMODULE_SECTION;
15  import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.GITMODULES_NAME;
16  import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.GITMODULES_PARSE;
17  import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.GITMODULES_PATH;
18  import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.GITMODULES_URL;
19  
20  import java.text.MessageFormat;
21  
22  import org.eclipse.jgit.errors.ConfigInvalidException;
23  import org.eclipse.jgit.internal.JGitText;
24  import org.eclipse.jgit.lib.Config;
25  import org.eclipse.jgit.lib.ObjectChecker;
26  
27  /**
28   * Validations for the git submodule fields (name, path, uri).
29   *
30   * Invalid values in these fields can cause security problems as reported in
31   * CVE-2018-11235 and CVE-2018-17456
32   */
33  public class SubmoduleValidator {
34  
35  	/**
36  	 * Error validating a git submodule declaration
37  	 */
38  	public static class SubmoduleValidationException extends Exception {
39  
40  		private static final long serialVersionUID = 1L;
41  
42  		private final ObjectChecker.ErrorType fsckMessageId;
43  
44  		/**
45  		 * @param message
46  		 *            Description of the problem
47  		 * @param fsckMessageId
48  		 *            Error identifier, following the git fsck fsck.<msg-id>
49  		 *            format
50  		 */
51  		SubmoduleValidationException(String message,
52  				ObjectChecker.ErrorType fsckMessageId) {
53  			super(message);
54  			this.fsckMessageId = fsckMessageId;
55  		}
56  
57  
58  		/**
59  		 * @return the error identifier
60  		 */
61  		public ObjectChecker.ErrorType getFsckMessageId() {
62  			return fsckMessageId;
63  		}
64  	}
65  
66  	/**
67  	 * Validate name for a submodule
68  	 *
69  	 * @param name
70  	 *            name of a submodule
71  	 * @throws SubmoduleValidationException
72  	 *             name doesn't seem valid (detail in message)
73  	 */
74  	public static void assertValidSubmoduleName(String name)
75  			throws SubmoduleValidationException {
76  		if (name.contains("/../") || name.contains("\\..\\") //$NON-NLS-1$ //$NON-NLS-2$
77  				|| name.startsWith("../") || name.startsWith("..\\") //$NON-NLS-1$ //$NON-NLS-2$
78  				|| name.endsWith("/..") || name.endsWith("\\..")) { //$NON-NLS-1$ //$NON-NLS-2$
79  			// Submodule names are used to store the submodule repositories
80  			// under $GIT_DIR/modules. Having ".." in submodule names makes a
81  			// vulnerability (CVE-2018-11235
82  			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=535027#c0)
83  			// Reject names containing ".." path segments. We don't
84  			// automatically replace these characters or canonicalize by
85  			// regarding the name as a file path.
86  			// Since Path class is platform dependent, we manually check '/' and
87  			// '\\' patterns here.
88  			throw new SubmoduleValidationException(MessageFormat
89  					.format(JGitText.get().invalidNameContainsDotDot, name),
90  					GITMODULES_NAME);
91  		}
92  
93  		if (name.startsWith("-")) { //$NON-NLS-1$
94  			throw new SubmoduleValidationException(
95  					MessageFormat.format(
96  							JGitText.get().submoduleNameInvalid, name),
97  					GITMODULES_NAME);
98  		}
99  	}
100 
101 	/**
102 	 * Validate URI for a submodule
103 	 *
104 	 * @param uri
105 	 *            uri of a submodule
106 	 * @throws SubmoduleValidationException
107 	 *             uri doesn't seem valid
108 	 */
109 	public static void assertValidSubmoduleUri(String uri)
110 			throws SubmoduleValidationException {
111 		if (uri.startsWith("-")) { //$NON-NLS-1$
112 			throw new SubmoduleValidationException(
113 					MessageFormat.format(
114 							JGitText.get().submoduleUrlInvalid, uri),
115 					GITMODULES_URL);
116 		}
117 	}
118 
119 	/**
120 	 * Validate path for a submodule
121 	 *
122 	 * @param path
123 	 *            path of a submodule
124 	 * @throws SubmoduleValidationException
125 	 *             path doesn't look right
126 	 */
127 	public static void assertValidSubmodulePath(String path)
128 			throws SubmoduleValidationException {
129 		if (path.startsWith("-")) { //$NON-NLS-1$
130 			throw new SubmoduleValidationException(
131 					MessageFormat.format(
132 							JGitText.get().submodulePathInvalid, path),
133 					GITMODULES_PATH);
134 		}
135 	}
136 
137 	/**
138 	 * Validate a .gitmodules file
139 	 *
140 	 * @param gitModulesContents
141 	 *            Contents of a .gitmodule file. They will be parsed internally.
142 	 * @throws SubmoduleValidationException
143 	 *             if the contents don't look like a configuration file or field
144 	 *             values are not valid
145 	 */
146 	public static void assertValidGitModulesFile(String gitModulesContents)
147 			throws SubmoduleValidationException {
148 		Config c = new Config();
149 		try {
150 			c.fromText(gitModulesContents);
151 			for (String subsection :
152 					c.getSubsections(CONFIG_SUBMODULE_SECTION)) {
153 				assertValidSubmoduleName(subsection);
154 
155 				String url = c.getString(
156 						CONFIG_SUBMODULE_SECTION, subsection, CONFIG_KEY_URL);
157 				if (url != null) {
158 					assertValidSubmoduleUri(url);
159 				}
160 
161 				String path = c.getString(
162 						CONFIG_SUBMODULE_SECTION, subsection, CONFIG_KEY_PATH);
163 				if (path != null) {
164 					assertValidSubmodulePath(path);
165 				}
166 			}
167 		} catch (ConfigInvalidException e) {
168 			SubmoduleValidationException sve = new SubmoduleValidationException(
169 					JGitText.get().invalidGitModules, GITMODULES_PARSE);
170 			sve.initCause(e);
171 			throw sve;
172 		}
173 	}
174 }