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:
Andrew Hundt
2026-07-01 06:46:20 -04:00
parent d2e8eee6e1
commit c686cd5ba8
2 changed files with 115 additions and 7 deletions
+59 -7
View File
@@ -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. */
+56
View File
@@ -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);