Skip to content

Commit 4f1b42a

Browse files
committed
fix: address Copilot review findings from PR github#2401
- SSRF protection: reject private/loopback/reserved IPs and non-HTTP(S) schemes in check_url_reachable() before making network requests - Table generator: exit non-zero when --target is set but markers are missing, so CI fails loudly instead of silently skipping the update - Add catalog-table-start/end markers to docs/community/presets.md so the table generator can update it automatically - Use RELEASE_PAT instead of GITHUB_TOKEN in catalog-pr.yml so auto-generated PRs trigger downstream CI workflows - Reword extension safety FAQ to distinguish verified vs unverified community extensions
1 parent 02872e7 commit 4f1b42a

5 files changed

Lines changed: 34 additions & 7 deletions

File tree

.github/scripts/catalog-generate-table.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,11 @@ def main() -> None:
181181
print(f"Updated {target}")
182182
else:
183183
print(
184-
f"Warning: markers {START_MARKER} / {END_MARKER} not found "
185-
f"in {target}. Printing table to stdout.",
184+
f"Error: markers {START_MARKER} / {END_MARKER} not found "
185+
f"in {target}.",
186186
file=sys.stderr,
187187
)
188-
print(table)
188+
sys.exit(1)
189189
else:
190190
print(table)
191191

.github/scripts/catalog-validate.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
from __future__ import annotations
2020

2121
import argparse
22+
import ipaddress
2223
import json
2324
import os
2425
import re
26+
import socket
2527
import sys
2628
import urllib.error
2729
import urllib.parse
@@ -240,8 +242,27 @@ def check_url_reachable(
240242
if not _present(url):
241243
return True, "" # skip if empty/optional
242244
url = url.strip()
245+
246+
# --- SSRF guard: reject non-HTTPS, private/loopback IPs ---
247+
parsed = urllib.parse.urlparse(url)
248+
if parsed.scheme not in ("http", "https"):
249+
return False, f"{field_name} URL must use http or https scheme."
250+
hostname = parsed.hostname
251+
if not hostname:
252+
return False, f"{field_name} URL has no hostname."
253+
try:
254+
addr_info = socket.getaddrinfo(hostname, None)
255+
for _family, _type, _proto, _canonname, sockaddr in addr_info:
256+
ip = ipaddress.ip_address(sockaddr[0])
257+
if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved:
258+
return False, (
259+
f"{field_name} URL `{url}` resolves to a private/reserved address."
260+
)
261+
except (socket.gaierror, ValueError):
262+
pass # DNS resolution may fail for unreachable hosts — let urlopen handle it
263+
243264
_gh_hosts = {"github.com", "www.github.com", "codeload.github.com", "raw.githubusercontent.com"}
244-
_is_github = urllib.parse.urlparse(url).hostname in _gh_hosts
265+
_is_github = hostname in _gh_hosts
245266
req = urllib.request.Request(url, method="HEAD")
246267
req.add_header("User-Agent", "spec-kit-catalog-validator/1.0")
247268
if token and _is_github:

.github/workflows/catalog-pr.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ jobs:
2020
issues: write
2121
steps:
2222
- uses: actions/checkout@v4
23+
with:
24+
token: ${{ secrets.RELEASE_PAT }}
2325

2426
- uses: actions/setup-python@v5
2527
with:
@@ -45,7 +47,7 @@ jobs:
4547
- name: Create or update pull request
4648
if: steps.update.outputs.skipped != 'true'
4749
env:
48-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50+
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
4951
ISSUE_NUMBER: ${{ github.event.issue.number }}
5052
ISSUE_AUTHOR: ${{ github.event.issue.user.login }}
5153
ITEM_ID: ${{ steps.update.outputs.item_id }}
@@ -136,6 +138,8 @@ jobs:
136138
issues: write
137139
steps:
138140
- uses: actions/checkout@v4
141+
with:
142+
token: ${{ secrets.RELEASE_PAT }}
139143

140144
- uses: actions/setup-python@v5
141145
with:
@@ -162,7 +166,7 @@ jobs:
162166
- name: Create or update pull request
163167
if: steps.update.outputs.skipped != 'true'
164168
env:
165-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
169+
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
166170
ISSUE_NUMBER: ${{ github.event.issue.number }}
167171
ISSUE_AUTHOR: ${{ github.event.issue.user.login }}
168172
ITEM_ID: ${{ steps.update.outputs.item_id }}

docs/community/presets.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
The following community-contributed presets customize how Spec Kit behaves — overriding templates, commands, and terminology without changing any tooling. Presets are available in [`catalog.community.json`](https://github.com/github/spec-kit/blob/main/presets/catalog.community.json):
77

8+
<!-- catalog-table-start -->
89
| Preset | Purpose | Provides | Requires | URL |
910
|--------|---------|----------|----------|-----|
1011
| AIDE In-Place Migration | Adapts the AIDE extension workflow for in-place technology migrations (X → Y pattern) — adds migration objectives, verification gates, knowledge documents, and behavioral equivalence criteria | 2 templates, 8 commands | AIDE extension | [spec-kit-presets](https://github.com/mnriem/spec-kit-presets) |
@@ -18,5 +19,6 @@ The following community-contributed presets customize how Spec Kit behaves — o
1819
| Screenwriting | Spec-Driven Development for screenwriting/scriptwriting/tutorials: feature films, television (pilot, episode, limited series), and stage plays. Adapts the Spec Kit workflow to screenplay craft — slug lines, action lines, act breaks, beat sheets, and industry-standard pitch documents. Supports three-act, Save the Cat, TV pilot, network episode, cable/streaming episode, and stage-play structural frameworks. Export to Fountain, FTX, PDF | 26 templates, 32 commands, 1 script || [speckit-preset-screenwriting](https://github.com/adaumann/speckit-preset-screenwriting) |
1920
| Table of Contents Navigation | Adds a navigable Table of Contents to generated spec.md, plan.md, and tasks.md documents | 3 templates, 3 commands || [spec-kit-preset-toc-navigation](https://github.com/Quratulain-bilal/spec-kit-preset-toc-navigation) |
2021
| VS Code Ask Questions | Enhances the clarify command to use `vscode/askQuestions` for batched interactive questioning. | 1 command || [spec-kit-presets](https://github.com/fdcastel/spec-kit-presets) |
22+
<!-- catalog-table-end -->
2123

2224
To build and publish your own preset, see the [Presets Publishing Guide](https://github.com/github/spec-kit/blob/main/presets/PUBLISHING.md).

extensions/EXTENSION-USER-GUIDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -984,7 +984,7 @@ After creating tasks, sync to Jira:
984984

985985
### Q: How do I know if an extension is safe?
986986

987-
**A**: Community extensions are **not** reviewed, audited, or tested by maintainers — only their catalog metadata is validated. Always review extension source code before installing.
987+
**A**: Spec Kit supports both **verified** extensions and unverified community extensions. Verified extensions are reviewed and approved according to project policy, and are identified in the catalog/CLI with the verified flag, `--verified` filter, or "✓ Verified" badge. Unverified community extensions have only their catalog metadata validated; they are not reviewed, audited, or tested by maintainers. Always review extension source code before installing an unverified extension.
988988

989989
### Q: Can extensions modify spec-kit core?
990990

0 commit comments

Comments
 (0)