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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0



## [Unreleased]
## [1.2.1] - 2026-09-08

### Added
- `tirith lint`: check policy files for the mistakes that otherwise reach CI looking like real
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ pip install -e .

```
tirith --version
tirith 1.2.0
tirith 1.2.1
```

Congratulations! Tirith has been setup in your system
Expand Down
2 changes: 1 addition & 1 deletion docs/platform-check.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ fabricated one would be worse than an honest `null`.
```json
{
"schema_version": 1,
"generator": {"name": "tirith", "version": "1.2.0"},
"generator": {"name": "tirith", "version": "1.2.1"},
"created_at": "2026-08-12T09:14:03Z",
"input_kind": "terraform_plan",
"origin": {"kind": "ci", "trigger_type": "tirith", "ci_run_url": "https://github.com/acme/infra/actions/runs/1"},
Expand Down
12 changes: 6 additions & 6 deletions documentation/docs/tirith-usage/ci-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ policy:
image: python:3.12
needs: [plan]
script:
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.1"
- tirith -policy-path .tirith/policies -input-path plan.json --fail-on-error
```

Expand All @@ -129,7 +129,7 @@ will never come, and the job hangs instead of failing.

Tirith is **not on PyPI** — `pip install tirith` installs an unrelated project of the same name.
Install from git, and pin a tag rather than tracking the default branch so a CI job cannot change
behaviour underneath you. `1.2.0` is the newest tag;
behaviour underneath you. `1.2.1` is the newest tag;
`git ls-remote --tags https://github.com/StackGuardian/tirith.git` lists them. Python 3.8 or newer.

To evaluate your organization's policies instead of the committed files, swap the last line for
Expand All @@ -142,7 +142,7 @@ policy:
variables:
SG_ORG: my-org # SG_API_TOKEN comes from a masked CI/CD variable
script:
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.1"
- tirith platform check --workflow-id my-repo --input-path plan.json --fail-on-error
```

Expand All @@ -154,7 +154,7 @@ Nothing above is GitLab-specific: any runner that can execute a container and pr
the same way. The recipe is always the same three steps —

1. produce the input document (`terraform show -json tfplan > plan.json`);
2. `pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"`;
2. `pip install "git+https://github.com/StackGuardian/tirith.git@1.2.1"`;
3. `tirith -policy-path <policies> -input-path plan.json --fail-on-error`

— and gate the job on the exit code, which every CI system does by default for a non-zero exit.
Expand Down Expand Up @@ -182,7 +182,7 @@ pipelines:
- step:
name: Policy gate
script:
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.1"
- tirith lint .tirith/policies # needs a build from main until the next release
- tirith -policy-path .tirith/policies -input-path plan.json --fail-on-error
```
Expand Down Expand Up @@ -226,7 +226,7 @@ Catch a broken policy before it is committed, let alone before CI runs it. Tirit
```yaml title=".pre-commit-config.yaml"
repos:
- repo: https://github.com/StackGuardian/tirith
rev: main # 1.2.0 predates the hook; pin the first tag that includes it
rev: 1.2.1 # the first tag that publishes the hooks
hooks:
- id: tirith-lint
- id: tirith-fmt
Expand Down
2 changes: 1 addition & 1 deletion documentation/docs/tirith-usage/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ passed. This is the flag that makes the command usable as a CI gate; the full co

### `--version`

Prints the version number (for example `1.2.0`) and exits `0`.
Prints the version number (for example `1.2.1`) and exits `0`.

## Output streams

Expand Down
10 changes: 1 addition & 9 deletions documentation/docs/tirith-usage/editor-and-local.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,6 @@ site_name: Tirith
slug: editor-and-local/
---

:::note Not in 1.2.0

`tirith lint`, `tirith fmt` and the pre-commit hooks are on `main` and will be in the next
release. `pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"` does not have them;
install from `main` until then.

:::

CI is the last place a policy should fail. This page is about the loop before that — running
Tirith on your own machine, while the code is still being written.

Expand Down Expand Up @@ -79,7 +71,7 @@ the interactive explorer — is
```yaml title=".pre-commit-config.yaml"
repos:
- repo: https://github.com/StackGuardian/tirith
rev: main # 1.2.0 predates the hooks; pin the first tag that includes them
rev: 1.2.1 # the first tag that publishes the hooks
hooks:
- id: tirith-lint
- id: tirith-fmt
Expand Down
12 changes: 6 additions & 6 deletions documentation/static/docs/tirith-usage/ci-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ policy:
image: python:3.12
needs: [plan]
script:
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.1"
- tirith -policy-path .tirith/policies -input-path plan.json --fail-on-error
```

