mirror of
https://github.com/prompt-security/clawsec.git
synced 2026-06-13 05:28:02 +03:00
4dbac421ab
* feat(advisories): add provisional ghsa feed * fix(workflows): include advisory signatures in checksums * fix(workflows): mirror ghsa feed at release root * feat(advisories): consolidate ghsa into agent feed * ci(advisories): consolidate ghsa during nvd poll * fix(advisories): retain unreplaced ghsa feed entries * chore(skills): bump advisory feed consumers * fix(release): resolve ts import closure dry run * fix(release): preserve urls while stripping comments * fix(release): ignore skill test-only changes * fix(advisories): follow ghsa pagination links * test(advisories): add nvd ghsa pipeline dry run
112 lines
4.1 KiB
Python
112 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
def _load_module():
|
|
module_path = Path(__file__).with_name("verify_skill_release_import_closure.py")
|
|
spec = importlib.util.spec_from_file_location("verify_skill_release_import_closure", module_path)
|
|
if spec is None or spec.loader is None:
|
|
raise RuntimeError(f"Unable to load {module_path}")
|
|
module = importlib.util.module_from_spec(spec)
|
|
sys.modules[spec.name] = module
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
class VerifySkillReleaseImportClosureTests(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls) -> None:
|
|
cls.module = _load_module()
|
|
|
|
def test_empty_directory_does_not_satisfy_relative_import(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
root = Path(tmpdir)
|
|
(root / "runtime-lib").mkdir()
|
|
(root / "main.mjs").write_text("import './runtime-lib';\n", encoding="utf-8")
|
|
|
|
failures = self.module.verify_import_closure(root)
|
|
|
|
self.assertEqual(len(failures), 1)
|
|
self.assertIn("main.mjs imports ./runtime-lib", failures[0])
|
|
|
|
def test_directory_import_requires_index_file(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
root = Path(tmpdir)
|
|
runtime_lib = root / "runtime-lib"
|
|
runtime_lib.mkdir()
|
|
(runtime_lib / "index.mjs").write_text("export {};\n", encoding="utf-8")
|
|
(root / "main.mjs").write_text("import './runtime-lib';\n", encoding="utf-8")
|
|
|
|
failures = self.module.verify_import_closure(root)
|
|
|
|
self.assertEqual(failures, [])
|
|
|
|
def test_ts_source_accepts_js_import_specifier(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
root = Path(tmpdir)
|
|
(root / "types.ts").write_text("export type Value = string;\n", encoding="utf-8")
|
|
(root / "main.ts").write_text("import type { Value } from './types.js';\n", encoding="utf-8")
|
|
|
|
failures = self.module.verify_import_closure(root)
|
|
|
|
self.assertEqual(failures, [])
|
|
|
|
def test_comment_import_examples_are_ignored(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
root = Path(tmpdir)
|
|
(root / "main.ts").write_text(
|
|
"/*\n"
|
|
" * Example integration:\n"
|
|
" * import { Missing } from '../external/project/file';\n"
|
|
" */\n"
|
|
"export {};\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
failures = self.module.verify_import_closure(root)
|
|
|
|
self.assertEqual(failures, [])
|
|
|
|
def test_url_string_does_not_hide_following_relative_import(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
root = Path(tmpdir)
|
|
(root / "main.ts").write_text(
|
|
'const feedUrl = "https://example.test/feed.json"; import value from "./missing.js";\n',
|
|
encoding="utf-8",
|
|
)
|
|
|
|
failures = self.module.verify_import_closure(root)
|
|
|
|
self.assertEqual(len(failures), 1)
|
|
self.assertIn("main.ts imports ./missing.js", failures[0])
|
|
|
|
def test_remote_import_spec_survives_comment_stripping(self) -> None:
|
|
source = 'import remote from "https://example.test/module.mjs";\n'
|
|
stripped = self.module.strip_js_ts_comments(source)
|
|
|
|
specs = [match.group("spec") for match in self.module.IMPORT_RE.finditer(stripped)]
|
|
|
|
self.assertEqual(specs, ["https://example.test/module.mjs"])
|
|
|
|
def test_remote_runtime_import_is_rejected(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
root = Path(tmpdir)
|
|
(root / "main.mjs").write_text(
|
|
'import remote from "https://example.test/module.mjs";\n',
|
|
encoding="utf-8",
|
|
)
|
|
|
|
failures = self.module.verify_import_closure(root)
|
|
|
|
self.assertEqual(len(failures), 1)
|
|
self.assertIn("remote runtime import https://example.test/module.mjs", failures[0])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|