Download tests/native_api/test_catalog.py from hunian003/message: direct link, hf CLI and curl.
- Browser
- Download file 17 kB
-
https://huggingface.co/spaces/hunian003/message/resolve/main/tests/native_api/test_catalog.py
- Command line
-
hf download hf://spaces/hunian003/message/tests/native_api/test_catalog.py
-
curl -L -o test_catalog.py https://huggingface.co/spaces/hunian003/message/resolve/main/tests/native_api/test_catalog.py
17 kB
| """覆盖原生目录入口的归档与 npm manifest 信任边界。""" | |
| import gzip | |
| import hashlib | |
| import importlib.util | |
| import io | |
| import json | |
| from pathlib import Path | |
| import tarfile | |
| import unittest | |
| from unittest.mock import patch | |
| ROOT = Path(__file__).resolve().parents[2] | |
| SPEC = importlib.util.spec_from_file_location("native_catalog_under_test", ROOT / "apps/market-api/app/native/catalog.py") | |
| catalog = importlib.util.module_from_spec(SPEC) | |
| SPEC.loader.exec_module(catalog) | |
| def manifest(): | |
| return { | |
| "name": "@message/example", | |
| "version": "1.2.3-rc.2+build.4", | |
| "description": "原生测试插件", | |
| "scripts": {"test": "node --test", "build": "tsc"}, | |
| "dependencies": {"example-dependency": "2.3.4"}, | |
| "peerDependencies": {"@deepseek-ai/dsh-tools": "0.2.0-rc.2"}, | |
| "dsh": {"manifestVersion": 1, "bundle": {"patch": "./cordis.patch.yml"}}, | |
| } | |
| def archive(entries, *, format=tarfile.PAX_FORMAT): | |
| stream = io.BytesIO() | |
| with tarfile.open(fileobj=stream, mode="w", format=format) as handle: | |
| for entry in entries: | |
| name, payload, *options = entry | |
| info = tarfile.TarInfo(name) | |
| info.size = len(payload) | |
| if options: | |
| info.type = options[0] | |
| if len(options) > 1: | |
| info.linkname = options[1] | |
| handle.addfile(info, io.BytesIO(payload)) | |
| return gzip.compress(stream.getvalue(), mtime=0) | |
| def bundle(metadata=None, extras=()): | |
| if metadata is None: | |
| metadata = manifest() | |
| return archive([ | |
| ("package/package.json", json.dumps(metadata, ensure_ascii=False).encode()), | |
| ("package/cordis.patch.yml", b"- name: example\n"), | |
| *extras, | |
| ]) | |
| class CatalogArchiveTests(unittest.TestCase): | |
| def assert_rejected(self, data, message=None): | |
| with self.assertRaises(ValueError) as caught: | |
| catalog.validate_archive(data) | |
| # 接口直接展示校验失败的原因,所有错误均应使用中文。 | |
| self.assertRegex(str(caught.exception), r"[\u4e00-\u9fff]") | |
| if message: | |
| self.assertIn(message, str(caught.exception)) | |
| def test_valid_bundle_returns_original_archive_digest(self): | |
| data = bundle() | |
| self.assertEqual(catalog.validate_archive(data), { | |
| "name": "@message/example", "version": "1.2.3-rc.2+build.4", | |
| "description": "原生测试插件", "sha256": hashlib.sha256(data).hexdigest(), | |
| "bytes": len(data), | |
| }) | |
| def test_official_bundle_manifests_and_files_are_accepted(self): | |
| for plugin in ("mcp-export", "text-tools"): | |
| with self.subTest(plugin=plugin): | |
| directory = ROOT / "plugins" / plugin | |
| entries = [("package/" + str(file.relative_to(directory)), file.read_bytes()) | |
| for file in sorted(directory.rglob("*")) if file.is_file()] | |
| result = catalog.validate_archive(archive(entries)) | |
| self.assertEqual(result["name"], json.loads((directory / "package.json").read_text())["name"]) | |
| def test_optional_description_and_manifest_version(self): | |
| metadata = manifest() | |
| del metadata["description"] | |
| del metadata["dsh"]["manifestVersion"] | |
| metadata["dsh"]["bundle"]["patch"] = "cordis.patch.yml" | |
| self.assertEqual(catalog.validate_archive(bundle(metadata))["description"], "") | |
| def test_directories_unicode_and_long_paths_are_supported(self): | |
| self.assertTrue(catalog.validate_archive(bundle(extras=[ | |
| ("package/", b"", tarfile.DIRTYPE), | |
| ("package/src/", b"", tarfile.DIRTYPE), | |
| ("package/src/工具.mjs", b"export {}"), | |
| ("package/src/" + "a" * 200 + ".mjs", b"export {}"), | |
| ]))) | |
| def test_gnu_long_paths_are_supported(self): | |
| data = archive([ | |
| ("package/package.json", json.dumps(manifest()).encode()), | |
| ("package/cordis.patch.yml", b"[]"), | |
| ("package/" + "a" * 200, b"test"), | |
| ], format=tarfile.GNU_FORMAT) | |
| self.assertTrue(catalog.validate_archive(data)) | |
| def test_empty_non_bytes_and_non_gzip_input(self): | |
| for data in (b"", "test", None, bytearray(b"test"), b"not a tar", bytes(2048)): | |
| with self.subTest(data=repr(data)[:30]): | |
| self.assert_rejected(data) | |
| def test_upload_limit_and_invalid_limit_configuration(self): | |
| data = bundle() | |
| self.assertTrue(catalog.validate_archive(data, len(data))) | |
| with self.assertRaises(ValueError): | |
| catalog.validate_archive(data, len(data) - 1) | |
| for value in (0, -1, True, "100"): | |
| with self.subTest(value=value), self.assertRaises(ValueError): | |
| catalog.validate_archive(data, value) | |
| def test_corrupt_and_truncated_gzip_are_rejected(self): | |
| data = bundle() | |
| corrupted = bytearray(data) | |
| corrupted[-8] ^= 255 | |
| for value in (data[:5], data[:-5], bytes(corrupted), data + b"garbage"): | |
| with self.subTest(size=len(value)): | |
| self.assert_rejected(value) | |
| def test_invalid_tar_checksum_and_truncated_tar(self): | |
| raw = gzip.decompress(bundle()) | |
| corrupt = bytearray(raw) | |
| corrupt[0] ^= 32 | |
| for value in (bytes(corrupt), raw[:513], raw[:1024], b"x" * 1024): | |
| with self.subTest(size=len(value)): | |
| self.assert_rejected(gzip.compress(value)) | |
| def test_missing_manifest_and_manifest_directory(self): | |
| self.assert_rejected(archive([("package/main.mjs", b"export {}")]), "package/package.json") | |
| self.assert_rejected(archive([("package/package.json", b"", tarfile.DIRTYPE)])) | |
| def test_unsafe_paths_are_rejected(self): | |
| for name in ( | |
| "/package/x", "../x", "package/../x", "package/a/../../x", | |
| "package/./x", "package//x", "./package/x", "other/x", | |
| "package\\x", "package/a\\..\\x", "C:/package/x", | |
| "package/C:x", "package/x\ny", "package/x\x7fy", "package", | |
| ): | |
| with self.subTest(name=name): | |
| self.assert_rejected(bundle(extras=[(name, b"evil")])) | |
| def test_noncanonical_directory_paths_are_rejected(self): | |
| for name in ("package//", "package/./", "package/a//", "package/../"): | |
| with self.subTest(name=name): | |
| self.assert_rejected(bundle(extras=[(name, b"", tarfile.DIRTYPE)])) | |
| def test_duplicate_and_conflicting_paths_are_rejected(self): | |
| self.assert_rejected(bundle(extras=[("package/package.json", b"{}")]), "重复") | |
| for entries in ( | |
| [("package/a", b"file"), ("package/a/b", b"child")], | |
| [("package/a/b", b"child"), ("package/a", b"file")], | |
| [("package/src/", b"", tarfile.DIRTYPE), ("package/src", b"", tarfile.DIRTYPE)], | |
| ): | |
| with self.subTest(entries=entries): | |
| self.assert_rejected(bundle(extras=entries)) | |
| def test_links_devices_fifo_sparse_and_unknown_types_are_rejected(self): | |
| for kind in (tarfile.SYMTYPE, tarfile.LNKTYPE, tarfile.CHRTYPE, tarfile.BLKTYPE, | |
| tarfile.FIFOTYPE, tarfile.GNUTYPE_SPARSE, tarfile.CONTTYPE, b"Z"): | |
| with self.subTest(kind=kind): | |
| self.assert_rejected(bundle(extras=[("package/evil", b"", kind, "package/package.json")])) | |
| def test_regular_files_cannot_smuggle_link_targets(self): | |
| self.assert_rejected(bundle(extras=[("package/evil", b"", tarfile.REGTYPE, "/tmp/evil")])) | |
| def test_hidden_second_archive_and_nonzero_padding_are_rejected(self): | |
| raw = gzip.decompress(bundle()) | |
| self.assert_rejected(gzip.compress(raw + gzip.decompress(bundle()))) | |
| modified = bytearray(raw) | |
| size = int(modified[124:136].rstrip(b"\0 "), 8) | |
| modified[512 + size] = 1 | |
| self.assert_rejected(gzip.compress(modified)) | |
| def test_file_count_limit(self): | |
| data = bundle(extras=[(f"package/f{index}", b"") for index in range(catalog.MAX_MEMBERS - 2)]) | |
| self.assertTrue(catalog.validate_archive(data)) | |
| self.assert_rejected(bundle(extras=[(f"package/f{index}", b"") for index in range(catalog.MAX_MEMBERS - 1)]), "4096") | |
| def test_file_and_manifest_size_limits(self): | |
| with patch.object(catalog, "MAX_FILE_BYTES", 1024): | |
| self.assert_rejected(bundle(extras=[("package/large", b"a" * 1025)]), "单文件") | |
| metadata = manifest() | |
| metadata["description"] = "a" * catalog.MAX_MANIFEST_BYTES | |
| self.assert_rejected(bundle(metadata), "1 MiB") | |
| def test_real_compression_bomb_is_rejected(self): | |
| data = gzip.compress(bytes(catalog.MAX_EXPANDED_BYTES + 1)) | |
| self.assertLess(len(data), 100000) | |
| self.assert_rejected(data, "64 MiB") | |
| def test_total_expansion_limit_counts_all_files(self): | |
| data = bundle(extras=[("package/a", b"a" * 4096), ("package/b", b"b" * 4096)]) | |
| with patch.object(catalog, "MAX_EXPANDED_BYTES", 8192): | |
| self.assert_rejected(data, "64 MiB") | |
| def test_lockfiles_manager_configs_and_bundled_modules_are_rejected(self): | |
| for name in (*catalog.FORBIDDEN_FILES, "node_modules/x/index.js", "nested/node_modules/x.js", "NODE_MODULES/a"): | |
| with self.subTest(name=name): | |
| self.assert_rejected(bundle(extras=[("package/" + name, b"{}")] )) | |
| def test_malformed_manifest_json(self): | |
| for value in (b"{", b"null", b"[]", b'"name"', b'\xff', b'{"name":"a","name":"b"}', b'{"x":NaN}'): | |
| with self.subTest(value=value): | |
| self.assert_rejected(archive([("package/package.json", value)])) | |
| def test_package_names_and_versions(self): | |
| for name in (None, 1, "", "../evil", "@scope", "A-name", ".hidden", "_private", "has space", "bad~name", "a" * 215, "node_modules", "favicon.ico", "@Scope/name"): | |
| with self.subTest(name=name): | |
| metadata = manifest() | |
| metadata["name"] = name | |
| self.assert_rejected(bundle(metadata), "包名") | |
| for version in (None, 1, "latest", "^1.0.0", "v1.0.0", "1.0", "1.01.0", "1.0.0-01", "1.0.0-", "1.0.0+", " 1.0.0", "1.0.0\n", "9007199254740992.0.0", "1.0.0+" + "a" * 256): | |
| with self.subTest(version=version): | |
| metadata = manifest() | |
| metadata["version"] = version | |
| self.assert_rejected(bundle(metadata), "semver") | |
| def test_non_string_description(self): | |
| metadata = manifest() | |
| metadata["description"] = {"untrusted": "value"} | |
| self.assert_rejected(bundle(metadata), "description") | |
| def test_all_install_publish_lifecycle_scripts_are_rejected(self): | |
| for script in catalog.LIFECYCLE_SCRIPTS: | |
| with self.subTest(script=script): | |
| metadata = manifest() | |
| metadata["scripts"][script] = "" | |
| self.assert_rejected(bundle(metadata), "生命周期") | |
| for scripts in ([], None, {"build": []}): | |
| with self.subTest(scripts=scripts): | |
| metadata = manifest() | |
| metadata["scripts"] = scripts | |
| self.assert_rejected(bundle(metadata), "scripts") | |
| def test_dependency_sources_ranges_and_bad_maps_are_rejected(self): | |
| specifiers = ( | |
| "^1.2.3", "~1.2.3", ">=1.0.0", "1", "1.2", "*", "latest", "1.2.3 || 2.0.0", | |
| "file:../package", "https://example.test/a.tgz", "git+https://example.test/a.git", | |
| "github:example/project", "user/repo", "workspace:*", "npm:other@1.2.3", "=1.2.3", None, | |
| ) | |
| for field in ("dependencies", "devDependencies", "optionalDependencies", "peerDependencies"): | |
| for value in specifiers: | |
| with self.subTest(field=field, specifier=value): | |
| metadata = manifest() | |
| metadata.setdefault(field, {})["example"] = value | |
| self.assert_rejected(bundle(metadata), "依赖") | |
| for dependencies in (None, [], "1.2.3"): | |
| with self.subTest(field=field, dependencies=dependencies): | |
| metadata = manifest() | |
| metadata[field] = dependencies | |
| self.assert_rejected(bundle(metadata), "依赖") | |
| def test_dependency_overrides_and_bundling_are_rejected(self): | |
| for field in ("overrides", "resolutions", "pnpm", "workspaces", "bundledDependencies", "bundleDependencies"): | |
| with self.subTest(field=field): | |
| metadata = manifest() | |
| metadata[field] = {} | |
| self.assert_rejected(bundle(metadata), "依赖") | |
| def test_core_peers_are_required_and_version_locked(self): | |
| for peers in ({}, {"@deepseek-ai/dsh-tools": "0.2.0"}, | |
| {"@deepseek-ai/dsh-tools": "0.2.0-rc.2", "@deepseek-ai/dsh-core": "0.1.0"}): | |
| with self.subTest(peers=peers): | |
| metadata = manifest() | |
| metadata["peerDependencies"] = peers | |
| self.assert_rejected(bundle(metadata)) | |
| def test_bundle_patch_must_be_present_and_safe(self): | |
| for value in (None, {}, [], {"bundle": {}}, {"bundle": []}): | |
| with self.subTest(dsh=value): | |
| metadata = manifest() | |
| metadata["dsh"] = value | |
| self.assert_rejected(bundle(metadata)) | |
| for value in (None, "", "../patch.yml", "/cordis.patch.yml", "././cordis.patch.yml", "a/../cordis.patch.yml", "a\\patch.yml", "missing.yml"): | |
| with self.subTest(patch=value): | |
| metadata = manifest() | |
| metadata["dsh"]["bundle"]["patch"] = value | |
| self.assert_rejected(bundle(metadata)) | |
| metadata = manifest() | |
| metadata["dsh"]["bundle"]["patch"] = "src" | |
| self.assert_rejected(bundle(metadata, extras=[("package/src", b"", tarfile.DIRTYPE)])) | |
| def test_unknown_manifest_versions_are_rejected(self): | |
| for value in (None, 0, 2, "1", True, 1.0, {}): | |
| with self.subTest(version=value): | |
| metadata = manifest() | |
| metadata["dsh"]["manifestVersion"] = value | |
| self.assert_rejected(bundle(metadata), "manifestVersion") | |
| def test_pax_traversal_duplicate_paths_and_size_override_are_rejected(self): | |
| def record(key, value): | |
| body = (" " + key + "=" + value + "\n").encode() | |
| size = len(body) + 1 | |
| while size != len(str(size)) + len(body): | |
| size = len(str(size)) + len(body) | |
| return str(size).encode() + body | |
| for payload in ( | |
| record("path", "../escape"), record("path", "/package/escape"), | |
| record("path", "package/a\\b"), record("path", "package/package.json"), | |
| record("size", "1000000000000"), record("GNU.sparse.realsize", "1000000000"), | |
| record("linkpath", "../escape"), record("path", "package/a") + record("path", "package/b"), | |
| b"invalid", b"0 x=y\n", b"999 path=package/a\n", b"9 path=a", | |
| b"9" * 5000 + b" path=a\n", | |
| ): | |
| with self.subTest(payload=payload[:80]): | |
| self.assert_rejected(bundle(extras=[ | |
| ("package/PaxHeader", payload, tarfile.XHDTYPE), | |
| ("package/innocent", b"abc"), | |
| ])) | |
| def test_dangling_chained_and_global_extension_headers_are_rejected(self): | |
| for extras in ( | |
| [("package/PaxHeader", b"", tarfile.XHDTYPE)], | |
| [("package/PaxHeader", b"", tarfile.XHDTYPE), ("package/SecondPax", b"", tarfile.XHDTYPE), ("package/a", b"")], | |
| [("package/GlobalPax", b"", tarfile.XGLTYPE)], | |
| [("././@LongLink", b"../escape\0", tarfile.GNUTYPE_LONGNAME), ("package/a", b"")], | |
| ): | |
| with self.subTest(extras=extras): | |
| self.assert_rejected(bundle(extras=extras)) | |
| def test_special_permissions_and_nonempty_directories_are_rejected(self): | |
| self.assert_rejected(bundle(extras=[("package/dir", b"not a directory", tarfile.DIRTYPE)])) | |
| raw = bytearray(gzip.decompress(bundle())) | |
| raw[100:108] = b"0004755\0" | |
| raw[148:156] = b" " | |
| raw[148:156] = f"{sum(raw[:512]):06o}\0 ".encode() | |
| self.assert_rejected(gzip.compress(raw), "特殊权限") | |
| def test_unbounded_json_numbers_and_deep_nesting_have_chinese_errors(self): | |
| for value in (b'{"number":' + b"9" * 5000 + b"}", b"[" * 2000 + b"]" * 2000): | |
| with self.subTest(prefix=value[:20]): | |
| self.assert_rejected(archive([("package/package.json", value)])) | |
| def test_path_lengths_and_depth_are_bounded(self): | |
| for name in ("package/" + "a/" * 1000 + "file", "package/" + "a" * 256, | |
| "package/" + "a" * 4097, "package/" + "深" * 100): | |
| with self.subTest(length=len(name)): | |
| self.assert_rejected(bundle(extras=[(name, b"test")])) | |
| if __name__ == "__main__": | |
| unittest.main() | |