Expand All @@ -117,7 +117,7 @@ will never come, and the job hangs instead of failing.

Tirith is **not on PyPI** — `pip install tirith` installs an unrelated project of the same name.
Install from git, and pin a tag rather than tracking the default branch so a CI job cannot change
behaviour underneath you. `1.2.0` is the newest tag;
behaviour underneath you. `1.2.1` is the newest tag;
`git ls-remote --tags https://github.com/StackGuardian/tirith.git` lists them. Python 3.8 or newer.

To evaluate your organization's policies instead of the committed files, swap the last line for
Expand All @@ -130,7 +130,7 @@ policy:
variables:
SG_ORG: my-org # SG_API_TOKEN comes from a masked CI/CD variable
script:
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.1"
- tirith platform check --workflow-id my-repo --input-path plan.json --fail-on-error
```

Expand All @@ -142,7 +142,7 @@ Nothing above is GitLab-specific: any runner that can execute a container and pr
the same way. The recipe is always the same three steps —

1. produce the input document (`terraform show -json tfplan > plan.json`);
2. `pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"`;
2. `pip install "git+https://github.com/StackGuardian/tirith.git@1.2.1"`;
3. `tirith -policy-path <policies> -input-path plan.json --fail-on-error`

— and gate the job on the exit code, which every CI system does by default for a non-zero exit.
Expand Down Expand Up @@ -170,7 +170,7 @@ pipelines:
- step:
name: Policy gate
script:
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.1"
- tirith lint .tirith/policies # needs a build from main until the next release
- tirith -policy-path .tirith/policies -input-path plan.json --fail-on-error
```
Expand Down Expand Up @@ -214,7 +214,7 @@ Catch a broken policy before it is committed, let alone before CI runs it. Tirit
```yaml
repos:
- repo: https://github.com/StackGuardian/tirith
rev: main # 1.2.0 predates the hook; pin the first tag that includes it
rev: 1.2.1 # the first tag that publishes the hooks
hooks:
- id: tirith-lint
- id: tirith-fmt
Expand Down
2 changes: 1 addition & 1 deletion documentation/static/docs/tirith-usage/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ passed. This is the flag that makes the command usable as a CI gate; the full co

### `--version`

Prints the version number (for example `1.2.0`) and exits `0`.
Prints the version number (for example `1.2.1`) and exits `0`.

## Output streams

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@
Source: https://stackguardian.github.io/tirith/docs/tirith-usage/editor-and-local/
Summary: VS Code tasks, a pre-commit hook, and the local loop to use when an AI agent is drafting the policy.

[NOTE] Not in 1.2.0

`tirith lint`, `tirith fmt` and the pre-commit hooks are on `main` and will be in the next
release. `pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"` does not have them;
install from `main` until then.


CI is the last place a policy should fail. This page is about the loop before that — running
Tirith on your own machine, while the code is still being written.

Expand Down Expand Up @@ -68,7 +61,7 @@ the interactive explorer — is
```yaml
repos:
- repo: https://github.com/StackGuardian/tirith
rev: main # 1.2.0 predates the hooks; pin the first tag that includes them
rev: 1.2.1 # the first tag that publishes the hooks
hooks:
- id: tirith-lint
- id: tirith-fmt
Expand Down
23 changes: 8 additions & 15 deletions documentation/static/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2773,7 +2773,7 @@ passed. This is the flag that makes the command usable as a CI gate; the full co

### `--version`

Prints the version number (for example `1.2.0`) and exits `0`.
Prints the version number (for example `1.2.1`) and exits `0`.

## Output streams

Expand Down Expand Up @@ -2982,7 +2982,7 @@ policy:
image: python:3.12
needs: [plan]
script:
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.1"
- tirith -policy-path .tirith/policies -input-path plan.json --fail-on-error
```

Expand All @@ -2992,7 +2992,7 @@ will never come, and the job hangs instead of failing.

Tirith is **not on PyPI** — `pip install tirith` installs an unrelated project of the same name.
Install from git, and pin a tag rather than tracking the default branch so a CI job cannot change
behaviour underneath you. `1.2.0` is the newest tag;
behaviour underneath you. `1.2.1` is the newest tag;
`git ls-remote --tags https://github.com/StackGuardian/tirith.git` lists them. Python 3.8 or newer.

