Skip to content

feat(ci): SARIF output and a GitHub Action that gates trust, cost, and drift - #107

Merged
singhharsh1708 merged 1 commit into
mainfrom
feat/ci-action-sarif
Aug 9, 2026
Merged

singhharsh1708 merged 1 commit into
mainfrom
feat/ci-action-sarif

Conversation

@singhharsh1708

Copy link
Copy Markdown
Owner

What

Everything Kitbash checks was previously something one maintainer ran locally and everyone else took on trust. This makes it a required check on the pull request.

kitbash lint --sarif <file> — writes findings as SARIF 2.1.0, so results land in the Security tab and as inline PR annotations instead of scrolling past in a log.

  • Hard-fail safety lints (remote-exec, visible-text, dynamic-context, secrets) → error
  • Heuristics and budget/standing overruns → warning
  • Passing checks are not emitted — a clean run produces a valid empty report, not noise
  • Locations are repo-relative with forward slashes, so the same report works on any runner
  • Written even when the lint fails (that's the run whose findings must reach the Security tab), and never outside the project (exit 2)

A composite GitHub Action — singhharsh1708/kitbash@v0.18.0, one step, three checks:

Check Fails when
Trust A skill trips a hard safety lint
Cost A skill exceeds the token budget or standing limit it declares
Drift kitbash compile changes the working tree

Drift is the one competitors don't have. A skill compiles to eleven targets; nothing stops someone hand-editing a generated .cursor/rules/*.mdc, or changing the source and forgetting to recompile — then each agent reads something different and nobody finds out. Competing scanners emit SARIF for skill content; none also measures token cost or catches source-vs-output drift.

Two bugs caught while writing it

  • GitHub runs shell: bash with -e, so a bare [ test ] && args+=(...) returns 1 when the test is false and fails the step instead of skipping the flag. Both conditionals are now full if blocks, and the array expands as ${args[@]+"${args[@]}"} so an empty array is safe.
  • git diff --exit-code misses newly created untracked files, so a target compiled for the first time would pass silently. Drift now checks git status --porcelain.

Verification

  • 16 new tests (SARIF shape, severity mapping, empty-report case, path-traversal refusal, forward-slash locations, exit codes). Full suite green.
  • Both shell paths simulated under bash -e, including the empty-array case.
  • Both YAML files parse; the exact CI command runs clean against our own example skill (0 findings, exit 0).
  • CI dogfoods the SARIF path with the locally built CLI — npm hasn't seen this version at PR time.

Ships as 0.18.0.

…d drift

Everything kitbash checks was previously something one maintainer ran
locally and everyone else took on trust. This makes it a required check on
the pull request.

`kitbash lint --sarif <file>` writes findings as SARIF 2.1.0, the format
GitHub code scanning reads, so results land in the Security tab and as
inline PR annotations instead of scrolling past in a log. Hard-fail safety
lints are `error`; heuristics and budget overruns are `warning`; passing
checks are not emitted, so a clean run produces a valid empty report rather
than noise. Locations are repo-relative with forward slashes so the same
report works on any runner. The report is written even when the lint fails —
that is exactly the run whose findings need to reach the Security tab — and
never outside the project (exit 2).

The composite action bundles three checks other scanners split up or skip:
the trust lints, the declared token budgets, and drift — it recompiles and
fails if the working tree changes, which catches a hand-edited generated
file or a source someone changed without recompiling. Competing scanners
emit SARIF for skill content; none also measures token cost or catches
source-vs-output drift, the failure a repo with eleven targets actually hits.

Both shell steps are written for `bash -e`: a bare `[ test ] && cmd` would
have failed the step whenever the test was false, and `git diff` alone would
have missed newly generated untracked files, so drift uses `git status
--porcelain`.

CI dogfoods the SARIF path on our own example skill with the locally built
CLI (npm has not seen this version at PR time). Adds 16 tests. 0.18.0.
@vercel

vercel Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
kitbash Ready Ready Preview Aug 9, 2026 6:51pm

@github-actions github-actions Bot added documentation Docs, spec, RFCs, README, site dependencies Dependency or action version bumps automation CI, workflows, Dependabot, bots labels Aug 9, 2026
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@singhharsh1708
singhharsh1708 merged commit a2e8721 into main Aug 9, 2026
10 checks passed
@singhharsh1708
singhharsh1708 deleted the feat/ci-action-sarif branch August 9, 2026 18:52

This branch was successfully deployed

1 active deployment
Preview — fd94e194 Deployed Aug 9, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automation CI, workflows, Dependabot, bots dependencies Dependency or action version bumps documentation Docs, spec, RFCs, README, site

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants