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
35 changes: 35 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,41 @@ file and line (`configHitLocation` in `rule_detector.go`), so `pathclass` can ta
a vendored fixture as test-origin. The pack loads unconditionally and the
`agent_config` `applies_to` token skips the SDK gate.

**GitHub Actions workflows (category `ci`, `schema_version` 28, provisional).** The walk
enters `.github/` only at the scan root and, inside it, only `.github/workflows/`
(a nested `.github` and the other `.github` subdirectories are skipped like any
dot-directory). Files `.github/workflows/*.yml|yaml` go to
`ScanManifest.WorkflowFiles` (not `YAMLFiles`, so no existing consumer sees them)
and become `github_workflow` components. `analysis.DiscoverCIWorkflows`
([ci_workflows.go](internal/analysis/ci_workflows.go)) parses them with
`yaml.v3` nodes (aliases and `<<` merges resolved under a depth and node budget;
a file that fails to parse is listed in `CIWorkflowFacts.Unparsed` and produces
no finding) into `RepoInventory.CIWorkflows` (`CIWorkflowFacts`): sorted, deduped
`CIHit{file, line, job, agent, reason}` lists behind five repo-scope predicates
(`repo_ci_agent_untrusted_trigger_ungated`, `..._privileged`,
`repo_ci_agent_checkout_persists_credentials`, `repo_ci_agent_unrestricted_shell`,
`repo_ci_agent_no_egress_restriction`) read by CI-001..005. An agent step is a
`uses:` of `anthropics/claude-code-action` (and its `base-action` /
`claude-code-base-action` forms), `google-github-actions/run-gemini-cli` or
`openai/codex-action`, or a `run:` command word `claude`, `gemini`, `codex`,
`cline` or `aider` (also via `npx` / `bunx` / `pnpm dlx`). Untrusted triggers are
`pull_request_target`, `issue_comment`, `issues`, `discussion`,
`discussion_comment` and `pull_request_review_comment`. `${{ }}` expressions are
never evaluated: an `if:` is matched textually for an `author_association`
allow-list, a literal actor check or (for `pull_request_target`) a same-repo
check, on the job, the step or a `needs` ancestor; `claude-code-action` and
`codex-action` count as gated through their own write-access check unless a `*`
bypass input is set. A job-level `uses: ./.github/workflows/x.yml` is followed one
hop (the callee inherits the caller's triggers, gate, permissions and
`secrets: inherit`); remote reusable workflows, composite actions and matrix
values are not resolved. Hits carry event, action and secret *names* only. Unlike
`agent_config`, a CI rule reports one finding per step that runs an offending agent
(`ciHitsFor` in `rule_detector.go`), attributed to that step with the job id in
`ToolName`; CI-003 reports one per `actions/checkout` step in the agent's job.
Two agents in one `run:` step share a line and give one finding. A local
reusable workflow is evaluated once per caller job and callee job pair. The pack loads unconditionally
and the `github_actions` `applies_to` token skips the SDK gate.

### Step 2 — Inventory ([internal/analysis/](internal/analysis/))

For each language recon cleared, do the AST work and produce a `RepoInventory`:
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ to follow Semantic Versioning once it reaches 1.0.

## [Unreleased]

### Added

- **Coding agents in GitHub Actions workflows (rule schema 27 -> 28, provisional).** A new
unconditionally loaded `ci` category (CI-001..005) with five repo-scope
predicates backed by `RepoInventory.CIWorkflows`. The walk now reads the
repo-root `.github/workflows/*.yml|yaml` into `ScanManifest.WorkflowFiles` and
`github_workflow` components. CI-001 and CI-002 flag an agent job on an
untrusted trigger (issues, issue and review comments, discussions,
`pull_request_target`) with no actor gate, CI-002 when it also holds a write
token or extra secrets; CI-003..005 flag persisted checkout credentials,
unrestricted shell and a missing egress restriction. `claude-code-action` and
`codex-action` count as gated through their own write-access check unless a
`*` bypass input is set. One finding per step that runs an offending agent (for CI-003, per
`actions/checkout` step in an agent job). `workflow_run` is not in
the trigger set yet.

### Fixed

- The `Harness files:` line in the scan summary no longer says that no rule judges
Expand Down
7 changes: 7 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,13 @@ When changing a rule (add / remove / edit severity, confidence, match, text):
6. Commit and push the rules repo **and** the rulebook (the user pushes engine
commits manually; confirm before pushing any of the three).

> **CI workflows (2026-10-19, `schema_version` 27 → 28, provisional; +5 rules):** new unconditionally-loaded category `ci` (**CI-001..005**, repo
> scope, `applies_to: [github_actions]`) over five `repo_ci_agent_*` predicates backed by `RepoInventory.CIWorkflows`
> (`analysis.DiscoverCIWorkflows`). The walk reads only the repo-root `.github/workflows/*.yml|yaml` (into `ScanManifest.WorkflowFiles`, not
> `YAMLFiles`). The IDs sit outside the SDK-prefix `NNN`/`1NN`/`2NN` convention, like AGCFG. A CI rule emits one finding per step that runs an
> offending agent, and for CI-003 one per `actions/checkout` step in that job (not one per scan). Hits carry event, action and secret names only, never a `with` / `env` / `run` value. Rulebook docs (`docs/Policy/ci/`) are a
> separate follow-up. Built in parallel with the otel-labels correctness batch, which merged first and took 26 → 27, so this batch takes 28. The fixture holds **352** rules (347 + 5; re-derive, do not trust this figure).
>
> **Pre-trust agent config (2026-10-12, `schema_version` 22 → 23; +5 rules):** new unconditionally-loaded category `agent_config`
> (**AGCFG-004..007**, **MCPCFG-002**, repo scope) over five new repo predicates backed by `RepoInventory.ProjectConfig`
> (`analysis.DiscoverProjectConfig`). The walk now enters `.gemini/`, `.vscode/`, `.cursor/`, `.codex/`. Env files are read for **key
Expand Down
Loading
Loading