To evaluate your organization's policies instead of the committed files, swap the last line for
Expand All @@ -3005,7 +3005,7 @@ policy:
variables:
SG_ORG: my-org # SG_API_TOKEN comes from a masked CI/CD variable
script:
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.1"
- tirith platform check --workflow-id my-repo --input-path plan.json --fail-on-error
```

Expand All @@ -3017,7 +3017,7 @@ Nothing above is GitLab-specific: any runner that can execute a container and pr
the same way. The recipe is always the same three steps —

1. produce the input document (`terraform show -json tfplan > plan.json`);
2. `pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"`;
2. `pip install "git+https://github.com/StackGuardian/tirith.git@1.2.1"`;
3. `tirith -policy-path <policies> -input-path plan.json --fail-on-error`

— and gate the job on the exit code, which every CI system does by default for a non-zero exit.
Expand Down Expand Up @@ -3045,7 +3045,7 @@ pipelines:
- step:
name: Policy gate
script:
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"
- pip install "git+https://github.com/StackGuardian/tirith.git@1.2.1"
- tirith lint .tirith/policies # needs a build from main until the next release
- tirith -policy-path .tirith/policies -input-path plan.json --fail-on-error
```
Expand Down Expand Up @@ -3089,7 +3089,7 @@ Catch a broken policy before it is committed, let alone before CI runs it. Tirit
```yaml
repos:
- repo: https://github.com/StackGuardian/tirith
rev: main # 1.2.0 predates the hook; pin the first tag that includes it
rev: 1.2.1 # the first tag that publishes the hooks
hooks:
- id: tirith-lint
- id: tirith-fmt
Expand Down Expand Up @@ -3252,13 +3252,6 @@ Source: https://stackguardian.github.io/tirith/docs/tirith-usage/editor-and-loca
Summary: VS Code tasks, a pre-commit hook, and the local loop to use when an AI agent is drafting the policy.
==============================================================================

[NOTE] Not in 1.2.0

`tirith lint`, `tirith fmt` and the pre-commit hooks are on `main` and will be in the next
release. `pip install "git+https://github.com/StackGuardian/tirith.git@1.2.0"` does not have them;
install from `main` until then.


CI is the last place a policy should fail. This page is about the loop before that — running
Tirith on your own machine, while the code is still being written.

Expand Down Expand Up @@ -3317,7 +3310,7 @@ the interactive explorer — is
```yaml
repos:
- repo: https://github.com/StackGuardian/tirith
rev: main # 1.2.0 predates the hooks; pin the first tag that includes them
rev: 1.2.1 # the first tag that publishes the hooks
hooks:
- id: tirith-lint
- id: tirith-fmt
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

setup(
name="py-tirith",
version="1.2.0",
version="1.2.1",
license="Apache",
description="Tirith simplifies defining Policy as Code.",
long_description_content_type="text/markdown",
Expand Down Expand Up @@ -56,7 +56,7 @@
"Operating System :: POSIX",
# 'Operating System :: Microsoft :: Windows',
"Programming Language :: Python",
# 'Programming Language :: Python :: 2.7',

Check warning on line 59 in setup.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this commented out code.

See more on https://sonarcloud.io/project/issues?id=StackGuardian_policy-framework&issues=AaB-MB6TIYsULuX3N-ZV&open=AaB-MB6TIYsULuX3N-ZV&pullRequest=372
# 'Programming Language :: Python :: 3',
# 'Programming Language :: Python :: 3.5',
# 'Programming Language :: Python :: 3.6',
Expand Down
2 changes: 1 addition & 1 deletion src/tirith/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
tirith: Execute policies defined using Tirith (StackGuardian Policy Framework)
"""

__version__ = "1.2.0"
__version__ = "1.2.1"
__author__ = "StackGuardian"
__license__ = "Apache"
1 change: 0 additions & 1 deletion src/tirith/platform/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
return lines + trailer


def render_plan_block(plan):

Check failure on line 94 in src/tirith/platform/report.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 20 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=StackGuardian_policy-framework&issues=AaB-MB3fIYsULuX3N-ZQ&open=AaB-MB3fIYsULuX3N-ZQ&pullRequest=372
"""
The planned changes, as a diff-fenced list plus terraform's summary line.

Expand Down Expand Up @@ -127,7 +127,7 @@
row = f"{marker} {address:<48} {_fence_safe(plan_actions.action_summary(actions))}".rstrip()
entries.append((row, _render_attributes(change.get("change") or {})))

listed_resources = len(entries)

Check warning on line 130 in src/tirith/platform/report.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unused local variable "listed_resources".

See more on https://sonarcloud.io/project/issues?id=StackGuardian_policy-framework&issues=AaB-MB3fIYsULuX3N-ZP&open=AaB-MB3fIYsULuX3N-ZP&pullRequest=372
rows, hidden_detail, dropped_resources = _fit_plan_rows(entries)

summary = plan_actions.summary_line(counts)
Expand Down Expand Up @@ -180,7 +180,6 @@
return bare[:PLAN_LINE_LIMIT], hidden_detail, len(bare) - PLAN_LINE_LIMIT



def summarize(policy_results):
"""
Collapse the results into counts plus a flat finding list.
Expand Down Expand Up @@ -229,7 +228,7 @@
return counts, findings


def _extract_detail(rule):

Check failure on line 231 in src/tirith/platform/report.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 28 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=StackGuardian_policy-framework&issues=AaB-MB3fIYsULuX3N-ZR&open=AaB-MB3fIYsULuX3N-ZR&pullRequest=372
"""Pull human-readable messages and resource addresses out of a rule's evaluations."""
messages = []
resources = []
Expand Down Expand Up @@ -411,7 +410,7 @@
return ["", f"<sub>{line}</sub>"]


def render_markdown(

Check failure on line 413 in src/tirith/platform/report.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 20 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=StackGuardian_policy-framework&issues=AaB-MB3fIYsULuX3N-ZS&open=AaB-MB3fIYsULuX3N-ZS&pullRequest=372
policy_results,
run_status,
run_url,
Expand Down Expand Up @@ -500,7 +499,7 @@
while kept and len(body) > limit:
kept.pop()
omitted = len(detail_sections) - len(kept)
note = [f"", f"_… and {omitted} more finding(s). See the full run in StackGuardian._", ""]

Check warning on line 502 in src/tirith/platform/report.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add replacement fields or use a normal string instead of an f-string.

See more on https://sonarcloud.io/project/issues?id=StackGuardian_policy-framework&issues=AaB-MB3fIYsULuX3N-ZT&open=AaB-MB3fIYsULuX3N-ZT&pullRequest=372
body = "\n".join(header + plan_block + table + kept + note + footer)

if len(body) > limit:
Expand Down
10 changes: 5 additions & 5 deletions tests/platform/test_report_plan_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@


def _render(changes):
return report.render_markdown(
{}, "COMPLETED", "https://example.invalid/run", plan={"resource_changes": changes}
)
return report.render_markdown({}, "COMPLETED", "https://example.invalid/run", plan={"resource_changes": changes})


def _fence(body):
Expand Down Expand Up @@ -106,8 +104,10 @@
)
fence = _fence(body)
assert "# forces replacement" in fence
assert [line for line in fence.splitlines() if "forces replacement" in line][0].lstrip().startswith(
"~ triggers_replace"
assert (
[line for line in fence.splitlines() if "forces replacement" in line][0]
.lstrip()
.startswith("~ triggers_replace")
)


Expand All @@ -131,7 +131,7 @@
]
)
fence = _fence(body)
assert "hunter2" not in fence and "hunter3" not in fence

Check warning on line 134 in tests/platform/test_report_plan_attributes.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Split this composite assertion into separate assertions.

See more on https://sonarcloud.io/project/issues?id=StackGuardian_policy-framework&issues=AaB-MB6HIYsULuX3N-ZU&open=AaB-MB6HIYsULuX3N-ZU&pullRequest=372
assert "(sensitive value)" in fence


Expand Down
4 changes: 1 addition & 3 deletions tests/platform/test_report_plan_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,6 @@ def test_the_plan_is_dropped_before_any_finding():
]
}
plan = _plan(*[_change(f"aws_s3_bucket.b{i}", ["create"]) for i in range(20)])
body = report.render_markdown(
results, "COMPLETED", "https://example.invalid/run", plan=plan, limit=3000
)
body = report.render_markdown(results, "COMPLETED", "https://example.invalid/run", plan=plan, limit=3000)
assert "```diff" not in body
assert "policy-a" in body
Loading