Metadata-Version: 2.4
Name: ts-obom
Version: 0.4.0
Summary: TrustSource OBOM scanner: extracts a CycloneDX Operations Bill of Materials from infrastructure-as-code
Author: EACG GmbH
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/trustsource/ts-obom.git
Project-URL: Documentation, https://trustsource.github.io/ts-obom
Keywords: obom,cyclonedx,iam,access graph,infrastructure as code,cloudformation,terraform,opentofu,compliance,TrustSource
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: click==8.1.7
Requires-Dist: toml~=0.10.2
Requires-Dist: wasabi~=1.1.3
Requires-Dist: bc-python-hcl2>=0.4
Requires-Dist: cyclonedx-python-lib<8,>=7.6
Requires-Dist: packageurl-python>=0.11
Requires-Dist: termcolor>=2.0
Requires-Dist: bc-jsonpath-ng>=1.6
Requires-Dist: networkx>=2.6
Requires-Dist: dpath>=2.1
Requires-Dist: asteval>=0.9
Requires-Dist: PyYAML>=6.0
Requires-Dist: requests>=2.31
Requires-Dist: packaging>=23.0
Requires-Dist: typing-extensions>=4.4
Requires-Dist: tqdm>=4.66
Requires-Dist: colorama>=0.4
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pyright>=1.1.390; extra == "dev"
Dynamic: license-file

