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
27 changes: 27 additions & 0 deletions .claude/skills/add-repo-override/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
name: add-repo-override
description: Add a per-repo settings override to overrides.json
user-invocable: true
---

# Add Repository Override

Add a per-repo exception to `config/overrides.json`.

## Arguments

`$ARGUMENTS` should be in the format: `<repo-name> <setting-path> <value>`

Examples:

- `my-repo branch_protection.required_status_checks.contexts '["Build","Test"]'`
- `my-repo repo_settings.has_wiki true`

## Steps

1. Read the current `config/overrides.json`
2. Parse `$ARGUMENTS` to extract repo name, setting path, and value
3. Add or update the override for the specified repo
4. Validate the resulting JSON with `jq empty`
5. Show the diff of what changed
6. Remind the user to create a PR for the change
24 changes: 24 additions & 0 deletions .claude/skills/audit/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
name: audit
description: Run a dry-run settings audit across all repositories
user-invocable: true
disable-model-invocation: true
---

# Audit Repository Settings

Run a dry-run sync to check for drift without applying changes.

## Steps

1. Run the sync script in dry-run mode:

```bash
./scripts/sync-repo-settings.sh --dry-run
```

1. Display the report:

```bash
cat reports/sync-report.md
```
21 changes: 21 additions & 0 deletions .claude/skills/exclude-repo/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
name: exclude-repo
description: Exclude a repository from settings governance
user-invocable: true
---

# Exclude Repository

Add a repository to the exclusion list in `config/overrides.json`.

## Arguments

`$ARGUMENTS` should be the repository name to exclude.

## Steps

1. Read the current `config/overrides.json`
2. Add `$ARGUMENTS` to the `excluded` array (if not already present)
3. Validate the resulting JSON with `jq empty`
4. Show the updated exclusion list
5. Remind the user to create a PR for the change
2 changes: 2 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Pull Request

## What changed

<!-- Brief description of the change -->
Expand Down
31 changes: 31 additions & 0 deletions .github/actions/security-scan/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Security Scan
description: Run SAST and SCA security scans

inputs:
scan-path:
description: Path to scan
required: false
default: "."

runs:
using: composite
steps:
- name: Run Semgrep SAST
uses: semgrep/semgrep-action@713efdd345f3035192eaa63f56867b88e63e4e5d # v1.0.0
with:
config: auto

- name: Run Trivy vulnerability scanner
if: always()
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
with:
scan-type: fs
scan-ref: ${{ inputs.scan-path }}
format: sarif
output: trivy-results.sarif

- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
if: always()
with:
sarif_file: trivy-results.sarif
47 changes: 47 additions & 0 deletions .github/actions/sync-settings/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Sync Repository Settings
description: Compare and apply GitHub settings across all repos against a baseline

inputs:
mode:
description: "Run mode: --dry-run or --apply"
required: true
default: "--dry-run"
github_token:
description: PAT with repo and admin scopes
required: true

outputs:
report_file:
description: Path to the generated report
value: ${{ steps.sync.outputs.report_file }}
total_repos:
description: Number of repos scanned
value: ${{ steps.parse.outputs.total_repos }}
compliant:
description: Number of compliant repos
value: ${{ steps.parse.outputs.compliant }}
drift:
description: Number of repos with drift
value: ${{ steps.parse.outputs.drift }}
has_drift:
description: Whether any drift was detected
value: ${{ steps.parse.outputs.has_drift }}

runs:
using: composite
steps:
- name: Run settings sync
id: sync
shell: bash
env:
GH_TOKEN: ${{ inputs.github_token }}
REPORT_FILE: reports/sync-report.md
SYNC_MODE: ${{ inputs.mode }}
run: |
./scripts/sync-repo-settings.sh "$SYNC_MODE"
echo "report_file=reports/sync-report.md" >> "$GITHUB_OUTPUT"

- name: Parse report
id: parse
shell: bash
run: ./scripts/generate-report.sh reports/sync-report.md
36 changes: 36 additions & 0 deletions .github/actions/update-pre-commit-composite/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Update Pre-commit Hooks Composite Action
description: Updates pre-commit hook versions and creates a PR

inputs:
github_token:
description: GitHub token for creating PRs
required: true

runs:
using: composite
steps:
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.x"

- name: Install pre-commit
shell: bash
run: pip install pre-commit

- name: Update hooks
shell: bash
run: pre-commit autoupdate

- name: Create Pull Request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
with:
token: ${{ inputs.github_token }}
commit-message: "chore: update pre-commit hook versions"
title: "chore: update pre-commit hook versions"
body: |
Automated update of pre-commit hook versions.

Review the changes to `.pre-commit-config.yaml` and merge if CI passes.
branch: chore/update-pre-commit-hooks
delete-branch: true
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Review priorities for this repository:
# Review Priorities

1. Shell script quality: shellcheck and shellharden compliance, proper
quoting, error handling (set -euo pipefail), no hardcoded tokens
Expand Down
50 changes: 41 additions & 9 deletions .github/workflows/quality-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,24 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Lint markdown
uses: DavidAnson/markdownlint-cli2-action@db4c2f7b1e4a6de4660458dd8d547f94deaac667 # v22.0.0
uses: DavidAnson/markdownlint-cli2-action@07035fd053f7be764496c0f8d8f9f41f98305101 # v22.0.0

yaml-lint:
name: YAML Validation
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Lint YAML
uses: ibiqlik/action-yamllint@2576f72e4b4e5aef56e60fc8a24fa17e25be1fef # v3.1.1
uses: ibiqlik/action-yamllint@2576378a8e339169678f9939646ee3ee325e845c # v3.1.1
with:
config_file: .yamllint.yml

