Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions skills/toy/references/content-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@
- 框架项目**只传构建产物**(`dist` / `build`),不要传源码(`src/`、`package.json` 那一坨)。先 `npm run build`,确认产物里有 `index.html`。
- 上传支持 `.zip` / `.html` / `.htm` / 文件夹(文件夹会自动打包)。
- 别把 `.git`、`node_modules`、`__MACOSX`、`.DS_Store` 这类打进包里。
- **ZIP 包内文件类型白名单**:出于安全考虑,只有白名单内后缀的文件会被发布,其余文件(如可执行文件、后端脚本)会被自动过滤掉。当前支持的后缀:
- 网页与脚本:`.html` `.htm` `.css` `.js` `.json` `.wasm`
- 数据:`.data` `.md` `.csv` `.tsv`
- 图片:`.png` `.jpg` `.jpeg` `.gif` `.svg` `.webp` `.ico`
- 字体:`.woff2` `.woff` `.ttf` `.eot`
- 音频:`.mp3` `.wav` `.ogg` `.m4a`
- 视频:`.mp4` `.webm`
- 其他:`.nani` `.unityweb`
如果页面依赖被过滤的文件,请改用上述支持的格式或走 CDN 外链。
- 旧项目里的 `toy.yaml` 只作本地兼容线索读取,不是页面资源,别打进上传包(`toy_doctor.py` 已自动排除)。

(官方 FAQ Q6/Q7/Q11。)
Expand Down
24 changes: 24 additions & 0 deletions skills/toy/scripts/toy_doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@
CSS_URL_RE = re.compile(r"""url\(\s*(?P<quote>["']?)(?P<url>[^'")]+)(?P=quote)\s*\)""", re.I)
TITLE_RE = re.compile(r"<title[^>]*>(.*?)</title>", re.I | re.S)

# ZIP 包内文件类型白名单(服务端自动过滤不合规后缀,此处静态预检提前告警)
_ALLOWED_EXTENSIONS: frozenset[str] = frozenset({
".html", ".htm", ".css", ".js", ".json", ".wasm",
".data", ".md", ".csv", ".tsv",
".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp", ".ico",
".woff2", ".woff", ".ttf", ".eot",
".mp3", ".wav", ".ogg", ".m4a",
".mp4", ".webm",
".nani", ".unityweb",
})


@dataclass
class Finding:
Expand Down Expand Up @@ -190,6 +201,18 @@ def validate_index(pkg: StaticPackage, require_root: bool, reporter: Reporter) -
reporter.error(".", "missing index.html at root or first-level folder")


def validate_file_extensions(pkg: StaticPackage, reporter: Reporter) -> None:
"""检查 ZIP/目录中是否有不在白名单内的文件后缀。"""
for rel in sorted(pkg.files):
ext = PurePosixPath(rel).suffix.lower()
if ext and ext not in _ALLOWED_EXTENSIONS:
reporter.warn(
rel,
f"unsupported file extension {ext}; will be filtered by server — "
"use a supported format or CDN external link instead",
)


def validate_framework_source(pkg: StaticPackage, reporter: Reporter) -> None:
if "package.json" in pkg.files and any(f.startswith(("src/", "app/", "pages/")) for f in pkg.files):
reporter.warn(
Expand Down Expand Up @@ -346,6 +369,7 @@ def run_checks(args: argparse.Namespace) -> Reporter:
if not reporter.has_errors:
validate_index(pkg, args.require_root_index, reporter)
validate_framework_source(pkg, reporter)
validate_file_extensions(pkg, reporter)
for rel in pkg.html_files():
try:
check_html(pkg, rel, pkg.read_text(rel), reporter)
Expand Down