From: Christian Couder <christian.couder@gmail.com>
To: git@vger.kernel.org
Cc: Junio C Hamano <gitster@pobox.com>,
Patrick Steinhardt <ps@pks.im>, Taylor Blau <me@ttaylorr.com>,
Karthik Nayak <karthik.188@gmail.com>,
Justin Tobler <jltobler@gmail.com>,
Christian Couder <christian.couder@gmail.com>,
Christian Couder <chriscool@tuxfamily.org>
Subject: [PATCH v5 4/5] promisor-remote: allow a client to check fields
Date: Wed, 25 Jun 2025 14:50:54 +0200 [thread overview]
Message-ID: <20250625125055.1375596-5-christian.couder@gmail.com> (raw)
In-Reply-To: <20250625125055.1375596-1-christian.couder@gmail.com>
A previous commit allowed a server to pass additional fields through
the "promisor-remote" protocol capability after the "name" and "url"
fields, specifically the "partialCloneFilter" and "token" fields.
Let's make it possible for a client to check if these fields match
what it expects before accepting a promisor remote.
We allow this by introducing a new "promisor.checkFields"
configuration variable. It should contain a comma or space separated
list of fields that will be checked.
By limiting the protocol to specific well-defined fields, we ensure
both server and client have a shared understanding of field
semantics and usage.
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
Documentation/config/promisor.adoc | 40 ++++++++++++
promisor-remote.c | 89 ++++++++++++++++++++++++---
t/t5710-promisor-remote-capability.sh | 34 ++++++++++
3 files changed, 155 insertions(+), 8 deletions(-)
diff --git a/Documentation/config/promisor.adoc b/Documentation/config/promisor.adoc
index beb8f65518..9682ada23c 100644
--- a/Documentation/config/promisor.adoc
+++ b/Documentation/config/promisor.adoc
@@ -50,3 +50,43 @@ promisor.acceptFromServer::
lazily fetchable from this promisor remote from its responses
to "fetch" and "clone" requests from the client. Name and URL
comparisons are case sensitive. See linkgit:gitprotocol-v2[5].
+
+promisor.checkFields::
+ A comma or space separated list of additional remote related
+ field names. A client will check if the values of these fields
+ transmitted by a server correspond to the values of these
+ fields in its own configuration before accepting a promisor
+ remote. Currently, "partialCloneFilter" and "token" are the
+ only supported field names.
++
+If one of these field names (e.g., "token") is being checked for an
+advertised promisor remote (e.g., "foo"), three conditions must be met
+for the check of this specific field to pass:
++
+1. The corresponding local configuration (e.g., `remote.foo.token`)
+ must be set.
+2. The server must advertise the "token" field for remote "foo".
+3. The value of the locally configured `remote.foo.token` must exactly
+ match the value advertised by the server for the "token" field.
++
+If any of these conditions is not met for any field name listed in
+`promisor.checkFields`, the advertised remote "foo" will be rejected.
++
+For the "partialCloneFilter" field, this allows the client to ensure
+that the server's filter matches what it expects locally, preventing
+inconsistencies in filtering behavior. For the "token" field, this can
+be used to verify that authentication credentials match expected
+values.
++
+Field names are compared case-insensitively. Field values are compared
+case-sensitively.
++
+The "name" and "url" fields are always checked according to the
+`promisor.acceptFromServer` policy, independently of this setting.
++
+The field names and values should be passed by the server through the
+"promisor-remote" capability by using the `promisor.sendFields` config
+variable. The fields will be checked only if the
+`promisor.acceptFromServer` config variable is not set to "None". If
+set to "None", this config variable will have no effect. See
+linkgit:gitprotocol-v2[5].
diff --git a/promisor-remote.c b/promisor-remote.c
index b68772d573..049406c882 100644
--- a/promisor-remote.c
+++ b/promisor-remote.c
@@ -388,6 +388,20 @@ static struct string_list *fields_sent(void)
return &fields_list;
}
+static struct string_list *fields_checked(void)
+{
+ static struct string_list fields_list = STRING_LIST_INIT_NODUP;
+ static int initialized = 0;
+
+ if (!initialized) {
+ fields_list.cmp = strcasecmp;
+ fields_from_config(&fields_list, "promisor.checkFields");
+ initialized = 1;
+ }
+
+ return &fields_list;
+}
+
/*
* Struct for promisor remotes involved in the "promisor-remote"
* protocol capability.
@@ -533,6 +547,61 @@ enum accept_promisor {
ACCEPT_ALL
};
+static int match_field_against_config(const char *field, const char *value,
+ struct promisor_info *config_info)
+{
+ if (config_info->filter && !strcasecmp(field, promisor_field_filter))
+ return !strcmp(config_info->filter, value);
+ else if (config_info->token && !strcasecmp(field, promisor_field_token))
+ return !strcmp(config_info->token, value);
+
+ return 0;
+}
+
+static int all_fields_match(struct promisor_info *advertised,
+ struct string_list *config_info,
+ int in_list)
+{
+ struct string_list* fields = fields_checked();
+ struct string_list_item *item_checked;
+
+ for_each_string_list_item(item_checked, fields) {
+ int match = 0;
+ const char *field = item_checked->string;
+ const char *value = NULL;
+ struct string_list_item *item;
+
+ if (!strcasecmp(field, promisor_field_filter))
+ value = advertised->filter;
+ else if (!strcasecmp(field, promisor_field_token))
+ value = advertised->token;
+
+ if (!value)
+ return 0;
+
+ if (in_list) {
+ for_each_string_list_item(item, config_info) {
+ struct promisor_info *p = item->util;
+ if (match_field_against_config(field, value, p)) {
+ match = 1;
+ break;
+ }
+ }
+ } else {
+ item = string_list_lookup(config_info, advertised->name);
+ if (item) {
+ struct promisor_info *p = item->util;
+ match = match_field_against_config(field, value, p);
+ }
+ }
+
+ if (!match)
+ return 0;
+ }
+
+ return 1;
+}
+
static int should_accept_remote(enum accept_promisor accept,
struct promisor_info *advertised,
struct string_list *config_info)
@@ -543,7 +612,7 @@ static int should_accept_remote(enum accept_promisor accept,
const char *remote_url = advertised->url;
if (accept == ACCEPT_ALL)
- return 1;
+ return all_fields_match(advertised, config_info, 1);
/* Get config info for that promisor remote */
item = string_list_lookup(config_info, remote_name);
@@ -555,7 +624,7 @@ static int should_accept_remote(enum accept_promisor accept,
p = item->util;
if (accept == ACCEPT_KNOWN_NAME)
- return 1;
+ return all_fields_match(advertised, config_info, 0);
if (accept != ACCEPT_KNOWN_URL)
BUG("Unhandled 'enum accept_promisor' value '%d'", accept);
@@ -570,7 +639,7 @@ static int should_accept_remote(enum accept_promisor accept,
remote_name);
if (!strcmp(p->url, remote_url))
- return 1;
+ return all_fields_match(advertised, config_info, 0);
warning(_("known remote named '%s' but with URL '%s' instead of '%s'"),
remote_name, p->url, remote_url);
@@ -602,6 +671,10 @@ static struct promisor_info *parse_one_advertised_remote(struct strbuf *remote_i
info->name = value;
else if (!strcmp(elem, "url"))
info->url = value;
+ else if (!strcasecmp(elem, promisor_field_filter))
+ info->filter = value;
+ else if (!strcasecmp(elem, promisor_field_token))
+ info->token = value;
else
free(value);
}
@@ -644,11 +717,6 @@ static void filter_promisor_remote(struct repository *repo,
if (accept == ACCEPT_NONE)
return;
- if (accept != ACCEPT_ALL) {
- promisor_config_info_list(repo, &config_info, NULL);
- string_list_sort(&config_info);
- }
-
/* Parse remote info received */
remotes = strbuf_split_str(info, ';', 0);
@@ -663,6 +731,11 @@ static void filter_promisor_remote(struct repository *repo,
if (!advertised)
continue;
+ if (!config_info.nr) {
+ promisor_config_info_list(repo, &config_info, fields_checked());
+ string_list_sort(&config_info);
+ }
+
if (should_accept_remote(accept, advertised, &config_info))
strvec_push(accepted, advertised->name);
diff --git a/t/t5710-promisor-remote-capability.sh b/t/t5710-promisor-remote-capability.sh
index 204528b2e0..023735d6a8 100755
--- a/t/t5710-promisor-remote-capability.sh
+++ b/t/t5710-promisor-remote-capability.sh
@@ -326,6 +326,40 @@ test_expect_success "clone with promisor.sendFields" '
check_missing_objects server 1 "$oid"
'
+test_expect_success "clone with promisor.checkFields" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ git -C server remote add otherLop "https://invalid.invalid" &&
+ git -C server config remote.otherLop.token "fooBar" &&
+ git -C server config remote.otherLop.stuff "baz" &&
+ git -C server config remote.otherLop.partialCloneFilter "blob:limit=10k" &&
+ test_when_finished "git -C server remote remove otherLop" &&
+ test_config -C server promisor.sendFields "partialCloneFilter, token" &&
+ test_when_finished "rm trace" &&
+
+ # Clone from server to create a client
+ GIT_TRACE_PACKET="$(pwd)/trace" GIT_NO_LAZY_FETCH=0 git clone \
+ -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c remote.lop.partialCloneFilter="blob:none" \
+ -c promisor.acceptfromserver=All \
+ -c promisor.checkFields=partialcloneFilter \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that fields are properly transmitted
+ ENCODED_URL=$(echo "file://$(pwd)/lop" | sed -e "s/ /%20/g") &&
+ PR1="name=lop,url=$ENCODED_URL,partialCloneFilter=blob:none" &&
+ PR2="name=otherLop,url=https://invalid.invalid,partialCloneFilter=blob:limit=10k,token=fooBar" &&
+ test_grep "clone< promisor-remote=$PR1;$PR2" trace &&
+ test_grep "clone> promisor-remote=lop" trace &&
+ test_grep ! "clone> promisor-remote=lop;otherLop" trace &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
test_expect_success "clone with promisor.advertise set to 'true' but don't delete the client" '
git -C server config promisor.advertise true &&
--
2.50.0.2.g875523421d
next prev parent reply other threads:[~2025-06-25 12:51 UTC|newest]
Thread overview: 107+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-14 16:03 [PATCH 0/4] Make the "promisor-remote" capability support extra fields Christian Couder
2025-04-14 16:03 ` [PATCH 1/4] config: move is_config_key_char() to "config.h" Christian Couder
2025-04-14 16:03 ` [PATCH 2/4] promisor-remote: refactor to get rid of 'struct strvec' Christian Couder
2025-04-22 10:13 ` Patrick Steinhardt
2025-04-29 15:12 ` Christian Couder
2025-04-14 16:03 ` [PATCH 3/4] promisor-remote: allow a server to advertise extra fields Christian Couder
2025-04-14 22:04 ` Junio C Hamano
2025-04-22 10:13 ` Patrick Steinhardt
2025-04-29 15:12 ` Christian Couder
2025-04-29 15:12 ` Christian Couder
2025-04-14 16:03 ` [PATCH 4/4] promisor-remote: allow a client to check " Christian Couder
2025-04-29 14:52 ` [PATCH v2 0/3] Make the "promisor-remote" capability support more fields Christian Couder
2025-04-29 14:52 ` [PATCH v2 1/3] promisor-remote: refactor to get rid of 'struct strvec' Christian Couder
2025-05-07 8:25 ` Patrick Steinhardt
2025-05-19 14:10 ` Christian Couder
2025-05-07 12:27 ` Karthik Nayak
2025-05-19 14:10 ` Christian Couder
2025-04-29 14:52 ` [PATCH v2 2/3] promisor-remote: allow a server to advertise more fields Christian Couder
2025-05-07 8:25 ` Patrick Steinhardt
2025-05-19 14:11 ` Christian Couder
2025-05-27 7:50 ` Patrick Steinhardt
2025-05-27 15:30 ` Junio C Hamano
2025-06-11 13:46 ` Christian Couder
2025-05-07 12:44 ` Karthik Nayak
2025-05-19 14:11 ` Christian Couder
2025-04-29 14:52 ` [PATCH v2 3/3] promisor-remote: allow a client to check fields Christian Couder
2025-05-07 8:25 ` Patrick Steinhardt
2025-05-19 14:11 ` Christian Couder
2025-05-02 9:34 ` [PATCH v2 0/3] Make the "promisor-remote" capability support more fields Christian Couder
2025-05-19 14:12 ` [PATCH v3 0/5] " Christian Couder
2025-05-19 14:12 ` [PATCH v3 1/5] promisor-remote: refactor to get rid of 'struct strvec' Christian Couder
2025-05-20 9:37 ` Karthik Nayak
2025-05-20 13:32 ` Christian Couder
2025-05-20 16:45 ` Junio C Hamano
2025-05-21 6:33 ` Christian Couder
2025-05-21 15:00 ` Junio C Hamano
2025-06-11 13:47 ` Christian Couder
2025-05-19 14:12 ` [PATCH v3 2/5] promisor-remote: allow a server to advertise more fields Christian Couder
2025-05-21 20:31 ` Justin Tobler
2025-06-11 13:46 ` Christian Couder
2025-05-27 7:51 ` Patrick Steinhardt
2025-06-11 13:46 ` Christian Couder
2025-05-19 14:12 ` [PATCH v3 3/5] promisor-remote: refactor how we parse advertised fields Christian Couder
2025-05-19 14:12 ` [PATCH v3 4/5] promisor-remote: allow a client to check fields Christian Couder
2025-05-19 14:12 ` [PATCH v3 5/5] promisor-remote: use string constants for 'name' and 'url' too Christian Couder
2025-06-11 13:45 ` [PATCH v4 0/5] Make the "promisor-remote" capability support more fields Christian Couder
2025-06-11 13:45 ` [PATCH v4 1/5] promisor-remote: refactor to get rid of 'struct strvec' Christian Couder
2025-06-19 11:53 ` Karthik Nayak
2025-06-25 12:53 ` Christian Couder
2025-06-23 19:38 ` Justin Tobler
2025-06-25 12:52 ` Christian Couder
2025-06-11 13:45 ` [PATCH v4 2/5] promisor-remote: allow a server to advertise more fields Christian Couder
2025-06-19 12:15 ` Karthik Nayak
2025-06-25 12:51 ` Christian Couder
2025-06-23 19:59 ` Justin Tobler
2025-06-25 12:51 ` Christian Couder
2025-06-11 13:45 ` [PATCH v4 3/5] promisor-remote: refactor how we parse advertised fields Christian Couder
2025-06-11 13:45 ` [PATCH v4 4/5] promisor-remote: allow a client to check fields Christian Couder
2025-06-11 13:45 ` [PATCH v4 5/5] promisor-remote: use string constants for 'name' and 'url' too Christian Couder
2025-06-19 12:18 ` [PATCH v4 0/5] Make the "promisor-remote" capability support more fields Karthik Nayak
2025-06-25 12:50 ` [PATCH v5 " Christian Couder
2025-06-25 12:50 ` [PATCH v5 1/5] promisor-remote: refactor to get rid of 'struct strvec' Christian Couder
2025-06-25 17:05 ` Junio C Hamano
2025-07-21 14:08 ` Christian Couder
2025-06-25 12:50 ` [PATCH v5 2/5] promisor-remote: allow a server to advertise more fields Christian Couder
2025-06-25 22:29 ` Junio C Hamano
2025-07-21 14:09 ` Christian Couder
2025-07-21 18:53 ` Junio C Hamano
2025-07-31 7:20 ` Christian Couder
2025-06-27 18:47 ` Jean-Noël Avila
2025-07-21 14:09 ` Christian Couder
2025-06-25 12:50 ` [PATCH v5 3/5] promisor-remote: refactor how we parse advertised fields Christian Couder
2025-06-25 12:50 ` Christian Couder [this message]
2025-06-25 12:50 ` [PATCH v5 5/5] promisor-remote: use string constants for 'name' and 'url' too Christian Couder
2025-07-07 22:35 ` [PATCH v5 0/5] Make the "promisor-remote" capability support more fields Junio C Hamano
2025-07-08 3:34 ` Christian Couder
2025-07-21 14:10 ` [PATCH v6 " Christian Couder
2025-07-21 14:10 ` [PATCH v6 1/5] promisor-remote: refactor to get rid of 'struct strvec' Christian Couder
2025-07-21 14:10 ` [PATCH v6 2/5] promisor-remote: allow a server to advertise more fields Christian Couder
2025-07-21 14:10 ` [PATCH v6 3/5] promisor-remote: refactor how we parse advertised fields Christian Couder
2025-07-21 20:39 ` Junio C Hamano
2025-07-31 7:22 ` Christian Couder
2025-07-21 14:10 ` [PATCH v6 4/5] promisor-remote: allow a client to check fields Christian Couder
2025-07-21 20:59 ` Junio C Hamano
2025-07-31 7:21 ` Christian Couder
2025-07-21 14:10 ` [PATCH v6 5/5] promisor-remote: use string constants for 'name' and 'url' too Christian Couder
2025-07-21 20:18 ` Junio C Hamano
2025-07-31 7:23 ` [PATCH v7 0/5] Make the "promisor-remote" capability support more fields Christian Couder
2025-07-31 7:23 ` [PATCH v7 1/5] promisor-remote: refactor to get rid of 'struct strvec' Christian Couder
2025-07-31 7:23 ` [PATCH v7 2/5] promisor-remote: allow a server to advertise more fields Christian Couder
2025-07-31 7:23 ` [PATCH v7 3/5] promisor-remote: refactor how we parse advertised fields Christian Couder
2025-07-31 16:03 ` Junio C Hamano
2025-09-08 5:31 ` Christian Couder
2025-07-31 7:23 ` [PATCH v7 4/5] promisor-remote: allow a client to check fields Christian Couder
2025-07-31 7:23 ` [PATCH v7 5/5] promisor-remote: use string constants for 'name' and 'url' too Christian Couder
2025-07-31 15:48 ` [PATCH v7 0/5] Make the "promisor-remote" capability support more fields Junio C Hamano
2025-08-28 23:32 ` Junio C Hamano
2025-09-08 5:36 ` Christian Couder
2025-09-08 5:30 ` [PATCH v8 0/7] " Christian Couder
2025-09-08 5:30 ` [PATCH v8 1/7] promisor-remote: refactor to get rid of 'struct strvec' Christian Couder
2025-09-08 5:30 ` [PATCH v8 2/7] promisor-remote: allow a server to advertise more fields Christian Couder
2025-09-08 5:30 ` [PATCH v8 3/7] promisor-remote: use string constants for 'name' and 'url' too Christian Couder
2025-09-08 5:30 ` [PATCH v8 4/7] promisor-remote: refactor how we parse advertised fields Christian Couder
2025-09-08 5:30 ` [PATCH v8 5/7] promisor-remote: use string_list_split() in filter_promisor_remote() Christian Couder
2025-09-08 5:30 ` [PATCH v8 6/7] promisor-remote: allow a client to check fields Christian Couder
2025-09-08 5:30 ` [PATCH v8 7/7] promisor-remote: use string_list_split() in mark_remotes_as_accepted() Christian Couder
2025-09-08 17:34 ` [PATCH v8 0/7] Make the "promisor-remote" capability support more fields Junio C Hamano
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=20250625125055.1375596-5-christian.couder@gmail.com \
--to=christian.couder@gmail.com \
--cc=chriscool@tuxfamily.org \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=jltobler@gmail.com \
--cc=karthik.188@gmail.com \
--cc=me@ttaylorr.com \
--cc=ps@pks.im \
/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).