Skip to content

Commit e904deb

Browse files
jrndscho
authored andcommitted
submodule: reject submodule.update = !command in .gitmodules
Since ac1fbbd (submodule: do not copy unknown update mode from .gitmodules, 2013-12-02), Git has been careful to avoid copying [submodule "foo"] update = !run an arbitrary scary command from .gitmodules to a repository's local config, copying in the setting 'update = none' instead. The gitmodules(5) manpage documents the intention: The !command form is intentionally ignored here for security reasons Unfortunately, starting with v2.20.0-rc0 (which integrated ee69b2a (submodule--helper: introduce new update-module-mode helper, 2018-08-13, first released in v2.20.0-rc0)), there are scenarios where we *don't* ignore it: if the config store contains no submodule.foo.update setting, the submodule-config API falls back to reading .gitmodules and the repository-supplied !command gets run after all. This was part of a general change over time in submodule support to read more directly from .gitmodules, since unlike .git/config it allows a project to change values between branches and over time (while still allowing .git/config to override things). But it was never intended to apply to this kind of dangerous configuration. The behavior change was not advertised in ee69b2a's commit message and was missed in review. Let's take the opportunity to make the protection more robust, even in Git versions that are technically not affected: instead of quietly converting 'update = !command' to 'update = none', noisily treat it as an error. Allowing the setting but treating it as meaning something else was just confusing; users are better served by seeing the error sooner. Forbidding the construct makes the semantics simpler and means we can check for it in fsck (in a separate patch). As a result, the submodule-config API cannot read this value from .gitmodules under any circumstance, and we can declare with confidence For security reasons, the '!command' form is not accepted here. Reported-by: Joern Schneeweisz <[email protected]> Signed-off-by: Jonathan Nieder <[email protected]> Signed-off-by: Johannes Schindelin <[email protected]>
1 parent d3ac8c3 commit e904deb

File tree

3 files changed

+20
-11
lines changed

3 files changed

+20
-11
lines changed

Documentation/gitmodules.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,8 @@ submodule.<name>.update::
4444
submodule init` to initialize the configuration variable of
4545
the same name. Allowed values here are 'checkout', 'rebase',
4646
'merge' or 'none'. See description of 'update' command in
47-
linkgit:git-submodule[1] for their meaning. Note that the
48-
'!command' form is intentionally ignored here for security
49-
reasons.
47+
linkgit:git-submodule[1] for their meaning. For security
48+
reasons, the '!command' form is not accepted here.
5049

5150
submodule.<name>.branch::
5251
A remote branch name for tracking updates in the upstream submodule.

submodule-config.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,13 @@ struct parse_config_parameter {
396396
int overwrite;
397397
};
398398

399+
/*
400+
* Parse a config item from .gitmodules.
401+
*
402+
* This does not handle submodule-related configuration from the main
403+
* config store (.git/config, etc). Callers are responsible for
404+
* checking for overrides in the main config store when appropriate.
405+
*/
399406
static int parse_config(const char *var, const char *value, void *data)
400407
{
401408
struct parse_config_parameter *me = data;
@@ -473,8 +480,9 @@ static int parse_config(const char *var, const char *value, void *data)
473480
warn_multiple_config(me->treeish_name, submodule->name,
474481
"update");
475482
else if (parse_submodule_update_strategy(value,
476-
&submodule->update_strategy) < 0)
477-
die(_("invalid value for %s"), var);
483+
&submodule->update_strategy) < 0 ||
484+
submodule->update_strategy.type == SM_UPDATE_COMMAND)
485+
die(_("invalid value for %s"), var);
478486
} else if (!strcmp(item.buf, "shallow")) {
479487
if (!me->overwrite && submodule->recommend_shallow != -1)
480488
warn_multiple_config(me->treeish_name, submodule->name,

t/t7406-submodule-update.sh

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -406,12 +406,12 @@ test_expect_success 'submodule update - command in .git/config' '
406406
)
407407
'
408408

409-
test_expect_success 'submodule update - command in .gitmodules is ignored' '
409+
test_expect_success 'submodule update - command in .gitmodules is rejected' '
410410
test_when_finished "git -C super reset --hard HEAD^" &&
411411
git -C super config -f .gitmodules submodule.submodule.update "!false" &&
412412
git -C super commit -a -m "add command to .gitmodules file" &&
413413
git -C super/submodule reset --hard $submodulesha1^ &&
414-
git -C super submodule update submodule
414+
test_must_fail git -C super submodule update submodule
415415
'
416416

417417
cat << EOF >expect
@@ -480,17 +480,19 @@ test_expect_success 'recursive submodule update - command in .git/config catches
480480
'
481481

482482
test_expect_success 'submodule init does not copy command into .git/config' '
483+
test_when_finished "git -C super update-index --force-remove submodule1" &&
484+
test_when_finished git config -f super/.gitmodules \
485+
--remove-section submodule.submodule1 &&
483486
(cd super &&
484487
H=$(git ls-files -s submodule | cut -d" " -f2) &&
485488
mkdir submodule1 &&
486489
git update-index --add --cacheinfo 160000 $H submodule1 &&
487490
git config -f .gitmodules submodule.submodule1.path submodule1 &&
488491
git config -f .gitmodules submodule.submodule1.url ../submodule &&
489492
git config -f .gitmodules submodule.submodule1.update !false &&
490-
git submodule init submodule1 &&
491-
echo "none" >expect &&
492-
git config submodule.submodule1.update >actual &&
493-
test_cmp expect actual
493+
test_must_fail git submodule init submodule1 &&
494+
test_expect_code 1 git config submodule.submodule1.update >actual &&
495+
test_must_be_empty actual
494496
)
495497
'
496498

0 commit comments

Comments
 (0)