What this does
One skill, three investigation modes over container/Kubernetes data in the SubImage graph:
- Mode 1: Image provenance: given an image URI / tag / digest, resolve the full chain: registry repository → running workloads (Container → Pod → Service, and Functions) → source repo (
PACKAGED_FROM) → vulnerabilities. - Mode 2: Cluster exposure: given a cluster name, enumerate the internet-facing surface: LoadBalancer/NodePort services, ingress, public-IP nodes, and everything flagged
exposed_internet=true. - Mode 3: Node reconciliation: when EKS node counts look inflated, separate
runningEC2 instances from terminated/stopped ones still linked byMEMBER_OF_EKS_CLUSTER.
When to use
✅ User pastes an image reference and asks where it runs / what's wrong with it → Mode 1. ✅ "What's exposed in cluster X?" / "audit the attack surface of this EKS cluster" → Mode 2. ✅ "The chatbot says cluster X has N nodes but kubectl shows fewer" → Mode 3.
❌ A single CVE across the whole environment → subimage-mcp:investigate-cve.
❌ An attack path from a workload → subimage-mcp:review-attack-path.
❌ Ambiguous which mode: ask the user which of the three they want before running.
Picking the mode
- Input looks like an image (
sha256:…,…/repo:tag, registry URI) → Mode 1. - Input is a cluster name, or the question is about exposure/attack surface → Mode 2.
- Question is about node counts looking wrong / stale graph data → Mode 3.
Prerequisites
- Relevant module synced (
subimageListModules): ECS/EKS/Kubernetes for cluster modes, ECR/GAR/GitLab for registry data, GitHub for source provenance. - The CVE section of Mode 1 reads
:VulnerabilitySignal:Signalnodes off the image; see the Step 5 template inreferences/cypher-templates.md. - All Cypher follows
subimage-mcp:build-cypher-querydiscipline: schema-validate labels/properties withsubimageGetNodesSchema/searchModelQueriesbefore trusting a template, then run withsubimageRunCypher. Templates live inreferences/cypher-templates.mdand are starting points, not guarantees.
Reusable correctness note (applies to Modes 1-3)
The graph retains AWSEC2Instance nodes with state != 'running' (terminated/stopped) that still carry MEMBER_OF_EKS_CLUSTER edges. Any count or listing of cluster nodes must filter WHERE ec2.state = 'running', or it over-reports. This is the whole of Mode 3 and a guardrail for the node parts of Mode 2.
Workflow
Mode 1: Image provenance
Run the § Image Provenance queries from the templates, threading the resolved image_id from step 1 into the rest:
- Resolve the image node: by digest, exact tag URI, or fuzzy partial match. If several images match, list them and ask which (or report all if fewer than ~5).
- Registry repository: ECR / GCP Artifact Registry / GitLab.
- Running workloads: Containers (
state = 'running') → Pod → Service for cluster-backed providers, or Container → Service directly for serverless (Cloud Run); plus Functions. - Source origin:
PACKAGED_FROM→ GitHub repo + Dockerfile path. - Vulnerabilities:
:VulnerabilitySignal:Signalaffecting the image, via the Step 5 CVE template. The template returns both open and accepted rows, so bucket them by thestatuscolumn: an accepted CVE is one a human signed off on, and folding it into the open count overstates the backlog. Omit the+<n> acceptedclause when there are none.
Mode 2: Cluster exposure
Resolve the cluster first: if the user gives an EKS name, map it to the backing KubernetesCluster via (:AWSEKSCluster)-[:MAPS_TO]->(:KubernetesCluster). If the cluster is not found, say so and stop. Then run the § Cluster Exposure queries: cluster overview, namespaces, internet-exposed services (LoadBalancer/NodePort or exposed_internet=true), ingress, backing nodes with public IPs (reported from the running AWSEC2Instance side, since KubernetesNode has no reliable join key to EC2), and all exposed_internet=true objects.
Mode 3: Node reconciliation
Run the § Node Reconciliation query: per cluster, count total vs running vs stale (terminated/stopped) linked instances, flag an anomaly_level (GHOST_CLUSTER / HIGH_STALENESS / STALE_EDGES / OK), and list the stale instance ids. Note that Cartography retains terminated instances until its next cleanup pass; extreme staleness (>50%) hints the AWS module has not synced recently for that account.
Output
In-chat report (no file output). Tag resources with [[entity:<Label>:<id>|<name>]]. Pick the section for the mode you ran; if a query returns nothing, keep the heading and write "None found".
# Container investigation: <mode>: <image / cluster>
## (Mode 1) Image provenance
- identity: digest <…>, tags <…>
- registry: [[entity:AWSECRRepository:<id>|<name>]]
- running workloads: [[entity:Container:<id>|<name>]] in pod <…> / service <…> (+<rest>)
- source: [[entity:GitHubRepository:<id>|<org/repo>]] (Dockerfile <path>)
- vulns: <n> open CVEs (<n> critical, <n> high)<, +<n> accepted>; top: <CVE> (CVSS <x>, KEV <y/n>)
## (Mode 2) Cluster exposure
- overview: API public access <y/n>, version <…>, region <…>
- exposed services: <n> (LoadBalancer/NodePort): [[entity:KubernetesService:<id>|<name>]] (+<rest>)
- ingress: <n>: hosts <…>
- backing public-IP instances (running): <n>: [[entity:AWSEC2Instance:<id>|<id>]] <public-ip>
- exposed_internet=true objects: <n>
## (Mode 3) EKS node reconciliation
- [[entity:AWSEKSCluster:<id>|<name>]]: running <r>, stale <s>, total <t> → <anomaly_level>
- stale edges: i-… (terminated), i-… (stopped) (+<rest>)
## Summary / risk notes
<one or two lines: biggest exposure, most stale cluster, or worst CVE>
Anti-patterns
- Counting cluster nodes without
WHERE ec2.state = 'running'. This is the documented cause of inflated counts; always filter. - Trusting a template label/relationship without schema-validating it. Container/K8s labels (
Image,Container,ComputePod,KubernetesService,AWSEKSCluster) drift; an unvalidatedMATCHsilently returns nothing. - Reading severity off
m.severityor KEV offm.kev. The properties arebase_severity(null often enough to need the score fallback) andis_kev. - Running all three modes when the user asked about one. Pick the mode; offer the others as follow-ups.
- Reformatting raw query output as a wall-of-text table. Summarize and tag the notable resources.
- Unbounded queries.
LIMITeverything.
References
- Cypher templates:
references/cypher-templates.md(schema-validate before trusting). - Query discipline:
subimage-mcp:build-cypher-query. - CVE deep-dive on a surfaced vulnerability:
subimage-mcp:investigate-cve. - Attack path from an exposed workload:
subimage-mcp:review-attack-path. - Tool guide (loaded by
subimageReadMe): Domain 3 "Vulnerability Management" and the graph-query domain.