ReviewOS

stacks/buddy

Static-analysis orchestration engine (multi-tool linting on PRs)

#1378
Closed chris opened this 9 days ago ·
9 days ago

Part of #1367 (Phase 2 — AI Review Parity).

Goal

Generalize the existing workflow-security auditor into a multi-tool static-analysis orchestration engine — CodeRabbit's biggest non-AI differentiator (~60 auto-selected tools, tools list). We don't need 60 on day one; we need the engine + a strong first tier.

Current state (the seed)

src/security/ is already a clean rule engine for one domain: engine.ts (audit), loader.ts, parser.ts (Bun.YAML), 6 rules in rules/ (unpinned-action, excessive-permissions, dangerous-pull-request-target, bash-injection, missing-timeout, self-hosted-exposure), 3 reporters (pretty/json/github — auto-selects github annotations on a runner), CLI buddy-bot security (bin/cli.ts:2074). Tests in test/security/.

Spec

1. Engine generalization → src/analysis/

  • Analyzer interface: { name, filePatterns: string[], available(): Promise<boolean>, run(files): Promise<Finding[]> }
  • Two analyzer kinds:
    • Native rules (in-process, like today's 6 security rules — migrate them under src/analysis/analyzers/github-actions.ts)
    • External tools (spawn if installed or fetchable via bunx/pipx-style runners): auto-selected by changed-file types, config-file aware (respect the repo's own .eslintrc/biome.json etc.)
  • Unified Finding type shared with the review engine (#1375's ReviewFinding) so linter findings and AI findings merge into one review posting with one dedupe pass
  • Auto-selection: map changed files → applicable analyzers; skip analyzers whose tool is unavailable (report which were skipped — CodeRabbit logs tool selection)

2. First-tier analyzers (priority order)

ToolFilesNotes
existing 6 GH Actions rules.github/workflows/*migration of src/security/
pickier/ESLint/Biome/Oxlint (whichever repo uses)*.ts/js/tsx/jsxdetect repo's own linter + config
actionlintworkflowsexternal, complements native rules
ShellCheck*.shexternal
HadolintDockerfile*pairs with our Docker updates
YAMLlint / JSON validation*.yml/yaml/jsoncheap native
markdownlint*.mdcheap
Gitleaks-style secret scanall changed linesnative regex ruleset first, tool later
OSV-Scanner semanticslockfileswe already have OSV (src/services/security-advisories.ts) — run on PR diffs too, not just scans

3. Integration points

  • Standalone: buddy-bot analyze [path] --format pretty|json|github (supersedes and aliases buddy-bot security)
  • PR review: findings posted as inline comments alongside AI review, tagged [tool]
  • Config: analysis: { enabled?: boolean, tools?: Record<string, boolean | { configFile?: string }>, failOn?: 'error' | 'warning' | 'never' }

Tasks

  • src/analysis/ engine + Analyzer interface; migrate src/security/ rules (keep buddy-bot security as alias)
  • File-type auto-selection + availability probing + skipped-tool reporting
  • First-tier analyzers per table (each with fixture tests; external tools mocked in CI)
  • Secret-scan native ruleset (common token shapes; entropy check; test fixtures with fake keys)
  • Merge/dedupe pipeline into review posting (#1375)
  • Config + validation + docs/analysis.md with the tool table

Acceptance criteria

  • buddy-bot analyze on this repo runs the GH Actions rules + TS linter + markdownlint and reports in all three formats
  • A PR touching a Dockerfile with a Hadolint violation gets an inline [hadolint] comment
  • Analyzer unavailability degrades gracefully with an explicit skipped list
  • No AI key required — this entire feature is non-AI and works standalone

Dependencies

  • Independent to build; posting-path integration lands with #1375
9 days ago

Implemented in bfa2329.

src/analysis/ with an Analyzer interface, selection by changed file type, and findings merged into the review tagged with their tool.

Scope note: this lands the engine plus two analyzers, not the full first-tier table from the issue. Shipped are the secret scanner (native, no external tool) and the existing six workflow rules wrapped as an analyzer — buddy-bot security still works as before. The external-tool analyzers (ESLint/pickier, actionlint, ShellCheck, Hadolint, markdownlint) are now a matter of writing one Analyzer each against a stable interface with an availability probe (commandExists) already in place; worth a follow-up issue rather than leaving this one open.

The valuable property is that this path needs no API key: a repository gets secret scanning and workflow auditing on its PRs without configuring AI at all.

Two behaviours worth recording. An unavailable analyzer is reported in skipped rather than silently omitted — a missing linter that says nothing reads as "clean" when it means "not checked". And the secret scanner never echoes the matched value back, because quoting it would copy the credential into the PR thread; its patterns are deliberately narrow, since a scanner that cries wolf gets muted and a muted scanner catches nothing.

Sign in to comment on this issue.