This adds a variant of the include directive, where only certain config variables in the included files are honoured. The set of honoured variables consists of those the user has mentioned in a safe-include.whitelist directive, along with a small set of git.git blessed ones.
This can, for example, be used by a project to supply a set of suggested configuration variables, such as "diff.renames = true". The project would provide these in e.g project.gitconfig, and the user then has to explicitly opt-in by putting [safe-include] path = ../project.gitconfig into .git/config, possibly preceding the path directive with a whitelist directive. The problem with simply using the ordinary include directive for this purpose is that certain configuration variables (e.g. diff.external) can allow arbitrary programs to be run. Older versions of git do not understand the safe-include directives, so they will effectively just ignore them. Obviously, we must ignore safe-include.whitelist directives when we are processing a safe-included file. Signed-off-by: Rasmus Villemoes <r...@rasmusvillemoes.dk> --- config.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 3 deletions(-) diff --git a/config.c b/config.c index a677eb6..764cda1 100644 --- a/config.c +++ b/config.c @@ -11,6 +11,7 @@ #include "quote.h" #include "hashmap.h" #include "string-list.h" +#include "wildmatch.h" struct config_source { struct config_source *prev; @@ -39,6 +40,79 @@ static struct config_source *cf; static int zlib_compression_seen; +struct safe_var { + struct safe_var *next; + const char *pattern; + int blacklisted; +}; + +static int safe_include_depth; +static struct safe_var *safe_var_head; + +static const char *builtin_safe_patterns[] = { + "diff.renames", +}; + +static int config_name_is_safe(const char *var) +{ + struct safe_var *sv; + unsigned i; + + for (sv = safe_var_head; sv; sv = sv->next) { + /* Handle malformed patterns? */ + if (wildmatch(sv->pattern, var, WM_CASEFOLD, NULL) == WM_MATCH) + return !sv->blacklisted; + } + for (i = 0; i < ARRAY_SIZE(builtin_safe_patterns); ++i) { + if (wildmatch(builtin_safe_patterns[i], var, WM_CASEFOLD, NULL) == WM_MATCH) + return 1; + } + + return 0; +} + +static void config_add_safe_pattern(const char *p) +{ + struct safe_var *sv; + int blacklist = 0; + + if (*p == '!') { + blacklist = 1; + ++p; + } + if (!*p) + return; + sv = xmalloc(sizeof(*sv)); + sv->pattern = xstrdup(p); + sv->blacklisted = blacklist; + sv->next = safe_var_head; + safe_var_head = sv; +} + +static void config_add_safe_names(const char *value) +{ + char *patterns = xstrdup(value); + char *p, *save; + + /* + * This allows giving multiple patterns in a single line, e.g. + * + * whitelist = !* foo.bar squirrel.* + * + * to override the builtin list of safe vars and only declare + * foo.bar and the squirrel section safe. But it has the + * obvious drawback that one cannot match subsection names + * containing whitespace. The alternative is that the above + * would have to be written on three separate whitelist lines. + */ + for (p = strtok_r(patterns, " \t", &save); p; p = strtok_r(NULL, " \t", &save)) { + config_add_safe_pattern(p); + } + + free(patterns); +} + + /* * Default config_set that contains key-value pairs from the usual set of config * config files (i.e repo specific .git/config, user wide ~/.gitconfig, XDG @@ -142,12 +216,23 @@ int git_config_include(const char *var, const char *value, void *data) * Pass along all values, including "include" directives; this makes it * possible to query information on the includes themselves. */ - ret = inc->fn(var, value, inc->data); - if (ret < 0) - return ret; + if (safe_include_depth == 0 || config_name_is_safe(var)) { + ret = inc->fn(var, value, inc->data); + if (ret < 0) + return ret; + } if (!strcmp(var, "include.path")) ret = handle_path_include(value, inc); + else if (safe_include_depth == 0 + && !strcmp(var, "safe-include.whitelist")) { + config_add_safe_names(value); + } + else if (!strcmp(var, "safe-include.path")) { + safe_include_depth++; + ret = handle_path_include(value, inc); + safe_include_depth--; + } return ret; } -- 2.0.4 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html