Skip to content

Commit ce81b1d

Browse files
pks-tpeff
authored andcommitted
config: add new way to pass config via --config-env
While it's already possible to pass runtime configuration via `git -c <key>=<value>`, it may be undesirable to use when the value contains 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. To enable this usecase without leaking credentials, this commit introduces a new switch `--config-env=<key>=<envvar>`. Instead of directly passing a value for the given key, it instead allows the user to specify the name of an environment variable. The value of that variable will then be used as value of the key. Co-authored-by: Jeff King <[email protected]> Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b0812b6 commit ce81b1d

File tree

5 files changed

+100
-2
lines changed

5 files changed

+100
-2
lines changed

Documentation/git.txt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ SYNOPSIS
1313
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
1414
[-p|--paginate|-P|--no-pager] [--no-replace-objects] [--bare]
1515
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
16-
[--super-prefix=<path>]
16+
[--super-prefix=<path>] [--config-env <name>=<envvar>]
1717
<command> [<args>]
1818

1919
DESCRIPTION
@@ -80,6 +80,28 @@ config file). Including the equals but with an empty value (like `git -c
8080
foo.bar= ...`) sets `foo.bar` to the empty string which `git config
8181
--type=bool` will convert to `false`.
8282

83+
--config-env=<name>=<envvar>::
84+
Like `-c <name>=<value>`, give configuration variable
85+
'<name>' a value, where <envvar> is the name of an
86+
environment variable from which to retrieve the value. Unlike
87+
`-c` there is no shortcut for directly setting the value to an
88+
empty string, instead the environment variable itself must be
89+
set to the empty string. It is an error if the `<envvar>` does not exist
90+
in the environment. `<envvar>` may not contain an equals sign
91+
to avoid ambiguity with `<name>`s which contain one.
92+
+
93+
This is useful for cases where you want to pass transitory
94+
configuration options to git, but are doing so on OS's where
95+
other processes might be able to read your cmdline
96+
(e.g. `/proc/self/cmdline`), but not your environ
97+
(e.g. `/proc/self/environ`). That behavior is the default on
98+
Linux, but may not be on your system.
99+
+
100+
Note that this might add security for variables such as
101+
`http.extraHeader` where the sensitive information is part of
102+
the value, but not e.g. `url.<base>.insteadOf` where the
103+
sensitive information can be part of the key.
104+
83105
--exec-path[=<path>]::
84106
Path to wherever your core Git programs are installed.
85107
This can also be controlled by setting the GIT_EXEC_PATH

config.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,31 @@ void git_config_push_parameter(const char *text)
345345
strbuf_release(&env);
346346
}
347347

348+
void git_config_push_env(const char *spec)
349+
{
350+
struct strbuf buf = STRBUF_INIT;
351+
const char *env_name;
352+
const char *env_value;
353+
354+
env_name = strrchr(spec, '=');
355+
if (!env_name)
356+
die(_("invalid config format: %s"), spec);
357+
env_name++;
358+
if (!*env_name)
359+
die(_("missing environment variable name for configuration '%.*s'"),
360+
(int)(env_name - spec - 1), spec);
361+
362+
env_value = getenv(env_name);
363+
if (!env_value)
364+
die(_("missing environment variable '%s' for configuration '%.*s'"),
365+
env_name, (int)(env_name - spec - 1), spec);
366+
367+
strbuf_add(&buf, spec, env_name - spec);
368+
strbuf_addstr(&buf, env_value);
369+
git_config_push_parameter(buf.buf);
370+
strbuf_release(&buf);
371+
}
372+
348373
static inline int iskeychar(int c)
349374
{
350375
return isalnum(c) || c == '-';

config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ int git_config_from_mem(config_fn_t fn,
138138
int git_config_from_blob_oid(config_fn_t fn, const char *name,
139139
const struct object_id *oid, void *data);
140140
void git_config_push_parameter(const char *text);
141+
void git_config_push_env(const char *spec);
141142
int git_config_from_parameters(config_fn_t fn, void *data);
142143
void read_early_config(config_fn_t cb, void *data);
143144
void read_very_early_config(config_fn_t cb, void *data);

git.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const char git_usage_string[] =
2929
" [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
3030
" [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]\n"
3131
" [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
32-
" [--super-prefix=<path>]\n"
32+
" [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
3333
" <command> [<args>]");
3434

3535
const char git_more_info_string[] =
@@ -255,6 +255,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
255255
git_config_push_parameter((*argv)[1]);
256256
(*argv)++;
257257
(*argc)--;
258+
} else if (skip_prefix(cmd, "--config-env=", &cmd)) {
259+
git_config_push_env(cmd);
258260
} else if (!strcmp(cmd, "--literal-pathspecs")) {
259261
setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "1", 1);
260262
if (envchanged)

t/t1300-config.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,6 +1316,54 @@ test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
13161316
git config --get-regexp "env.*"
13171317
'
13181318

1319+
test_expect_success 'git --config-env=key=envvar support' '
1320+
cat >expect <<-\EOF &&
1321+
value
1322+
value
1323+
false
1324+
EOF
1325+
{
1326+
ENVVAR=value git --config-env=core.name=ENVVAR config core.name &&
1327+
ENVVAR=value git --config-env=foo.CamelCase=ENVVAR config foo.camelcase &&
1328+
ENVVAR= git --config-env=foo.flag=ENVVAR config --bool foo.flag
1329+
} >actual &&
1330+
test_cmp expect actual
1331+
'
1332+
1333+
test_expect_success 'git --config-env fails with invalid parameters' '
1334+
test_must_fail git --config-env=foo.flag config --bool foo.flag 2>error &&
1335+
test_i18ngrep "invalid config format: foo.flag" error &&
1336+
test_must_fail git --config-env=foo.flag= config --bool foo.flag 2>error &&
1337+
test_i18ngrep "missing environment variable name for configuration ${SQ}foo.flag${SQ}" error &&
1338+
sane_unset NONEXISTENT &&
1339+
test_must_fail git --config-env=foo.flag=NONEXISTENT config --bool foo.flag 2>error &&
1340+
test_i18ngrep "missing environment variable ${SQ}NONEXISTENT${SQ} for configuration ${SQ}foo.flag${SQ}" error
1341+
'
1342+
1343+
test_expect_success 'git -c and --config-env work together' '
1344+
cat >expect <<-\EOF &&
1345+
bar.cmd cmd-value
1346+
bar.env env-value
1347+
EOF
1348+
ENVVAR=env-value git \
1349+
-c bar.cmd=cmd-value \
1350+
--config-env=bar.env=ENVVAR \
1351+
config --get-regexp "^bar.*" >actual &&
1352+
test_cmp expect actual
1353+
'
1354+
1355+
test_expect_success 'git -c and --config-env override each other' '
1356+
cat >expect <<-\EOF &&
1357+
env
1358+
cmd
1359+
EOF
1360+
{
1361+
ENVVAR=env git -c bar.bar=cmd --config-env=bar.bar=ENVVAR config bar.bar &&
1362+
ENVVAR=env git --config-env=bar.bar=ENVVAR -c bar.bar=cmd config bar.bar
1363+
} >actual &&
1364+
test_cmp expect actual
1365+
'
1366+
13191367
test_expect_success 'git config --edit works' '
13201368
git config -f tmp test.value no &&
13211369
echo test.value=yes >expect &&

0 commit comments

Comments
 (0)