Sanity-check config variable names

Sanity-check config variable names when adding and retrieving them.  As a side
effect code duplication between git_config_set_multivar and get_value (in
builtin/config.c) was removed and the common functionality was placed in
git_config_parse_key.

This breaks a test in t1300 which used invalid section-less keys in the tests
for "git -c". However, allowing such names there was useless, since there was
no way to set them via config file, and no part of git actually tried to use
section-less keys. This patch updates the test to use more realistic examples
as well as adding its own test.

Signed-off-by: Libor Pechacek <lpechacek@suse.cz>
Acked-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Libor Pechacek 2011-01-30 20:40:41 +01:00 committed by Junio C Hamano
parent 597a630542
commit b09c53a3e3
4 changed files with 102 additions and 50 deletions

View File

@ -153,7 +153,6 @@ static int show_config(const char *key_, const char *value_, void *cb)
static int get_value(const char *key_, const char *regex_)
{
int ret = -1;
char *tl;
char *global = NULL, *repo_config = NULL;
const char *system_wide = NULL, *local;

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

key = xstrdup(key_);
for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl)
*tl = tolower(*tl);
for (tl=key; *tl && *tl != '.'; ++tl)
*tl = tolower(*tl);

if (use_key_regexp) {
char *tl;

/*
* NEEDSWORK: this naive pattern lowercasing obviously does not
* work for more complex patterns like "^[^.]*Foo.*bar".
* Perhaps we should deprecate this altogether someday.
*/

key = xstrdup(key_);
for (tl = key + strlen(key) - 1;
tl >= key && *tl != '.';
tl--)
*tl = tolower(*tl);
for (tl = key; *tl && *tl != '.'; tl++)
*tl = tolower(*tl);

key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
if (regcomp(key_regexp, key, REG_EXTENDED)) {
fprintf(stderr, "Invalid key pattern: %s\n", key_);
free(key);
goto free_strings;
}
} else {
if (git_config_parse_key(key_, &key, NULL))
goto free_strings;
}

if (regex_) {

View File

@ -997,6 +997,7 @@ extern int git_config_maybe_bool(const char *, const char *);
extern int git_config_string(const char **, const char *, const char *);
extern int git_config_pathname(const char **, const char *, const char *);
extern int git_config_set(const char *, const char *);
extern int git_config_parse_key(const char *, char **, int *);
extern int git_config_set_multivar(const char *, const char *, const char *, int);
extern int git_config_rename_section(const char *, const char *);
extern const char *git_etc_gitconfig(void);

106
config.c
View File

@ -1098,6 +1098,70 @@ int git_config_set(const char *key, const char *value)
return git_config_set_multivar(key, value, NULL, 0);
}

/*
* Auxiliary function to sanity-check and split the key into the section
* identifier and variable name.
*
* Returns 0 on success, -1 when there is an invalid character in the key and
* -2 if there is no section name in the key.
*
* store_key - pointer to char* which will hold a copy of the key with
* lowercase section and variable name
* baselen - pointer to int which will hold the length of the
* section + subsection part, can be NULL
*/
int git_config_parse_key(const char *key, char **store_key, int *baselen_)
{
int i, dot, baselen;
const char *last_dot = strrchr(key, '.');

/*
* Since "key" actually contains the section name and the real
* key name separated by a dot, we have to know where the dot is.
*/

if (last_dot == NULL) {
error("key does not contain a section: %s", key);
return -2;
}

baselen = last_dot - key;
if (baselen_)
*baselen_ = baselen;

/*
* Validate the key and while at it, lower case it for matching.
*/
*store_key = xmalloc(strlen(key) + 1);

dot = 0;
for (i = 0; key[i]; i++) {
unsigned char c = key[i];
if (c == '.')
dot = 1;
/* Leave the extended basename untouched.. */
if (!dot || i > baselen) {
if (!iskeychar(c) ||
(i == baselen + 1 && !isalpha(c))) {
error("invalid key: %s", key);
goto out_free_ret_1;
}
c = tolower(c);
} else if (c == '\n') {
error("invalid key (newline): %s", key);
goto out_free_ret_1;
}
(*store_key)[i] = c;
}
(*store_key)[i] = 0;

return 0;

out_free_ret_1:
free(*store_key);
return -1;
}

/*
* If value==NULL, unset in (remove from) config,
* if value_regex!=NULL, disregard key/value pairs where value does not match.
@ -1124,59 +1188,23 @@ int git_config_set(const char *key, const char *value)
int git_config_set_multivar(const char *key, const char *value,
const char *value_regex, int multi_replace)
{
int i, dot;
int fd = -1, in_fd;
int ret;
char *config_filename;
struct lock_file *lock = NULL;
const char *last_dot = strrchr(key, '.');

if (config_exclusive_filename)
config_filename = xstrdup(config_exclusive_filename);
else
config_filename = git_pathdup("config");

/*
* Since "key" actually contains the section name and the real
* key name separated by a dot, we have to know where the dot is.
*/

if (last_dot == NULL) {
error("key does not contain a section: %s", key);
ret = 2;
/* parse-key returns negative; flip the sign to feed exit(3) */
ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
if (ret)
goto out_free;
}
store.baselen = last_dot - key;

store.multi_replace = multi_replace;

/*
* Validate the key and while at it, lower case it for matching.
*/
store.key = xmalloc(strlen(key) + 1);
dot = 0;
for (i = 0; key[i]; i++) {
unsigned char c = key[i];
if (c == '.')
dot = 1;
/* Leave the extended basename untouched.. */
if (!dot || i > store.baselen) {
if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
error("invalid key: %s", key);
free(store.key);
ret = 1;
goto out_free;
}
c = tolower(c);
} else if (c == '\n') {
error("invalid key (newline): %s", key);
free(store.key);
ret = 1;
goto out_free;
}
store.key[i] = c;
}
store.key[i] = 0;

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

View File

@ -876,11 +876,21 @@ test_expect_success 'check split_cmdline return' "
"

test_expect_success 'git -c "key=value" support' '
test "z$(git -c name=value config name)" = zvalue &&
test "z$(git -c core.name=value config core.name)" = zvalue &&
test "z$(git -c CamelCase=value config camelcase)" = zvalue &&
test "z$(git -c flag config --bool flag)" = ztrue &&
test_must_fail git -c core.name=value config name
test "z$(git -c foo.CamelCase=value config foo.camelcase)" = zvalue &&
test "z$(git -c foo.flag config --bool foo.flag)" = ztrue &&
test_must_fail git -c name=value config core.name
'

test_expect_success 'key sanity-checking' '
test_must_fail git config foo=bar &&
test_must_fail git config foo=.bar &&
test_must_fail git config foo.ba=r &&
test_must_fail git config foo.1bar &&
test_must_fail git config foo."ba
z".bar &&
git config foo.bar true &&
git config foo."ba =z".bar false
'

test_done