View on GitHub

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

  1. Assets: Foundational data is provided as simple CSV files. We ingest SBOM components, CVE intelligence data, and application ownership information.
  2. Model: A model/*.toml file uses link_resources to link the sbom_component with CVE data, matching on package names.
  3. Compliance: A compliance/*.toml file formally models ownership, linking applications to their responsible teams (providers).
  4. Output: An output/*.toml file queries the final graph for vulnerable applications. Instead of relying on a propagated flag, it uses path traversal directly in its match_on clause to find any application connected to a CRITICAL CVE. 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.

graph LR subgraph "Vulnerability Impact Trace" CVE[CVE Database
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.