Skip to content

Commit 05e9cd6

Browse files
jltoblerdgl
authored andcommitted
config: quote values containing CR character
When reading the config, values that contain a trailing CRLF are stripped. If the value itself has a trailing CR, the normal LF that follows results in the CR being unintentionally stripped. This may lead to unintended behavior due to the config value written being different when it gets read. One such issue involves a repository with a submodule path containing a trailing CR. When the submodule gets initialized, the submodule is cloned without being checked out and has "core.worktree" set to the submodule path. The git-checkout(1) that gets spawned later reads the "core.worktree" config value, but without the trailing CR, and consequently attempts to checkout to a different path than intended. If the repository contains a matching path that is a symlink, it is possible for the submodule repository to be checked out in arbitrary locations. This is extra bad when the symlink points to the submodule hooks directory and the submodule repository contains an executable "post-checkout" hook. Once the submodule repository checkout completes, the "post-checkout" hook immediately executes. To prevent mismatched config state due to misinterpreting a trailing CR, wrap config values containing CR in double quotes when writing the entry. This ensures a trailing CR is always separated for an LF and thus prevented from getting stripped. Note that this problem cannot be addressed by just quoting each CR with "\r". The reading side of the config interprets only a few backslash escapes, and "\r" is not among them. This fix is sufficient though because it only affects the CR at the end of a line and any literal CR in the interior is already preserved. Co-authored-by: David Leadbeater <[email protected]> Signed-off-by: Justin Tobler <[email protected]> Signed-off-by: Taylor Blau <[email protected]>
1 parent 664d4fa commit 05e9cd6

File tree

3 files changed

+45
-1
lines changed

3 files changed

+45
-1
lines changed

config.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2999,7 +2999,7 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
29992999
if (value[0] == ' ')
30003000
quote = "\"";
30013001
for (i = 0; value[i]; i++)
3002-
if (value[i] == ';' || value[i] == '#')
3002+
if (value[i] == ';' || value[i] == '#' || value[i] == '\r')
30033003
quote = "\"";
30043004
if (i && value[i - 1] == ' ')
30053005
quote = "\"";

t/t1300-config.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2590,4 +2590,15 @@ test_expect_success 'includeIf.hasconfig:remote.*.url forbids remote url in such
25902590
grep "fatal: remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url" err
25912591
'
25922592

2593+
test_expect_success 'writing value with trailing CR not stripped on read' '
2594+
test_when_finished "rm -rf cr-test" &&
2595+
2596+
printf "bar\r\n" >expect &&
2597+
git init cr-test &&
2598+
git -C cr-test config set core.foo $(printf "bar\r") &&
2599+
git -C cr-test config get core.foo >actual &&
2600+
2601+
test_cmp expect actual
2602+
'
2603+
25932604
test_done

t/t7450-bad-git-dotfiles.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,4 +347,37 @@ test_expect_success 'checkout -f --recurse-submodules must not use a nested gitd
347347
test_path_is_missing nested_checkout/thing2/.git
348348
'
349349

350+
test_expect_success SYMLINKS,!WINDOWS,!MINGW 'submodule must not checkout into different directory' '
351+
test_when_finished "rm -rf sub repo bad-clone" &&
352+
353+
git init sub &&
354+
write_script sub/post-checkout <<-\EOF &&
355+
touch "$PWD/foo"
356+
EOF
357+
git -C sub add post-checkout &&
358+
git -C sub commit -m hook &&
359+
360+
git init repo &&
361+
git -C repo -c protocol.file.allow=always submodule add "$PWD/sub" sub &&
362+
git -C repo mv sub $(printf "sub\r") &&
363+
364+
# Ensure config values containing CR are wrapped in quotes.
365+
git config unset -f repo/.gitmodules submodule.sub.path &&
366+
printf "\tpath = \"sub\r\"\n" >>repo/.gitmodules &&
367+
368+
git config unset -f repo/.git/modules/sub/config core.worktree &&
369+
{
370+
printf "[core]\n" &&
371+
printf "\tworktree = \"../../../sub\r\"\n"
372+
} >>repo/.git/modules/sub/config &&
373+
374+
ln -s .git/modules/sub/hooks repo/sub &&
375+
git -C repo add -A &&
376+
git -C repo commit -m submodule &&
377+
378+
git -c protocol.file.allow=always clone --recurse-submodules repo bad-clone &&
379+
! test -f "$PWD/foo" &&
380+
test -f $(printf "bad-clone/sub\r/post-checkout")
381+
'
382+
350383
test_done

0 commit comments

Comments
 (0)