feat: add NetBox plugin store
CI / php-store (push) Waiting to run
CI / python-components (push) Waiting to run

This commit is contained in:
2026-08-24 20:51:25 +02:00
commit f36d6be511
135 changed files with 15160 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
from __future__ import annotations
import tempfile
import unittest
from pathlib import Path
from support import make_config, plan
from netbox_store_agent.errors import PolicyError
from netbox_store_agent.journal import ManagedPlugin
from netbox_store_agent.managed_files import ManagedFiles
class ManagedFilesTests(unittest.TestCase):
def setUp(self) -> None:
self.temporary = tempfile.TemporaryDirectory()
self.root = Path(self.temporary.name)
self.config = make_config(self.root, dry_run=False)
self.files = ManagedFiles(self.config)
release = plan()
self.plugin = ManagedPlugin(
"demo-plugin",
"demo-plugin",
"demo_plugin",
"1.2.3",
True,
(release.requirement(),),
)
def tearDown(self) -> None:
self.temporary.cleanup()
def test_include_exports_constant_without_referencing_plugins(self) -> None:
text = self.files.render_include([self.plugin]).decode()
self.assertIn('STORE_PLUGINS = [\n "demo_plugin"\n]', text)
self.assertNotIn("PLUGINS = list", text)
def test_atomic_write_backup_and_restore(self) -> None:
self.config.paths.include_path.write_text("old include", encoding="utf-8")
self.config.paths.requirements_path.write_text("old requirements", encoding="utf-8")
snapshot = self.files.write(
"20f4274f-d4e5-42bf-9164-967b1a774481", [self.plugin]
)
self.assertIn("STORE_PLUGINS", self.config.paths.include_path.read_text())
self.assertIn("--hash=sha256:", self.config.paths.requirements_path.read_text())
self.files.restore(snapshot)
self.assertEqual(self.config.paths.include_path.read_text(), "old include")
self.assertEqual(self.config.paths.requirements_path.read_text(), "old requirements")
def test_conflicting_locked_distribution_rejected(self) -> None:
other_requirement = {**self.plugin.requirements[0], "version": "2.0.0"}
other = ManagedPlugin(
"other", "demo-plugin", "other_plugin", "2.0.0", False, (other_requirement,)
)
with self.assertRaises(PolicyError):
self.files.render_requirements([self.plugin, other])
def test_requirement_host_must_be_allowlisted(self) -> None:
requirement = {**self.plugin.requirements[0], "download_url": "http://evil.test/x.whl"}
plugin = ManagedPlugin(
"demo-plugin", "demo-plugin", "demo_plugin", "1.2.3", False, (requirement,)
)
with self.assertRaises(Exception):
self.files.render_requirements([plugin])
def test_path_escape_rejected(self) -> None:
escaped = self.root / "outside.py"
changed = self.config.__class__(
self.config.agent,
self.config.store,
self.config.paths.__class__(
self.config.paths.allowed_root,
escaped,
self.config.paths.requirements_path,
self.config.paths.temp_dir,
),
self.config.commands,
self.config.policy,
)
with self.assertRaises(PolicyError):
ManagedFiles(changed).write(
"20f4274f-d4e5-42bf-9164-967b1a774481", [self.plugin]
)
if __name__ == "__main__":
unittest.main()