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: 2 additions & 2 deletions .github/workflows/build-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
node-version: 24
cache: npm
- run: npm ci
- run: npm run typecheck
- run: npm test
- run: npm run build
# A node20 action runs dist/index.js straight from the consumer's checkout,
# A node24 action runs dist/index.js straight from the consumer's checkout,
# so a stale committed bundle ships stale code. Fail if the freshly built
# dist/ differs from what's committed.
- name: Verify dist/ is up to date
Expand Down
22 changes: 10 additions & 12 deletions .github/workflows/selftest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ permissions:
security-events: write # SARIF → Code Scanning (upload-sarif default true)
pull-requests: write # sticky PR comment on the pull_request job

# TEMPORARY PIN — remove once an engine release with `mcp`-category support is the
# `latest` release. The released engine v0.1.2 predates the `mcp` rule category
# that trustabl-rules now ships, so `latest` engine + latest rules fails to load
# ("unknown category mcp"). Every selftest job is pinned to v0.1.2 + the `pre-mcp`
# rules tag (a known-compatible pair) so CI exercises the ACTION, not the upstream
# engine↔rules drift. Engine fix: trustabl/trustabl#12.
# Engine pin — every selftest job pins `version: v0.1.4` (with the engine's default
# rules) rather than `latest`, so CI is deterministic and exercises the ACTION
# against a known engine. v0.1.4 is the release that introduced the finding
# line-range shape (start_line/end_line) this action consumes, so it validates that
# path end to end. It also supports the `mcp` rule category natively and loads any
# newer rule leniently, so default rules need no compatibility pin. Bump this when a
# newer engine release should gate the action.

jobs:
scan-remote-target:
Expand All @@ -31,8 +32,7 @@ jobs:
continue-on-error: true
with:
target: "https://github.com/openai/openai-agents-python"
version: "v0.1.2" # pinned — see note above
rules-ref: "pre-mcp" # pinned — see note above
version: "v0.1.4" # pinned — see note above
severity-threshold: "none"
risk-score-threshold: "0"
comment-on-pr: "false" # remote target; nothing to comment about on this PR
Expand All @@ -58,8 +58,7 @@ jobs:
- uses: ./
with:
target: "."
version: "v0.1.2"
rules-ref: "pre-mcp"
version: "v0.1.4"
comment-on-pr: "false"

scan-pr-surfaces:
Expand All @@ -72,7 +71,6 @@ jobs:
- uses: ./
with:
target: "."
version: "v0.1.2"
rules-ref: "pre-mcp"
version: "v0.1.4"
severity-threshold: "none" # plumbing check only; don't gate the selftest PR
risk-score-threshold: "0"
53 changes: 53 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,59 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [0.3.1] — 2026-06-09

### Changed

- **Runtime is now Node.js 24** (`runs.using: node24`). GitHub is deprecating the
Node 20 Actions runtime (runners default to Node 24 on 2026-06-16; Node 20 is
removed on 2026-09-16), so this moves ahead of the removal. No behavior change —
the bundled `dist/` is identical; the build CI and the `engines` field bump to
Node 24 to match the runtime.

### Docs

- README + capabilities now document the full SDK coverage (LangChain, CrewAI,
Pydantic AI, Vercel AI, AutoGen, MCP servers, and Claude subagents & skills),
the opt-in dependency CVE scan, and the complete `detectors` token list; install
pins bumped to `v0.3.1`.


## [0.3.0] — 2026-06-09

Tracks trustabl engine **v0.1.4**: consumes the new finding line-range shape and
adds an opt-in dependency CVE scan.

### Added

- **`vuln-scan` input** (default `false`). Passes `--vuln-scan`, so trustabl matches
declared dependencies against a pinned OSV snapshot and reports known CVEs. Each
match is a finding, so it flows through the readiness score, gating, inline
annotations, and the Security tab like any other — plus a dependency headline
(dependencies scanned / known vulnerabilities) in the console panel, Step
Summary, and PR comment.

### Changed

- **Finding line ranges.** The action reads the engine's `start_line`/`end_line`
(engine ≥ v0.1.4) and renders multi-line inline annotations across the finding's
full span. The legacy single `line` field is still read as a fallback, so the
action stays correct against older pinned engines.
- **`skill` scope** added to the typed scope / surface-kind unions, matching the
engine's five detection scopes (tool, agent, subagent, skill, repo).
- **`MIN_ENGINE_VERSION` is now `v0.1.3`** (previously an unset placeholder) — the
release that introduced single-scan dual output, Code-Scanning-valid SARIF, and
the projected-scores headroom ladder. Older engines still run via the two-scan
fallback with a soft upgrade warning.

### Fixed

- **Inline annotations no longer collapse to the top of the file** against engine
v0.1.4. The engine renamed the finding `line` field to `start_line`/`end_line`;
the action still read `line`, so each annotation lost its line number. It now
resolves the range from either shape.


## [0.2.0] — 2026-06-04

A full rewrite from the bash composite action to a **node20 TypeScript action**,
Expand Down
86 changes: 79 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
<p align="center">
<img src="assets/github_banner.jpg" alt="Trustabl — open-source tooling for production-ready agentic tools" width="100%">
</p>

# Trustabl Action

A GitHub Action that runs [trustabl](https://github.com/trustabl/trustabl) — the
static reliability/safety analyzer for agent-SDK repos (Claude Agent SDK, OpenAI
Agents SDK, Google ADK, MCP) — and surfaces the results where you work:
static reliability/safety analyzer for agent repos (Claude Agent SDK, OpenAI
Agents SDK, Google ADK, LangChain, CrewAI, Pydantic AI, Vercel AI, AutoGen, MCP
servers, and Claude subagents & skills) — and surfaces the results where you work:

- **Inline PR annotations + the Security tab.** Findings are uploaded to GitHub
Code Scanning, so they appear on the changed lines in the PR diff and in the
Expand All @@ -12,6 +17,9 @@ Agents SDK, Google ADK, MCP) — and surfaces the results where you work:
- **Status-check gating.** Optionally fail the job on a risk-score or severity
threshold so it can be a required check.
- **A readiness panel** in the run log and the Step Summary.
- **Optional dependency CVE scan** (`vuln-scan: true`) — matches your declared
dependencies against a pinned OSV snapshot and reports known CVEs as findings,
so they appear on every surface (score, gate, annotations, Security tab).

It downloads the official `trustabl` release binary (sha256-verified against the
release `checksums.txt`), tool-caches it, scans your checkout, and reports.
Expand Down Expand Up @@ -70,6 +78,7 @@ jobs:
with: # every input is optional
# detectors: openai_sdk # limit SDKs: claude_sdk,openai_sdk,google_adk,openshell
# version: latest # trustabl release to run; pin e.g. v0.5.0 for reproducible CI
# vuln-scan: true # also scan dependencies for known CVEs (OSV)
# severity-threshold: high # fail if any finding >= level (none|low|medium|high|critical)
# risk-score-threshold: 70 # fail if risk (100 - readiness) >= N (0 disables)
# comment-on-pr: true # sticky PR summary comment
Expand All @@ -81,7 +90,7 @@ jobs:
## Pinned + gated

```yaml
- uses: trustabl/trustabl-action@v0.2.0
- uses: trustabl/trustabl-action@v0.3.1
with:
version: v0.5.0
detectors: claude_sdk,openai_sdk
Expand All @@ -90,14 +99,62 @@ jobs:
artifact-retention-days: "30"
```

## Enrich + auto-enrich

When `enrich: true`, after the scan the action calls `trustabl enrich` with your
LLM API key to generate AI explanations and code fixes for each finding.
With `auto-enrich: true`, high-confidence fixes are applied directly to source
files. With `create-fix-pr: true`, the patches are committed on a new branch
and a pull request is opened for human review.

```yaml
permissions:
contents: write # push fix branch
pull-requests: write # open fix PR
security-events: write

jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: trustabl/trustabl-action@v0
with:
enrich: true
llm-key: ${{ secrets.ANTHROPIC_API_KEY }}
auto-enrich: true
create-fix-pr: true
```

Enrich is best-effort — if it fails the scan result and gate decision are
unaffected and a warning is emitted instead of failing the job.

