From 3ce2a5bc26a9b1eb34f20dac354e6d76390fd6cb Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Mon, 6 Apr 2026 15:12:44 -0400 Subject: [PATCH 1/2] More tightly constrain node profile requests Normalize paths using abspath and validate the result is within the expected path. --- confluent_server/confluent/selfservice.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/confluent_server/confluent/selfservice.py b/confluent_server/confluent/selfservice.py index 44aa5619..872463ec 100644 --- a/confluent_server/confluent/selfservice.py +++ b/confluent_server/confluent/selfservice.py @@ -578,8 +578,13 @@ def handle_request(env, start_response): yield 'No profile' return fname = '/var/lib/confluent/private/os/{}/{}'.format(profile, fname) + fullpath = os.path.abspath(fname) + if not fullpath.startswith('/var/lib/confluent/private/os/{}/'.format(profile)): + start_response('400 Bad Request', ()) + yield 'Bad Request' + return try: - with open(fname, 'rb') as privdata: + with open(fullpath, 'rb') as privdata: start_response('200 OK', ()) yield privdata.read() return @@ -623,6 +628,11 @@ def get_scriptlist(scriptcat, cfg, nodename, pathtemplate): 'deployment.profile', {}).get('value', '') slist = [] target = pathtemplate.format(profile, scriptcat) + target = os.path.abspath(target) + allowedbase = os.path.abspath(pathtemplate.format(profile, '').rstrip('/')) + allowedbaseprefix = os.path.join(allowedbase, '') + if not target.startswith(allowedbaseprefix): + return None, None if not os.path.isdir(target) and os.path.isdir(target + '.d'): target = target + '.d' try: From c49b2fd8abeb66c9bf16a45c4fad1acddf00b463 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 7 Apr 2026 14:57:09 -0400 Subject: [PATCH 2/2] Update quorum on deletion If deletion of a node brings quorum, notify followers of the good news --- confluent_server/confluent/config/configmanager.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/confluent_server/confluent/config/configmanager.py b/confluent_server/confluent/config/configmanager.py index 10eff81c..2112ed7f 100644 --- a/confluent_server/confluent/config/configmanager.py +++ b/confluent_server/confluent/config/configmanager.py @@ -1013,6 +1013,14 @@ def del_collective_member(name): if cfgstreams: exec_on_followers_unconditional('_true_del_collective_member', name) _true_del_collective_member(name) + if cfgstreams: + _hasquorum = has_quorum() + pushes = eventlet.GreenPool() + payload = msgpack.packb({'quorum': _hasquorum}, use_bin_type=False) + for _ in pushes.starmap( + _push_rpc, + [(cfgstreams[s]['stream'], payload) for s in cfgstreams]): + pass def _true_del_collective_member(name, sync=True): global cfgleader