Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/analysis-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ tmforge analyze model.tm7 --rules ./rules.tmrules.json # one spec file
tmforge analyze model.tm7 --rules ./rules/ # a directory of specs (searched recursively)
```

The [starter rule-pack library](../examples/README.md#starter-rule-packs) provides opt-in PCI-inspired,
HIPAA-inspired, and internal-service examples, with a runnable synthetic model, source/control
references, and tested command snippets. These examples do not certify compliance.

To compile an existing MTMT template instead of hand-authoring JSON, use [`rules import`](cli-reference.md#rules):

```bash
Expand Down
99 changes: 97 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Sample threat models
# Sample threat models and rule packs

Small, synthetic threat models used to demo Threat Model Forge, exercise the first-party
[GitHub Action](../action.yml), and dogfood the CLI in [CI](../.github/workflows/ci.yml).
[GitHub Action](../action.yml), and dogfood the CLI in [CI](../.github/workflows/ci.yml), plus
opt-in starter packs for authoring model-specific policies.

## `webshop`

Expand Down Expand Up @@ -58,3 +59,97 @@ tmforge analyze examples/webshop.tm7 \
CI runs this pair through both the CLI and the first-party Action and requires the same outcome from
each, which is what keeps the Action's rule and suppression wiring honest. A suppressed finding is
still produced and recorded — it simply stops gating the build.

## Starter rule packs

