feat: enhance skill security scan for forked PRs
- Added handling for missing SKILL_SCANNER_LLM_API_KEY in forked pull requests. - Implemented a sticky comment to inform users when the scan is skipped due to the absence of the API key. - Updated workflow documentation to clarify the behavior of the scanner in forked contexts.
This commit is contained in:
2
.github/workflows/pr-skill-scan.yml
vendored
2
.github/workflows/pr-skill-scan.yml
vendored
@@ -73,6 +73,8 @@ jobs:
|
||||
if: steps.changed.outputs.skill_dirs != ''
|
||||
run: uv sync --python 3.13
|
||||
|
||||
# Fork PRs do not receive SKILL_SCANNER_LLM_API_KEY. scan_pr_skills.py
|
||||
# detects the missing key, writes an explanatory sticky comment, and exits 0.
|
||||
- name: Run scanner on changed skills
|
||||
if: steps.changed.outputs.skill_dirs != ''
|
||||
id: scan
|
||||
|
||||
@@ -15,6 +15,7 @@ on the pull request. It exits non-zero when any scanned skill has a finding at
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
@@ -31,6 +32,21 @@ load_dotenv()
|
||||
SEVERITY_ORDER = ["CRITICAL", "HIGH", "MEDIUM", "LOW", "INFO", "SAFE"]
|
||||
COMMENT_MARKER = "<!-- skill-security-scan -->"
|
||||
|
||||
SKIP_NO_API_KEY_COMMENT = f"""\
|
||||
{COMMENT_MARKER}
|
||||
## 🛡️ Skill Security Scan
|
||||
|
||||
_Skipped:_ `SKILL_SCANNER_LLM_API_KEY` is not available in this workflow run.
|
||||
|
||||
GitHub does not expose repository secrets to `pull_request` workflows from forks,
|
||||
so the LLM analyzer cannot start. This is not a finding about the diff.
|
||||
|
||||
A maintainer can re-run the scan from a trusted context (for example after the
|
||||
branch is pushed to this repository, or via a maintainer-triggered run with
|
||||
secrets). Structural CI checks (spec validation, repo-wide contract, skill suites)
|
||||
are unaffected.
|
||||
"""
|
||||
|
||||
|
||||
def _sev_str(obj) -> str:
|
||||
sev = getattr(obj, "max_severity", None) or getattr(obj, "severity", None)
|
||||
@@ -212,6 +228,17 @@ def main() -> int:
|
||||
Path(args.output).write_text(md)
|
||||
return 0
|
||||
|
||||
if not os.getenv("SKILL_SCANNER_LLM_API_KEY"):
|
||||
# Fork PRs never receive repository secrets under pull_request; fail open
|
||||
# with an explanatory sticky comment instead of ValueError from LLMAnalyzer.
|
||||
print(
|
||||
"SKILL_SCANNER_LLM_API_KEY unset — skipping scan "
|
||||
"(typical for pull_request workflows from forks)."
|
||||
)
|
||||
Path(args.output).write_text(SKIP_NO_API_KEY_COMMENT)
|
||||
print(f"Comment written to {args.output}")
|
||||
return 0
|
||||
|
||||
print("Building scanner (LLM + behavioral + trigger + balanced policy)...")
|
||||
scanner = build_scanner()
|
||||
print(f"Analyzers: {scanner.list_analyzers()}\n")
|
||||
|
||||
Reference in New Issue
Block a user