![Supported Versions](https://img.shields.io/badge/Python-%203.10,%203.11,%203.12-blue) ![License](https://img.shields.io/badge/License-Apache--2.0-green)

# TrustSource OBOM Scanner

The **ts-obom** scanner extracts an **Operations Bill of Materials (OBOM)** from infrastructure-as-code: the resources a module is deployed as -- functions, tables, buckets, queues, task definitions -- together with the IAM grants that say which of them may do what to which other. It reads CloudFormation, AWS SAM, Terraform and OpenTofu sources and writes a CycloneDX document that the TrustSource platform stores. It is the infrastructure counterpart to [ts-scan](https://github.com/trustsource/ts-scan), which produces the Software Bill of Materials (SBOM) of the code.

Where an SBOM answers *what is in the software*, an OBOM answers *what the software is operated as, and what it is allowed to touch once it runs*. That is the evidence trust-boundary and threat-modelling work needs and which SBOM signals alone cannot provide.

## Description

**ts-obom** scans a directory for IaC sources and emits, per scanned directory, a document with

* `resources` - every resource declared in the sources, with its type and the file it comes from. In the CycloneDX output these are the components
* `edges` - access grants `{principal, resource, actions[], effect, grantedVia}`, for example the role of a Lambda function that may `s3:GetObject` on a bucket ARN, with the policy or SAM policy template that granted it. In the CycloneDX output these are component properties and `dependencies` edges
* `unresolved` - grants the scanner saw but could not expand, for example managed policy ARNs that would need AWS API access to resolve

Supported IaC front-ends:

| Front-end | Sources | What is extracted |
|-----------|---------|-------------------|
| `cloudformation` | CloudFormation and AWS SAM templates (`.yaml`, `.yml`, `.json`) | `AWS::IAM::Role` inline policies, `AWS::Serverless::Function` policies including the common SAM policy templates (`DynamoDBCrudPolicy`, `S3ReadPolicy`, ...) |
| `terraform` | Terraform and OpenTofu (`.tf`, `.tf.json`, `.tofu`, `.tofu.json`; a `.tofu` file shadows a `.tf` file of the same name, as in OpenTofu) | `aws_iam_role_policy` / `aws_iam_user_policy` / `aws_iam_group_policy` inline documents, `*_policy_attachment` resources, resolved to the compute resource assuming the role where possible |

The graph is built with a trimmed, vendored subset of [Checkov](https://github.com/bridgecrewio/checkov)'s resource graph builder; see [`src/ts_obom/_vendor/NOTICE.md`](src/ts_obom/_vendor/NOTICE.md) for provenance and changes. No Checkov installation and no cloud credentials are required; the scan is fully offline.

## Installation

### Installation from the PyPI repository

```shell
pip install ts-obom
```

### Installation from a local folder

```shell
git clone https://github.com/trustsource/ts-obom.git
cd ts-obom
pip install .
```

### Installation as a Docker image

```shell
docker pull trustsource/ts-obom
```

Scan a local checkout by mounting it into the container:

```shell
docker run --rm -v "$(pwd)":/workspace trustsource/ts-obom scan -o /workspace/obom.json /workspace/infrastructure
```

## Usage

The command set follows the conventions of **ts-scan**: verbs as sub-commands, `-o/--output` and `-f/--format` for results, `--<front-end>:<option>` for front-end specific switches, a profile-based config file and an optional `tsproject.toml` in the scanned directory.

```shell
ts-obom scan -o <path to the output file> [-f <output format>] <path to one or more directories>
```

The `-f <output format>` option controls the output format and can be:

* `ts` - the scanner's own JSON format (default), one document per scanned directory
* `cyclonedx` - a CycloneDX 1.6 Operations BOM, the format the TrustSource platform stores
* `dot` - a Graphviz digraph of the access graph, for example `ts-obom scan -f dot infra | dot -Tsvg -o obom.svg`

### Options

Which sources are read:

* `--cloudformation:ignore` - Skip CloudFormation and SAM templates
* `--terraform:ignore` - Skip Terraform and OpenTofu sources

Which *deployment* the result describes — see [Deployments](#deployments) below:

* `--deployment <NAME>` - Names the deployment: an environment (`DEV`, `PRD`) or a customer setup (`kunde1`)
* `--cloudformation:parameters <FILE>` - Applies a CloudFormation parameter file on top of the templates' declared defaults. Reads `[{"ParameterKey": ..., "ParameterValue": ...}]` — the format `sam deploy --parameter-overrides` is fed from — or a flat name/value mapping
* `--terraform:var-file <FILE>` - Applies a `.tfvars` file on top of the variable defaults, as `terraform -var-file` would

Recorded alongside the result:

* `--tag <TAG>` - Stores the SCM tag `<TAG>` in the result
* `--branch <BRANCH>` - Stores the SCM branch `<BRANCH>` in the result
* `--verbose` - Enables verbose mode

The full list of options can be printed using:

```shell
ts-obom scan --help
```

### Deployments

One project is usually deployed several times, and the interesting questions are comparative: what does production have that development does not, what does one customer setup grant that another does not.

```shell
ts-obom scan -f cyclonedx --deployment PRD \
  --cloudformation:parameters params4PRD.json -o obom-prd.cdx.json .
```

**A name on its own is not enough.** The scan resolves `Ref`, `!Sub` and Terraform variables against the **defaults declared in the sources**. In the usual pattern — one template, one parameter file per environment — the entire difference between two deployments lives in those parameter files, so without them two scans of the same sources produce identical documents. Naming one `DEV` and the other `PRD` would then show no difference, which says nothing about the deployments and everything about how the documents were made.

Every result therefore records where its parameter values came from, and this field is never omitted:

| `parameterSource` | Meaning |
|-------------------|---------|
| `defaults` | Nothing was supplied. Two documents that both say this are **not** comparable, however they are named |
| `cloudformation=params4PRD.json` | Values came from that file |
| `cloudformation=defaults,terraform=params4PRD.tfvars` | Half parameterised — Terraform got values, CloudFormation did not |

Naming a deployment without supplying values is allowed, and warned about — once when the scan produces such a document, and again before it is uploaded. A value supplied for a parameter that no scanned template declares is reported under `unresolved` as `unused-parameter`, rather than silently doing nothing.

### Example

Scanning a directory with a SAM template

```shell
ts-obom scan -o obom.json ./backend
```

produces a document like

```json
[
  {
    "module": "backend",
    "moduleId": "obom:backend",
    "source": "/work/orderdesk/backend",
    "deployment": "PRD",
    "parameterSource": "cloudformation=params4PRD.json",
    "tool": { "name": "ts-obom", "version": "0.4.0", "frontends": ["cloudformation", "terraform"], "generatedAt": "2026-09-22T12:00:00+00:00" },
    "edges": [
      {
        "principal": "AWS::Serverless::Function.OrderApiFunction",
        "resource": "Ref:OrdersTable",
        "actions": ["dynamodb:GetItem", "dynamodb:DeleteItem", "dynamodb:PutItem", "dynamodb:Scan", "dynamodb:Query", "dynamodb:UpdateItem", "dynamodb:BatchWriteItem", "dynamodb:BatchGetItem", "dynamodb:DescribeTable", "dynamodb:ConditionCheckItem"],
        "effect": "Allow",
        "grantedVia": "SAM policy template 'DynamoDBCrudPolicy'"
      }
    ],
    "unresolved": []
  }
]
```

The exact `principal`, `resource` and `grantedVia` strings depend on the front-end; the [format documentation](https://trustsource.github.io/ts-obom/format) describes them.

## Upload to TrustSource

The platform stores OBOMs as CycloneDX, so a transfer is a scan in that format followed by an upload:

```shell
ts-obom scan -f cyclonedx -o obom.cdx.json .
ts-obom upload --api-key "$TS_API_KEY" --project-name Orderdesk obom.cdx.json
```

Scan the **project root** and upload at **project scope**: the front-ends recurse, so one run covers the whole project, and that is where an OBOM belongs. A module is something that produces a deployment artefact, which infrastructure does not divide into -- a queue or a table belongs to no artefact. `--module-name` exists for the case where the scanned sources really are one module's own infrastructure. The `obom` feature has to be enabled for the company; the key travels as the `x-api-key` header, and `--base-url` carries the API version (default `https://api.trustsource.io/v2`).

Uploading a file written with `-f ts` is refused locally -- the API would only reject it -- with a pointer to `-f cyclonedx`.

## User Settings

Like **ts-scan**, **ts-obom** reads defaults from a TOML config file with profiles. The default location is `~/.ts-obom/config`; it is created on first run:

```toml
[default]
format = "ts"

[ci]
format = "ts"
verbose = true
```

Select a profile with `ts-obom -p ci scan ...` and a different file with `ts-obom --config <path> scan ...`. Options can also be set through environment variables prefixed with `TS_OBOM_`, for example `TS_OBOM_SCAN_OUTPUT_PATH`. A `tsproject.toml` in a scanned directory provides per-project defaults.

## Relationship to ts-scan

**ts-scan** and **ts-obom** are deliberately separate tools. Software composition (SBOM) and operational deployment (OBOM) are different questions with different consumers, and mixing them into one command set would blur what each result means. Both share the same conventions, packaging and release process, and the OBOM document header (`module`, `moduleId`, `source`, `tag`, `branch`) mirrors ts-scan's scan header so that results can be correlated per module.

## License

**ts-obom** is licensed under the Apache License 2.0. The vendored Checkov subset under `src/ts_obom/_vendor/checkov` is licensed under the Apache License 2.0 by its respective copyright holders; see the `LICENSE` and `NOTICE.md` files in that directory.
