Skip to content

Commit 337694d

Browse files
test(k8s): tests for detection helpers, extractors, and pipeline pass
- Add TEST(infra_is_kustomize_file): positive/negative/NULL cases - Add TEST(infra_is_k8s_manifest): apiVersion present/absent, kustomize file returns false, NULL guards - Add TEST(k8s_extract_kustomize): asserts 2 imports (deployment.yaml, service.yaml) from Kustomization resources list - Add TEST(k8s_extract_manifest): asserts Resource def with label "Resource" and name containing "Deployment" - Add TEST(k8s_extract_manifest_no_name): no crash, has_error==false - Fix extract_k8s.c get_scalar_text() to unwrap flow_node wrappers (tree-sitter YAML grammar wraps plain_scalar in flow_node) - Fix pass_k8s.c missing #include "foundation/compat.h" for CBM_TLS Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent bf3af55 commit 337694d

3 files changed

Lines changed: 110 additions & 0 deletions

File tree

internal/cbm/extract_k8s.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,19 @@
2222

2323
// Return the raw source text for a scalar node (plain, single-quoted, or
2424
// double-quoted). Surrounding quote characters are stripped for quoted forms.
25+
// Handles flow_node wrappers transparently by descending into the first named
26+
// child (the tree-sitter YAML grammar often wraps scalars in flow_node).
2527
// Returns NULL for non-scalar node types.
2628
static const char *get_scalar_text(CBMArena *a, TSNode node, const char *source) {
2729
const char *type = ts_node_type(node);
30+
// Unwrap flow_node: the actual scalar is the first named child
31+
if (strcmp(type, "flow_node") == 0) {
32+
TSNode inner = ts_node_named_child(node, 0);
33+
if (ts_node_is_null(inner)) {
34+
return NULL;
35+
}
36+
return get_scalar_text(a, inner, source);
37+
}
2838
if (strcmp(type, "plain_scalar") == 0) {
2939
return cbm_node_text(a, node, source);
3040
}

src/pipeline/pass_k8s.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "graph_buffer/graph_buffer.h"
1616
#include "discover/discover.h"
1717
#include "foundation/log.h"
18+
#include "foundation/compat.h"
1819
#include "cbm.h"
1920

2021
#include <stdlib.h>

tests/test_pipeline.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3331,6 +3331,30 @@ TEST(infra_is_dockerfile) {
33313331
PASS();
33323332
}
33333333

3334+
TEST(infra_is_kustomize_file) {
3335+
ASSERT(cbm_is_kustomize_file("kustomization.yaml"));
3336+
ASSERT(cbm_is_kustomize_file("kustomization.yml"));
3337+
ASSERT(cbm_is_kustomize_file("KUSTOMIZATION.YAML")); /* case-insensitive */
3338+
ASSERT(!cbm_is_kustomize_file("deployment.yaml"));
3339+
ASSERT(!cbm_is_kustomize_file("kustomize.yaml"));
3340+
ASSERT(!cbm_is_kustomize_file(NULL));
3341+
PASS();
3342+
}
3343+
3344+
TEST(infra_is_k8s_manifest) {
3345+
const char *deploy = "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: my-app\n";
3346+
const char *plain = "name: foo\nvalue: bar\n";
3347+
const char *kust = "apiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\n";
3348+
3349+
ASSERT(cbm_is_k8s_manifest("deployment.yaml", deploy));
3350+
ASSERT(!cbm_is_k8s_manifest("deployment.yaml", plain));
3351+
/* kustomize file should return false even if it has apiVersion */
3352+
ASSERT(!cbm_is_k8s_manifest("kustomization.yaml", kust));
3353+
ASSERT(!cbm_is_k8s_manifest(NULL, deploy));
3354+
ASSERT(!cbm_is_k8s_manifest("deployment.yaml", NULL));
3355+
PASS();
3356+
}
3357+
33343358
TEST(infra_is_env_file) {
33353359
ASSERT(cbm_is_env_file(".env"));
33363360
ASSERT(cbm_is_env_file(".env.local"));
@@ -4139,6 +4163,75 @@ TEST(infra_pipeline_idempotent) {
41394163
PASS();
41404164
}
41414165

4166+
/* ── K8s / Kustomize extraction tests ──────────────────────────── */
4167+
4168+
TEST(k8s_extract_kustomize) {
4169+
const char *src =
4170+
"apiVersion: kustomize.config.k8s.io/v1beta1\n"
4171+
"kind: Kustomization\n"
4172+
"resources:\n"
4173+
" - deployment.yaml\n"
4174+
" - service.yaml\n";
4175+
CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_KUSTOMIZE,
4176+
"myproj", "base/kustomization.yaml",
4177+
0, NULL, NULL);
4178+
ASSERT(r != NULL);
4179+
ASSERT_GTE(r->imports.count, 2);
4180+
4181+
bool found_deploy = false, found_svc = false;
4182+
for (int i = 0; i < r->imports.count; i++) {
4183+
if (r->imports.items[i].module_path &&
4184+
strcmp(r->imports.items[i].module_path, "deployment.yaml") == 0)
4185+
found_deploy = true;
4186+
if (r->imports.items[i].module_path &&
4187+
strcmp(r->imports.items[i].module_path, "service.yaml") == 0)
4188+
found_svc = true;
4189+
}
4190+
ASSERT_TRUE(found_deploy);
4191+
ASSERT_TRUE(found_svc);
4192+
4193+
cbm_free_result(r);
4194+
PASS();
4195+
}
4196+
4197+
TEST(k8s_extract_manifest) {
4198+
const char *src =
4199+
"apiVersion: apps/v1\n"
4200+
"kind: Deployment\n"
4201+
"metadata:\n"
4202+
" name: my-app\n"
4203+
" namespace: production\n";
4204+
CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_K8S,
4205+
"myproj", "k8s/deployment.yaml",
4206+
0, NULL, NULL);
4207+
ASSERT(r != NULL);
4208+
ASSERT_GTE(r->defs.count, 1);
4209+
4210+
bool found_resource = false;
4211+
for (int d = 0; d < r->defs.count; d++) {
4212+
if (r->defs.items[d].label &&
4213+
strcmp(r->defs.items[d].label, "Resource") == 0 &&
4214+
r->defs.items[d].name &&
4215+
strstr(r->defs.items[d].name, "Deployment") != NULL)
4216+
found_resource = true;
4217+
}
4218+
ASSERT_TRUE(found_resource);
4219+
4220+
cbm_free_result(r);
4221+
PASS();
4222+
}
4223+
4224+
TEST(k8s_extract_manifest_no_name) {
4225+
const char *src = "apiVersion: apps/v1\nkind: Deployment\n";
4226+
CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_K8S,
4227+
"myproj", "k8s/deploy.yaml", 0, NULL, NULL);
4228+
ASSERT(r != NULL);
4229+
/* No crash — defs count may be 0 because metadata.name is absent */
4230+
ASSERT(!r->has_error);
4231+
cbm_free_result(r);
4232+
PASS();
4233+
}
4234+
41424235
/* ── Envscan tests (port of envscan_test.go) ───────────────────── */
41434236

