Through the Looking Glass: Humpty Dumpty

This is post 4 in a seven-part series on what the Anchore Enterprise API makes possible for container security teams. 

“When I use a word,” Humpty Dumpty said in rather a scornful tone, “it means just what I choose it to mean — neither more nor less.” Anchore Enterprise’s GraphQL subsystem takes the same approach to data: you define exactly what you want back, in exactly the shape you want it, without over-fetching or stringing together multiple REST calls to assemble the picture yourself.

The REST endpoints we’ve explored in earlier posts are ideal for targeted, per-image operations. GraphQL is the right tool when you need to ask broader questions across your container fleet — things like “which images have Critical vulnerabilities and which runtime containers are they running in?” or “what is the policy compliance posture across all of my registered tags?” These are questions that would require multiple REST calls, response aggregation, and client-side filtering. With GraphQL, they become a single query.

Pick Your Wall: The Reporting Endpoints

Humpty Dumpty had one wall; you get two. Anchore Enterprise exposes two GraphQL endpoints depending on the scope you need.

https://<anchore-host>/v2/reports/graphql

This endpoint is scoped to the authenticated user’s account. The x-anchore-account header is required:

curl -s -u _api_key:<your-api-key> \
  -X POST "https://wonderland.example.com/v2/reports/graphql" \
  -H "Content-Type: application/json" \
  -H "x-anchore-account: mad-hatter-team" \
  -d '{"query": "{ __typename }"}'
https://<anchore-host>/v2/reports/global/graphql

This endpoint requires administrator access and returns data across all accounts. No account header required:

curl -s -u _api_key:<your-api-key> \
  -X POST "https://wonderland.example.com/v2/reports/global/graphql" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ __typename }"}'

Glory For You: Exploring the Schema

“There’s glory for you!” Humpty Dumpty told Alice. “I don’t know what you mean by ‘glory,’” Alice replied. “Of course you don’t — till I tell you.” Before writing queries, let GraphQL tell you. Introspection returns the full list of available query types, filter inputs, and response fields directly from your deployment:

curl -s -u _api_key:<your-api-key> \
  -X POST "https://wonderland.example.com/v2/reports/graphql" \
  -H "Content-Type: application/json" \
  -H "x-anchore-account: mad-hatter-team" \
  -d '{
    "query": "{ __schema { queryType { fields { name description args { name type { name kind ofType { name kind } } } } } } }"
  }' | jq .

You can also import the endpoint into a tool like Insomnia or GraphQL Playground — both support introspection natively and give you an interactive schema explorer and query builder. The full list of available query types is summarised below. All queries accept limit, nextToken, and a typed filter object.

QueryDescription
imagesByVulnerabilityUnique vulnerabilities and the images affected
tagsByVulnerabilityUnique vulnerabilities and affected tags (hierarchical)
artifactsByVulnerabilityUnique vulnerabilities and affected artifacts
runtimeInventoryImagesByVulnerabilityVulnerabilities in runtime inventory images
kubernetesRuntimeVulnerabilitiesByNamespaceVulnerabilities by Kubernetes namespace
vulnerabilitiesByKubernetesContainerVulnerabilities by Kubernetes container
vulnerabilitiesByEcsContainerVulnerabilities by ECS container
runtimeInventoryUnscannedImagesRuntime inventory images not yet analyzed
policyEvaluationsByTagPolicy evaluations for tags (hierarchical)
policyEvaluationsByRuntimeInventoryImagePolicy evaluations for runtime inventory images
imagesWithStigImages with STIG compliance information
runtimeImagesWithStigRuntime images with STIG compliance information
metricsAvailable metrics in the system
metricDataMetric data points, chronologically descending
scheduledQueriesConfigured scheduled queries
scheduledQueryExecutionsExecutions for a given scheduled query

Putting Humpty Back Together: Cross-Image Vulnerability Summaries

All the King’s horses and all the King’s men couldn’t put Humpty together again. A single GraphQL query can. Where REST would need a series of round trips — list images, fetch vulnerabilities per image, fetch tags per image, join client-side — imagesByVulnerability returns a list of unique vulnerabilities alongside every container image affected by each one, in one response.

Filtering is done via nested filter objects — severity filtering lives under vulnerability, with separate filter objects available for artifact, registry, repository, tag, and image. Note that severity values are GraphQL enum literals and are written without quotes.

{
  imagesByVulnerability(
    limit: 500
    filter: { vulnerability: { severity: Critical } }
  ) {
    pageInfo { nextToken count }
    results {
      vulnerabilityId
      cve
      imagesCount
      images {
        digest
        distro
        tags {
          registryName
          repositoryName
          tagName
          current
        }
        artifacts {
          artifactName
          artifactVersion
          artifactType
          severity
          fixedIn
          isKev
          epssScore
        }
      }
    }
  }
}

To filter on multiple severities, use severities instead of severity:

filter: { vulnerability: { severities: [Critical, High] } }

In Python, with pagination handled via pageInfo.nextToken:

import requests

ANCHORE_URL = "https://wonderland.example.com/v2/reports/graphql"
AUTH = ("_api_key", "<your-api-key>")
HEADERS = {"x-anchore-account": "mad-hatter-team"}

VULN_QUERY = """
query($nextToken: String) {
  imagesByVulnerability(
    limit: 500
    nextToken: $nextToken
    filter: { vulnerability: { severity: Critical } }
  ) {
    pageInfo { nextToken count }
    results {
      vulnerabilityId
      cve
      imagesCount
      images {
        digest
        distro
        tags {
          registryName
          repositoryName
          tagName
          current
        }
        artifacts {
          artifactName
          artifactVersion
          artifactType
          severity
          fixedIn
          isKev
          epssScore
        }
      }
    }
  }
}
"""

def get_images_by_vulnerability():
    results = []
    next_token = None
    while True:
        resp = requests.post(
            ANCHORE_URL,
            json={
                "query": VULN_QUERY,
                "variables": {"nextToken": next_token},
            },
            auth=AUTH,
            headers=HEADERS,
        )
        resp.raise_for_status()
        data = resp.json()["data"]["imagesByVulnerability"]
        results.extend(data.get("results", []))
        next_token = data.get("pageInfo", {}).get("nextToken")
        if not next_token:
            break
    return results

def main():
    findings = get_images_by_vulnerability()
    print(
        f"Critical vulnerabilities affecting images: "
        f"{len(findings)}\n"
    )
    for f in findings:
        cve = f.get("cve") or f.get("vulnerabilityId")
        print(f"{cve} — {f.get('imagesCount')} image(s)")
        for img in f.get("images", []):
            for tag in img.get("tags", []):
                if tag.get("current"):
                    print(
                        f"  {tag['registryName']}/"
                        f"{tag['repositoryName']}:{tag['tagName']}"
                    )
            for artifact in img.get("artifacts", []):
                fix = artifact.get("fixedIn")
                fix_str = f"fix: {fix}" if fix else "no fix"
                kev_str = " [KEV]" if artifact.get("isKev") else ""
                print(
                    f"    {artifact['artifactName']} "
                    f"{artifact['artifactVersion']} "
                    f"— {fix_str}{kev_str}"
                )

if __name__ == "__main__":
    main()

From the Wall: Policy Compliance Across the Fleet

What the cross-image vulnerability query did for CVE exposure, policyEvaluationsByTag does for compliance — give you the whole fleet’s view from one vantage point. It returns policy evaluations in a hierarchical view: registry → repository → tag → evaluations. Each tag may have multiple evaluation records; use the latest field to identify the most current result.

{
  policyEvaluationsByTag(limit: 1000) {
    pageInfo { nextToken count }
    results {
      registryName
      repositoriesCount
      tagsCount
      account
      repositories {
        repositoryName
        tagsCount
        tags {
          tagName
          imageDigest
          current
          evaluations {
            result
            reason
            lastEvaluatedAt
            latest
          }
        }
      }
    }
  }
}

In Python, flattening the hierarchy to produce a compliance summary:

import requests
from collections import Counter

ANCHORE_URL = "https://wonderland.example.com/v2/reports/graphql"
AUTH = ("_api_key", "<your-api-key>")
HEADERS = {"x-anchore-account": "mad-hatter-team"}

POLICY_QUERY = """
query($nextToken: String) {
  policyEvaluationsByTag(limit: 1000, nextToken: $nextToken) {
    pageInfo { nextToken count }
    results {
      registryName
      account
      repositories {
        repositoryName
        tags {
          tagName
          imageDigest
          current
          evaluations {
            result
            reason
            lastEvaluatedAt
            latest
          }
        }
      }
    }
  }
}
"""

def get_policy_evaluations():
    all_registries = []
    next_token = None
    while True:
        resp = requests.post(
            ANCHORE_URL,
            json={
                "query": POLICY_QUERY,
                "variables": {"nextToken": next_token},
            },            auth=AUTH,
            headers=HEADERS,
        )
        resp.raise_for_status()
        data = resp.json()["data"]["policyEvaluationsByTag"]
        all_registries.extend(data.get("results", []))
        next_token = data.get("pageInfo", {}).get("nextToken")
        if not next_token:
            break
    return all_registries
<br>def flatten_latest_evaluations(registries):
    flat = []
    for registry in registries:
        for repo in registry.get("repositories", []):
            for tag in repo.get("tags", []):
                for evaluation in tag.get("evaluations", []):
                    if evaluation.get("latest"):
                        flat.append({<br>                            "full_tag": (
                                f"{registry['registryName']}/"
                                f"{repo['repositoryName']}:"
                                f"{tag['tagName']}"                            ),
                            "image_digest": tag.get("imageDigest"),
                            "result": evaluation.get("result"),
                            "reason": evaluation.get("reason"),
                            "last_evaluated_at": evaluation.get(
                                "lastEvaluatedAt"
                            ),
                        })
    return flat

def main():
    registries = get_policy_evaluations()
    evaluations = flatten_latest_evaluations(registries)
    counts = Counter(e["result"] for e in evaluations)

    print("Fleet Policy Compliance Summary")
    print(f"  Total tags evaluated: {len(evaluations)}")
    print(f"  Pass: {counts.get('Pass', 0)}")
    print(f"  Fail: {counts.get('Fail', 0)}")
    print()

    failing = [e for e in evaluations if e["result"] == "Fail"]
    if failing:
        print("Failing tags:")
        for e in failing:
            print(
                f"  {e['full_tag']}  "
                f"(evaluated: {e['last_evaluated_at']})"
            )

if __name__ == "__main__":
    main()

For administrator access across all accounts, swap the endpoint and remove the account header:

ANCHORE_URL = "https://wonderland.example.com/v2/reports/global/graphql"
HEADERS = {}

Beyond the Wall: Runtime Inventory Queries

Sitting on the wall, Humpty Dumpty had a view of the static landscape. Runtime inventory queries let you see what’s actually moving on the other side. One of the more distinctive aspects of this GraphQL schema is its deep integration with runtime inventory. runtimeInventoryImagesByVulnerability combines vulnerability data with live Kubernetes and ECS inventory, answering not just “which container images are vulnerable?” but “which vulnerable images are currently running in my clusters?”

{
  runtimeInventoryImagesByVulnerability(
    limit: 500
    filter: { vulnerability: { severity: Critical } }
  ) {
    pageInfo { nextToken count }
    results {
      vulnerabilityId
      cve
      imagesCount
      images {
        digest
        tags {
          registryName
          repositoryName
          tagName
          current
        }
        artifacts {
          artifactName
          artifactVersion
          severity
          fixedIn
          isKev
        }
      }
    }
  }
}

Use introspection to explore kubernetesRuntimeVulnerabilitiesByNamespace and vulnerabilitiesByKubernetesContainer for even more granular Kubernetes-scoped views.

Up Next

The GraphQL reporting interface gives you a query language designed around the questions container security and platform teams actually ask — not individual image lookups, but fleet-wide views of vulnerability exposure, policy compliance, and runtime context. The typed filter inputs and paginated responses are consistent across all query types, so once you’re comfortable with the pattern, exploring the rest of the schema is straightforward.

Next in the series: Who Stole the Tarts? — Chasing the Cheshire Cat Through Zero-Day Vulnerabilities. When a new CVE drops, the question changes from “what does our fleet look like?” to “are we on fire?” — and you need an answer in minutes, not hours. We’ll use the /query/vulnerabilities and /query/images/by-package endpoints to rapidly assess blast radius the moment a vulnerability is disclosed.

If you’re an Anchore Enterprise customer looking to build with the API, the Customer Success team is the fastest way to get unblocked — reach out through the Anchore Support Portal. If you’re not a customer yet but want to see what any of this looks like against your own container images, request a demo and we’ll walk you through it.