Skip to content

Commit 625589b

Browse files
committed
Merge branch 'lp/config-vername-check' into maint
* lp/config-vername-check: Disallow empty section and variable names Sanity-check config variable names
2 parents ebae9ff + 2169ddc commit 625589b

File tree

4 files changed

+111
-50
lines changed

4 files changed

+111
-50
lines changed

builtin/config.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ static int show_config(const char *key_, const char *value_, void *cb)
153153
static int get_value(const char *key_, const char *regex_)
154154
{
155155
int ret = -1;
156-
char *tl;
157156
char *global = NULL, *repo_config = NULL;
158157
const char *system_wide = NULL, *local;
159158

@@ -167,18 +166,32 @@ static int get_value(const char *key_, const char *regex_)
167166
system_wide = git_etc_gitconfig();
168167
}
169168

170-
key = xstrdup(key_);
171-
for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl)
172-
*tl = tolower(*tl);
173-
for (tl=key; *tl && *tl != '.'; ++tl)
174-
*tl = tolower(*tl);
175-
176169
if (use_key_regexp) {
170+
char *tl;
171+
172+
/*
173+
* NEEDSWORK: this naive pattern lowercasing obviously does not
174+
* work for more complex patterns like "^[^.]*Foo.*bar".
175+
* Perhaps we should deprecate this altogether someday.
176+
*/
177+
178+
key = xstrdup(key_);
179+
for (tl = key + strlen(key) - 1;
180+
tl >= key && *tl != '.';
181+
tl--)
182+
*tl = tolower(*tl);
183+
for (tl = key; *tl && *tl != '.'; tl++)
184+
*tl = tolower(*tl);
185+
177186
key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
178187
if (regcomp(key_regexp, key, REG_EXTENDED)) {
179188
fprintf(stderr, "Invalid key pattern: %s\n", key_);
189+
free(key);
180190
goto free_strings;
181191
}
192+
} else {
193+
if (git_config_parse_key(key_, &key, NULL))
194+
goto free_strings;
182195
}
183196

184197
if (regex_) {

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,7 @@ extern int git_config_maybe_bool(const char *, const char *);
999999
extern int git_config_string(const char **, const char *, const char *);
10001000
extern int git_config_pathname(const char **, const char *, const char *);
10011001
extern int git_config_set(const char *, const char *);
1002+
extern int git_config_parse_key(const char *, char **, int *);
10021003
extern int git_config_set_multivar(const char *, const char *, const char *, int);
10031004
extern int git_config_rename_section(const char *, const char *);
10041005
extern const char *git_etc_gitconfig(void);

config.c

Lines changed: 72 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,75 @@ int git_config_set(const char *key, const char *value)
10921092
return git_config_set_multivar(key, value, NULL, 0);
10931093
}
10941094

