Skip to content

Result format

ts-obom writes two formats. -f cyclonedx produces the Operations BOM the TrustSource platform stores; -f ts is the scanner's own, fuller result, useful for tooling that wants the access graph without reading it back out of CycloneDX properties.

CycloneDX (-f cyclonedx)

A CycloneDX 1.6 document, one per scanned directory. This is the only format the TrustSource OBOM API accepts, and what ts-obom upload sends.

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.6",
  "serialNumber": "urn:uuid:437f88e0-1042-487e-8b14-0c36d83564e5",
  "version": 1,
  "metadata": {
    "timestamp": "2026-09-22T08:00:00+00:00",
    "lifecycles": [{"phase": "operations"}],
    "component": {"bom-ref": "obom:backend", "name": "backend", "type": "application"},
    "properties": [
      {"name": "trustsource:bomType", "value": "obom"},
      {"name": "trustsource:obom:source", "value": "/work/orderdesk/backend"},
      {"name": "trustsource:obom:frontends", "value": "cloudformation,terraform"},
      {"name": "trustsource:obom:tag", "value": "v2.4.17"},
      {"name": "trustsource:obom:branch", "value": "main"},
      {"name": "trustsource:obom:deployment", "value": "PRD"},
      {"name": "trustsource:obom:parameterSource", "value": "terraform=params4PRD.tfvars"},
      {"name": "trustsource:obom:unresolved", "value": "{\"detail\": \"...\", \"principal\": \"aws_lambda_function.worker\", \"reason\": \"aws-managed-policy\"}"}
    ],
    "tools": [
      {"vendor": "EACG", "name": "ts-obom", "version": "0.3.0"},
      {"vendor": "bridgecrew", "name": "checkov", "version": "3.3.15+ts-obom.vendored"}
    ]
  },
  "components": [
    {
      "bom-ref": "pkg:cloudformation/backend/template.yaml/AWS::Serverless::Function.OrderApiFunction@sha1:6c94fa0b",
      "name": "AWS::Serverless::Function.OrderApiFunction",
      "type": "platform",
      "version": "sha1:6c94fa0b",
      "purl": "pkg:cloudformation/backend/template.yaml/AWS::Serverless::Function.OrderApiFunction@sha1:6c94fa0b",
      "properties": [
        {"name": "trustsource:obom:resourceType", "value": "AWS::Serverless::Function"},
        {"name": "trustsource:obom:frontend", "value": "cloudformation"},
        {"name": "trustsource:obom:grant", "value": "{\"actions\": [\"dynamodb:GetItem\"], \"effect\": \"Allow\", \"grantedVia\": \"SAM policy template 'DynamoDBCrudPolicy'\", \"resource\": \"Ref:OrdersTable\"}"}
      ]
    }
  ],
  "dependencies": [
    {"ref": "pkg:cloudformation/backend/template.yaml/AWS::Serverless::Function.OrderApiFunction@sha1:6c94fa0b",
     "dependsOn": ["pkg:cloudformation/backend/template.yaml/AWS::DynamoDB::Table.OrdersTable@sha1:6c94fa0b"]}
  ]
}

metadata.lifecycles declares phase: operations -- CycloneDX's own way of saying this is an Operations BOM, next to the TrustSource marker a platform consumer looks for.

Components

One component per resource declared in the scanned sources -- not only the ones holding an IAM grant. A resource nobody was granted anything on is still part of how the module is operated.

Field Meaning
name The resource as the graph names it: <type>.<logical id> for CloudFormation, <type>.<name> for Terraform
type CycloneDX component type, mapped from the resource type (see below)
version, purl, hashes Checkov's own component identity: the SHA-1 of the file declaring the resource. IaC resources carry no version of their own, so the hash of the declaring file is what identifies the state a component was read in. Identical to what checkov -o cyclonedx_json produces for the same sources

The type mapping is deliberately partial, in the same spirit as the SAM policy template table:

CycloneDX type Resource types
platform Compute: Lambda and SAM functions, EC2 instances, ECS services, Batch job definitions, API gateways
container ECS task definitions, ECR repositories
data Storage and state: S3 buckets, DynamoDB tables, SQS queues, SNS topics, RDS instances and clusters, Secrets Manager secrets, KMS keys, EFS file systems
application Everything else, which is Checkov's own blanket type -- an unmapped resource type is not guessed at

Properties

Everything ts-obom adds is namespaced trustsource:obom:*, so it is distinguishable from CycloneDX's own fields and from Checkov's. The one exception is trustsource:bomType, which is the platform's marker and spelled the way the platform defines it -- without it, ts-obom-ingest rejects the document.

Property Where Meaning
trustsource:bomType metadata Always obom. What makes this an OBOM rather than an SBOM
trustsource:obom:source metadata The absolute path that was scanned
trustsource:obom:frontends metadata The front-ends active for this scan
trustsource:obom:tag, :branch metadata Values of --tag / --branch, omitted when not given
trustsource:obom:deployment metadata Which deployment this document describes -- an environment or a customer setup. Value of --deployment, omitted when not given
trustsource:obom:parameterSource metadata Where the parameter values came from. Never omitted: defaults, or <front-end>=<file> per active front-end. Two documents that both say defaults are not comparable, however they are named -- see Deployments and parameters
trustsource:obom:unresolved metadata One entry per grant the scanner recognised but could not expand, as JSON
trustsource:obom:resourceType component The IaC resource type, unmapped and as written
trustsource:obom:frontend component Which front-end found it
trustsource:obom:grant component One IAM grant held by this resource, as JSON: {resource, actions[], effect, grantedVia}. Repeated per grant. A grant whose principal is not itself a declared resource sits on metadata instead, with an extra principal field

