From: Christian Couder <christian.couder@gmail.com>
To: git@vger.kernel.org
Cc: Junio C Hamano <gitster@pobox.com>,
"brian m . carlson" <sandals@crustytoothpaste.net>,
Patrick Steinhardt <ps@pks.im>,
Karthik Nayak <karthik.188@gmail.com>, Jeff King <peff@peff.net>,
Elijah Newren <newren@gmail.com>,
Christian Couder <christian.couder@gmail.com>
Subject: [PATCH v3 2/5] setup: extract path_allowlist_apply()
Date: Tue, 8 Sep 2026 18:41:26 +0200 [thread overview]
Message-ID: <20260908164129.560396-3-christian.couder@gmail.com> (raw)
In-Reply-To: <20260908164129.560396-1-christian.couder@gmail.com>
In a following commit we are going to check whether a repository is
part of an allowlist specified in a config variable.
To prepare for that let's extract existing code from
safe_directory_cb() into a new path_allowlist_apply() helper that will
help with such checks.
While at it let's make the helper's code simpler and more generic, by
passing it a `bool (*allow_path)(const char *path, void *cbdata)`
function that decides if a path is acceptable by the caller.
To further simplify how to reuse that new helper, and avoid duplicating
the config-value handling in a future commit, let's also introduce a
path_allowlist_config_apply() helper.
For clarity, let's change the `int is_safe` to `bool safe` in
`struct safe_directory_data`.
Signed-off-by: Christian Couder <christian.couder@gmail.com>
---
setup.c | 138 ++++++++++++++++++++++++++++++++++++--------------------
setup.h | 50 ++++++++++++++++++++
2 files changed, 138 insertions(+), 50 deletions(-)
diff --git a/setup.c b/setup.c
index dfe05d9a03..366a7dc5c0 100644
--- a/setup.c
+++ b/setup.c
@@ -1338,67 +1338,105 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
}
}
+void path_allowlist_apply(const char *allowed, const char *target_path,
+ bool *matches,
+ bool (*allow_path)(const char *path, void *cbdata),
+ void *allow_path_cbdata)
+{
+ char *normalized = NULL;
+
+ if (!allowed || !*allowed) {
+ *matches = false;
+ return;
+ }
+
+ if (!strcmp(allowed, "*")) {
+ *matches = true;
+ return;
+ }
+
+ if (!allow_path(allowed, allow_path_cbdata))
+ return;
+
+ /*
+ * A .gitconfig in $HOME may be shared across different
+ * machines and the config variable entries may or may not
+ * exist as paths on all of these machines. In other words,
+ * it is not a warning worthy event when there is no such path
+ * on this machine---the entry may be useful elsewhere.
+ */
+ normalized = real_pathdup(allowed, 0);
+ if (!normalized)
+ return;
+
+ if (ends_with(normalized, "/*")) {
+ size_t len = strlen(normalized);
+ if (!fspathncmp(normalized, target_path, len - 1))
+ *matches = true;
+ } else if (!fspathcmp(target_path, normalized)) {
+ *matches = true;
+ }
+
+ free(normalized);
+}
+
+void path_allowlist_config_apply(const char *key, const char *value,
+ const char *target_path, bool *matches,
+ bool (*allow_path)(const char *path, void *cbdata),
+ void *allow_path_cbdata)
+{
+ char *allowed = NULL;
+
+ if (!value || !*value || !strcmp(value, "*")) {
+ path_allowlist_apply(value, target_path, matches,
+ allow_path, allow_path_cbdata);
+ return;
+ }
+
+ if (git_config_pathname(&allowed, key, value) || !allowed)
+ return;
+
+ path_allowlist_apply(allowed, target_path, matches,
+ allow_path, allow_path_cbdata);
+
+ free(allowed);
+}
+
+/*
+ * Setting the config variable to a non-absolute path makes
+ * little sense---it won't be relative to the configuration
+ * file the item is defined in. Except for ".", which means
+ * "if we are at the top level of a repository, then it is
+ * OK", which is slightly tighter than "*" that allows
+ * discovery.
+ */
+static bool allow_safe_dir(const char *path, void *cbdata_)
+{
+ struct path_allowlist_cb_data *cbdata = cbdata_;
+
+ if (is_absolute_path(path) || !strcmp(path, "."))
+ return true;
+
+ warning(_("%s '%s' not absolute"), cbdata->key, path);
+ return false;
+}
+
struct safe_directory_data {
char *path;
- int is_safe;
+ bool safe;
};
static int safe_directory_cb(const char *key, const char *value,
const struct config_context *ctx UNUSED, void *d)
{
struct safe_directory_data *data = d;
+ struct path_allowlist_cb_data cbdata = { .key = key };
if (strcmp(key, "safe.directory"))
return 0;
- if (!value || !*value) {
- data->is_safe = 0;
- } else if (!strcmp(value, "*")) {
- data->is_safe = 1;
- } else {
- char *allowed = NULL;
-
- if (!git_config_pathname(&allowed, key, value) && allowed) {
- char *normalized = NULL;
-
- /*
- * Setting safe.directory to a non-absolute path
- * makes little sense---it won't be relative to
- * the configuration file the item is defined in.
- * Except for ".", which means "if we are at the top
- * level of a repository, then it is OK", which is
- * slightly tighter than "*" that allows discovery.
- */
- if (!is_absolute_path(allowed) && strcmp(allowed, ".")) {
- warning(_("safe.directory '%s' not absolute"),
- allowed);
- goto next;
- }
-
- /*
- * A .gitconfig in $HOME may be shared across
- * different machines and safe.directory entries
- * may or may not exist as paths on all of these
- * machines. In other words, it is not a warning
- * worthy event when there is no such path on this
- * machine---the entry may be useful elsewhere.
- */
- normalized = real_pathdup(allowed, 0);
- if (!normalized)
- goto next;
-
- if (ends_with(normalized, "/*")) {
- size_t len = strlen(normalized);
- if (!fspathncmp(normalized, data->path, len - 1))
- data->is_safe = 1;
- } else if (!fspathcmp(data->path, normalized)) {
- data->is_safe = 1;
- }
- next:
- free(normalized);
- free(allowed);
- }
- }
+ path_allowlist_config_apply(key, value, data->path, &data->safe,
+ allow_safe_dir, &cbdata);
return 0;
}
@@ -1440,7 +1478,7 @@ static int ensure_valid_ownership(const char *gitfile,
git_protected_config(safe_directory_cb, &data);
free(data.path);
- return data.is_safe;
+ return data.safe;
}
void die_upon_dubious_ownership(const char *gitfile, const char *worktree,
diff --git a/setup.h b/setup.h
index 763fd384e8..6b84fbe507 100644
--- a/setup.h
+++ b/setup.h
@@ -304,4 +304,54 @@ struct startup_info {
extern struct startup_info *startup_info;
extern const char *tmp_original_cwd;
+/* Path allowlist */
+
+struct path_allowlist_cb_data {
+ const char *key;
+};
+
+/*
+ * Check the allowlist entry in `allowed` against `target_path`,
+ * updating `*matches` accordingly.
+ *
+ * `allowed` is a single entry of an allowlist of paths, typically one
+ * value of a multi-valued config variable, already expanded by
+ * git_config_pathname(). `target_path` is the (normalized) path being
+ * tested. `*matches` is updated in place:
+ *
+ * - an empty `allowed` resets it to 'false' (so a later, more
+ * specific config scope can clear entries from a broader one),
+ * - "*" sets it to 'true' (allow everything),
+ * - "<path>" sets it to 'true' if <path> equals `target_path`,
+ * - "<path>" + "/" + "*" sets it to 'true' if <path> is a leading
+ * directory of `target_path`,
+ * - anything else leaves `*matches` unchanged.
+ *
+ * `allow_path` is called with `allowed` and `allow_path_cbdata`, and
+ * should return 'true' if the entry is acceptable to the caller. It
+ * lets each caller decide which paths it is willing to consider, and
+ * whether to warn about the ones it rejects. Returning 'false' leaves
+ * `*matches` unchanged.
+ *
+ * Callers are expected to invoke this once per allowlist entry,
+ * typically from a protected-config callback, so that untrusted
+ * repository config cannot influence the decision.
+ */
+void path_allowlist_apply(const char *allowed, const char *target_path,
+ bool *matches,
+ bool (*allow_path)(const char *path, void *cbdata),
+ void *allow_path_cbdata);
+
+/*
+ * Apply one value of a multi-valued config variable holding an
+ * allowlist of paths, expanding it with git_config_pathname() before
+ * checking it against `target_path`. Empty and "*" values are passed
+ * through without expansion, as interpolating them is not
+ * meaningful. See path_allowlist_apply().
+ */
+void path_allowlist_config_apply(const char *key, const char *value,
+ const char *target_path, bool *matches,
+ bool (*allow_path)(const char *path, void *cbdata),
+ void *allow_path_cbdata);
+
#endif /* SETUP_H */
--
2.55.0.792.ged91fccac1.dirty
next prev parent reply other threads:[~2026-09-08 16:41 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-10 8:51 [PATCH 0/3] Introduce a 'fromAccepted' option to GIT_NO_LAZY_FETCH Christian Couder
2026-07-10 8:51 ` [PATCH 1/3] promisor-remote: factor out lazy_fetch_objects() Christian Couder
2026-07-10 8:51 ` [PATCH 2/3] promisor-remote: introduce enum allow_lazy_fetch Christian Couder
2026-07-10 8:51 ` [PATCH 3/3] promisor-remote: teach 'fromAccepted' to GIT_NO_LAZY_FETCH Christian Couder
2026-07-10 19:50 ` [PATCH 0/3] Introduce a 'fromAccepted' option " brian m. carlson
2026-07-12 9:06 ` Christian Couder
2026-08-07 13:55 ` [PATCH 0/5] Introduce 'uploadpack.lazyFetchTrusted' Christian Couder
2026-08-07 13:55 ` [PATCH 1/5] promisor-remote: factor out lazy_fetch_objects() Christian Couder
2026-08-07 13:58 ` Christian Couder
2026-08-07 13:55 ` [PATCH 2/5] setup: extract path_allowlist_apply() Christian Couder
2026-08-07 13:55 ` [PATCH 3/5] setup: add 'allow_dot' arg to path_allowlist_apply() Christian Couder
2026-08-07 13:55 ` [PATCH 4/5] upload-pack: read uploadpack.lazyFetchTrusted Christian Couder
2026-08-07 13:55 ` [PATCH 5/5] builtin/upload-pack: set GIT_NO_LAZY_FETCH to 0 on trusted repo Christian Couder
2026-08-07 18:31 ` [PATCH 0/5] Introduce 'uploadpack.lazyFetchTrusted' Junio C Hamano
2026-08-10 8:06 ` Christian Couder
2026-08-11 5:55 ` Junio C Hamano
2026-08-13 15:47 ` [PATCH v2 " Christian Couder
2026-08-13 20:31 ` Junio C Hamano
2026-08-14 16:31 ` Christian Couder
2026-08-14 16:40 ` Junio C Hamano
2026-09-08 16:41 ` [PATCH v3 " Christian Couder
2026-09-08 16:41 ` [PATCH v3 1/5] promisor-remote: factor out lazy_fetch_objects() Christian Couder
2026-09-08 17:39 ` Junio C Hamano
2026-09-08 16:41 ` Christian Couder [this message]
2026-09-08 17:48 ` [PATCH v3 2/5] setup: extract path_allowlist_apply() Junio C Hamano
2026-09-08 16:41 ` [PATCH v3 3/5] upload-pack: read uploadpack.lazyFetchTrusted Christian Couder
2026-09-08 16:41 ` [PATCH v3 4/5] promisor-remote: prevent infinite recursion when lazy fetching Christian Couder
2026-09-08 18:12 ` Junio C Hamano
2026-09-09 10:00 ` Christian Couder
2026-09-09 21:39 ` Junio C Hamano
2026-09-08 16:41 ` [PATCH v3 5/5] builtin/upload-pack: set GIT_NO_LAZY_FETCH to 0 on trusted repo Christian Couder
2026-09-08 18:34 ` Junio C Hamano
2026-08-13 15:47 ` [PATCH v2 1/5] promisor-remote: factor out lazy_fetch_objects() Christian Couder
2026-08-14 17:49 ` Junio C Hamano
2026-09-08 17:11 ` Christian Couder
2026-08-13 15:47 ` [PATCH v2 2/5] setup: extract path_allowlist_apply() Christian Couder
2026-08-14 17:56 ` Junio C Hamano
2026-09-08 16:46 ` Christian Couder
2026-09-08 17:49 ` Junio C Hamano
2026-08-13 15:47 ` [PATCH v2 3/5] setup: add 'allow_dot' arg to path_allowlist_apply() Christian Couder
2026-08-14 18:12 ` Junio C Hamano
2026-09-08 16:55 ` Christian Couder
2026-08-13 15:47 ` [PATCH v2 4/5] upload-pack: read uploadpack.lazyFetchTrusted Christian Couder
2026-08-14 18:56 ` Junio C Hamano
2026-08-13 15:47 ` [PATCH v2 5/5] builtin/upload-pack: set GIT_NO_LAZY_FETCH to 0 on trusted repo Christian Couder
2026-08-14 19:35 ` Junio C Hamano
2026-09-08 17:02 ` Christian Couder
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=20260908164129.560396-3-christian.couder@gmail.com \
--to=christian.couder@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=karthik.188@gmail.com \
--cc=newren@gmail.com \
--cc=peff@peff.net \
--cc=ps@pks.im \
--cc=sandals@crustytoothpaste.net \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.