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

Reply via email to