fix: tighten skill safety hygiene
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
PyYAML==6.0.3
|
||||
+15
-1
@@ -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:
|
||||
|
||||
@@ -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": [],
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user