SBOM Vulnerability Tracing
Combine asset data with security intelligence to instantly trace a CVE from an SBOM component up to every affected application and its owner.
Tracing Vulnerabilities with a Software Bill of Materials (SBOM)
When a new vulnerability like Log4Shell is discovered, the first question is always “Where are we vulnerable?”. With a rescile graph enriched with SBOM data, you can answer this in seconds and automatically generate remediation tickets for the responsible teams.
The Model
- Assets: Foundational data is provided as simple CSV files. We ingest SBOM components, CVE intelligence data, and application ownership information.
- Model: A
model/*.tomlfile useslink_resourcesto link thesbom_componentwith CVE data, matching on package names. - Compliance: A
compliance/*.tomlfile formally models ownership, linking applications to their responsible teams (providers). - Output: An
output/*.tomlfile queries the final graph for vulnerable applications. Instead of relying on a propagated flag, it uses path traversal directly in itsmatch_onclause to find any application connected to aCRITICALCVE. It then uses a Tera template to generate a structured JSON payload for creating a Jira ticket.
The Graph
The importer builds a graph that connects these disparate data sources into a single, queryable model.
log4j vulnerability] --> SC[sbom_component
log4j-core-2.14.1.jar] SC -- "PART_OF" --> Image[image
billing-api-img:1.2] Image -- "USES" --> App[application
billing-api] App -- "HAS_RESPONSIBILITY" --> Owner[provider
team-alpha] end
This provides rapid impact analysis, turning a potential crisis into a manageable, data-driven response.
Complete Example: Generating a Jira Ticket
Let’s walk through a full example of how rescile can identify a critical vulnerability and generate a Jira ticket payload for the responsible team.
1. Asset Data (data/assets/)
First, we define our raw assets. We have applications, the teams that own them, the container images they use, the SBOM components within those images, and a feed of CVEs.
application.csv
name,image,owner
billing-api,billing-api-img:1.2,team-alpha
frontend-app,frontend-img:2.5,team-beta
provider.csv
name,contact_email
team-alpha,alpha@example.com
team-beta,beta@example.com
image.csv
name,sbom_component
billing-api-img:1.2,log4j-core-2.14.1.jar
frontend-img:2.5,react-18.2.0.js
sbom_component.csv
name,package_name,version
log4j-core-2.14.1.jar,log4j-core,2.14.1
react-18.2.0.js,react,18.2.0
cve.csv
cve_id,package_name,severity,summary
CVE-2021-44228,log4j-core,CRITICAL,"Remote code execution in log4j-core"
CVE-2022-25858,react,HIGH,"Regular expression denial of service"
2. Architectural Model (data/models/vulnerability.toml)
This model’s sole responsibility is to link our SBOM components to the CVE feed using link_resources. This creates the necessary relationship in the graph to allow an application to be traced to its vulnerabilities, which is used by the output model.
# Link sbom_component with CVE data to allow traversal from applications
# down to vulnerability information.
origin_resource = "sbom_component"
# Join with `cve` resources on package name to link them.
[[link_resources]]
with = "cve"
on = { local = "package_name", remote = "package_name" }
# Also create a relationship to allow traversal in templates.
create_relation = { type = "cve" }
3. Compliance Model (data/compliance/ownership.toml)
We use a compliance file to formally model the relationship between an application and its owner. This uses the advanced map directive to create a HAS_RESPONSIBILITY edge.
audit_id = "OWNERSHIP-POLICY"
audit_name = "Ownership and Responsibility Policy"
[[control]]
id = "OWN-1"
name = "Map Asset Roles to Providers"
description = "Finds the provider listed in an application's 'owner' property and creates a 'HAS_RESPONSIBILITY' relationship to it."
[[control.target]]
[control.target.map]
# The 'role' resource is not used in this simplified example, but is required by the directive.
derived_type = "role"
origin_resource_types = ["application"]
target_resource_type = "provider"
primary_relation_type = "HAS_RESPONSIBILITY"
property_on_relation = "role"
[control.target.map.property_map_overrides]
owner = "owner"
4. Output Generation (data/output/jira_tickets.toml)
This is the final step. The output model uses a powerful feature: path traversal in its match_on clause. Instead of checking for a pre-calculated flag, it directly queries the graph for any application connected down the chain to a CVE with CRITICAL severity. For each match, it renders a complete JSON payload for creating a Jira ticket, pulling in all the necessary context from the graph.
origin_resource = "application"
# This output generates a JSON payload for creating a Jira ticket for each
# application found to have a critical vulnerability.
[[output]]
resource_type = "jira_ticket_payload"
name = "jira-ticket-for-{{ origin_resource.name }}"
# Match on applications that are linked to a CVE with CRITICAL severity.
# This uses path traversal over the pre-populated graph relationships.
match_on = [
{ property = "image.sbom_component.cve.severity", value = "CRITICAL" }
]
# The template renders a complete JSON object that can be sent to the Jira API.
# It traverses the graph from the application to its owner and the vulnerable component.
template = """
{
"fields": {
"project": {
"key": "SEC"
},
"summary": "Critical Vulnerability Detected in Application: {{ origin_resource.name }}",
"description": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "A critical vulnerability has been detected in the application '{{ origin_resource.name }}', which is owned by your team."
}
]
},
{
"type": "heading",
"attrs": { "level": 2 },
"content": [ { "type": "text", "text": "Vulnerability Details" } ]
},
{
"type": "bulletList",
"content": [
{
"type": "listItem",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "CVE ID: {{ origin_resource.image[0].sbom_component[0].cve[0].cve_id }}" }
]
}
]
},
{
"type": "listItem",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "Summary: {{ origin_resource.image[0].sbom_component[0].cve[0].summary }}" }
]
}
]
},
{
"type": "listItem",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "Vulnerable Component: {{ origin_resource.image[0].sbom_component[0].name }}" }
]
}
]
}
]
},
{
"type": "heading",
"attrs": { "level": 2 },
"content": [ { "type": "text", "text": "Action Required" } ]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Please investigate this vulnerability and apply the necessary patches or mitigations as soon as possible. This ticket has been automatically assigned to the designated owner of the application."
}
]
}
]
},
"issuetype": {
"name": "Task"
},
"assignee": {
"name": "{{ origin_resource.provider[0].name }}"
},
"labels": [
"security",
"vulnerability",
"autogenerated",
"{{ origin_resource.image[0].sbom_component[0].cve[0].cve_id }}"
]
}
}
"""
5. Generated Output
After running the importer, rescile generates a new resource with the jira_ticket_payload label. Its properties contain the rendered JSON. For the billing-api application, the generated Jira payload would look like this:
query {
jira_ticket_payload {
name
fields {
issuetype
description
summary
assignee
project
}
}
}
{
"data": {
"jira_ticket_payload": [
{
"name": "jira-ticket-for-billing-api",
"fields": {
"issuetype": {
"name": "Task"
},
"description": {
"content": [
{
"content": [
{
"text": "A critical vulnerability has been detected in the application 'billing-api', which is owned by your team.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"level": 2
},
"content": [
{
"text": "Vulnerability Details",
"type": "text"
}
],
"type": "heading"
},
{
"content": [
{
"content": [
{
"content": [
{
"text": "CVE ID: CVE-2021-44228",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
},
{
"content": [
{
"content": [
{
"text": "Summary: Remote code execution in log4j-core",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
},
{
"content": [
{
"content": [
{
"text": "Vulnerable Component: log4j-core-2.14.1.jar",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
}
],
"type": "bulletList"
},
{
"attrs": {
"level": 2
},
"content": [
{
"text": "Action Required",
"type": "text"
}
],
"type": "heading"
},
{
"content": [
{
"text": "Please investigate this vulnerability and apply the necessary patches or mitigations as soon as possible. This ticket has been automatically assigned to the designated owner of the application.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "doc",
"version": 1
},
"summary": "Critical Vulnerability Detected in Application: billing-api",
"assignee": {
"name": "team-alpha"
},
"project": {
"key": "SEC"
}
}
}
]
}
}
This JSON can be directly sent to the Jira API to create a ticket, automatically assigning it to the correct team (team-alpha) and providing all necessary context for remediation.