mailmap: do not respect symlinks for in-tree .mailmap

As with .gitattributes and .gitignore, we would like to make sure that .mailmap files are handled consistently whether read from the a blob (as is the default behavior in a bare repo) or from the filesystem. Likewise, we would like to avoid reading out-of-tree files pointed to by a symlink, which could have security implications in certain setups. We can cover both by using open_nofollow() when opening the in-tree files. We'll continue to follow links for mailmap.file, as well as when reading .mailmap from the current directory when outside of a repository entirely. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Feb 16, 2021 at 09:44 UTC adcd9f54729e41aa63c3a1b80a19023ea8ede203
2 files changed +48 -5
mailmap.c
+17 -5
@@ -157,20 +157,30 @@ static void read_mailmap_line(struct string_list *map, char *buffer)
157 add_mapping(map, name1, email1, name2, email2);
158 }
159
160 -static int read_mailmap_file(struct string_list *map, const char *filename)
160 +/* Flags for read_mailmap_file() */
161 +#define MAILMAP_NOFOLLOW (1<<0)
162 +
163 +static int read_mailmap_file(struct string_list *map, const char *filename,
164 + unsigned flags)
165 {
166 char buffer[1024];
167 FILE *f;
168 + int fd;
169
170 if (!filename)
171 return 0;
172
168 - f = fopen(filename, "r");
169 - if (!f) {
173 + if (flags & MAILMAP_NOFOLLOW)
174 + fd = open_nofollow(filename, O_RDONLY);
175 + else
176 + fd = open(filename, O_RDONLY);
177 +
178 + if (fd < 0) {
179 if (errno == ENOENT)
180 return 0;
181 return error_errno("unable to open mailmap at %s", filename);
182 }
183 + f = xfdopen(fd, "r");
184
185 while (fgets(buffer, sizeof(buffer), f) != NULL)
186 read_mailmap_line(map, buffer);
@@ -225,10 +235,12 @@ int read_mailmap(struct string_list *map)
235 if (!git_mailmap_blob && is_bare_repository())
236 git_mailmap_blob = "HEAD:.mailmap";
237
228 - err |= read_mailmap_file(map, ".mailmap");
238 + err |= read_mailmap_file(map, ".mailmap",
239 + startup_info->have_repository ?
240 + MAILMAP_NOFOLLOW : 0);
241 if (startup_info->have_repository)
242 err |= read_mailmap_blob(map, git_mailmap_blob);
231 - err |= read_mailmap_file(map, git_mailmap_file);
243 + err |= read_mailmap_file(map, git_mailmap_file, 0);
244 return err;
245 }
246
t/t4203-mailmap.sh
+31
@@ -889,4 +889,35 @@ test_expect_success 'empty syntax: setup' '
889 test_cmp expect actual
890 '
891
892 +test_expect_success SYMLINKS 'set up symlink tests' '
893 + git commit --allow-empty -m foo --author="Orig <orig@example.com>" &&
894 + echo "New <new@example.com> <orig@example.com>" >map &&
895 + rm -f .mailmap
896 +'
897 +
898 +test_expect_success SYMLINKS 'symlinks respected in mailmap.file' '
899 + test_when_finished "rm symlink" &&
900 + ln -s map symlink &&
901 + git -c mailmap.file="$(pwd)/symlink" log -1 --format=%aE >actual &&
902 + echo "new@example.com" >expect &&
903 + test_cmp expect actual
904 +'
905 +
906 +test_expect_success SYMLINKS 'symlinks respected in non-repo shortlog' '
907 + git log -1 >input &&
908 + test_when_finished "nongit rm .mailmap" &&
909 + nongit ln -sf "$TRASH_DIRECTORY/map" .mailmap &&
910 + nongit git shortlog -s <input >actual &&
911 + echo " 1 New" >expect &&
912 + test_cmp expect actual
913 +'
914 +
915 +test_expect_success SYMLINKS 'symlinks not respected in-tree' '
916 + test_when_finished "rm .mailmap" &&
917 + ln -s map .mailmap &&
918 + git log -1 --format=%aE >actual &&
919 + echo "orig@example.com" >expect&&
920 + test_cmp expect actual
921 +'
922 +
923 test_done