Expand All @@ -37,7 +41,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Run ShellCheck
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0
Expand All @@ -47,7 +53,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Check required files
run: |
Expand Down Expand Up @@ -79,20 +87,44 @@ jobs:
fi
done

- name: Validate baseline schema
run: |
ERRORS=0
for section in repo_settings security branch_protection labels required_files; do
if ! jq -e ".$section" config/baseline.json > /dev/null 2>&1; then
echo "ERROR: Missing section '$section' in baseline.json"
ERRORS=$((ERRORS + 1))
else
echo "OK: section '$section' present"
fi
done
# Validate label structure
LABEL_ERRORS=$(jq '[.labels[] | select(.name == null or .color == null or .description == null)] | length' config/baseline.json)
if [ "$LABEL_ERRORS" -gt 0 ]; then
echo "ERROR: $LABEL_ERRORS labels missing required fields (name, color, description)"
ERRORS=$((ERRORS + LABEL_ERRORS))
fi
if [ "$ERRORS" -gt 0 ]; then
echo "ERROR: baseline.json schema validation failed"
exit 1
fi
Comment on lines +90 to +110
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This only checks section presence, not the baseline schema.

Lines 85-91 will still pass if security.vulnerability_alerts is missing or if a label entry omits color/description. That leaves the new baseline contract effectively unvalidated.

Suggested jq-based validation
       - name: Validate baseline schema
         run: |
-          ERRORS=0
-          for section in repo_settings security branch_protection labels required_files; do
-            if ! jq -e ".$section" config/baseline.json > /dev/null 2>&1; then
-              echo "ERROR: Missing section '$section' in baseline.json"
-              ERRORS=$((ERRORS + 1))
-            else
-              echo "OK: section '$section' present"
-            fi
-          done
-          if [ "$ERRORS" -gt 0 ]; then
-            echo "ERROR: baseline.json schema validation failed"
-            exit 1
-          fi
+          jq -e '
+            (.repo_settings | type == "object") and
+            (.security | type == "object") and
+            (.security.vulnerability_alerts | type == "boolean") and
+            (.branch_protection | type == "object") and
+            (.required_files | type == "array") and
+            (.labels | type == "array") and
+            ([.labels[] | (.name | type == "string")
+                         and (.color | type == "string" and test("^[0-9A-Fa-f]{6}$"))
+                         and (.description | type == "string")] | all)
+          ' config/baseline.json >/dev/null || {
+            echo "ERROR: baseline.json schema validation failed"
+            exit 1
+          }

As per coding guidelines, "Confirm the JSON structure aligns with the baseline schema validated by quality checks (Validate baseline schema step) and used by per-repo drift/metadata checks."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Validate baseline schema
run: |
ERRORS=0
for section in repo_settings security branch_protection labels required_files; do
if ! jq -e ".$section" config/baseline.json > /dev/null 2>&1; then
echo "ERROR: Missing section '$section' in baseline.json"
ERRORS=$((ERRORS + 1))
else
echo "OK: section '$section' present"
fi
done
if [ "$ERRORS" -gt 0 ]; then
echo "ERROR: baseline.json schema validation failed"
exit 1
fi
- name: Validate baseline schema
run: |
jq -e '
(.repo_settings | type == "object") and
(.security | type == "object") and
(.security.vulnerability_alerts | type == "boolean") and
(.branch_protection | type == "object") and
(.required_files | type == "array") and
(.labels | type == "array") and
([.labels[] | (.name | type == "string")
and (.color | type == "string" and test("^[0-9A-Fa-f]{6}$"))
and (.description | type == "string")] | all)
' config/baseline.json >/dev/null || {
echo "ERROR: baseline.json schema validation failed"
exit 1
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/quality-checks.yml around lines 82 - 96, The current
"Validate baseline schema" shell block only checks top-level section presence;
update it to validate required nested keys and label fields using jq so missing
subkeys fail CI. Replace or extend the loop that iterates sections
(repo_settings, security, branch_protection, labels, required_files) with
explicit jq checks such as verifying .security.vulnerability_alerts exists and
is an object, and that .labels is an array with every element containing .color
and .description (e.g. fail if any .labels[] | select(.color==null or
.description==null) is found); ensure ERRORS increments and the script exits
non‑zero with a clear message when any nested validation fails.


actions-security:
name: Actions Security
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Install zizmor
run: |
ZIZMOR_VERSION="1.5.0"
ZIZMOR_VERSION="1.23.1"
curl -sL "https://github.com/woodruffw/zizmor/releases/download/v${ZIZMOR_VERSION}/zizmor-x86_64-unknown-linux-gnu.tar.gz" -o /tmp/zizmor.tar.gz
mkdir -p /tmp/zizmor-extract
tar -xzf /tmp/zizmor.tar.gz -C /tmp/zizmor-extract
sudo mv /tmp/zizmor-extract/zizmor /usr/local/bin/zizmor
find /tmp/zizmor-extract -name zizmor -type f -exec sudo mv {} /usr/local/bin/zizmor \;
chmod +x /usr/local/bin/zizmor

- name: Run zizmor
Expand Down
22 changes: 6 additions & 16 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,19 @@ on:
permissions:
contents: read
security-events: write
actions: read

jobs:
security-scan:
name: Security Scan
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Run Semgrep
uses: returntocorp/semgrep-action@713efdd345f3035192eaa63f56867b88e63e4e5d # v1.0.0
with:
config: auto

- name: Run Trivy
uses: aquasecurity/trivy-action@18f2510ee396bbf400402947e7f3b01483832965 # v0.31.0
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
scan-type: fs
format: sarif
output: trivy-results.sarif
persist-credentials: false

- name: Upload Trivy SARIF
uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
if: always()
- name: Run Security Scan
uses: ./.github/actions/security-scan
with:
sarif_file: trivy-results.sarif
scan-path: "."
Loading
Loading