**PR-only auto-enrich.** To generate explanations on every push but only apply
fixes and open a fix PR on pull requests:

```yaml
- uses: trustabl/trustabl-action@v0
with:
enrich: true
llm-key: ${{ secrets.ANTHROPIC_API_KEY }}
auto-enrich: ${{ github.event_name == 'pull_request' }}
create-fix-pr: ${{ github.event_name == 'pull_request' }}
```


> **Required repo settings when using `create-fix-pr: true`:**
> Go to **Settings → Actions → General → Workflow permissions** and enable
> **Read and write permissions** + **Allow GitHub Actions to create and approve pull requests**.

## Inputs

| Name | Default | Description |
|---|---|---|
| `target` | `.` | Path or GitHub URL to scan. |
| `version` | `latest` | trustabl release tag (e.g. `v0.5.0`) or `latest`. |
| `detectors` | _(all)_ | Comma-separated subset: `claude_sdk,openai_sdk,google_adk,openshell`. |
| `detectors` | _(all)_ | Comma-separated SDK subset: `claude_sdk`, `openai_sdk`, `google_adk`, `openshell`, `mcp`, `langchain`, `crewai`, `pydantic_ai`, `vercel_ai`, `autogen`. |
| `strict` | `false` | Pass `--strict` (fail on any finding). |
| `vuln-scan` | `false` | Match dependencies against a pinned OSV snapshot; report known CVEs as findings. |
| `rules-ref` | _(default)_ | Pin a `trustabl-rules` git ref. |
| `rules-repo` | _(default)_ | Override the `trustabl-rules` source repo. |
| `upload-sarif` | `true` | Upload SARIF to Code Scanning. Needs `security-events: write`. |
Expand All @@ -113,6 +170,14 @@ jobs:
| `severity-threshold` | `none` | Fail when any finding `>= severity` (`none`/`low`/`medium`/`high`/`critical`). |
| `branch` | _(auto)_ | Report branch label; auto-detected from the checkout. |
| `github-token` | `${{ github.token }}` | Token for release lookup, SARIF upload, and PR comments. |
| `enrich` | `false` | Run AI enrichment on findings (explanations + fixes). Requires `llm-key`. |
| `llm-provider` | `anthropic` | LLM provider for enrichment (e.g. `anthropic`). |
| `llm-key` | _(none)_ | API key for the LLM provider (BYOK). Required when `enrich` is true. |
| `auto-enrich` | `false` | Apply AI-generated fixes to source files. Requires `enrich: true`. |
| `create-fix-pr` | `false` | Open a PR with applied fixes. Requires `auto-enrich: true`. Needs `contents: write` + `pull-requests: write`. |
| `enrich-model` | _(binary default)_ | Claude model for enrichment (e.g. `claude-sonnet-4-6`). Defaults to `claude-haiku-4-5`. |
| `enrich-rules` | _(all)_ | Comma-separated rule IDs to enrich (e.g. `ADK-201,ADK-105`). Empty = all findings. |
| `fix-pr-base` | _(current branch)_ | Base branch for the fix PR. |

## Outputs

Expand All @@ -127,6 +192,8 @@ jobs:
| `sarif-file` | Path to the emitted SARIF file. |
| `json-file` | Path to the emitted JSON file. |
| `artifact-name` | Artifact name used for the upload. |
| `enrich-json-file` | Path to `enriched.json` (when `enrich` is true). |
| `fix-pr-url` | URL of the opened fix PR (when `create-fix-pr` is true). |

## How it works

Expand All @@ -137,6 +204,11 @@ jobs:
one analysis pass produces both artifacts. Older engines fall back to two scans
automatically (and the headroom ladder is hidden, since it needs the engine's
`projected_scores`). Use `version: latest` to get the fast path.
- **Dependency CVE scan (opt-in).** With `vuln-scan: true`, declared dependencies
are matched against a pinned OSV snapshot; each known CVE becomes a finding (so
it counts toward the score, gate, annotations, and Security tab), plus a
dependencies-scanned / known-vulnerabilities line in every report. The OSV
database is fetched once on first use, then cached.
- **Honest gating.** A failed or empty scan errors the job rather than reporting a
clean score. The gate decision is exit-code/threshold-based, surfaced in the
Step Summary and the PR comment.
Expand All @@ -156,7 +228,7 @@ After a run, open the run page and find the **`trustabl-scan-results`** artifact

