π― Affected Component(s)
pkg/kubescape (kagent-tools MCP server) β the list-style handlers for ApplicationProfile (and likely other spdx.softwarecomposition.kubescape.io resources whose list handlers read spec-derived fields, e.g. vulnerabilityManifests).
π Bug Description
Note: I think the Kubescape tool is still in an early/beta stage and not yet listed among the GA tools. I'm filing this in case the feedback is useful for hardening it before GA β please feel free to deprioritize if this tool is out of active scope.
Kubescape's Storage aggregated API server implements metadata-only LIST: a LIST request (e.g., kubectl get <custom resource> -A -o json) returns object metadata only and omits the heavy spec payload. The full spec is only returned by an individual GET on a named object (or via the documented fullSpec resourceVersion mechanism).
The Kubescape tool's list handlers appear to aggregate values from spec (e.g., spec.containers[].execs, spec.containers[].opens) using data obtained through a LIST call. Because LIST returns no spec, the aggregation counts nothing and returns 0 for all fields β even containers_count β while the objects actually contain data.
This is a documented behavior of Kubescape Storage. Per the kubescape/storage README:
a listed object comes back with its spec fields at their zero values (for example a VulnerabilityManifestSummary reports all severity counts as 0)
(the same applies to ApplicationProfile, whose spec.containers[] is likewise omitted on LIST)
π Steps To Reproduce
- Install the Kubescape operator with relevancy enabled (default with node-agent/eBPF), and let the learning period complete for at least one workload.
- Invoke
kubescape_list_application_profiles through a kagent agent (or the MCP server directly).
- Observe that all profiles report
total_execs: 0, total_opens: 0, total_syscalls: 0, containers_count: 0.
π€ Expected Behavior
The tool should return the real spec-derived values (e.g., execs, opens, container counts) for each ApplicationProfile, or at minimum must not report 0 when the data is simply not present in a metadata-only LIST response.
Acceptable approaches:
- Fetch each object with an individual GET (which returns the full
spec) before aggregating, or
- If only metadata is available, clearly distinguish "not fetched / unknown" from "0", instead of emitting
0.
Note: The storage's fullSpec resourceVersion mechanism is not applicable here β per the kubescape/storage README, fullSpec is rejected on LIST for applicationprofiles and networkneighborhoods (it is honored only for the summary resources). Individual GET is therefore the appropriate approach for these resources.
π± Actual Behavior
All ApplicationProfiles are reported with zeroed counters (trimmed):
{
"application_profiles": [
{
"containers_count": 0,
"created_at": "2026-06-04T08:41:49Z",
"name": "replicaset-rockylinux-58468585c7",
"namespace": "kagent",
"total_capabilities": 0,
"total_endpoints": 0,
"total_execs": 0,
"total_opens": 0,
"total_syscalls": 0
},
{ "... same zeros for every profile ..." }
]
}
However, an individual GET on the same objects returns real data, and the learning is marked complete:
$ kubectl get applicationprofile replicaset-rockylinux-58468585c7 -n kagent -o json \
| jq '.spec.containers[]? | {name, execs:(.execs|length), opens:(.opens|length)}'
{
"name": "rockylinux",
"execs": 13,
"opens": 115
}
$ kubectl get applicationprofile replicaset-rockylinux-58468585c7 -n kagent -o json \
| jq '.metadata.annotations'
{
"kubescape.io/completion": "complete",
"kubescape.io/status": "completed",
...
}
So the data exists (execs: 13, opens: 115) and the profile is completed; the tool's 0 values come from LIST not returning spec, not from an actually-empty profile.
π» Environment
- OS version: Rocky Linux 8.10 (x86_64) bastion; nodes on Amazon Linux
- Kubernetes: v1.35.6 (EKS)
- Kubernetes Provider: AWS (EKS)
- kagent-tools / MCP server version: v0.2.1
- Kubescape operator: v4.0.5 (Helm chart 1.40.2)
π Additional Context
Root cause:
In pkg/kubescape/kubescape.go (v0.2.1, around L741), the list handler fetches profiles with a LIST call and then reads spec-derived fields directly from each listed item:
profiles, err := k.spdxClient.ApplicationProfiles(queryNamespace).List(ctx, metav1.ListOptions{})
// ...
for _, profile := range profiles.Items {
containersCount := len(profile.Spec.Containers)
// ...
for _, c := range profile.Spec.Containers {
totalExecs += len(c.Execs)
totalOpens += len(c.Opens)
totalSyscalls += len(c.Syscalls)
totalCapabilities += len(c.Capabilities)
totalEndpoints += len(c.Endpoints)
}
// ... written into profileMap as containers_count / total_execs / ...
}
The List call uses an empty metav1.ListOptions{}. Since Kubescape Storage serves metadata-only LIST (the payload/spec is not loaded from disk), profile.Spec comes back zero-valued: profile.Spec.Containers is empty, so containers_count and every total_* counter sum to 0. The values are read from a response that, by design, never contains spec.
Suggested fix: GET each object individually to obtain spec before aggregating, or report the counters as "unknown/not fetched" rather than 0 when spec is unavailable.
(The fullSpec LIST mechanism cannot be used here: the storage server rejects it for applicationprofiles and networkneighborhoods.)
References:
π― Affected Component(s)
pkg/kubescape(kagent-tools MCP server) β thelist-style handlers forApplicationProfile(and likely otherspdx.softwarecomposition.kubescape.ioresources whose list handlers read spec-derived fields, e.g. vulnerabilityManifests).π Bug Description
Kubescape's Storage aggregated API server implements metadata-only LIST: a LIST request (e.g.,
kubectl get <custom resource> -A -o json) returns object metadata only and omits the heavyspecpayload. The fullspecis only returned by an individual GET on a named object (or via the documentedfullSpecresourceVersion mechanism).The Kubescape tool's
listhandlers appear to aggregate values fromspec(e.g.,spec.containers[].execs,spec.containers[].opens) using data obtained through a LIST call. Because LIST returns nospec, the aggregation counts nothing and returns0for all fields β evencontainers_countβ while the objects actually contain data.This is a documented behavior of Kubescape Storage. Per the
kubescape/storageREADME:(the same applies to ApplicationProfile, whose
spec.containers[]is likewise omitted on LIST)π Steps To Reproduce
kubescape_list_application_profilesthrough a kagent agent (or the MCP server directly).total_execs: 0,total_opens: 0,total_syscalls: 0,containers_count: 0.π€ Expected Behavior
The tool should return the real
spec-derived values (e.g.,execs,opens, container counts) for each ApplicationProfile, or at minimum must not report0when the data is simply not present in a metadata-only LIST response.Acceptable approaches:
spec) before aggregating, or0.Note: The storage's
fullSpecresourceVersion mechanism is not applicable here β per thekubescape/storageREADME,fullSpecis rejected on LIST forapplicationprofilesandnetworkneighborhoods(it is honored only for the summary resources). Individual GET is therefore the appropriate approach for these resources.π± Actual Behavior
All ApplicationProfiles are reported with zeroed counters (trimmed):
{ "application_profiles": [ { "containers_count": 0, "created_at": "2026-06-04T08:41:49Z", "name": "replicaset-rockylinux-58468585c7", "namespace": "kagent", "total_capabilities": 0, "total_endpoints": 0, "total_execs": 0, "total_opens": 0, "total_syscalls": 0 }, { "... same zeros for every profile ..." } ] }However, an individual GET on the same objects returns real data, and the learning is marked complete:
So the data exists (
execs: 13,opens: 115) and the profile iscompleted; the tool's0values come from LIST not returningspec, not from an actually-empty profile.π» Environment
π Additional Context
Root cause:
In
pkg/kubescape/kubescape.go(v0.2.1, around L741), the list handler fetches profiles with a LIST call and then readsspec-derived fields directly from each listed item:The
Listcall uses an emptymetav1.ListOptions{}. Since Kubescape Storage serves metadata-only LIST (the payload/specis not loaded from disk),profile.Speccomes back zero-valued:profile.Spec.Containersis empty, socontainers_countand everytotal_*counter sum to0. The values are read from a response that, by design, never containsspec.Suggested fix: GET each object individually to obtain
specbefore aggregating, or report the counters as "unknown/not fetched" rather than0whenspecis unavailable.(The
fullSpecLIST mechanism cannot be used here: the storage server rejects it forapplicationprofilesandnetworkneighborhoods.)References: