AWS Container Images#
Cartography models ECR image identity, tags, multi-platform manifests, attestations, and layers separately. This makes digest identity stable while preserving repository-specific names and tags.
Repository and image identity#
An AWSECRRepository is identified by its ARN. An
AWSECRRepositoryImage represents a repository reference and is identified by
its URI, which preserves the tag and repository context. It points to an
AWSECRImage, whose identity is the content digest.
(:AWSECRRepository)-[:REPO_IMAGE]->(:AWSECRRepositoryImage)
(:AWSECRRepositoryImage)-[:IMAGE]->(:AWSECRImage)
This indirection allows multiple tags and repositories to reference the same digest without creating duplicate image nodes.
List all repository references for a digest:
MATCH (repository:AWSECRRepository)-[:REPO_IMAGE]->
(reference:AWSECRRepositoryImage)-[:IMAGE]->
(image:AWSECRImage {digest: $digest})
RETURN repository.arn, reference.uri, reference.tag,
reference.image_pushed_at, image.type
ORDER BY repository.arn, reference.tag
Manifest lists and attestations#
AWSECRImage.type distinguishes:
image: a single-platform image with architecture, OS, and ordered layers.manifest_list: a multi-platform OCI index or Docker manifest list.attestation: an attestation manifest, withattests_digestand optional provenance metadata.
Manifest lists contain platform images but exclude attestations from
CONTAINS_IMAGE:
(:AWSECRImage {type: "manifest_list"})-[:CONTAINS_IMAGE]->
(:AWSECRImage {type: "image"})
Attestations use a separate edge:
(:AWSECRImage {type: "attestation"})-[:ATTESTS]->(:AWSECRImage)
Inspect the platforms in a manifest list:
MATCH (index:AWSECRImage {digest: $digest, type: "manifest_list"})
-[:CONTAINS_IMAGE]->(image:AWSECRImage {type: "image"})
RETURN image.architecture, image.os, image.variant, image.digest
ORDER BY image.os, image.architecture, image.variant
Inspect attestations for an image:
MATCH (attestation:AWSECRImage {type: "attestation"})
-[:ATTESTS]->(image:AWSECRImage {digest: $digest})
RETURN attestation.digest, attestation.attestation_type,
attestation.source_uri, attestation.source_revision,
attestation.invocation_uri, attestation.invocation_workflow,
attestation.invocation_run_number
SLSA provenance can also create BUILT_FROM relationships with
parent_image_uri, from_attestation=true, and confidence="explicit".
Provenance may link an image to the GitHubWorkflow that packaged it.
Ordered layers#
Image layers are de-duplicated globally by their uncompressed DiffID. The
layer_diff_ids property retains manifest order, while HEAD, NEXT, TAIL,
and HAS_LAYER expose the same sequence as graph relationships.
Docker’s canonical empty layer has DiffID
sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef
and is marked is_empty=true. This is the uncompressed DiffID, not the
compressed registry blob digest.
Read the stored manifest order:
MATCH (image:AWSECRImage {digest: $digest, type: "image"})
UNWIND range(0, size(image.layer_diff_ids) - 1) AS position
RETURN position, image.layer_diff_ids[position] AS diff_id
ORDER BY position
Traverse the graph representation while constraining shared NEXT edges to
layers that belong to the image:
MATCH (image:AWSECRImage {digest: $digest, type: "image"})-[:HEAD]->
(head:AWSECRImageLayer)
MATCH (image)-[:TAIL]->(tail:AWSECRImageLayer)
MATCH path = (head)-[:NEXT*0..]->(tail)
WHERE ALL(layer IN nodes(path) WHERE (image)-[:HAS_LAYER]->(layer))
WITH path
ORDER BY length(path) DESC
LIMIT 1
UNWIND range(0, length(path)) AS position
RETURN position, nodes(path)[position].diff_id AS diff_id
ORDER BY position
Because layers are shared, repeated layers can produce multiple valid NEXT
successors. Detect image-local branching with:
MATCH (image:AWSECRImage {type: "image"})-[:HAS_LAYER]->
(layer:AWSECRImageLayer)-[:NEXT]->(successor:AWSECRImageLayer)
WHERE (image)-[:HAS_LAYER]->(successor)
WITH image, layer, collect(DISTINCT successor.diff_id) AS successors
WHERE size(successors) > 1
RETURN image.digest, layer.diff_id AS branching_layer, successors
ORDER BY image.digest, branching_layer
Parent-image detection#
An explicit BUILT_FROM relationship derived from provenance is the strongest
parent-image signal. When provenance is unavailable, a candidate base image
can be inferred when all of its ordered layers are a prefix of the target
image’s layers:
MATCH (target:AWSECRImage {digest: $target_digest, type: "image"})
MATCH (repository:AWSECRRepository {name: $base_repository})
-[:REPO_IMAGE]->(reference:AWSECRRepositoryImage)
-[:IMAGE]->(base:AWSECRImage {type: "image"})
WHERE target.layer_diff_ids IS NOT NULL
AND base.layer_diff_ids IS NOT NULL
AND size(base.layer_diff_ids) <= size(target.layer_diff_ids)
AND ALL(position IN range(0, size(base.layer_diff_ids) - 1)
WHERE base.layer_diff_ids[position] = target.layer_diff_ids[position])
RETURN base.digest, reference.uri, reference.tag,
reference.image_pushed_at, size(base.layer_diff_ids) AS matched_layers
ORDER BY matched_layers DESC, reference.image_pushed_at DESC
LIMIT 1
Layer-prefix inference is a heuristic. Restrict the candidate repository and prefer explicit provenance when available.
Lambda container images#
For Lambda functions with packagetype="Image", image_uri stores the
container reference and image_digest stores the digest when the URI is
digest-pinned. HAS_IMAGE links the function to the matching registry object.
RESOLVED_IMAGE has narrower runtime semantics: it points to the concrete
single-platform Image that the function runs. For a multi-architecture
manifest list, Cartography uses architecture_normalized (amd64 or arm64)
to select the matching child image.
(:AWSLambda)-[:HAS_IMAGE]->(:AWSECRImage)
(:AWSLambda)-[:HAS_IMAGE]->(:GitLabContainerImage)
(:AWSLambda)-[:HAS_IMAGE]->(:GCPArtifactRegistryImage)
(:AWSLambda)-[:HAS_IMAGE]->(:GitHubContainerImage)
(:AWSLambda)-[:RESOLVED_IMAGE]->(:Image)
Use HAS_IMAGE when investigating the configured artifact or tag. Use
RESOLVED_IMAGE when investigating runtime packages, layers,
vulnerabilities, attestations, or provenance for the platform that executes.
MATCH (function:AWSLambda)-[:RESOLVED_IMAGE]->(image:Image)
RETURN function.arn, function.architecture_normalized,
image.digest, image.architecture, image.os
ORDER BY function.arn