From: Zeger-Jan van de Weg <git@zjvandeweg.nl>
To: git@vger.kernel.org
Cc: Zeger-Jan van de Weg <git@zjvandeweg.nl>
Subject: [PATCH v2 1/1] config: learn the --stdin option for instructions
Date: Mon, 27 Jan 2020 11:09:33 +0100 [thread overview]
Message-ID: <20200127100933.10765-2-git@zjvandeweg.nl> (raw)
In-Reply-To: <20200127100933.10765-1-git@zjvandeweg.nl>
When setting values in the git config, the value is part of the
arguments for execution. This potentially leaks the value through
logging, or other programs like `ps`.
Add the `--stdin` option that reads from stdin for instructions to set
and unset values to hide them from prying eyes. The instructions are based
on the `update-ref` DSL, and accept the set and unset commands.
Signed-off-by: Zeger-Jan van de Weg <git@zjvandeweg.nl>
---
Documentation/git-config.txt | 29 ++++++++++
builtin/config.c | 104 +++++++++++++++++++++++++++++++++++
t/t1300-config.sh | 60 ++++++++++++++++++++
3 files changed, 193 insertions(+)
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 899e92a1c9..9f7462284d 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -23,6 +23,7 @@ SYNOPSIS
'git config' [<file-option>] [--show-origin] [-z|--null] [--name-only] -l | --list
'git config' [<file-option>] --get-color name [default]
'git config' [<file-option>] --get-colorbool name [stdout-is-tty]
+'git config' [<file-option>] [-z|--null] --stdin
'git config' [<file-option>] -e | --edit
DESCRIPTION
@@ -212,6 +213,10 @@ Valid `<type>`'s include:
output without getting confused e.g. by values that
contain line breaks.
+--stdin::
+ Instructions are read from the standard input, instead of the command
+ line interface. Refer to the STDIN section.
+
--name-only::
Output only the names of config variables for `--list` or
`--get-regexp`.
@@ -259,6 +264,30 @@ Valid `<type>`'s include:
When using `--get`, and the requested variable is not found, behave as if
<value> were the value assigned to the that variable.
+STDIN
+-----
+
+With `--stdin`, config reads instructions from standard input and performs
+all modifications in sequence.
+
+Specify commands of the form:
+
+ set SP <key> SP <newvalue>
+ unset SP <key>
+
+Alternatively, use `-z` or `--null` to specify in NUL-terminated format, without
+quoting:
+
+ set SP <key> NULL <newvalue>
+ unset SP <key>
+
+set::
+ Set or update the value for <key> to <newvalue>.
+
+unset:
+ Remove the value from the configuration by <key>, when the <key> isn't
+ present in the configuration no error is returned.
+
CONFIGURATION
-------------
`pager.config` is only respected when listing configuration, i.e., when
diff --git a/builtin/config.c b/builtin/config.c
index 98d65bc0ad..a449b00b65 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -50,6 +50,7 @@ static int show_origin;
#define ACTION_GET_COLOR (1<<13)
#define ACTION_GET_COLORBOOL (1<<14)
#define ACTION_GET_URLMATCH (1<<15)
+#define ACTION_STDIN (1<<16)
/*
* The actions "ACTION_LIST | ACTION_GET_*" which may produce more than
@@ -143,6 +144,7 @@ static struct option builtin_config_options[] = {
OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
+ OPT_BIT(0, "stdin", &actions, N_("input changes from stdin"), ACTION_STDIN),
OPT_GROUP(N_("Type")),
OPT_CALLBACK('t', "type", &type, "", N_("value is given this type"), option_parse_type),
OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
@@ -594,6 +596,102 @@ static char *default_user_config(void)
return strbuf_detach(&buf, NULL);
}
+static char *parse_key_or_value(const char **next, const char *max_char) {
+ struct strbuf token = STRBUF_INIT;
+
+ if (term) {
+ if (**next == '"') {
+ const char **orig = next;
+
+ if (unquote_c_style(&token, *next, next))
+ die("badly quoted argument: %s", *orig);
+ if (*next && !isspace(**next))
+ die("unexpected character after quoted argument: %s", *orig);
+ } else {
+ while (*next && !isspace(**next)) {
+ if (*next > max_char)
+ die("unexpected end of buffer");
+
+ strbuf_addch(&token, *(*next)++);
+ }
+ }
+
+ (*next)++;
+ } else {
+ strbuf_addstr(&token, *next);
+ *next += token.len + 1;
+ }
+
+ return strbuf_detach(&token, NULL);
+}
+
+static const char *parse_cmd_set(const char *next, const char *max_char)
+{
+ char *key, *value;
+ int ret;
+
+ key = parse_key_or_value(&next, max_char);
+ if (!key)
+ die(_("set: missing key"));
+
+ value = parse_key_or_value(&next, max_char);
+ if (!value)
+ die(_("set: missing value"));
+
+ ret = git_config_set_in_file_gently(given_config_source.file, key, value);
+ if (ret)
+ die(_("cannot set key value pair: %d"), ret);
+
+ free(key);
+ free(value);
+ return next;
+}
+
+
+static const char *parse_cmd_unset(const char *next, const char *max_char)
+{
+ char *key;
+ int ret;
+
+ key = parse_key_or_value(&next, max_char);
+ if (!key)
+ die(_("no key found to unset"));
+
+ ret = git_config_set_in_file_gently(given_config_source.file, key, NULL);
+ if (ret)
+ die(_("cannot unset key: %d"), ret);
+
+ free(key);
+ return next;
+}
+
+static void update_config_stdin()
+{
+ struct strbuf input = STRBUF_INIT;
+ const char *next;
+ const char *max_char;
+
+ if (strbuf_read(&input, 0, 1000) < 0)
+ die_errno(_("could not read from stdin"));
+ next = input.buf;
+
+ max_char = input.buf + input.len;
+ while(next < max_char) {
+ if (*next == term)
+ die(_("empty command in input"));
+ else if (isspace(*next))
+ die(_("whitespace before command"));
+ else if (skip_prefix(next, "set ", &next))
+ next = parse_cmd_set(next, max_char);
+ else if (skip_prefix(next, "unset ", &next))
+ next = parse_cmd_unset(next, max_char);
+ else
+ die(_("unknown command %s"), next);
+ }
+
+ strbuf_release(&input);
+}
+
int cmd_config(int argc, const char **argv, const char *prefix)
{
int nongit = !startup_info->have_repository;
@@ -867,6 +965,12 @@ int cmd_config(int argc, const char **argv, const char *prefix)
color_stdout_is_tty = git_config_bool("command line", argv[1]);
return get_colorbool(argv[0], argc == 2);
}
+ else if (actions == ACTION_STDIN) {
+ check_write();
+ check_argc(argc, 0, 0);
+
+ update_config_stdin();
+ }
return 0;
}
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 983a0a1583..d8ad82922d 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -380,6 +380,66 @@ test_expect_success '--add' '
test_cmp expect output
'
+test_expect_success '--stdin to set a value' '
+ echo "set foo.mepmep true" | git config --stdin &&
+ git config --get foo.mepmep >output &&
+ echo true >expect &&
+ test_cmp expect output
+'
+
+test_expect_success '--stdin multiline support' '
+ cat >stdin <<-EOF &&
+ set bar.mepmep true
+ set foo.mepmep true
+ EOF
+ git config --stdin <stdin &&
+ git config --get bar.mepmep >output &&
+ git config --get foo.mepmep >>output &&
+ echo -e "true\ntrue" > expect &&
+ test_cmp expect output
+'
+
+test_expect_success '--stdin hides input on errors' '
+ cat >stdin <<-EOF &&
+ set bar.mepmep
+ EOF
+ test_must_fail git config --stdin <stdin 2>output &&
+ echo "fatal: unexpected end of buffer" >expect &&
+ test_cmp expect output
+'
+
+test_expect_success '--stdin fails on leading whitespace' '
+ cat >stdin <<-EOF &&
+ set bar.mepmep
+ EOF
+ test_must_fail git config --stdin <stdin
+'
+
+test_expect_success '--stdin fails on unknown command' '
+ cat >stdin <<-EOF &&
+ foo bar.mepmep
+ EOF
+ test_must_fail git config --stdin <stdin
+'
+
+test_expect_success '--stdin works on no input' '
+ echo -n "" | git config --stdin
+'
+
+test_expect_success '--stdin fails on unbalanced quotes' '
+ cat >stdin <<-EOF &&
+ set "foo.bar
+ EOF
+ test_must_fail git config --stdin <stdin
+'
+
+test_expect_success '--stdin with --null flag' '
+ echo -ne "set bar.baz\0false" | git config --stdin --null &&
+ git config --get bar.baz >output &&
+ echo false >expect &&
+ test_cmp expect output
+'
+
cat > .git/config << EOF
[novalue]
variable
--
2.24.1
next prev parent reply other threads:[~2020-01-27 10:17 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-01-27 10:09 [PATCH v2 0/1] config: read instructions from stdin Zeger-Jan van de Weg
2020-01-27 10:09 ` Zeger-Jan van de Weg [this message]
2020-01-27 11:44 ` [PATCH v2 1/1] config: learn the --stdin option for instructions Christian Couder
2020-01-27 16:59 ` Eric Sunshine
2020-01-28 9:24 ` Jeff King
2020-01-28 13:42 ` Eric Sunshine
2020-01-28 19:28 ` Junio C Hamano
2020-01-29 2:37 ` Jeff King
2020-01-28 9:19 ` Jeff King
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200127100933.10765-2-git@zjvandeweg.nl \
--to=git@zjvandeweg.nl \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).