diff --git a/PUBLIC_SOURCE_EXPORT.md b/PUBLIC_SOURCE_EXPORT.md index 9da9d1c..5595929 100644 --- a/PUBLIC_SOURCE_EXPORT.md +++ b/PUBLIC_SOURCE_EXPORT.md @@ -1,3 +1,3 @@ # Public source export -This source snapshot was generated from the private canonical repository at revision `034c9f844d2455850ba37b31379b7edaa786583e`. It contains no private Git history or operational evidence. Changes are published from the canonical repository. +This source snapshot was generated from the private canonical repository at revision `fe49858778940715742d53eea89241f01878f7ee`. It contains no private Git history or operational evidence. Changes are published from the canonical repository. diff --git a/PUBLIC_SOURCE_MANIFEST.json b/PUBLIC_SOURCE_MANIFEST.json index d26ce6e..1acb546 100644 --- a/PUBLIC_SOURCE_MANIFEST.json +++ b/PUBLIC_SOURCE_MANIFEST.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "sourceRevision": "034c9f844d2455850ba37b31379b7edaa786583e", + "sourceRevision": "fe49858778940715742d53eea89241f01878f7ee", "files": [ { "path": ".dockerignore", @@ -799,8 +799,8 @@ }, { "path": "backend/tests/test_appops_self_upgrade_controller.py", - "sha256": "3d367f02bf510a3d3495d0e105971408fa295a50ef5ec739386a6c4aaa80b23b", - "bytes": 18823 + "sha256": "ad6d8fba2213c1c8018bc9ff1c2ebd61684792c27fa00a07195c5d6aea99d69f", + "bytes": 20392 }, { "path": "backend/tests/test_authentik_connector.py", diff --git a/backend/tests/test_appops_self_upgrade_controller.py b/backend/tests/test_appops_self_upgrade_controller.py index df0e645..498299e 100644 --- a/backend/tests/test_appops_self_upgrade_controller.py +++ b/backend/tests/test_appops_self_upgrade_controller.py @@ -172,6 +172,44 @@ def test_runtime_requires_exact_container_mount_port_and_health(controller, monk assert controller._self_upgrade_runtime() is None +@pytest.mark.parametrize( + ("source", "accepted"), + [ + ("jens/unraid-appops-gateway", True), + ("https://gitea.example.com/Jens/Unraid-AppOps-Gateway", True), + ("other/repository", False), + ("https://evil.example/Jens/Unraid-AppOps-Gateway", False), + ("https://gitea.example.com/Jens/Other-App", False), + ("https://gitea.example.com/Jens/Unraid-AppOps-Gateway.evil", False), + ("http://gitea.example.com/Jens/Unraid-AppOps-Gateway", False), + ], +) +def test_runtime_accepts_only_fixed_appops_oci_sources(controller, monkeypatch, source, accepted): + monkeypatch.setattr( + controller, + "_gitea_validation_settings", + lambda: ("https://gitea.example.com/api/v1", b"t" * 32), + ) + payload = [{ + "State": {"Running": True, "Health": {"Status": "healthy"}}, + "Mounts": [{"Destination": "/data", "Source": "/srv/example/appdata/unraid-appops-gateway-1216", "RW": True}], + "Config": {"Labels": { + "org.opencontainers.image.revision": "a" * 40, + "org.opencontainers.image.source": source, + }}, + "NetworkSettings": {"Ports": {"1216/tcp": [{"HostPort": "1216"}]}}, + "Image": "sha256:" + "b" * 64, + }] + + def fake_run(args, **kwargs): + stdout = json.dumps(payload) if args[1] == "inspect" else "Unraid-AppOps-Gateway" + return subprocess.CompletedProcess(args, 0, stdout, "") + + monkeypatch.setattr(controller.subprocess, "run", fake_run) + + assert (controller._self_upgrade_runtime() is not None) is accepted + + def test_mcp_self_upgrade_rejects_arbitrary_repository(monkeypatch): from backend.app import mcp_server