1095+
/*
1096+
* Auxiliary function to sanity-check and split the key into the section
1097+
* identifier and variable name.
1098+
*
1099+
* Returns 0 on success, -1 when there is an invalid character in the key and
1100+
* -2 if there is no section name in the key.
1101+
*
1102+
* store_key - pointer to char* which will hold a copy of the key with
1103+
* lowercase section and variable name
1104+
* baselen - pointer to int which will hold the length of the
1105+
* section + subsection part, can be NULL
1106+
*/
1107+
int git_config_parse_key(const char *key, char **store_key, int *baselen_)
1108+
{
1109+
int i, dot, baselen;
1110+
const char *last_dot = strrchr(key, '.');
1111+
1112+
/*
1113+
* Since "key" actually contains the section name and the real
1114+
* key name separated by a dot, we have to know where the dot is.
1115+
*/
1116+
1117+
if (last_dot == NULL || last_dot == key) {
1118+
error("key does not contain a section: %s", key);
1119+
return -2;
1120+
}
1121+
1122+
if (!last_dot[1]) {
1123+
error("key does not contain variable name: %s", key);
1124+
return -2;
1125+
}
1126+
1127+
baselen = last_dot - key;
1128+
if (baselen_)
1129+
*baselen_ = baselen;
1130+
1131+
/*
1132+
* Validate the key and while at it, lower case it for matching.
1133+
*/
1134+
*store_key = xmalloc(strlen(key) + 1);
1135+
1136+
dot = 0;
1137+
for (i = 0; key[i]; i++) {
1138+
unsigned char c = key[i];
1139+
if (c == '.')
1140+
dot = 1;
1141+
/* Leave the extended basename untouched.. */
1142+
if (!dot || i > baselen) {
1143+
if (!iskeychar(c) ||
1144+
(i == baselen + 1 && !isalpha(c))) {
1145+
error("invalid key: %s", key);
1146+
goto out_free_ret_1;
1147+
}
1148+
c = tolower(c);
1149+
} else if (c == '\n') {
1150+
error("invalid key (newline): %s", key);
1151+
goto out_free_ret_1;
1152+
}
1153+
(*store_key)[i] = c;
1154+
}
1155+
(*store_key)[i] = 0;
1156+
1157+
return 0;
1158+
1159+
out_free_ret_1:
1160+
free(*store_key);
1161+
return -1;
1162+
}
1163+
10951164
/*
10961165
* If value==NULL, unset in (remove from) config,
10971166
* if value_regex!=NULL, disregard key/value pairs where value does not match.
@@ -1118,59 +1187,23 @@ int git_config_set(const char *key, const char *value)
11181187
int git_config_set_multivar(const char *key, const char *value,
11191188
const char *value_regex, int multi_replace)
11201189
{
1121-
int i, dot;
11221190
int fd = -1, in_fd;
11231191
int ret;
11241192
char *config_filename;
11251193
struct lock_file *lock = NULL;
1126-
const char *last_dot = strrchr(key, '.');
11271194

11281195
if (config_exclusive_filename)
11291196
config_filename = xstrdup(config_exclusive_filename);
11301197
else
11311198
config_filename = git_pathdup("config");
11321199

1133-
/*
1134-
* Since "key" actually contains the section name and the real
1135-
* key name separated by a dot, we have to know where the dot is.
1136-
*/
1137-
1138-
if (last_dot == NULL) {
1139-
error("key does not contain a section: %s", key);
1140-
ret = 2;
1200+
/* parse-key returns negative; flip the sign to feed exit(3) */
1201+
ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
1202+
if (ret)
11411203
goto out_free;
1142-
}
1143-
store.baselen = last_dot - key;
11441204

11451205
store.multi_replace = multi_replace;
11461206

1147-
/*
1148-
* Validate the key and while at it, lower case it for matching.
1149-
*/
1150-
store.key = xmalloc(strlen(key) + 1);
1151-
dot = 0;
1152-
for (i = 0; key[i]; i++) {
1153-
unsigned char c = key[i];
1154-
if (c == '.')
1155-
dot = 1;
1156-
/* Leave the extended basename untouched.. */
1157-
if (!dot || i > store.baselen) {
1158-
if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
1159-
error("invalid key: %s", key);
1160-
free(store.key);
1161-
ret = 1;
1162-
goto out_free;
1163-
}
1164-
c = tolower(c);
1165-
} else if (c == '\n') {
1166-
error("invalid key (newline): %s", key);
1167-
free(store.key);
1168-
ret = 1;
1169-
goto out_free;
1170-
}
1171-
store.key[i] = c;
1172-
}
1173-
store.key[i] = 0;
11741207

11751208
/*
11761209
* The lock serves a purpose in addition to locking: the new

t/t1300-repo-config.sh

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -876,11 +876,25 @@ test_expect_success 'check split_cmdline return' "
876876
"
877877

878878
test_expect_success 'git -c "key=value" support' '
879-
test "z$(git -c name=value config name)" = zvalue &&
880879
test "z$(git -c core.name=value config core.name)" = zvalue &&
881-
test "z$(git -c CamelCase=value config camelcase)" = zvalue &&
882-
test "z$(git -c flag config --bool flag)" = ztrue &&
883-
test_must_fail git -c core.name=value config name
880+
test "z$(git -c foo.CamelCase=value config foo.camelcase)" = zvalue &&
881+
test "z$(git -c foo.flag config --bool foo.flag)" = ztrue &&
882+
test_must_fail git -c name=value config core.name
883+
'
884+
885+
test_expect_success 'key sanity-checking' '
886+
test_must_fail git config foo=bar &&
887+
test_must_fail git config foo=.bar &&
888+
test_must_fail git config foo.ba=r &&
889+
test_must_fail git config foo.1bar &&
890+
test_must_fail git config foo."ba
891+
z".bar &&
892+
test_must_fail git config . false &&
893+
test_must_fail git config .foo false &&
894+
test_must_fail git config foo. false &&
895+
test_must_fail git config .foo. false &&
896+
git config foo.bar true &&
897+
git config foo."ba =z".bar false
884898
'
885899

886900
test_done

0 commit comments

Comments
 (0)