Three small, inspectable version 2 packs demonstrate numeric, regex, Boolean, and connectivity
policies. **These examples do not certify compliance.** They evaluate recorded model properties and
directed connections, not deployed configurations, legal applicability, or control effectiveness.
They are opt-in examples, not new built-in rules. Use a build with the additional matchers described
in [the rule guide](../docs/analysis-rules.md#numeric-and-regex-predicates); older builds reject
unsupported version 2 fields.

| Pack | Checks | Source and Intent |
| --- | --- | --- |
| [PCI-inspired](rule-packs/pci-inspired.tmrules.json) | Stored PAN encryption; at least 12 months of CDE audit retention. | Inspired by [PCI DSS v4.0.1](https://www.pcisecuritystandards.org/document_library/), 3.5.1 and 10.5.1. The first rule covers encryption-based designs only; other permitted ways to protect PAN require adapting the policy. The second checks duration only, not log availability or completeness. |
| [HIPAA-inspired](rule-packs/hipaa-inspired.tmrules.json) | ePHI flows use an allowed TLS transport and certificate validation; receiving processes have a direct audit-store connection. | Inspired by [45 CFR 164.312(b) and (e)](https://www.hhs.gov/hipaa/for-professionals/security/laws-regulations/index.html). TLS allowlists and direct-store topology are example choices, not the legal text. Addressable encryption specifications still require assessment; this pack does not assess alternatives. |
| [Internal service](rule-packs/internal-service.tmrules.json) | Internal service names follow `svc-name`; externally reachable internal processes connect directly to an audit store. | Illustrative `EXAMPLE-SVC-1`/`EXAMPLE-SVC-2` policies, with [OWASP logging guidance](https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html#event-collection). Naming is hygiene, not a threat or an OWASP mandate. |

Each file contains two rules, versioned pack identity, control references, remediation guidance, and
scope limitations. PCI uses flat guard/requirement predicates and numeric retention; HIPAA shows
`allOf`/`not` Boolean composition; internal service shows whole-string regex and directed reachability.
All findings are warnings by default. Five rules declare STRIDE metadata and produce triageable
threats; `SERVICE-NAME` is a finding only. Use your existing ruleset/severity policy to choose gates.

### Record scope and evidence

Use these **property names** consistently; property lookup and enum comparisons are case-insensitive.
The service-name regex is case-sensitive. Record custom properties on the appropriate objects;
there is no automatic classification or data discovery.

| Property | Where | Meaning in These Examples |
| --- | --- | --- |
| `StoresPAN` | Datastore | `Yes` opts into the PAN rule; `No` and `Unknown` do not. |
| `Encrypted` | Datastore | The PAN rule accepts `At-rest`, `TDE`, `Client-side`, or `Platform`; `No`, `Unknown`, or missing evidence fails. These labels do not prove cryptographic strength or key management. |
| `AuditScope` | Datastore | `CDE` opts into audit retention; `Other` and `Unknown` do not. This marker denotes an audit store, not every datastore in a CDE. |
| `RetentionMonths` | Datastore | An invariant numeric string of at least `12`. No approximation of a month/year using days is made. |
| `DataClassification` | Flow | `ePHI` opts into HIPAA-inspired rules; `Other` and `Unknown` do not. This custom field is separate from the built-in `DataType` vocabulary. |
| `Protocol`, `CertificateValidation` | Flow | `HTTPS`, `TLS`, or `mTLS`, together with `CertificateValidation=Yes`. Unqualified `gRPC`, for example, does not establish TLS use. |
| `ServiceScope`, `ServiceName` | Process | `ServiceScope=Internal` opts into internal-service rules. Names use the case-sensitive `svc-` prefix and lowercase alphanumeric segments separated by single hyphens, such as `svc-billing-v2`. |
| `StoresLogData` | Datastore | `Yes` qualifies a direct audit destination. A process/collector with the same property does not qualify as a datastore. |

**Missing or Unknown scope means unassessed, not compliant or proven out of scope.** Within an
explicit scope, missing/Unknown control evidence fails the relevant requirement. Do not clear
findings by removing scope markers or asserting unevidenced controls.

Connectivity is directed and page-local. Trust boundaries do not block traversal. The audit checks
require a direct outgoing store connection; brokered/collector-based logging needs a policy adapted
to that architecture. HIPAA audit findings are per incoming ePHI flow, not per process, and isolated
processes are not assessed by an interaction rule. See the rule guide for
[graph and input limitations](../docs/analysis-rules.md#directed-connectivity).

### Try the demonstration model

[starter-model.tmforge.json](rule-packs/starter-model.tmforge.json) is a deliberately incomplete,
synthetic model that combines independent examples so every rule fires once. It contains no real
payment or health data and is not a recommended architecture. From the repository root:

```bash
tmforge analyze examples/rule-packs/starter-model.tmforge.json --rules examples/rule-packs/pci-inspired.tmrules.json --max-severity warning
tmforge analyze examples/rule-packs/starter-model.tmforge.json --rules examples/rule-packs/hipaa-inspired.tmrules.json --max-severity warning
tmforge analyze examples/rule-packs/starter-model.tmforge.json --rules examples/rule-packs/internal-service.tmrules.json --max-severity warning
tmforge analyze examples/rule-packs/starter-model.tmforge.json --rules examples/rule-packs --max-severity warning
```

Each command exits `2` for findings at the warning threshold, not a tool failure. Each individual
pack adds two findings; the directory loads all three and adds six. Built-in findings are also
reported, so total output is not limited to those counts. Add `--reportFolder <directory>` to retain
the analysis JSON, HTML, and SARIF using the usual CLI workflow. Tests run the command lines above
and check the resulting rule IDs and report identities.

To satisfy the example policies while retaining their scope declarations, change the demonstration
model as follows (only record such values on a real model when evidenced):

| Object | Example Change |
| --- | --- |
| Example PAN store | Set `Encrypted=At-rest`. |
| Example audit store | Set `RetentionMonths=12`. |
| Example gateway | Set `ServiceName=svc-gateway`. |
| Example ePHI request | Set `Protocol=TLS` and `CertificateValidation=Yes`. |
| Example gateway to Example audit store | Add a direct outgoing audit flow. The existing route through Example worker is indirect. |

Tests verify these changes clear the six starter findings without erasing scope markers. This does
not clear every built-in finding or establish compliance.

### Use Your Own Model

In Studio, open a model, load a chosen `.tmrules.json` under **Analysis Rules**, and run **Analyze**.
The browser/WASM engine accepts custom pack content; an API-backed Studio uses the server's trusted
startup configuration instead. API operators can point `TmForge__Rules` at the chosen pack or pack
directory, and MCP clients can use the existing sandboxed `rulesPath`. The first-party Action's
`rules` input accepts the same files. No model or pack is uploaded automatically by these examples.

Fork a pack with your own pack ID and version before adapting it; preserve useful source references
and record the changes from the example policy. Use the existing expected-pack fingerprints to
detect missing or changed policy content. Do not claim these selected checks implement the whole
standard, and do not treat a regex, protocol label, or drawn connection as proof of runtime behavior.
64 changes: 64 additions & 0 deletions examples/rule-packs/hipaa-inspired.tmrules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"schema": "tmforge-rules",
"version": 2,
"dialect": "urn:tmforge:rules:interaction-v1",
"pack": {
"id": "example-hipaa",
"name": "HIPAA-inspired starter policy",
"version": "1.0.0",
"description": "Two illustrative model checks inspired by HIPAA technical safeguards, not a complete implementation of the Security Rule. These examples do not certify compliance or verify deployed controls.",
"source": {
"type": "urn:tmforge:source:example-policy",
"name": "HHS Summary of the HIPAA Security Rule; 45 CFR 164.312",
"uri": "https://www.hhs.gov/hipaa/for-professionals/security/laws-regulations/index.html"
}
},
"properties": [
{ "name": "DataClassification", "allowedValues": ["ePHI", "Other", "Unknown"] },
{ "name": "Protocol", "allowedValues": ["Unknown", "HTTPS", "HTTP", "TLS", "mTLS", "gRPC", "AMQP", "SQL", "SSH", "FTP", "Other"] },
{ "name": "CertificateValidation", "allowedValues": ["Yes", "No", "Unknown"] },
{ "name": "StoresLogData", "allowedValues": ["Yes", "No", "Unknown"] }
],
"rules": [
{
"id": "EPHI-TRANSPORT",
"severity": "warning",
"stride": "InformationDisclosure",
"message": "{flow.Name} is marked ePHI but does not evidence both an allowed TLS transport and certificate validation.",
"fullDescription": "Example TLS policy inspired by 45 CFR 164.312(e), transmission security. The allowlist and CertificateValidation=Yes are example design choices, not the legal text. Encryption under 164.312(e)(2)(ii) is addressable: a documented assessment and appropriate implementation or alternative is still required. A protocol label alone cannot prove deployed protection.",
"helpText": "For an explicitly ePHI flow, record Protocol as HTTPS, TLS, or mTLS and CertificateValidation=Yes only when evidenced. Adapt this policy for other justified protections; do not relabel an unencrypted flow or treat Unknown classification as out-of-scope evidence.",
"helpUri": "https://www.hhs.gov/hipaa/for-professionals/security/laws-regulations/index.html",
"provenance": { "sourceId": "45-CFR-164.312(e)", "location": "45 CFR 164.312(e)(1) and (e)(2)(ii); illustrative TLS policy" },
"expression": {
"allOf": [
{ "subject": "flow", "property": "DataClassification", "valueIn": ["ePHI"] },
{
"not": {
"allOf": [
{ "subject": "flow", "property": "Protocol", "valueIn": ["HTTPS", "TLS", "mTLS"] },
{ "subject": "flow", "property": "CertificateValidation", "valueIn": ["Yes"] }
]
}
}
]
}
},
{
"id": "EPHI-AUDIT",
"severity": "warning",
"stride": "Repudiation",
"message": "{target.Name} receives ePHI on {flow.Name} but has no direct outgoing connection to a store marked StoresLogData=Yes.",
"fullDescription": "Audit-destination example inspired by 45 CFR 164.312(b), audit controls. A modeled process receiving an explicitly ePHI flow should have an audit destination. A direct store connection is an example topology, not a HIPAA requirement, and does not prove that the required activities are recorded or examined. Findings are per incoming ePHI flow.",
"helpText": "Model the evidenced audit destination and mark the destination StoresLogData=Yes. Adapt the direct-connection policy for collectors or message brokers. This interaction rule does not assess isolated processes or infer ePHI classification from a name or boundary.",
"helpUri": "https://www.hhs.gov/hipaa/for-professionals/security/laws-regulations/index.html",
"provenance": { "sourceId": "45-CFR-164.312(b)", "location": "45 CFR 164.312(b); illustrative audit-destination policy" },
"expression": {
"allOf": [
{ "subject": "flow", "property": "DataClassification", "valueIn": ["ePHI"] },
{ "subject": "target", "kind": "process" },
{ "not": { "subject": "target", "connectsTo": { "kind": "datastore", "property": "StoresLogData", "equals": "Yes" } } }
]
}
}
]
}
51 changes: 51 additions & 0 deletions examples/rule-packs/internal-service.tmrules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"schema": "tmforge-rules",
"version": 2,
"dialect": "urn:tmforge:rules:flat-v1",
"pack": {
"id": "example-internal-service",
"name": "Internal-service starter policy",
"version": "1.0.0",
"description": "Illustrative policies over recorded model properties and connections, not an organization standard. These examples do not certify compliance or verify deployed controls.",
"source": {
"type": "urn:tmforge:source:example-policy",
"name": "Illustrative internal-service policy; logging guidance from OWASP",
"uri": "https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html"
}
},
"properties": [
{ "name": "ServiceScope", "allowedValues": ["Internal", "External", "Unknown"] },
{ "name": "ServiceName" },
{ "name": "StoresLogData", "allowedValues": ["Yes", "No", "Unknown"] }
],
"rules": [
{
"id": "SERVICE-NAME",
"severity": "warning",
"appliesTo": "process",
"message": "{name} is marked Internal but does not record a ServiceName matching the example svc-name convention.",
"fullDescription": "EXAMPLE-SVC-1: Internal services use a svc- prefix and lowercase alphanumeric segments separated by single hyphens. This is an illustrative naming policy, not an OWASP requirement or evidence of a security control.",
"helpText": "Record an evidenced ServiceName such as svc-billing, or adapt the example pattern to your own naming policy. Missing names also fail this requirement.",
"provenance": { "sourceId": "EXAMPLE-SVC-1", "location": "examples/README.md#starter-rule-packs" },
"when": { "property": "ServiceScope", "equals": "Internal" },
"assert": { "property": "ServiceName", "matches": "\\Asvc-[a-z0-9]+(?:-[a-z0-9]+)*\\z" }
},
{
"id": "AUDIT-CONNECTION",
"severity": "warning",
"appliesTo": "process",
"stride": "Repudiation",
"message": "{name} is an externally reachable Internal service with no direct outgoing connection to a store marked StoresLogData=Yes.",
"fullDescription": "EXAMPLE-SVC-2: A service reachable along modeled directed paths from an external entity should have an explicit audit destination. The direct-store topology is an illustrative policy, not a requirement imposed by OWASP. It does not prove that events are actually logged.",
"helpText": "Model the evidenced audit-store connection and mark that store StoresLogData=Yes. Adapt this example for brokered or collector-based logging; an indirect path does not satisfy connectsTo.",
"helpUri": "https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html",
"provenance": { "sourceId": "EXAMPLE-SVC-2", "location": "OWASP Logging Cheat Sheet: Event collection" },
"when": {
"property": "ServiceScope",
"equals": "Internal",
"reachableFrom": { "kind": "external" }
},
"assert": { "connectsTo": { "kind": "datastore", "property": "StoresLogData", "equals": "Yes" } }
}
]
}
51 changes: 51 additions & 0 deletions examples/rule-packs/pci-inspired.tmrules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"schema": "tmforge-rules",
"version": 2,
"dialect": "urn:tmforge:rules:flat-v1",
"pack": {
"id": "example-pci",
"name": "PCI-inspired starter policy",
"version": "1.0.0",
"description": "Two illustrative model checks inspired by PCI DSS v4.0.1, not a complete implementation of any requirement. These examples do not certify compliance or verify deployed controls.",
"source": {
"type": "urn:tmforge:source:example-policy",
"name": "PCI DSS",
"version": "4.0.1",
"uri": "https://www.pcisecuritystandards.org/document_library/"
}
},
"properties": [
{ "name": "StoresPAN", "allowedValues": ["Yes", "No", "Unknown"] },
{ "name": "Encrypted", "allowedValues": ["Unknown", "No", "At-rest", "TDE", "Client-side", "Platform"] },
{ "name": "AuditScope", "allowedValues": ["CDE", "Other", "Unknown"] },
{ "name": "RetentionMonths" }
],
"rules": [
{
"id": "PAN-ENCRYPTION",
"severity": "warning",
"appliesTo": "datastore",
"stride": "InformationDisclosure",
"message": "{name} is marked StoresPAN=Yes but does not evidence an allowed at-rest encryption mode.",
"fullDescription": "Encryption-focused example inspired by PCI DSS v4.0.1 requirement 3.5.1 (protect stored PAN). The standard also permits non-encryption approaches; this rule intentionally covers only encryption-based designs. An encryption label does not establish strong cryptography, key management, or full requirement satisfaction.",
"helpText": "Record the implemented Encrypted mode as At-rest, TDE, Client-side, or Platform. Adapt the policy for evidenced tokenization, truncation, or hashing designs rather than claiming encryption that is not implemented. Missing or Unknown scope is not assessed.",
"helpUri": "https://www.pcisecuritystandards.org/document_library/",
"provenance": { "sourceId": "PCI-DSS-4.0.1-3.5.1", "location": "PCI DSS v4.0.1, requirement 3.5.1; encryption-only example" },
"when": { "property": "StoresPAN", "equals": "Yes" },
"assert": { "property": "Encrypted", "anyOf": ["At-rest", "TDE", "Client-side", "Platform"] }
},
{
"id": "AUDIT-RETENTION",
"severity": "warning",
"appliesTo": "datastore",
"stride": "Repudiation",
"message": "{name} is marked AuditScope=CDE but does not evidence at least 12 months of audit-log retention.",
"fullDescription": "Retention-duration example inspired by PCI DSS v4.0.1 requirement 10.5.1. It checks only a declared minimum of 12 months, not actual log history, the recent three months' availability, or the other logging requirements. Months are used deliberately rather than approximating a year with 365 days.",
"helpText": "For an audit store in the cardholder data environment, set AuditScope=CDE and record the evidenced RetentionMonths value. A missing, non-numeric, or Unknown duration fails; unclassified stores are not assessed.",
"helpUri": "https://www.pcisecuritystandards.org/document_library/",
"provenance": { "sourceId": "PCI-DSS-4.0.1-10.5.1", "location": "PCI DSS v4.0.1, requirement 10.5.1; minimum duration only" },
"when": { "property": "AuditScope", "equals": "CDE" },
"assert": { "property": "RetentionMonths", "greaterThanOrEqual": 12 }
}
]
}
Loading
Loading