submodule-config.h: move check_submodule_url
Move 'check_submodule_url' out of 'fsck.c' and into 'submodule-config.h' as a public method, similar to 'check_submodule_name'. With the function now accessible outside of 'fsck', it can be used in a later commit to extend 'test-tool submodule' to check the validity of submodule URLs as it does with names in the 'check-name' subcommand. Other than its location, no changes are made to 'check_submodule_url' in this patch. Signed-off-by: Victoria Dye <vdye@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Victoria Dye committed
Jan 18, 2024 at 01:55 UTC
13320ff610b4083341175c4f8e636a1bc52145f5
3 files changed
+137
-133
fsck.c
-133
@@ -21,7 +21,6 @@
21
#include "packfile.h"
22
#include "submodule-config.h"
23
#include "config.h"
24
-#include "credential.h"
24
#include "help.h"
25
26
static ssize_t max_tree_entry_len = 4096;
@@ -1048,138 +1047,6 @@ done:
1047
return ret;
1048
}
1049
1051
-static int starts_with_dot_slash(const char *const path)
1052
-{
1053
- return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |
1054
- PATH_MATCH_XPLATFORM);
1055
-}
1056
-
1057
-static int starts_with_dot_dot_slash(const char *const path)
1058
-{
1059
- return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |
1060
- PATH_MATCH_XPLATFORM);
1061
-}
1062
-
1063
-static int submodule_url_is_relative(const char *url)
1064
-{
1065
- return starts_with_dot_slash(url) || starts_with_dot_dot_slash(url);
1066
-}
1067
-
1068
-/*
1069
- * Count directory components that a relative submodule URL should chop
1070
- * from the remote_url it is to be resolved against.
1071
- *
1072
- * In other words, this counts "../" components at the start of a
1073
- * submodule URL.
1074
- *
1075
- * Returns the number of directory components to chop and writes a
1076
- * pointer to the next character of url after all leading "./" and
1077
- * "../" components to out.
1078
- */
1079
-static int count_leading_dotdots(const char *url, const char **out)
1080
-{
1081
- int result = 0;
1082
- while (1) {
1083
- if (starts_with_dot_dot_slash(url)) {
1084
- result++;
1085
- url += strlen("../");
1086
- continue;
1087
- }
1088
- if (starts_with_dot_slash(url)) {
1089
- url += strlen("./");
1090
- continue;
1091
- }
1092
- *out = url;
1093
- return result;
1094
- }
1095
-}
1096
-/*
1097
- * Check whether a transport is implemented by git-remote-curl.
1098
- *
1099
- * If it is, returns 1 and writes the URL that would be passed to
1100
- * git-remote-curl to the "out" parameter.
1101
- *
1102
- * Otherwise, returns 0 and leaves "out" untouched.
1103
- *
1104
- * Examples:
1105
- * http::https://example.com/repo.git -> 1, https://example.com/repo.git
1106
- * https://example.com/repo.git -> 1, https://example.com/repo.git
1107
- * git://example.com/repo.git -> 0
1108
- *
1109
- * This is for use in checking for previously exploitable bugs that
1110
- * required a submodule URL to be passed to git-remote-curl.
1111
- */
1112
-static int url_to_curl_url(const char *url, const char **out)
1113
-{
1114
- /*
1115
- * We don't need to check for case-aliases, "http.exe", and so
1116
- * on because in the default configuration, is_transport_allowed
1117
- * prevents URLs with those schemes from being cloned
1118
- * automatically.
1119
- */
1120
- if (skip_prefix(url, "http::", out) ||
1121
- skip_prefix(url, "https::", out) ||
1122
- skip_prefix(url, "ftp::", out) ||
1123
- skip_prefix(url, "ftps::", out))
1124
- return 1;
1125
- if (starts_with(url, "http://") ||
1126
- starts_with(url, "https://") ||
1127
- starts_with(url, "ftp://") ||
1128
- starts_with(url, "ftps://")) {
1129
- *out = url;
1130
- return 1;
1131
- }
1132
- return 0;
1133
-}
1134
-
1135
-static int check_submodule_url(const char *url)
1136
-{
1137
- const char *curl_url;
1138
-
1139
- if (looks_like_command_line_option(url))
1140
- return -1;
1141
-
1142
- if (submodule_url_is_relative(url) || starts_with(url, "git://")) {
1143
- char *decoded;
1144
- const char *next;
1145
- int has_nl;
1146
-
1147
- /*
1148
- * This could be appended to an http URL and url-decoded;
1149
- * check for malicious characters.
1150
- */
1151
- decoded = url_decode(url);
1152
- has_nl = !!strchr(decoded, '\n');
1153
-
1154
- free(decoded);
1155
- if (has_nl)
1156
- return -1;
1157
-
1158
- /*
1159
- * URLs which escape their root via "../" can overwrite
1160
- * the host field and previous components, resolving to
1161
- * URLs like https::example.com/submodule.git and
1162
- * https:///example.com/submodule.git that were
1163
- * susceptible to CVE-2020-11008.
1164
- */
1165
- if (count_leading_dotdots(url, &next) > 0 &&
1166
- (*next == ':' || *next == '/'))
1167
- return -1;
1168
- }
1169
-
1170
- else if (url_to_curl_url(url, &curl_url)) {
1171
- struct credential c = CREDENTIAL_INIT;
1172
- int ret = 0;
1173
- if (credential_from_url_gently(&c, curl_url, 1) ||
1174
- !*c.host)
1175
- ret = -1;
1176
- credential_clear(&c);
1177
- return ret;
1178
- }
1179
-
1180
- return 0;
1181
-}
1182
-
1050
struct fsck_gitmodules_data {
1051
const struct object_id *oid;
1052
struct fsck_options *options;
submodule-config.c
+134
@@ -14,6 +14,8 @@
14
#include "parse-options.h"
15
#include "thread-utils.h"
16
#include "tree-walk.h"
17
+#include "url.h"
18
+#include "credential.h"
19
20
/*
21
* submodule cache lookup structure
@@ -228,6 +230,138 @@ in_component:
230
return 0;
231
}
232
233
+static int starts_with_dot_slash(const char *const path)
234
+{
235
+ return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |
236
+ PATH_MATCH_XPLATFORM);
237
+}
238
+
239
+static int starts_with_dot_dot_slash(const char *const path)
240
+{
241
+ return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |
242
+ PATH_MATCH_XPLATFORM);
243
+}
244
+
245
+static int submodule_url_is_relative(const char *url)
246
+{
247
+ return starts_with_dot_slash(url) || starts_with_dot_dot_slash(url);
248
+}
249
+
250
+/*
251
+ * Count directory components that a relative submodule URL should chop
252
+ * from the remote_url it is to be resolved against.
253
+ *
254
+ * In other words, this counts "../" components at the start of a
255
+ * submodule URL.
256
+ *
257
+ * Returns the number of directory components to chop and writes a
258
+ * pointer to the next character of url after all leading "./" and
259
+ * "../" components to out.
260
+ */
261
+static int count_leading_dotdots(const char *url, const char **out)
262
+{
263
+ int result = 0;
264
+ while (1) {
265
+ if (starts_with_dot_dot_slash(url)) {
266
+ result++;
267
+ url += strlen("../");
268
+ continue;
269
+ }
270
+ if (starts_with_dot_slash(url)) {
271
+ url += strlen("./");
272
+ continue;
273
+ }
274
+ *out = url;
275
+ return result;
276
+ }
277
+}
278
+/*
279
+ * Check whether a transport is implemented by git-remote-curl.
280
+ *
281
+ * If it is, returns 1 and writes the URL that would be passed to
282
+ * git-remote-curl to the "out" parameter.
283
+ *
284
+ * Otherwise, returns 0 and leaves "out" untouched.
285
+ *
286
+ * Examples:
287
+ * http::https://example.com/repo.git -> 1, https://example.com/repo.git
288
+ * https://example.com/repo.git -> 1, https://example.com/repo.git
289
+ * git://example.com/repo.git -> 0
290
+ *
291
+ * This is for use in checking for previously exploitable bugs that
292
+ * required a submodule URL to be passed to git-remote-curl.
293
+ */
294
+static int url_to_curl_url(const char *url, const char **out)
295
+{
296
+ /*
297
+ * We don't need to check for case-aliases, "http.exe", and so
298
+ * on because in the default configuration, is_transport_allowed
299
+ * prevents URLs with those schemes from being cloned
300
+ * automatically.
301
+ */
302
+ if (skip_prefix(url, "http::", out) ||
303
+ skip_prefix(url, "https::", out) ||
304
+ skip_prefix(url, "ftp::", out) ||
305
+ skip_prefix(url, "ftps::", out))
306
+ return 1;
307
+ if (starts_with(url, "http://") ||
308
+ starts_with(url, "https://") ||
309
+ starts_with(url, "ftp://") ||
310
+ starts_with(url, "ftps://")) {
311
+ *out = url;
312
+ return 1;
313
+ }
314
+ return 0;
315
+}
316
+
317
+int check_submodule_url(const char *url)
318
+{
319
+ const char *curl_url;
320
+
321
+ if (looks_like_command_line_option(url))
322
+ return -1;
323
+
324
+ if (submodule_url_is_relative(url) || starts_with(url, "git://")) {
325
+ char *decoded;
326
+ const char *next;
327
+ int has_nl;
328
+
329
+ /*
330
+ * This could be appended to an http URL and url-decoded;
331
+ * check for malicious characters.
332
+ */
333
+ decoded = url_decode(url);
334
+ has_nl = !!strchr(decoded, '\n');
335
+
336
+ free(decoded);
337
+ if (has_nl)
338
+ return -1;
339
+
340
+ /*
341
+ * URLs which escape their root via "../" can overwrite
342
+ * the host field and previous components, resolving to
343
+ * URLs like https::example.com/submodule.git and
344
+ * https:///example.com/submodule.git that were
345
+ * susceptible to CVE-2020-11008.
346
+ */
347
+ if (count_leading_dotdots(url, &next) > 0 &&
348
+ (*next == ':' || *next == '/'))
349
+ return -1;
350
+ }
351
+
352
+ else if (url_to_curl_url(url, &curl_url)) {
353
+ struct credential c = CREDENTIAL_INIT;
354
+ int ret = 0;
355
+ if (credential_from_url_gently(&c, curl_url, 1) ||
356
+ !*c.host)
357
+ ret = -1;
358
+ credential_clear(&c);
359
+ return ret;
360
+ }
361
+
362
+ return 0;
363
+}
364
+
365
static int name_and_item_from_var(const char *var, struct strbuf *name,
366
struct strbuf *item)
367
{
submodule-config.h
+3
@@ -91,6 +91,9 @@ int config_set_in_gitmodules_file_gently(const char *key, const char *value);
91
*/
92
int check_submodule_name(const char *name);
93
94
+/* Returns 0 if the URL valid per RFC3986 and -1 otherwise. */
95
+int check_submodule_url(const char *url);
96
+
97
/*
98
* Note: these helper functions exist solely to maintain backward
99
* compatibility with 'fetch' and 'update_clone' storing configuration in