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
93 changes: 93 additions & 0 deletions .github/workflows/justice-bot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ on:
paths:
- '**/package.json'
- '**/package-lock.json'
workflow_run:
workflows: ['CI']
types: [completed]
workflow_dispatch:
inputs:
message:
Expand Down Expand Up @@ -262,6 +265,22 @@ jobs:
echo "Posted new Justice verdict comment."
fi

# NOTE: ci-gated-approve also approves after CI passes. This early approval
# allows dependency bot PRs to merge without waiting for full CI when the
# safety verdict is clear. ci-gated-approve has a duplicate check to skip
# if Justice has already approved.
- name: ✅ Approve PR
if: >
steps.verify.outputs.is_bot == 'true' &&
steps.analyze.outputs.should_comment == 'true' &&
(steps.analyze.outputs.verdict == 'approve' || steps.analyze.outputs.verdict == 'security')
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
gh pr review "$PR_NUMBER" --approve \
--body "⚖️ Justice approves this dependency update. All safety checks passed."

- name: ⚖️ Verdict delivered
if: steps.verify.outputs.is_bot == 'true' && steps.analyze.outputs.should_comment == 'true'
env:
Expand All @@ -274,6 +293,76 @@ jobs:
exit 1
fi

ci-gated-approve:
name: 👮 CI-Gated Approval
runs-on: ubuntu-latest
# SECURITY: Reject fork PRs — only same-repo branches are trusted for auto-approval.
if: >
github.event_name == 'workflow_run' &&
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_repository != null &&
github.event.workflow_run.head_repository.full_name == github.repository
permissions:
contents: read
pull-requests: write

steps:
- name: Harden Runner
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
with:
egress-policy: audit

- name: 🔑 Get App Token
id: app-token
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2
with:
app_id: ${{ secrets.JUSTICE_BOT_APP_ID }}
private_key: ${{ secrets.JUSTICE_BOT_PRIVATE_KEY }}

Comment thread
qodo-code-review[bot] marked this conversation as resolved.
# SECURITY: workflow_run.pull_requests may be empty when CI was triggered
# by push (not pull_request). Use commits API for reliable PR resolution.
- name: 🔍 Find associated PR
id: find-pr
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
PR_DATA=$(gh api "repos/${GITHUB_REPOSITORY}/commits/${HEAD_SHA}/pulls" \
2>/dev/null || echo "[]")
PR_COUNT=$(echo "$PR_DATA" | jq 'length')

if [ "$PR_COUNT" -eq 0 ]; then
echo "No open PR found for SHA ${HEAD_SHA}. Skipping."
echo "found=false" >> "$GITHUB_OUTPUT"
elif [ "$PR_COUNT" -gt 1 ]; then
echo "::warning::Multiple PRs ($PR_COUNT) found for SHA ${HEAD_SHA}. Skipping auto-approval."
echo "found=false" >> "$GITHUB_OUTPUT"
else
PR_NUMBER=$(echo "$PR_DATA" | jq -r '.[0].number')
echo "Found PR #${PR_NUMBER}"
echo "found=true" >> "$GITHUB_OUTPUT"
echo "number=${PR_NUMBER}" >> "$GITHUB_OUTPUT"
fi

- name: ✅ Approve PR
if: steps.find-pr.outputs.found == 'true'
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
PR_NUMBER: ${{ steps.find-pr.outputs.number }}
run: |
# Avoid duplicate approvals
EXISTING=$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/reviews" \
--jq '[.[] | select(.user.login == "nullvariant-justice[bot]" and .state == "APPROVED")] | length' \
2>/dev/null || echo "0")

if [ "$EXISTING" -gt 0 ]; then
echo "PR already approved by Justice. Skipping."
else
gh pr review "$PR_NUMBER" --approve \
--body "⚖️ Justice grants passage. CI checks passed — this code meets the garden's standards."
fi

justice-commit:
name: 👮 Enforce the law
runs-on: ubuntu-latest
Expand Down Expand Up @@ -311,6 +400,10 @@ jobs:
env:
USER_MESSAGE: ${{ github.event.inputs.message }}
run: |
# Sanitize: truncate to 500 chars, strip control characters except newlines
USER_MESSAGE=$(printf '%s' "$USER_MESSAGE" | head -c 500 | tr -d '\000-\010\013\014\016-\037')
export USER_MESSAGE

mkdir -p docs
cat > docs/JUSTICE.md << 'EOF'
# 👮 Justice's Code Review
Expand Down
1 change: 1 addition & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ This repository contains VS Code extensions with the following security measures
- **SAST (Snyk Code)**: [Snyk](https://snyk.io/) runs static analysis; test fixtures are excluded via `.snyk` policy
- **License & Vulnerability Scanning**: [FOSSA](https://app.fossa.com/) runs license compliance, dependency quality, and security analysis on every commit (GitHub App integration)
- **Runtime Security Monitoring**: [StepSecurity Harden-Runner](https://github.com/step-security/harden-runner) monitors all workflow runs for suspicious network egress, file access, and process execution
- **Branch Protection**: Main branch requires PR approval (1 reviewer minimum). All PRs are automatically approved by nullvariant-justice[bot] after CI passes (solo-developer workflow for [OpenSSF Scorecard](https://securityscorecards.dev/) compliance). Dependency bot PRs receive additional safety review before approval. Enforced by [Allstar](https://github.com/ossf/allstar/)

## Security Testing

Expand Down
2 changes: 1 addition & 1 deletion extensions/git-id-switcher/src/ui/documentationInternal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const DOCUMENT_HASHES: Record<string, string> = {
'GOVERNANCE.md': '806cf32ec9fe9fd964a782927f8eaa7696d408c42d31f554eebd6755bd911c71',
'LICENSE': 'e2383295422577622666fa2ff00e5f03abd8f2772d74cca5d5443020ab23d03d',
'README.md': '990262ba0df29c031ee24c50d342fbf376bd1cc5c33849ec6b3ba1c09e898bb7',
'SECURITY.md': 'bbae0e2a679851ea0ede598102c7d809aea11b7bab99ddf6a20f4dde31070c23',
'SECURITY.md': '6379722a43118cb0ca81625ac8c3b0d0a8878dddd1e190e3a68a498385a35df6',
};

/** Supported locales for documentation */
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading