mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-03 08:03:38 +08:00
fix(pipeline): restore infra route deny filtering
Port the upstream deny-wins-by-value behavior for infra URL route extraction so upstream/config/healthcheck URLs cannot be re-minted by duplicate string refs with less specific key paths. Preserve the fork's manifest skip and existing route-literal filtering, use the existing hash table and logging paths, and add an integrated pipeline canary that keeps a positive push_endpoint route while rejecting registry and healthcheck URLs. Validation: git diff --check; bash scripts/check-source-safety.sh; make -j8 -f Makefile.cbm build/c/test-runner; CBM_ONLY_SUITE=pipeline ./build/c/test-runner (284 passed). Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
This commit is contained in:
+59
-7
@@ -730,11 +730,32 @@ static bool is_infra_file(const char *fp) {
|
||||
strstr(fp, ".tf") != NULL || strstr(fp, ".hcl") != NULL || strstr(fp, ".toml") != NULL);
|
||||
}
|
||||
|
||||
/* True when an infra key path denotes an upstream dependency, config value, or
|
||||
* healthcheck target rather than an endpoint this service exposes. Exposed
|
||||
* endpoint keys such as push_endpoint, callback, and webhook are intentionally
|
||||
* absent so they can still produce infra Route nodes. */
|
||||
static bool is_upstream_config_key(const char *key_path) {
|
||||
if (!key_path) {
|
||||
return false;
|
||||
}
|
||||
static const char *const deny[] = {"jwks", "registry", "registries", "healthcheck",
|
||||
"upstream", "_service_url", "auth", NULL};
|
||||
for (int i = 0; deny[i]; i++) {
|
||||
if (strstr(key_path, deny[i]) != NULL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try to create an infra Route node from one string_ref. */
|
||||
static void try_upsert_infra_route(cbm_gbuf_t *gbuf, const CBMStringRef *sr, const char *fp) {
|
||||
if (sr->kind != CBM_STRREF_URL || !sr->value || !strstr(sr->value, "://")) {
|
||||
return;
|
||||
}
|
||||
if (is_upstream_config_key(sr->key_path)) {
|
||||
return;
|
||||
}
|
||||
char route_qn[CBM_ROUTE_QN_SIZE];
|
||||
snprintf(route_qn, sizeof(route_qn), "__route__infra__%s", sr->value);
|
||||
char route_props[CBM_SZ_512];
|
||||
@@ -747,17 +768,48 @@ static void try_upsert_infra_route(cbm_gbuf_t *gbuf, const CBMStringRef *sr, con
|
||||
cbm_gbuf_upsert_node(gbuf, "Route", sr->value, route_qn, fp, 0, 0, route_props);
|
||||
}
|
||||
|
||||
/* The graph node is keyed by URL value, while extraction may emit several refs
|
||||
* for the same value at different key-path granularities. If any ref says the
|
||||
* value is upstream/config/healthcheck-only, suppress that URL globally. */
|
||||
static bool route_sr_denied(const CBMStringRef *sr) {
|
||||
if (!sr || !sr->value || strpbrk(sr->value, " \t\r\n") != NULL) {
|
||||
return true;
|
||||
}
|
||||
if (!sr->key_path) {
|
||||
return true;
|
||||
}
|
||||
return is_upstream_config_key(sr->key_path);
|
||||
}
|
||||
|
||||
static void cbm_pipeline_extract_infra_routes(cbm_gbuf_t *gbuf, const cbm_file_info_t *files,
|
||||
CBMFileResult **result_cache, int file_count) {
|
||||
for (int i = 0; i < file_count; i++) {
|
||||
if (!result_cache[i] || !is_infra_file(files[i].rel_path)) {
|
||||
continue;
|
||||
}
|
||||
for (int si = 0; si < result_cache[i]->string_refs.count; si++) {
|
||||
try_upsert_infra_route(gbuf, &result_cache[i]->string_refs.items[si],
|
||||
files[i].rel_path);
|
||||
enum { CBM_INFRA_ROUTE_DENY_PASS_COUNT = 2 };
|
||||
CBMHashTable *denied = cbm_ht_create(CBM_SZ_16);
|
||||
if (!denied) {
|
||||
cbm_log_warn("pass.infra_routes", "reason", "deny_set_alloc_failed");
|
||||
return;
|
||||
}
|
||||
for (int pass = 0; pass < CBM_INFRA_ROUTE_DENY_PASS_COUNT; pass++) {
|
||||
for (int i = 0; i < file_count; i++) {
|
||||
if (!result_cache[i] || !is_infra_file(files[i].rel_path)) {
|
||||
continue;
|
||||
}
|
||||
for (int si = 0; si < result_cache[i]->string_refs.count; si++) {
|
||||
const CBMStringRef *sr = &result_cache[i]->string_refs.items[si];
|
||||
if (sr->kind != CBM_STRREF_URL || !sr->value || !strstr(sr->value, "://")) {
|
||||
continue;
|
||||
}
|
||||
if (pass == 0) {
|
||||
if (route_sr_denied(sr)) {
|
||||
cbm_ht_set(denied, sr->value, intptr_to_ptr(1));
|
||||
}
|
||||
} else if (!cbm_ht_has(denied, sr->value)) {
|
||||
try_upsert_infra_route(gbuf, sr, files[i].rel_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cbm_ht_free(denied);
|
||||
}
|
||||
|
||||
/* Run decorator_tags, configlink, and route matching passes. */
|
||||
|
||||
@@ -808,6 +808,61 @@ TEST(pipeline_persisted_route_purity_for_http_literals) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(pipeline_infra_route_deny_wins_by_url_value) {
|
||||
if (setup_test_repo() != 0) {
|
||||
FAIL("failed to create temp dir");
|
||||
}
|
||||
char path[CBM_PATH_MAX];
|
||||
int n = snprintf(path, sizeof(path), "%s/infra.yaml", g_tmpdir);
|
||||
ASSERT_GT(n, 0);
|
||||
ASSERT_LT((size_t)n, sizeof(path));
|
||||
FILE *f = fopen(path, "w");
|
||||
if (!f) {
|
||||
teardown_test_repo();
|
||||
FAIL("failed to write infra.yaml");
|
||||
}
|
||||
fprintf(f, "registries:\n"
|
||||
" terraform_registry:\n"
|
||||
" url: https://registry.terraform.io\n"
|
||||
"healthcheck: curl --fail http://localhost:8080/health || exit 1\n"
|
||||
"push_endpoint: https://hooks.example.test/push\n");
|
||||
fclose(f);
|
||||
|
||||
char db_path[CBM_PATH_MAX];
|
||||
n = snprintf(db_path, sizeof(db_path), "%s/test_infra_route_deny.db", g_tmpdir);
|
||||
ASSERT_GT(n, 0);
|
||||
ASSERT_LT((size_t)n, sizeof(db_path));
|
||||
cbm_pipeline_t *p = cbm_pipeline_new(g_tmpdir, db_path, CBM_MODE_FULL);
|
||||
ASSERT_NOT_NULL(p);
|
||||
ASSERT_EQ(cbm_pipeline_run(p), 0);
|
||||
|
||||
cbm_store_t *s = cbm_store_open_path(db_path);
|
||||
ASSERT_NOT_NULL(s);
|
||||
const char *project = cbm_pipeline_project_name(p);
|
||||
|
||||
cbm_node_t *routes = NULL;
|
||||
int route_count = 0;
|
||||
ASSERT_EQ(cbm_store_find_nodes_by_label(s, project, "Route", &routes, &route_count),
|
||||
CBM_STORE_OK);
|
||||
bool saw_push_endpoint = false;
|
||||
for (int i = 0; i < route_count; i++) {
|
||||
const char *name = routes[i].name ? routes[i].name : "";
|
||||
ASSERT_FALSE(strcmp(name, "https://registry.terraform.io") == 0);
|
||||
ASSERT_FALSE(strcmp(name, "http://localhost:8080/health") == 0);
|
||||
ASSERT_FALSE(strstr(name, "curl --fail http://localhost:8080/health") != NULL);
|
||||
if (strcmp(name, "https://hooks.example.test/push") == 0) {
|
||||
saw_push_endpoint = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(saw_push_endpoint);
|
||||
|
||||
cbm_store_free_nodes(routes, route_count);
|
||||
cbm_store_close(s);
|
||||
cbm_pipeline_free(p);
|
||||
teardown_test_repo();
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* ── Calls pass tests ──────────────────────────────────────────── */
|
||||
|
||||
TEST(pipeline_calls_resolution) {
|
||||
@@ -10653,6 +10708,7 @@ SUITE(pipeline) {
|
||||
RUN_TEST(pipeline_def_props_valid_json_when_oversized);
|
||||
RUN_TEST(pipeline_edge_props_valid_json);
|
||||
RUN_TEST(pipeline_persisted_route_purity_for_http_literals);
|
||||
RUN_TEST(pipeline_infra_route_deny_wins_by_url_value);
|
||||
/* Complexity propagation pass (Tier B) */
|
||||
RUN_TEST(pipeline_complexity_transitive_loop_depth);
|
||||
RUN_TEST(pipeline_complexity_scc_tld_is_deterministic);
|
||||
|
||||
Reference in New Issue
Block a user