fix: tighten skill safety hygiene

This commit is contained in:
yaojingang
2026-04-19 11:47:24 +08:00
parent edd8ac9951
commit 0e5776496f
8 changed files with 40 additions and 11 deletions
+2 -1
View File
@@ -12,5 +12,6 @@ jobs:
- uses: actions/setup-python@v6
with:
python-version: "3.11"
- run: python -m pip install --upgrade pip pyyaml
- run: python -m pip install --upgrade pip
- run: python -m pip install --requirement requirements-ci.txt
- run: make ci-test
+2 -2
View File
@@ -6,9 +6,9 @@
"context_budget_tier": "production",
"context_budget_limit": 1000,
"skill_body_tokens": 824,
"other_text_tokens": 276829,
"other_text_tokens": 277008,
"estimated_initial_load_tokens": 1000,
"estimated_total_text_tokens": 277653,
"estimated_total_text_tokens": 277832,
"relevant_file_count": 167,
"unused_resource_dirs": [],
"quality_signal_points": 140,
+1
View File
@@ -0,0 +1 @@
PyYAML==6.0.3
+15 -1
View File
@@ -9,6 +9,18 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
SAFE_ENV_KEYS = (
"HOME",
"LANG",
"LC_ALL",
"PATH",
"PYTHONPATH",
"PYTHONIOENCODING",
"TEMP",
"TERM",
"TMP",
"TMPDIR",
)
DEFAULT_TARGETS = [
"eval",
"eval-suite",
@@ -57,6 +69,8 @@ def run_target(target: str, index: int, total: int, tail_lines: int) -> None:
start = time.perf_counter()
with tempfile.NamedTemporaryFile(prefix=f"ci-{target}-", suffix=".log", delete=False) as handle:
log_path = Path(handle.name)
child_env = {key: os.environ[key] for key in SAFE_ENV_KEYS if key in os.environ}
child_env["CI"] = "1"
try:
with log_path.open("w", encoding="utf-8") as log_file:
proc = subprocess.run(
@@ -65,7 +79,7 @@ def run_target(target: str, index: int, total: int, tail_lines: int) -> None:
stdout=log_file,
stderr=subprocess.STDOUT,
text=True,
env={**os.environ, "CI": "1"},
env=child_env,
)
elapsed = time.perf_counter() - start
if proc.returncode != 0:
+16 -3
View File
@@ -5,7 +5,7 @@ import os
import re
from pathlib import Path
from typing import Any
from urllib.parse import quote
from urllib.parse import quote, urlparse
from urllib.request import Request, urlopen
@@ -15,6 +15,7 @@ USER_AGENT = "yao-meta-skill/1.0"
DEFAULT_TOP_N = 3
DEFAULT_PER_PAGE = 8
MIN_STARS = 50
ALLOWED_API_HOSTS = {"api.github.com"}
STOPWORDS = {
"the",
@@ -100,8 +101,15 @@ def github_headers() -> dict[str, str]:
return headers
def ensure_allowed_api_url(url: str) -> str:
parsed = urlparse(url)
if parsed.scheme != "https" or parsed.hostname not in ALLOWED_API_HOSTS:
raise ValueError(f"Refusing outbound request to non-GitHub API host: {url}")
return url
def api_get(url: str) -> dict[str, Any]:
request = Request(url, headers=github_headers())
request = Request(ensure_allowed_api_url(url), headers=github_headers())
with urlopen(request, timeout=15) as response: # nosec B310 - GitHub API fetch
data = response.read().decode("utf-8")
return json.loads(data)
@@ -154,7 +162,10 @@ def fetch_readme(full_name: str, fixture_bundle: dict[str, Any] | None = None) -
return fixture_bundle.get("readmes", {}).get(full_name, "")
url = f"{API_ROOT}/repos/{full_name}/readme"
request = Request(url, headers={**github_headers(), "Accept": "application/vnd.github.raw+json"})
request = Request(
ensure_allowed_api_url(url),
headers={**github_headers(), "Accept": "application/vnd.github.raw+json"},
)
with urlopen(request, timeout=15) as response: # nosec B310 - GitHub API fetch
return response.read().decode("utf-8", errors="replace")
@@ -344,6 +355,7 @@ def run_github_benchmark_scan(
"ok": True,
"query": query,
"source": "fixture" if fixture_bundle is not None else "github-api",
"network_boundary": "github-api-only",
"repositories": repo_summaries,
"cross_repo": cross_repo,
"external_references": [repo_to_external_reference(repo) for repo in repo_summaries],
@@ -355,6 +367,7 @@ def run_github_benchmark_scan(
"ok": False,
"query": query,
"source": "fixture" if fixture_bundle is not None else "github-api",
"network_boundary": "github-api-only",
"repositories": [],
"cross_repo": {
"borrow": [],
+2 -2
View File
@@ -52,7 +52,7 @@ def classify_focus(description: str) -> str:
def build_questions(focus: str) -> list[dict]:
base = [
{
"question": "If this skill worked beautifully, what recurring job would it quietly take off the user's plate every time?",
"question": "If this skill worked beautifully, what recurring job would it reliably handle for the user every time?",
"why": "This reveals the real job-to-be-done and gives the package a humane center instead of a guessed prompt shape.",
},
{
@@ -156,7 +156,7 @@ def build_summary(skill_dir: Path) -> dict:
],
"opening_styles": opening_styles,
"optional_scaffold": [
"The repeated job it should take over",
"The repeated job it should reliably handle",
"The real inputs people will hand to it",
"The useful output it should hand back",
"What it should clearly refuse",
+1 -1
View File
@@ -185,7 +185,7 @@ def card_items(interface_data: dict, logic_steps: list[str], package_map: list[d
def introduction_lines(description: str) -> list[str]:
return [
f"This skill is trying to quietly take over: {description}",
f"This skill is designed to handle: {description}",
"Start by clarifying the recurring job, the real input shape, and the output that lets the next person keep moving.",
"If the idea is still fuzzy, use the intent dialogue first and let the structure come second.",
]
+1 -1
View File
@@ -226,7 +226,7 @@ def command_quickstart(args: argparse.Namespace) -> int:
sys.stderr.write("Before we deepen the package, I will also look at a few strong public GitHub references so we can borrow patterns without copying them.\n")
name = args.name or prompt_with_default("Skill name", "my-skill")
job = args.job or prompt_with_default(
"In your own words, what repeated work do you most want this skill to quietly take over",
"In your own words, what repeated work do you most want this skill to reliably handle",
"Turn a repeated workflow into a reusable skill.",
)
primary_output = args.primary_output or prompt_with_default(