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
76 changes: 76 additions & 0 deletions .github/workflows/claude-remediation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Claude Remediation (suggest-only)

on:
issue_comment:
types: [created]

permissions:
contents: read
pull-requests: write
issues: write

concurrency:
group: claude-remediation-${{ github.event.issue.number }}
cancel-in-progress: true

jobs:
remediate:
# Only PR comments (issue_comment fires for both issues and PRs) that
# contain the trigger phrase. This workflow never runs on a bare scan
# failure - a maintainer has to ask for it explicitly.
if: >
github.event.issue.pull_request != null &&
(contains(github.event.comment.body, '/remediate') ||
contains(github.event.comment.body, '/claude fix'))
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Get PR head ref
id: pr
uses: actions/github-script@v7
with:
script: |
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
core.setOutput('sha', pr.head.sha);

- name: Checkout PR (read-only)
uses: actions/checkout@v4
with:
ref: ${{ steps.pr.outputs.sha }}
fetch-depth: 0

# `secrets` isn't a valid context in a job/step `if:`, so this
# produces a step output instead. That way the job skips cleanly
# (still a passing status) instead of failing every /remediate
# request until ANTHROPIC_API_KEY is actually configured.
- name: Check for Anthropic API key
id: check-key
run: echo "present=${{ secrets.ANTHROPIC_API_KEY != '' }}" >> "$GITHUB_OUTPUT"

- name: Run Claude Code diagnosis
if: steps.check-key.outputs.present == 'true'
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
trigger_phrase: "/remediate"
# Deliberately no Edit/Write/git-commit tools and no
# contents:write permission above: this workflow diagnoses and
# proposes a patch, it never writes to the branch. A human
# applies the suggested diff themselves.
claude_args: |
--allowedTools "Read,Grep,Glob,Bash(git diff:*),Bash(git log:*),Bash(gh pr diff:*),Bash(gh run view:*),Bash(gh api:*)"
prompt: |
A maintainer asked for remediation help on this PR via /remediate
or /claude fix. Diagnose the failing security/CI checks (Gitleaks,
Trivy, govulncheck, CodeQL, or test failures) using `gh run view`
and `gh api` against this PR's head commit.

Produce the minimal fix as a unified diff and post it in a single
PR comment along with a short explanation of the root cause.
Do not modify any files and do not commit anything - the
maintainer will apply the patch by hand.
62 changes: 62 additions & 0 deletions .github/workflows/claude-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Claude PR Review

on:
pull_request:
types: [opened, synchronize]
branches: [ "master" ]

permissions:
contents: read
pull-requests: write
issues: write

concurrency:
group: claude-review-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
review:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout PR
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

# `secrets` isn't a valid context in a job/step `if:`, so this
# produces a step output instead. That way the job skips cleanly
# (still a passing status) instead of failing every PR until
# ANTHROPIC_API_KEY is actually configured.
- name: Check for Anthropic API key
id: check-key
run: echo "present=${{ secrets.ANTHROPIC_API_KEY != '' }}" >> "$GITHUB_OUTPUT"

- name: Run Claude Code review
if: steps.check-key.outputs.present == 'true'
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
# Read-only tool set: this job diagnoses and comments, it never
# edits files or pushes commits. Auto-remediation is a separate,
# explicitly-triggered workflow (claude-remediation.yml).
claude_args: |
--allowedTools "Read,Grep,Glob,Bash(git diff:*),Bash(git log:*),Bash(gh pr diff:*)"
prompt: |
Review this pull request's diff against joltrin's security standards:
- No hardcoded secrets, credentials, or tokens.
- No new command/SQL/path injection surfaces.
- Containers and CI steps keep least-privilege defaults: non-root
execution, no unnecessary write scopes, pinned action/image
versions.
- Go: no unchecked errors on security-relevant paths (auth, crypto,
file access), no unsafe use of user input.
- Dependency changes: flag anything pulling in an unpinned or
unusually broad version range.

Post findings as a single PR comment: a short summary, then any
issues ranked by severity with file:line references. If nothing
is found, say so briefly. Do not modify any files.
73 changes: 73 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: CodeQL

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: '0 7 * * 1'
workflow_dispatch:

jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
include:
- language: go
build-mode: manual
- language: javascript-typescript
build-mode: none

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Go
if: matrix.language == 'go'
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
queries: security-extended

# demo and demo-agents are WASM-only (import syscall/js) and only
# compile under GOOS=js GOARCH=wasm, same exclusion ci.yml applies
# to vet/build/test. autobuild would fail on them, so build the
# native tree and the wasm packages as two separate passes.
- name: Build Go (native)
if: matrix.language == 'go'
shell: bash
run: go build $(go list ./... 2>/dev/null | grep -Ev '/demo$|/demo-agents$')

# -o is required here: without it, `go build ./demo/...` under
# GOOS=js GOARCH=wasm writes a binary named after the package
# directory into the cwd, which collides with the demo/ directory
# itself (same reason deploy-demo.yml always passes -o).
- name: Build Go (wasm demo packages)
if: matrix.language == 'go'
shell: bash
env:
GOOS: js
GOARCH: wasm
run: |
go build -o /tmp/demo.wasm ./demo
go build -o /tmp/demo-agents.wasm ./demo-agents

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{ matrix.language }}"
Loading
Loading