Trace one service from source to runtime
Produce an application inventory that joins repository revisions, artifact digests, deployments and owners. Keep missing relationships visible before counting scanner findings.
30 MIN
By AISecurityInterviews · Updated
TL;DR: A repair can be closed only for the deployments it actually reaches. Build the source-to-runtime map before assigning findings or measuring completion.
Where you are. You are starting the application-security course by establishing the objects every later assessment must refer to.
Build the inventory from records you can join
Use the fictional document service as a small part of a large customer platform. Its components have different release cadences: the API runs continuously, the conversion worker scales with demand, and the importer may remain idle between monthly jobs. An inventory based only on currently running processes will miss configured future execution.
| Component | Source | Artifact observation | Deployment observation |
|---|---|---|---|
| API | web at R2 | Build b12 produced A2 | East production runs A2 |
| Worker | document at R2 | Tag stable, digest unresolved | West production references stable |
| Importer | document at R1 | Build b08 produced D1 | Schedule still selects D1 |
R1, R2, A2, D1 and D2 are symbolic exercise identifiers, not actual hashes. In real records, store the digest algorithm and full value. The current checkout is not necessarily the source of deployed bytes, and the current value of a mutable tag is not historical proof of what ran.
Start with the release packet
The fictional service has two repositories. The web repository produces a frontend and an API. The document repository produces a conversion worker and a monthly importer. Both repositories use a shared parser package. The API team owns application code, while the platform team owns the base image and production deployment.
Your first deliverable is an inventory with one row per deployed component. Include repository revision, build run, artifact digest, environment, service owner and business purpose. Do not combine the frontend and API simply because they share a repository. Their data and permissions can differ.
Preserve missing joins
The frontend has a verified build record. The worker has only a mutable image tag. The importer has not run this month, but its scheduled template still references an older digest. Record the worker's artifact relationship as unresolved and the importer's configured artifact separately from its last execution.
This distinction changes security work. A source patch may already be available while the importer still schedules vulnerable bytes. A quiet runtime view may simply mean the importer did not execute during the observation period. Neither condition supports closing its dependency finding.
Give each relationship an owner
The repository owner can explain the source and dependency graph. The build owner can provide provenance and artifact identity. The service owner can confirm the production rollout and operational impact. Assign responsibility for obtaining missing evidence rather than asking one team to certify facts it does not control.
Avoid treating an ownership tag as permanent truth. A team rename, service transfer or acquisition can leave tags stale. Record how ownership was confirmed and who accepts the repair deadline. A security platform can suggest an owner, but a real handoff needs acknowledgment.
Review the inventory as a security boundary
The map also exposes authority. The monthly importer reads customer documents, while the frontend does not. A shared parser issue can therefore have different consequences across components. Keep those differences visible when grouping a common repair.
Only include identifiers needed for the exercise. An inventory should not store live credentials or raw customer documents. Restrict access to detailed production relationships when they reveal sensitive infrastructure. The course uses fictional records so the review can be performed entirely offline.
Repository
Revision and code owner
Build
Run and digest
Deployment
Environment and instance
Service
Purpose and repair owner
Repository
Revision and code owner
Model assertions, not just asset rows
The evidence graph has several kinds of edge: source revision built into an artifact, artifact selected by a deployment, deployment observed on a workload, and a team accepting ownership. Each edge needs its own observer and timestamp. A build system can attest what it produced without knowing whether every production region installed it. Qualify revisions by repository: web at R2 and document at R2 are different source subjects. A2 is the API artifact; D1 and D2 are document-worker artifacts.
Preserve both desired and observed deployment state. During a rolling update, the configured digest may be D2 while some instances still run D1. For a scheduled job, retain the template's selected digest as well as the last completed run. Otherwise “no affected running instance” can hide an affected task that starts tomorrow.
builds = [
{"builder": "release-builder", "run": "b12", "repository": "web", "revision": "R2", "digest": "A2"},
{"builder": "release-builder", "run": "b08", "repository": "document", "revision": "R1", "digest": "D1"},
]
assets = [
{"name": "api-east", "kind": "running", "repository": "web", "digest": "A2"},
{"name": "worker-west", "kind": "running", "repository": "document", "digest": None, "tag": "stable"},
{"name": "monthly-importer", "kind": "scheduled", "repository": "document", "digest": "D1"},
]
# These claims are prevalidated exercise inputs, not unverified attestations.
by_digest = {}
for build in builds:
by_digest.setdefault(build["digest"], []).append(build)
def classify(asset):
candidates = by_digest.get(asset["digest"], [])
if len(candidates) != 1 or candidates[0]["repository"] != asset["repository"]:
return "unresolved"
return "current" if candidates[0]["revision"] == "R2" else "prior-revision"
result = {asset["name"]: classify(asset) for asset in assets}
assert result == {"api-east": "current", "worker-west": "unresolved", "monthly-importer": "prior-revision"}
# A conflicting second claim requires review rather than last-write-wins.
by_digest["A2"].append({"repository": "web", "revision": "R1", "digest": "A2"})
assert classify(assets[0]) == "unresolved"
print(result["api-east"], result["worker-west"], result["monthly-importer"]) # => current unresolved prior-revision
This conservative example requires one unambiguous claim. Multiple legitimate builds can produce the same bytes, so a real verifier should evaluate claim consistency and trusted producers rather than rejecting every duplicate. The important failure mode is selecting whichever claim arrived last when trusted claims disagree about relevant inputs.
A signature authenticates a statement under a key or identity; it does not establish that the builder was uncompromised or that the statement describes the required build process. Verification must bind the subject digest, expected builder, source inputs and permitted build configuration. Keep the trust policy version alongside the attestation so a later policy change can trigger reevaluation.
Source
Reviewed revision and dependencies
Build
Trusted producer and subject digest
Deploy
Desired and observed artifact
Owner
Accepted responsibility and deadline
Source
Reviewed revision and dependencies
Make the graph useful for remediation
Suppose one parser dependency is affected. Identify every component containing that dependency, then join to reachable deployments and scheduled templates. A monorepo label is too coarse: the frontend and importer may share source ownership but not a dependency, data class or runtime permission.
Use explicit relationship states such as confirmed, conflicting and absent. “Absent” means the relationship is not known, not that the component is unaffected. Assign an evidence owner to unresolved edges. The build team resolves the worker's digest; the platform team checks the scheduled importer; the application owner confirms which parser paths accept customer documents.
Keep a stable service identity through team renames and acquisitions. Ownership tags are useful discovery hints, but closure requires someone to accept the service and its remediation scope. Store that acceptance with a date. An orphaned deployment should remain in the denominator until its retirement is verified, not disappear because its team no longer exists.
Finally, define the snapshot's boundary: accounts, regions, clusters and collection time. “Three of three components reviewed” is meaningful for this fixture, but cannot describe the enterprise until the inventory boundary is established. Retain unknown assets and failed collectors alongside the graph so a platform outage does not improve the apparent repair rate.
Runtime-only view
No running importer
Remove it from inventory
Next schedule
Old digest executes
Outcome, unsafe: Future exposure omitted
Configured execution view
Template D1
Keep scheduled artifact in scope
Repair D2
Verify template and next run
Outcome, safe: Closure includes future execution
Runtime-only view
Future exposure omitted
Do this before moving on
Run the inventory program and add a second running API instance on an older API artifact A1. Produce one row per running instance or scheduled template, including desired digest, observed digest, source relationship, owner and evidence time. Leave the worker unresolved until a trusted runtime digest is supplied.
Write the closure request for the parser repair. It must include the old API instance and the monthly importer. Explain what evidence proves each has stopped selecting D1, and which team can provide that evidence.
Review your result. A passing inventory does not resolve a historical artifact through today's tag value. It retains idle configured work, handles conflicting claims explicitly and separates build evidence from deployment evidence. Each unknown edge has a named evidence owner and a next action.
Go deeper
- ASPM explains the application graph and why finding counts can mislead.
- SBOMs distinguishes inventory content from proof of deployed behavior.
- Verify an ASPM repair adds rollback and multiple-owner constraints to this inventory review.
Key takeaways
- Inventory deployed components rather than only repositories.
- Keep configured state separate from observed execution.
- Assign evidence requests to the owner who can answer them.
Check yourself
Say or write your answer first, then reveal. Explaining it from memory, in your own words, is the skill an interviewer is checking.
1Why keep desired and observed artifact identities?
2Does a valid attestation signature prove a safe build?
3How should two conflicting source claims for one digest be handled?
4Why does an idle importer remain in scope?
Sign in to track which lessons you have finished.