CycloneDX has no native model for an access grant, which is why they travel as properties. A consumer has to know the namespace; the trade-off is recorded in ADR-006.

Dependencies

The module component depends on every resource -- that is what it is deployed as. A resource additionally depends on the resources its grants name, wherever the grant's target can be resolved back to another component in the same sources (Ref:OrdersTable, GetAtt:OrdersTable.Arn). A grant against a literal ARN outside the scanned sources produces no edge: there is nothing in the document to point at, and inventing a node would be worse than leaving the edge out.

TrustSource native (-f ts)

A JSON array with one document per scanned directory. Not accepted by the API -- ts-obom upload rejects it and points at -f cyclonedx -- but it carries the access graph as data rather than as encoded properties.

[
  {
    "module": "backend",
    "moduleId": "obom:backend",
    "source": "/work/orderdesk/backend",
    "tag": "v2.4.17",
    "branch": "main",
    "deployment": "PRD",
    "parameterSource": "terraform=params4PRD.tfvars",
    "tool": {
      "name": "ts-obom",
      "version": "0.3.0",
      "frontends": ["cloudformation", "terraform"],
      "generatedAt": "2026-09-22T12:00:00+00:00"
    },
    "resources": [
      {
        "id": "AWS::Serverless::Function.OrderApiFunction",
        "name": "AWS::Serverless::Function.OrderApiFunction",
        "resourceType": "AWS::Serverless::Function",
        "frontend": "cloudformation",
        "filePath": "template.yaml",
        "fileAbsPath": "/work/orderdesk/backend/template.yaml"
      }
    ],
    "edges": [
      {
        "principal": "AWS::Serverless::Function.OrderApiFunction",
        "resource": "Ref:OrdersTable",
        "actions": ["dynamodb:GetItem", "dynamodb:PutItem"],
        "effect": "Allow",
        "grantedVia": "SAM policy template 'DynamoDBCrudPolicy'"
      }
    ],
    "unresolved": [
      {
        "principal": "aws_lambda_function.worker",
        "reason": "aws-managed-policy",
        "detail": "actions live in AWS's own policy JSON, not the template: arn:aws:iam::aws:policy/AmazonSQSReadOnlyAccess"
      }
    ]
  }
]

The header mirrors the header of a ts-scan dependency scan so that both results can be correlated per module.

Field Meaning
module Name of the scanned directory
moduleId obom:<module>. A local label, not a TrustSource module identifier -- do not pass it to the API as moduleIdentifier; that would file the OBOM under a module of its own, next to the one the SBOM lands in. The upload names the scope explicitly with --module-name
source Absolute path that was scanned
tag, branch Values of --tag / --branch, omitted when not given
deployment Value of --deployment, omitted when not given
parameterSource Where the parameter values came from; never omitted. Same meaning as the CycloneDX property above
tool.* Producer, the active front-ends and a UTC timestamp

Resources

The inventory: every resource block the graph builder found.

Field Meaning
id The graph's own id for the resource; what an edge's principal refers to
resourceType IaC resource type as written (AWS::IAM::Role, aws_lambda_function)
frontend cloudformation or terraform
filePath Declaring file, relative to the scanned directory
fileAbsPath The same file, absolute

Edges

Each edge is one grant. Several statements for the same principal produce several edges; the scanner does not merge or minimise them, so that every edge can be traced back to exactly one declaration.

Field Meaning
principal The identity that holds the grant. CloudFormation: resource type and logical id (AWS::IAM::Role.ReportRole). Terraform: the compute resource that assumes the role (aws_lambda_function.worker) when it can be resolved, otherwise the role, user or group itself
resource The resource the grant applies to, as written: an ARN, a !Ref target rendered as Ref:<logical id>, *
actions IAM actions, expanded for the SAM policy templates the scanner knows
effect Allow or Deny, as declared
grantedVia Where the grant comes from: AWS::IAM::Role inline policy '<name>', SAM policy template '<name>', SAM inline Policies[].Statement or the Terraform resource address

Unresolved

Grants the scanner recognised but could not expand without external knowledge.

Field Meaning
principal The identity concerned
reason Short category, for example a managed policy attachment, a SAM policy template the scanner does not know yet, or unused-parameter for a supplied parameter value no template declares
detail The reference that could not be expanded

Unresolved entries are not errors. They mark the places where a reviewer has to look up the policy by hand, and they are the backlog for extending the scanner.

Graphviz (-f dot)

-f dot renders the access graph as a directed graph: principals and resources as nodes, one edge per grant labelled with its actions, Deny edges dashed and red, unresolved principals dotted. Each scanned directory becomes a cluster.

A note on the name

OBOM here means Operations Bill of Materials, the same as in CycloneDX and the same as everywhere else on the TrustSource platform: a description of how something is operated -- what it runs on, what it is made of once deployed. Up to ts-obom 0.1.0 this documentation called it an Ownership Bill of Materials and described it as a different kind of document. That was wrong, and it is corrected in 0.2.0 (ADR-006). The IAM access graph is not gone -- it is what this scanner adds on top of the inventory.