41444237
/* Helper: write a file inside a temp dir */
@@ -5055,6 +5148,8 @@ SUITE(pipeline) {
50555148
RUN_TEST(infra_is_cloudbuild_file);
50565149
RUN_TEST(infra_is_shell_script);
50575150
RUN_TEST(infra_is_dockerfile);
5151+
RUN_TEST(infra_is_kustomize_file);
5152+
RUN_TEST(infra_is_k8s_manifest);
50585153
RUN_TEST(infra_is_env_file);
50595154
RUN_TEST(infra_clean_json_brackets);
50605155
RUN_TEST(infra_secret_detection);
@@ -5083,6 +5178,10 @@ SUITE(pipeline) {
50835178
/* Infrascan: pipeline integration */
50845179
RUN_TEST(infra_pipeline_integration);
50855180
RUN_TEST(infra_pipeline_idempotent);
5181+
/* K8s / Kustomize extraction */
5182+
RUN_TEST(k8s_extract_kustomize);
5183+
RUN_TEST(k8s_extract_manifest);
5184+
RUN_TEST(k8s_extract_manifest_no_name);
50865185
/* Env URL scanning */
50875186
RUN_TEST(envscan_dockerfile_env_urls);
50885187
RUN_TEST(envscan_shell_env_urls);

0 commit comments

Comments
 (0)