mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 04:07:44 +02:00
[eric] skills: an export never ships a venv, and a licence id is not an API key (ENG-401)
Both halves diagnosed by Haik Decie. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018foyDoK19jjbYdudfzQVkZ
This commit is contained in:
co-authored by
Claude Opus 5
parent
2e8d431118
commit
dc014c5377
@@ -7,6 +7,8 @@ but the body still rides the central scrub in case someone pasted a token in."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
from backend.apps.outputs.workspace_io import WALK_SKIP_DIRS
|
||||
import shutil
|
||||
|
||||
from backend.apps.skills import skills as store
|
||||
@@ -95,9 +97,18 @@ class SkillExportable:
|
||||
|
||||
|
||||
def p_read_supporting_files(skill_dir: str) -> dict[str, bytes]:
|
||||
"""Every file in a skill folder except SKILL.md, as {relpath: bytes}."""
|
||||
"""Every file in a skill folder except SKILL.md, as {relpath: bytes}.
|
||||
|
||||
Prunes the same build/venv dirs the app exporter has always pruned. This walker bound `dirs`
|
||||
and never used it, so exporting a skill with a Python venv swept every file under `.venv/` into
|
||||
the bundle: a python3.13 tree with absolute paths baked in, dead on any other machine, and big
|
||||
enough that one vendored SPDX licence list tripped the secret scanner and hard-blocked the
|
||||
export with advice ("remove the secret") that no user could follow. Credit: Haik Decie, who
|
||||
diagnosed both halves.
|
||||
"""
|
||||
out: dict[str, bytes] = {}
|
||||
for root, p_dirs, names in os.walk(skill_dir):
|
||||
p_dirs[:] = [d for d in p_dirs if d not in WALK_SKIP_DIRS]
|
||||
for n in names:
|
||||
full = os.path.join(root, n)
|
||||
rel = os.path.relpath(full, skill_dir)
|
||||
|
||||
@@ -15,7 +15,11 @@ REDACTED = "[redacted]"
|
||||
# Literal-secret shapes someone might paste into a file, skill body, or setting.
|
||||
SECRET_SHAPE_PATTERNS = (
|
||||
re.compile(r"sk-ant-[A-Za-z0-9_\-]{16,}"),
|
||||
re.compile(r"sk-[A-Za-z0-9_\-]{16,}"),
|
||||
# A real sk- key carries a long DASHLESS run; dashes were counted as key material, so the SPDX
|
||||
# licence id "Asterisk-linking-protocols-exception" read as an OpenAI key and hard-blocked a
|
||||
# skill export with advice ("remove the secret") that no user could act on. Requiring the run
|
||||
# keeps every real shape (sk-proj-<48>, sk-ant-api03-<95>) and drops dictionary-words-with-dashes.
|
||||
re.compile(r"sk-[A-Za-z0-9_\-]*[A-Za-z0-9_]{20,}"),
|
||||
re.compile(r"AIza[A-Za-z0-9_\-]{20,}"), # Google API key shape
|
||||
re.compile(r"gh[pousr]_[A-Za-z0-9]{20,}"), # GitHub tokens
|
||||
re.compile(r"Bearer\s+[A-Za-z0-9._\-]{16,}"),
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
"""The scanner blocks an export, so a false positive costs the user something they cannot fix.
|
||||
|
||||
Reported by Haik Decie: exporting a skill failed with "a secret-shaped value is in
|
||||
.../packaging/licenses/_spdx.py; remove it" and there was no secret to remove. The SPDX licence id
|
||||
`Asterisk-linking-protocols-exception` contains `sk-linking-protocols-exception`, and the pattern
|
||||
counted dashes as key material.
|
||||
"""
|
||||
|
||||
from backend.common.secret_scan import looks_secret, redact_secret_shapes
|
||||
|
||||
|
||||
def test_a_licence_identifier_is_not_a_key():
|
||||
assert looks_secret("Asterisk-linking-protocols-exception") is False
|
||||
assert looks_secret("sk-linking-protocols-exception") is False
|
||||
|
||||
|
||||
def test_real_key_shapes_are_still_caught():
|
||||
# The control that keeps this from being a blanket weakening.
|
||||
assert looks_secret("sk-proj-" + "a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0") is True
|
||||
assert looks_secret("sk-ant-api03-" + "x" * 95) is True
|
||||
assert looks_secret("sk-" + "A1b2C3d4E5f6G7h8I9j0K1l2") is True
|
||||
assert looks_secret("AIza" + "B" * 30) is True
|
||||
assert looks_secret("ghp_" + "c" * 30) is True
|
||||
|
||||
|
||||
def test_other_dictionary_dashed_words_stay_clear():
|
||||
for benign in ("task-list-runner-exception", "disk-usage-report-helper",
|
||||
"risk-scoring-model-weights", "sk-a-b-c-d-e-f"):
|
||||
assert looks_secret(benign) is False, benign
|
||||
|
||||
|
||||
def test_redaction_still_removes_a_real_key():
|
||||
out = redact_secret_shapes("key=sk-proj-" + "z" * 40)
|
||||
assert "sk-proj-" not in out and "[redacted]" in out
|
||||
@@ -0,0 +1,35 @@
|
||||
"""A skill export must not ship a venv.
|
||||
|
||||
Reported by Haik Decie: exporting a skill with a Python venv failed on a secret-shaped value inside
|
||||
`.venv/.../packaging/licenses/_spdx.py`. Two stacked bugs; this pins the root cause. The walker
|
||||
bound `dirs` and never pruned it, so it swept a python3.13 tree with absolute paths baked in, dead
|
||||
on any other machine, into the bundle. The app exporter has always pruned the same set.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from backend.apps.swarm.entities.skills import p_read_supporting_files
|
||||
from backend.apps.outputs.workspace_io import WALK_SKIP_DIRS
|
||||
|
||||
|
||||
def test_a_venv_never_reaches_the_bundle(tmp_path):
|
||||
skill = tmp_path / "s"
|
||||
(skill / ".venv" / "lib" / "python3.13" / "site-packages").mkdir(parents=True)
|
||||
(skill / ".venv" / "lib" / "python3.13" / "site-packages" / "_spdx.py").write_text("x")
|
||||
(skill / "node_modules").mkdir()
|
||||
(skill / "node_modules" / "big.js").write_text("x")
|
||||
(skill / "helper.py").write_text("real content")
|
||||
(skill / "SKILL.md").write_text("# skill")
|
||||
|
||||
out = p_read_supporting_files(str(skill))
|
||||
assert "helper.py" in out, "real supporting files must still ship"
|
||||
assert not any(".venv" in k for k in out), f"venv leaked: {list(out)}"
|
||||
assert not any("node_modules" in k for k in out)
|
||||
assert "SKILL.md" not in out
|
||||
|
||||
|
||||
def test_it_prunes_the_same_set_the_app_exporter_does():
|
||||
# One definition of "do not ship this", not two that can drift.
|
||||
src = open("backend/apps/swarm/entities/skills.py").read()
|
||||
assert "WALK_SKIP_DIRS" in src
|
||||
assert ".venv" in WALK_SKIP_DIRS and "node_modules" in WALK_SKIP_DIRS
|
||||
Reference in New Issue
Block a user