Skip to content

feat(analysis): add github_workflow component and CI-001..005 predicates (rule schema 30, provisional) - #264

Open
ivanpaghubasan wants to merge 6 commits into
mainfrom
feat/ci-agent-workflows
Open

ivanpaghubasan wants to merge 6 commits into
mainfrom
feat/ci-agent-workflows

Conversation

@ivanpaghubasan

@ivanpaghubasan ivanpaghubasan commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds support for scanning GitHub Actions workflows (.github/workflows/*.yml at the repo root) and the predicates behind the new CI-001..005 rules (AGS-07: coding agents running on events any GitHub user can cause). The rules themselves ship in the rules repo PR with the same branch name: #189.

What changed

  • New github_workflow component kind and CIWorkflowFacts in the repo inventory. Workflow files are kept out of YAMLFiles, so existing consumers are unchanged.
  • internal/analysis/ci_workflows.go: parses workflows with the yaml.v3 Node API (anchors and merge keys resolved with a depth and node budget; unparseable files are listed in ci_workflows.unparsed and skipped), recognises agent steps, and computes the facts for the five predicates.
  • Five repo-scoped predicates (repo_ci_agent_*), a new ci category and the github_actions applies_to token. One finding per agent step that runs an offending agent (CI-003: per actions/checkout step in an offending agent job).
  • Rule schema version 26 -> 30 (provisional; open branches hold 27, 28 and 29, so whichever of these PRs merges later renumbers again), schema.yaml docs, fixture rules, a synthetic corpus fixture, tests, and ARCHITECTURE / COVERAGE / README / CHANGELOG updates.

Behaviour decisions

  • Agent steps are matched by exact action path or CLI command word, not by substring regexes: anthropics/claude-code-action, anthropics/claude-code-base-action, google-github-actions/run-gemini-cli, openai/codex-action, and the claude, gemini, codex, cline and aider CLIs. The report's .*cline.*, .*aider.* and .*copilot.* patterns were dropped: no official action was found for them, and the patterns match unrelated names.
  • The built-in write-access check of claude-code-action and codex-action counts as an actor gate, unless its bypass input is set to * (or an expression). This follows the actions' own documentation.
  • CI-003..005 apply to agent jobs on untrusted triggers, gated or not. CI-002 requires the job to be ungated.
  • Local reusable workflows are followed one hop. A job with no permissions: block anywhere is not flagged, because the default is not visible to the scan.
  • Trigger set is the report's six events. workflow_run is left out (follow-up).
  • The report's actions/cache sub-condition for CI-002 was dropped. GitHub's dependency-caching documentation says events outside a short write list, including pull_request_target, issue_comment and workflow_run, get read-only access to the default branch's cache scope. Adnan Khan's Clinejection post describes cache poisoning from an issue-triggered job. The two are not reconciled. The documentation also describes an opt-out (cache-mode: write), which may be the better signal to detect in a follow-up.

Known limits

Expressions are never evaluated and the boolean structure of if: is not modelled (so A || allow-list counts as gated). Remote reusable workflows, composite actions and matrix expansion are not followed. Each rule's explanation in the rules PR states its own limits.

Disagreements with the report

  • The report's own CI-001 fail example (on: issues with claude-code-action and no bypass input) does not fire, because the action gates on write access by default. The fixtures use allowed_non_write_users: "*" instead.
  • AGCFG-003 does not exist, so CI-004 reuses the existing launcher-shell and exec-primitive lists from AGCFG-005 and MCPCFG-002.
  • claude-code-action v1 has no allowed_tools input (it uses claude_args --allowedTools). Codex --yolo is unverified and left out; approval_policy=never is not a shell grant for codex exec.

Verification

  • go test ./... passes.
  • trustabl rules validate: 348 rules valid under rule schema version 30, on both the fixture and the rules branch. check-rules-sync.sh reports no drift.
  • Vendor example run (temporary test, not committed): run-gemini-cli examples (6) and the codex-action README example produce no CI findings. For claude-code-action examples, CI-003 fires on claude.yml, claude-wif.yml, issue-deduplication.yml and issue-triage.yml (default checkout in an agent job on an untrusted trigger), and CI-001 fires on issue-triage.yml (allowed_non_write_users: "*").
  • Older engines: A schema-26 build from main loaded with the schema-30 pack skips the five rules and reports one META-005 info finding; the scan succeeds.

Evidence notes

Fixtures are synthetic; the public GitInject workflows could not be obtained. GitInject's "every provider vulnerable" claim is unverified. CVE-2026-54316 exists but is a Claude Code WebFetch pre-approved-domain issue, not CI-specific.

Merge order

Engine (this PR), then rules #189, then the rationale docs in the rulebook repo. The rules PR should not be merged until the docs PR is ready, otherwise the rulebook gate goes red.

…mponent

Walk the repo-root .github/workflows, add the github_workflow component and CIWorkflowFacts, and wire DiscoverCIWorkflows into the scan.
Five repo_ci_agent_* predicates, the ci category, the github_actions applies_to token and one finding per agent job.
@sairenchristianbuerano

Copy link
Copy Markdown
Collaborator

@ivanpaghubasan heads-up on the rule schema number before this merges.

main is now at schema 25 in both the analyzer and the rules pack: AGCFG-008 (Codex hooks) took 24 and AGCFG-009 (AGENTS.md hidden text) took 25. This PR still sets 24 (it was 23), and the companion rules PR #189 sets 24 too. Merged as is, the engine constant would go backwards while the pack says 25, and an older engine would skip the CI rules without telling anyone.

Before merging:

  1. Merge or rebase main. Expect conflicts in internal/rules/schema_version.go, testdata/rules-fixture/manifest.yaml, schema.go, evaluator.go, schema.yaml and policies_test.go (AGCFG-008 and 009 touched the same places).
  2. Set the schema to the next free number, 26 (or 27 if the OTel labels pair in Add OBS-009..012 for agent-reliability-otel-labels label correctness #261 merges first). Check main right before you set it. Use the same number in internal/rules/schema_version.go, the analyzer's testdata/rules-fixture/manifest.yaml, and manifest.yaml in docs(rules): correct RuleDef language-default comment #189.
  3. Run go test ./... and check the rule files are still identical in the fixture and the rules pack.
  4. Merge the analyzer first, then docs(rules): correct RuleDef language-default comment #189 and the rulebook PR right after.

@ivanpaghubasan

Copy link
Copy Markdown
Collaborator Author

@ivanpaghubasan heads-up on the rule schema number before this merges.

main is now at schema 25 in both the analyzer and the rules pack: AGCFG-008 (Codex hooks) took 24 and AGCFG-009 (AGENTS.md hidden text) took 25. This PR still sets 24 (it was 23), and the companion rules PR #189 sets 24 too. Merged as is, the engine constant would go backwards while the pack says 25, and an older engine would skip the CI rules without telling anyone.

Before merging:

  1. Merge or rebase main. Expect conflicts in internal/rules/schema_version.go, testdata/rules-fixture/manifest.yaml, schema.go, evaluator.go, schema.yaml and policies_test.go (AGCFG-008 and 009 touched the same places).
  2. Set the schema to the next free number, 26 (or 27 if the OTel labels pair in Add OBS-009..012 for agent-reliability-otel-labels label correctness #261 merges first). Check main right before you set it. Use the same number in internal/rules/schema_version.go, the analyzer's testdata/rules-fixture/manifest.yaml, and manifest.yaml in docs(rules): correct RuleDef language-default comment #189.
  3. Run go test ./... and check the rule files are still identical in the fixture and the rules pack.
  4. Merge the analyzer first, then docs(rules): correct RuleDef language-default comment #189 and the rulebook PR right after.

Hi @sairenchristianbuerano , thanks for the heads-up. I'll update the schema version accordingly.

@ivanpaghubasan ivanpaghubasan changed the title feat(analysis): add github_workflow component and CI-001..005 predicates (rule schema 24) feat(analysis): add github_workflow component and CI-001..005 predicates (rule schema 30, provisional) Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants