diff --git a/scripts/memory-core-baseline.txt b/scripts/memory-core-baseline.txt index 8bc450f7f..19fdd363d 100644 --- a/scripts/memory-core-baseline.txt +++ b/scripts/memory-core-baseline.txt @@ -52,7 +52,7 @@ src/git/git_context.c 18 src/main.c 32 src/mcp/compact_out.c 39 src/mcp/index_supervisor.c 8 -src/mcp/mcp.c 814 +src/mcp/mcp.c 785 src/pipeline/artifact.c 38 src/pipeline/fqn.c 23 src/pipeline/lsp_resolve.h 4 diff --git a/src/mcp/mcp.c b/src/mcp/mcp.c index 39aaf0dcc..7bfca8d7a 100644 --- a/src/mcp/mcp.c +++ b/src/mcp/mcp.c @@ -11062,6 +11062,16 @@ static bool project_db_is_servable(const char *project, const char *db_path) { return servable; } +/* The three heap strings handle_index_repository owns from + * cbm_mcp_get_string_arg / resolved_repo_path_from_project_arg. One release + * point keeps the dozen early-return paths in step; free(NULL) is a no-op, so + * a path may pass NULL for an argument it never obtained. */ +static void index_args_free(char *repo_path, char *mode_str, char *name_override) { + free(repo_path); + free(mode_str); + free(name_override); +} + static char *handle_index_repository(cbm_mcp_server_t *srv, const char *args) { char *repo_path = cbm_mcp_get_string_arg(args, "repo_path"); char *mode_str = cbm_mcp_get_string_arg(args, "mode"); @@ -11074,15 +11084,12 @@ static char *handle_index_repository(cbm_mcp_server_t *srv, const char *args) { } if (!repo_path) { - free(mode_str); - free(name_override); + index_args_free(NULL, mode_str, name_override); return cbm_mcp_text_result("repo_path is required", true); } if (!resolve_session_repo_path(srv, &repo_path)) { - free(mode_str); - free(name_override); - free(repo_path); + index_args_free(repo_path, mode_str, name_override); return cbm_mcp_text_result("failed to resolve repo_path", true); } @@ -11102,26 +11109,20 @@ static char *handle_index_repository(cbm_mcp_server_t *srv, const char *args) { if (repo_path && repo_path[0] && !cbm_workspace_root_allowed(repo_path, cbm_workspace_home_dir(), cbm_workspace_cache_dir(), allowed_root, boundary_err, sizeof(boundary_err))) { - free(mode_str); - free(name_override); - free(repo_path); + index_args_free(repo_path, mode_str, name_override); return cbm_mcp_text_result(boundary_err, true); } if (mode_str && strcmp(mode_str, "cross-repo-intelligence") == 0) { - free(mode_str); char *result = handle_cross_repo_mode(srv, repo_path, name_override, args); - free(name_override); - free(repo_path); + index_args_free(repo_path, mode_str, name_override); return result; } cbm_index_resource_policy_t resource_policy; char policy_error[CBM_SZ_256] = {0}; if (!load_index_policy(srv, args, &resource_policy, policy_error, sizeof(policy_error))) { - free(mode_str); - free(name_override); - free(repo_path); + index_args_free(repo_path, mode_str, name_override); return cbm_mcp_text_result(policy_error, true); } @@ -11133,9 +11134,7 @@ static char *handle_index_repository(cbm_mcp_server_t *srv, const char *args) { worker_args ? srv->index_executor(srv->index_executor_context, repo_path, worker_args) : NULL; free(worker_args); - free(repo_path); - free(mode_str); - free(name_override); + index_args_free(repo_path, mode_str, name_override); return coordinated ? coordinated : cbm_mcp_text_result( "daemon index coordinator could not start the operation", true); @@ -11148,9 +11147,7 @@ static char *handle_index_repository(cbm_mcp_server_t *srv, const char *args) { char *mutation_project = cbm_project_name_from_path(name_override && name_override[0] ? name_override : repo_path); if (!mutation_project) { - free(repo_path); - free(mode_str); - free(name_override); + index_args_free(repo_path, mode_str, name_override); return cbm_mcp_text_result("could not resolve index project name", true); } @@ -11163,24 +11160,18 @@ static char *handle_index_repository(cbm_mcp_server_t *srv, const char *args) { char *worker_args = index_args_with_repo_path(args, repo_path, &resource_policy); if (!worker_args) { free(mutation_project); - free(repo_path); - free(mode_str); - free(name_override); + index_args_free(repo_path, mode_str, name_override); return cbm_mcp_text_result("failed to prepare supervised index request", true); } char *supervised = index_run_supervised(srv, worker_args); free(worker_args); if (supervised) { free(mutation_project); - free(repo_path); - free(mode_str); - free(name_override); + index_args_free(repo_path, mode_str, name_override); return supervised; } free(mutation_project); - free(repo_path); - free(mode_str); - free(name_override); + index_args_free(repo_path, mode_str, name_override); return cbm_mcp_text_result( "index supervision failed before a contained worker could start; no " "in-process fallback was attempted", @@ -11189,18 +11180,14 @@ static char *handle_index_repository(cbm_mcp_server_t *srv, const char *args) { if (!mcp_project_mutation_begin(srv, mutation_project)) { free(mutation_project); - free(repo_path); - free(mode_str); - free(name_override); + index_args_free(repo_path, mode_str, name_override); return cbm_mcp_text_result("index operation blocked by another mutation for this project", true); } if (mcp_request_cancelled(srv)) { mcp_project_mutation_end(srv, mutation_project); free(mutation_project); - free(repo_path); - free(mode_str); - free(name_override); + index_args_free(repo_path, mode_str, name_override); return cbm_mcp_text_result("index operation cancelled for this request", true); } diff --git a/tests/test_worker_session_scope.sh b/tests/test_worker_session_scope.sh index fed32e97e..ab018ba76 100644 --- a/tests/test_worker_session_scope.sh +++ b/tests/test_worker_session_scope.sh @@ -70,8 +70,15 @@ run_worker() { --response-out "${response}" >"${out}" 2>"${err}" } +# A worker never reads the resource policy from config or environment: the +# supervisor resolves it and sends it inside the request, and a request +# without one is refused ("missing or incomplete trusted worker policy"). +# This script plays the supervisor, so it sends what the supervisor sends -- +# both limits off, the default. +policy='"_cbm_index_policy":{"index_max_files":"off","index_max_source_mb":"off"}' + response="${tmpdir}/scoped.response" -if ! run_worker "{\"repo_path\":\"${repo}\",\"mode\":\"fast\"}" "${response}" \ +if ! run_worker "{\"repo_path\":\"${repo}\",\"mode\":\"fast\",${policy}}" "${response}" \ "${tmpdir}/scoped.out" "${tmpdir}/scoped.err"; then echo "worker exited nonzero for an admitted request" >&2 cat "${tmpdir}/scoped.err" >&2 @@ -94,7 +101,7 @@ fi # Fail closed: no repo_path means no request scope, so no indexing at all. unscoped="${tmpdir}/unscoped.response" -if run_worker '{"mode":"fast"}' "${unscoped}" "${tmpdir}/unscoped.out" "${tmpdir}/unscoped.err"; then +if run_worker "{\"mode\":\"fast\",${policy}}" "${unscoped}" "${tmpdir}/unscoped.out" "${tmpdir}/unscoped.err"; then echo "worker ran without a request workspace scope" >&2 exit 1 fi