## Versioning

- Pin a release: `uses: trustabl/trustabl-action@v0.2.0`.
- Pin a release: `uses: trustabl/trustabl-action@v0.3.1`.
- Or track the line: `uses: trustabl/trustabl-action@v0` (the moving major tag).

## Notes
Expand All @@ -169,7 +241,7 @@ After a run, open the run page and find the **`trustabl-scan-results`** artifact

## Development

This is a node20 TypeScript action bundled to `dist/` with
This is a node24 TypeScript action bundled to `dist/` with
[`ncc`](https://github.com/vercel/ncc).

```bash
Expand All @@ -180,7 +252,7 @@ npm run build # bundle to dist/index.js (commit the result)
npm run all # all of the above
```

`dist/` is committed because a node20 action runs `dist/index.js` directly from
`dist/` is committed because a node24 action runs `dist/index.js` directly from
the consumer's checkout of the release tag. The **Build check** workflow fails a
PR whose `dist/` is stale, so always `npm run build` and commit after changing
`src/`.
53 changes: 50 additions & 3 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description: >-
author: trustabl
branding:
icon: shield
color: blue
color: gray-dark

inputs:
# ── what to scan ────────────────────────────────────────────────────────
Expand All @@ -19,13 +19,21 @@ inputs:
required: false
default: latest
detectors:
description: Comma-separated SDK detectors (claude_sdk,openai_sdk,google_adk,openshell). Empty = all.
description: Comma-separated SDK detectors (claude_sdk,openai_sdk,google_adk,openshell,mcp,langchain,crewai,pydantic_ai,vercel_ai,autogen). Empty = all.
required: false
default: ""
strict:
description: Pass --strict (fail on any finding regardless of severity).
required: false
default: "false"
vuln-scan:
description: >-
Pass --vuln-scan: match declared dependencies against a pinned OSV snapshot
and report known CVEs as findings (off by default). Each match becomes a
finding, so it flows through the readiness score, gating, annotations, and
SARIF. Fetches the OSV database on first use, then caches it.
required: false
default: "false"
rules-ref:
description: Pin trustabl-rules git ref. Empty = engine default.
required: false
Expand Down Expand Up @@ -107,6 +115,41 @@ inputs:
required: false
default: ${{ github.token }}

# ── enrich ──────────────────────────────────────────────────────────────
enrich:
description: Run AI enrichment on findings (explanations + fixes). Requires llm-key.
required: false
default: "false"
llm-provider:
description: LLM provider for enrichment (e.g. anthropic). Defaults to anthropic.
required: false
default: "anthropic"
llm-key:
description: API key for the LLM provider (BYOK). Required when enrich is true.
required: false
default: ""
auto-enrich:
description: Apply AI-generated fixes to source files. Requires enrich true.
required: false
default: "false"
create-fix-pr:
description: Open a PR with applied fixes. Requires auto-enrich true.
required: false
default: "false"
enrich-model:
description: Claude model to use for enrichment (e.g. claude-sonnet-4-6). Defaults to the trustabl binary default (claude-haiku-4-5).
required: false
default: ""
enrich-rules:
description: Comma-separated rule IDs to enrich (e.g. ADK-201,ADK-105). Empty = all findings.
required: false
default: ""
fix-pr-base:
description: Base branch for the fix PR. Defaults to the branch being scanned.
required: false
default: ""


outputs:
exit-code:
description: trustabl native exit code (0 clean, 1 findings, 2 error).
Expand All @@ -126,7 +169,11 @@ outputs:
description: Name of the uploaded artifact (when upload-artifact is true).
sarif-uploaded:
description: Whether the SARIF was accepted by Code Scanning (true/false).
enrich-json-file:
description: Path to enriched.json (when enrich is true).
fix-pr-url:
description: URL of the opened fix PR (when create-fix-pr is true).

runs:
using: node20
using: node24
main: dist/index.js
Binary file added assets/github_banner.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading