From dab872a3db0a210487afbdddc5fbb743f7e9774c Mon Sep 17 00:00:00 2001 From: Ryan Melton Date: Sun, 27 Sep 2026 14:16:19 -0600 Subject: [PATCH 1/4] Add AI code review --- .github/workflows/ai_review.yml | 93 +++++++++++++++++++++++ .github/workflows/malicious_code_scan.yml | 59 ++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 .github/workflows/ai_review.yml create mode 100644 .github/workflows/malicious_code_scan.yml diff --git a/.github/workflows/ai_review.yml b/.github/workflows/ai_review.yml new file mode 100644 index 0000000000..e64431598f --- /dev/null +++ b/.github/workflows/ai_review.yml @@ -0,0 +1,93 @@ +# Copyright 2026 OpenC3, Inc. +# All Rights Reserved. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See LICENSE.md for more details. + +# This file may also be used under the terms of a commercial license +# if purchased from OpenC3, Inc. + + +# Adversarial AI review: once every CI run for the PR head has finished and the +# Malicious Code Scan has passed (see malicious_code_scan.yml), Claude and Codex +# take turns reviewing the PR, fixing CI failures and other issues, and +# committing fixes until one of them approves without changes. The workflow, +# scripts and prompt live in OpenC3/.github (ai-review-reusable.yml). +# +# workflow_run fires once per completed CI workflow, and the scan starts this +# workflow with workflow_dispatch when it passes. workflow_run only uses the copy +# of this file on the default branch, so edits here take effect after they merge. +# +# Secrets: ANTHROPIC_API_KEY, OPENAI_API_KEY, AI_REVIEW_PUSH_TOKEN (to push fixes) +# Variables (optional): AI_REVIEW_MAX_TURNS, AI_REVIEW_MAX_CI_ROUNDS, AI_REVIEW_CLAUDE_MODEL, +# AI_REVIEW_CODEX_MODEL, AI_REVIEW_CLAUDE_MAX_BUDGET_USD, AI_REVIEW_CODEX_SANDBOX +# Add the `skip-ai-review` label to a PR to opt out. + +name: AI Review + +on: + workflow_run: + # Every workflow that runs on pull_request, plus GitHub's CodeQL default setup. Each run warns + # about a missing one; the gate does not wait on built-in runs like CodeQL. + workflows: + - API Tests + - Build UBI + - CLI Tests + - CodeQL + - Dependency Review + - eslint + - Frontend Generator Tests + - Playwright Tests + - Python Lint + - Python Type Check + - Python Unit Tests + - Ruby Unit Tests + - Shell Lint + - Check Spelling + - Tool Version Check + - Trivy Scan + - TSDB Integration Tests + types: + - completed + # Pushes to main run CI too; skip them here instead of starting a run that does nothing + branches-ignore: + - main + workflow_dispatch: + inputs: + pr_number: + description: PR number to review + required: true + force: + description: Review even if this commit was already reviewed + type: boolean + default: false + +permissions: + contents: read + +jobs: + review: + uses: OpenC3/.github/.github/workflows/ai-review-reusable.yml@main + permissions: + actions: read + contents: write + pull-requests: write + statuses: read + with: + pr_number: ${{ inputs.pr_number }} + force: ${{ inputs.force || false }} + review_instructions: | + - Ruby/Python parity: COSMOS keeps equivalent Ruby (openc3/lib) and Python (openc3/python) + implementations, so a change to one usually needs the same change to the other. + max_turns: ${{ vars.AI_REVIEW_MAX_TURNS }} + max_ci_rounds: ${{ vars.AI_REVIEW_MAX_CI_ROUNDS }} + claude_model: ${{ vars.AI_REVIEW_CLAUDE_MODEL }} + codex_model: ${{ vars.AI_REVIEW_CODEX_MODEL }} + claude_max_budget_usd: ${{ vars.AI_REVIEW_CLAUDE_MAX_BUDGET_USD }} + codex_sandbox: ${{ vars.AI_REVIEW_CODEX_SANDBOX }} + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + AI_REVIEW_PUSH_TOKEN: ${{ secrets.AI_REVIEW_PUSH_TOKEN }} diff --git a/.github/workflows/malicious_code_scan.yml b/.github/workflows/malicious_code_scan.yml new file mode 100644 index 0000000000..eb31b2256c --- /dev/null +++ b/.github/workflows/malicious_code_scan.yml @@ -0,0 +1,59 @@ +# Copyright 2026 OpenC3, Inc. +# All Rights Reserved. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See LICENSE.md for more details. + +# This file may also be used under the terms of a commercial license +# if purchased from OpenC3, Inc. + + +# Scans every PR for obfuscated code, prompt injection, and other malicious +# changes, and gates the AI Review workflow on the result. The workflow and +# scanner live in OpenC3/.github (malicious-code-scan-reusable.yml). +# +# This runs on pull_request_target so the workflow and scanner never come from +# the PR: a PR cannot edit its way past the scan, and fork PRs get the Claude +# review too. The PR is only ever read as data. +# +# The result is posted as the commit status `security/malicious-code-scan` on +# the PR head; make that a required check in branch protection. A maintainer +# (write access or above) accepts blocking findings with the +# `malicious-scan-override` label. +# +# Secrets: ANTHROPIC_API_KEY (the Claude review is skipped with a warning without it) +# Variables (optional): MALICIOUS_SCAN_CLAUDE_MODEL + +name: Malicious Code Scan + +on: + pull_request_target: + types: + - opened + - reopened + - synchronize + - edited + - ready_for_review + - labeled + +permissions: + contents: read + +jobs: + scan: + uses: OpenC3/.github/.github/workflows/malicious-code-scan-reusable.yml@main + permissions: + contents: read + statuses: write + pull-requests: write + actions: write + with: + review_workflow: ai_review.yml + project_description: >- + OpenC3 COSMOS, an open-source command and control system for spacecraft and embedded systems + (Ruby, Python, Vue/JS, shell, Docker) + claude_model: ${{ vars.MALICIOUS_SCAN_CLAUDE_MODEL }} + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} From a8f357f19ce6adabb90a0b9f85d7e29086f86703 Mon Sep 17 00:00:00 2001 From: Ryan Melton Date: Sun, 27 Sep 2026 14:23:42 -0600 Subject: [PATCH 2/4] ignore generated docs files --- .github/workflows/ai_review.yml | 2 +- .github/workflows/malicious_code_scan.yml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ai_review.yml b/.github/workflows/ai_review.yml index e64431598f..6546e16f44 100644 --- a/.github/workflows/ai_review.yml +++ b/.github/workflows/ai_review.yml @@ -30,7 +30,7 @@ name: AI Review on: workflow_run: # Every workflow that runs on pull_request, plus GitHub's CodeQL default setup. Each run warns - # about a missing one; the gate does not wait on built-in runs like CodeQL. + # about a missing one; the gate waits only on pull_request runs, not built-in ones like CodeQL. workflows: - API Tests - Build UBI diff --git a/.github/workflows/malicious_code_scan.yml b/.github/workflows/malicious_code_scan.yml index eb31b2256c..69d8280791 100644 --- a/.github/workflows/malicious_code_scan.yml +++ b/.github/workflows/malicious_code_scan.yml @@ -55,5 +55,10 @@ jobs: OpenC3 COSMOS, an open-source command and control system for spacecraft and embedded systems (Ruby, Python, Vue/JS, shell, Docker) claude_model: ${{ vars.MALICIOUS_SCAN_CLAUDE_MODEL }} + # The built docs site (Docusaurus output published to GitHub Pages; the source is docs.openc3.com/) + generated_paths: | + docs/**/*.html + docs/assets/** + docs/search-*.json secrets: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} From ed089a27cbac1b19107920cade2bf6adf67867cf Mon Sep 17 00:00:00 2001 From: Ryan Melton Date: Sun, 27 Sep 2026 15:04:17 -0600 Subject: [PATCH 3/4] reduce permissions --- .github/workflows/ai_review.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ai_review.yml b/.github/workflows/ai_review.yml index 6546e16f44..70853e9f9f 100644 --- a/.github/workflows/ai_review.yml +++ b/.github/workflows/ai_review.yml @@ -20,7 +20,9 @@ # workflow with workflow_dispatch when it passes. workflow_run only uses the copy # of this file on the default branch, so edits here take effect after they merge. # -# Secrets: ANTHROPIC_API_KEY, OPENAI_API_KEY, AI_REVIEW_PUSH_TOKEN (to push fixes) +# Secrets: ANTHROPIC_API_KEY, OPENAI_API_KEY, AI_REVIEW_PUSH_TOKEN (to push fixes: a GitHub App +# token or fine-grained PAT with contents:write and no workflows permission, so GitHub refuses a +# fix that changes a workflow) # Variables (optional): AI_REVIEW_MAX_TURNS, AI_REVIEW_MAX_CI_ROUNDS, AI_REVIEW_CLAUDE_MODEL, # AI_REVIEW_CODEX_MODEL, AI_REVIEW_CLAUDE_MAX_BUDGET_USD, AI_REVIEW_CODEX_SANDBOX # Add the `skip-ai-review` label to a PR to opt out. @@ -72,7 +74,7 @@ jobs: uses: OpenC3/.github/.github/workflows/ai-review-reusable.yml@main permissions: actions: read - contents: write + contents: read pull-requests: write statuses: read with: From e8f9fa7cddb1e912604f3729292ae58f9898966d Mon Sep 17 00:00:00 2001 From: Ryan Melton Date: Mon, 28 Sep 2026 06:42:33 -0600 Subject: [PATCH 4/4] Initially only run on dispatch until tested --- .github/workflows/ai_review.yml | 52 ++++++++++++++++----------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ai_review.yml b/.github/workflows/ai_review.yml index 70853e9f9f..a58523c4a7 100644 --- a/.github/workflows/ai_review.yml +++ b/.github/workflows/ai_review.yml @@ -30,32 +30,32 @@ name: AI Review on: - workflow_run: - # Every workflow that runs on pull_request, plus GitHub's CodeQL default setup. Each run warns - # about a missing one; the gate waits only on pull_request runs, not built-in ones like CodeQL. - workflows: - - API Tests - - Build UBI - - CLI Tests - - CodeQL - - Dependency Review - - eslint - - Frontend Generator Tests - - Playwright Tests - - Python Lint - - Python Type Check - - Python Unit Tests - - Ruby Unit Tests - - Shell Lint - - Check Spelling - - Tool Version Check - - Trivy Scan - - TSDB Integration Tests - types: - - completed - # Pushes to main run CI too; skip them here instead of starting a run that does nothing - branches-ignore: - - main + # workflow_run: + # # Every workflow that runs on pull_request, plus GitHub's CodeQL default setup. Each run warns + # # about a missing one; the gate waits only on pull_request runs, not built-in ones like CodeQL. + # workflows: + # - API Tests + # - Build UBI + # - CLI Tests + # - CodeQL + # - Dependency Review + # - eslint + # - Frontend Generator Tests + # - Playwright Tests + # - Python Lint + # - Python Type Check + # - Python Unit Tests + # - Ruby Unit Tests + # - Shell Lint + # - Check Spelling + # - Tool Version Check + # - Trivy Scan + # - TSDB Integration Tests + # types: + # - completed + # # Pushes to main run CI too; skip them here instead of starting a run that does nothing + # branches-ignore: + # - main workflow_dispatch: inputs: pr_number: