Skip to content

Commit d8d7715

Browse files
pks-tgitster
authored andcommitted
config: allow specifying config entries via envvar pairs
While we currently have the `GIT_CONFIG_PARAMETERS` environment variable which can be used to pass runtime configuration data to git processes, it's an internal implementation detail and not supposed to be used by end users. Next to being for internal use only, this way of passing config entries has a major downside: the config keys need to be parsed as they contain both key and value in a single variable. As such, it is left to the user to escape any potentially harmful characters in the value, which is quite hard to do if values are controlled by a third party. This commit thus adds a new way of adding config entries via the environment which gets rid of this shortcoming. If the user passes the `GIT_CONFIG_COUNT=$n` environment variable, Git will parse environment variable pairs `GIT_CONFIG_KEY_$i` and `GIT_CONFIG_VALUE_$i` for each `i` in `[0,n)`. While the same can be achieved with `git -c <name>=<value>`, one may wish to not do so for potentially sensitive information. E.g. if one wants to set `http.extraHeader` to contain an authentication token, doing so via `-c` would trivially leak those credentials via e.g. ps(1), which typically also shows command arguments. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b9d147f commit d8d7715

File tree

5 files changed

+191
-9
lines changed

5 files changed

+191
-9
lines changed

Documentation/git-config.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,22 @@ GIT_CONFIG_NOSYSTEM::
337337

338338
See also <<FILES>>.
339339

340+
GIT_CONFIG_COUNT::
341+
GIT_CONFIG_KEY_<n>::
342+
GIT_CONFIG_VALUE_<n>::
343+
If GIT_CONFIG_COUNT is set to a positive number, all environment pairs
344+
GIT_CONFIG_KEY_<n> and GIT_CONFIG_VALUE_<n> up to that number will be
345+
added to the process's runtime configuration. The config pairs are
346+
zero-indexed. Any missing key or value is treated as an error. An empty
347+
GIT_CONFIG_COUNT is treated the same as GIT_CONFIG_COUNT=0, namely no
348+
pairs are processed. These environment variables will override values
349+
in configuration files, but will be overridden by any explicit options
350+
passed via `git -c`.
351+
+
352+
This is useful for cases where you want to spawn multiple git commands
353+
with a common configuration but cannot depend on a configuration file,
354+
for example when writing scripts.
355+
340356

341357
[[EXAMPLES]]
342358
EXAMPLES

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ static inline enum object_type object_type(unsigned int mode)
472472
#define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
473473
#define CONFIG_ENVIRONMENT "GIT_CONFIG"
474474
#define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS"
475+
#define CONFIG_COUNT_ENVIRONMENT "GIT_CONFIG_COUNT"
475476
#define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
476477
#define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
477478
#define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"

config.c

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "cache.h"
99
#include "branch.h"
1010
#include "config.h"
11+
#include "environment.h"
1112
#include "repository.h"
1213
#include "lockfile.h"
1314
#include "exec-cmd.h"
@@ -598,23 +599,73 @@ static int parse_config_env_list(char *env, config_fn_t fn, void *data)
598599

599600
int git_config_from_parameters(config_fn_t fn, void *data)
600601
{
601-
const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
602+
const char *env;
603+
struct strbuf envvar = STRBUF_INIT;
604+
struct strvec to_free = STRVEC_INIT;
602605
int ret = 0;
603-
char *envw;
606+
char *envw = NULL;
604607
struct config_source source;
605608

606-
if (!env)
607-
return 0;
608-
609609
memset(&source, 0, sizeof(source));
610610
source.prev = cf;
611611
source.origin_type = CONFIG_ORIGIN_CMDLINE;
612612
cf = &source;
613613

614-
/* sq_dequote will write over it */
615-
envw = xstrdup(env);
616-
ret = parse_config_env_list(envw, fn, data);
614+
env = getenv(CONFIG_COUNT_ENVIRONMENT);
615+
if (env) {
616+
unsigned long count;
617+
char *endp;
618+
int i;
617619

620+
count = strtoul(env, &endp, 10);
621+
if (*endp) {
622+
ret = error(_("bogus count in %s"), CONFIG_COUNT_ENVIRONMENT);
623+
goto out;
624+
}
625+
if (count > INT_MAX) {
626+
ret = error(_("too many entries in %s"), CONFIG_COUNT_ENVIRONMENT);
627+
goto out;
628+
}
629+
630+
for (i = 0; i < count; i++) {
631+
const char *key, *value;
632+
633+
strbuf_addf(&envvar, "GIT_CONFIG_KEY_%d", i);
634+
key = getenv_safe(&to_free, envvar.buf);
635+
if (!key) {
636+
ret = error(_("missing config key %s"), envvar.buf);
637+
goto out;
638+
}
639+
strbuf_reset(&envvar);
640+
641+
strbuf_addf(&envvar, "GIT_CONFIG_VALUE_%d", i);
642+
value = getenv_safe(&to_free, envvar.buf);
643+
if (!value) {
644+
ret = error(_("missing config value %s"), envvar.buf);
645+
goto out;
646+
}
647+
strbuf_reset(&envvar);
648+
649+
if (config_parse_pair(key, value, fn, data) < 0) {
650+
ret = -1;
651+
goto out;
652+
}
653+
}
654+
}
655+
656+
env = getenv(CONFIG_DATA_ENVIRONMENT);
657+
if (env) {
658+
/* sq_dequote will write over it */
659+
envw = xstrdup(env);
660+
if (parse_config_env_list(envw, fn, data) < 0) {
661+
ret = -1;
662+
goto out;
663+
}
664+
}
665+
666+
out:
667+
strbuf_release(&envvar);
668+
strvec_clear(&to_free);
618669
free(envw);
619670
cf = source.prev;
620671
return ret;

environment.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ const char * const local_repo_env[] = {
117117
ALTERNATE_DB_ENVIRONMENT,
118118
CONFIG_ENVIRONMENT,
119119
CONFIG_DATA_ENVIRONMENT,
120+
CONFIG_COUNT_ENVIRONMENT,
120121
DB_ENVIRONMENT,
121122
GIT_DIR_ENVIRONMENT,
122123
GIT_WORK_TREE_ENVIRONMENT,

t/t1300-config.sh

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,117 @@ test_expect_success '--config-env handles keys with equals' '
14241424
test_cmp expect actual
14251425
'
14261426

1427+
test_expect_success 'git config handles environment config pairs' '
1428+
GIT_CONFIG_COUNT=2 \
1429+
GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="foo" \
1430+
GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="bar" \
1431+
git config --get-regexp "pair.*" >actual &&
1432+
cat >expect <<-EOF &&
1433+
pair.one foo
1434+
pair.two bar
1435+
EOF
1436+
test_cmp expect actual
1437+
'
1438+
1439+
test_expect_success 'git config ignores pairs without count' '
1440+
test_must_fail env GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
1441+
git config pair.one 2>error &&
1442+
test_must_be_empty error
1443+
'
1444+
1445+
test_expect_success 'git config ignores pairs with zero count' '
1446+
test_must_fail env \
1447+
GIT_CONFIG_COUNT=0 \
1448+
GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
1449+
git config pair.one
1450+
'
1451+
1452+
test_expect_success 'git config ignores pairs exceeding count' '
1453+
GIT_CONFIG_COUNT=1 \
1454+
GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
1455+
GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="value" \
1456+
git config --get-regexp "pair.*" >actual &&
1457+
cat >expect <<-EOF &&
1458+
pair.one value
1459+
EOF
1460+
test_cmp expect actual
1461+
'
1462+
1463+
test_expect_success 'git config ignores pairs with zero count' '
1464+
test_must_fail env \
1465+
GIT_CONFIG_COUNT=0 GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
1466+
git config pair.one >error &&
1467+
test_must_be_empty error
1468+
'
1469+
1470+
test_expect_success 'git config ignores pairs with empty count' '
1471+
test_must_fail env \
1472+
GIT_CONFIG_COUNT= GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
1473+
git config pair.one >error &&
1474+
test_must_be_empty error
1475+
'
1476+
1477+
test_expect_success 'git config fails with invalid count' '
1478+
test_must_fail env GIT_CONFIG_COUNT=10a git config --list 2>error &&
1479+
test_i18ngrep "bogus count" error &&
1480+
test_must_fail env GIT_CONFIG_COUNT=9999999999999999 git config --list 2>error &&
1481+
test_i18ngrep "too many entries" error
1482+
'
1483+
1484+
test_expect_success 'git config fails with missing config key' '
1485+
test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_VALUE_0="value" \
1486+
git config --list 2>error &&
1487+
test_i18ngrep "missing config key" error
1488+
'
1489+
1490+
test_expect_success 'git config fails with missing config value' '
1491+
test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0="pair.one" \
1492+
git config --list 2>error &&
1493+
test_i18ngrep "missing config value" error
1494+
'
1495+
1496+
test_expect_success 'git config fails with invalid config pair key' '
1497+
test_must_fail env GIT_CONFIG_COUNT=1 \
1498+
GIT_CONFIG_KEY_0= GIT_CONFIG_VALUE_0=value \
1499+
git config --list &&
1500+
test_must_fail env GIT_CONFIG_COUNT=1 \
1501+
GIT_CONFIG_KEY_0=missing-section GIT_CONFIG_VALUE_0=value \
1502+
git config --list
1503+
'
1504+
1505+
test_expect_success 'environment overrides config file' '
1506+
test_when_finished "rm -f .git/config" &&
1507+
cat >.git/config <<-EOF &&
1508+
[pair]
1509+
one = value
1510+
EOF
1511+
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=override \
1512+
git config pair.one >actual &&
1513+
cat >expect <<-EOF &&
1514+
override
1515+
EOF
1516+
test_cmp expect actual
1517+
'
1518+
1519+
test_expect_success 'GIT_CONFIG_PARAMETERS overrides environment config' '
1520+
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=value \
1521+
GIT_CONFIG_PARAMETERS="${SQ}pair.one=override${SQ}" \
1522+
git config pair.one >actual &&
1523+
cat >expect <<-EOF &&
1524+
override
1525+
EOF
1526+
test_cmp expect actual
1527+
'
1528+
1529+
test_expect_success 'command line overrides environment config' '
1530+
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=value \
1531+
git -c pair.one=override config pair.one >actual &&
1532+
cat >expect <<-EOF &&
1533+
override
1534+
EOF
1535+
test_cmp expect actual
1536+
'
1537+
14271538
test_expect_success 'git config --edit works' '
14281539
git config -f tmp test.value no &&
14291540
echo test.value=yes >expect &&
@@ -1769,9 +1880,11 @@ test_expect_success '--show-origin with --list' '
17691880
file:.git/config user.override=local
17701881
file:.git/config include.path=../include/relative.include
17711882
file:.git/../include/relative.include user.relative=include
1883+
command line: user.environ=true
17721884
command line: user.cmdline=true
17731885
EOF
1774-
git -c user.cmdline=true config --list --show-origin >output &&
1886+
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=user.environ GIT_CONFIG_VALUE_0=true\
1887+
git -c user.cmdline=true config --list --show-origin >output &&
17751888
test_cmp expect output
17761889
'
17771890

0 commit comments

Comments
 (0)