File size: 4,641 Bytes
0568707
 
 
 
 
 
 
 
 
 
44553a9
0568707
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44553a9
 
 
 
 
 
 
 
 
 
 
 
 
0568707
 
 
 
 
 
 
 
dc5e1e2
 
c3a40c5
 
 
 
 
 
 
 
e579122
 
 
b5f7c2b
 
 
 
 
 
 
 
 
de7b65d
b5f7c2b
 
 
de7b65d
b5f7c2b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
from __future__ import annotations

import os
import subprocess
import tempfile
import unittest
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
INSTALLER = ROOT / "scripts" / "install.py"
UNINSTALLER = ROOT / "scripts" / "uninstall.py"


@unittest.skipUnless(os.name == "posix" and Path("/usr/bin/python3").is_file(), "Linux only")
class InstallerSecurityTests(unittest.TestCase):
    def test_documented_isolated_invocation_ignores_shadow_modules_and_startup_hooks(
        self,
    ) -> None:
        with tempfile.TemporaryDirectory() as raw:
            root = Path(raw)
            marker = root / "imported"
            (root / "argparse.py").write_text(
                f"open({str(marker)!r}, 'w').write('argparse')\nraise RuntimeError('shadowed')\n",
                encoding="utf-8",
            )
            (root / "sitecustomize.py").write_text(
                f"open({str(marker)!r}, 'w').write('sitecustomize')\n",
                encoding="utf-8",
            )
            environment = dict(os.environ)
            environment["PYTHONPATH"] = str(root)
            result = subprocess.run(
                ["/usr/bin/python3", "-I", "-S", str(INSTALLER)],
                cwd=root,
                env=environment,
                capture_output=True,
                text=True,
                check=False,
                timeout=15,
            )
            self.assertNotEqual(0, result.returncode)
            self.assertFalse(marker.exists())

    def test_symlinked_privileged_installer_is_rejected(self) -> None:
        with tempfile.TemporaryDirectory() as raw:
            link = Path(raw) / "install-link.py"
            link.symlink_to(INSTALLER)
            result = subprocess.run(
                ["/usr/bin/python3", "-I", "-S", str(link)],
                capture_output=True,
                text=True,
                check=False,
                timeout=15,
            )
            self.assertNotEqual(0, result.returncode)
            self.assertIn("symlinked privileged installer", result.stderr)

    def test_uninstaller_help_is_non_destructive(self) -> None:
        result = subprocess.run(
            ["/usr/bin/python3", "-I", "-S", str(UNINSTALLER), "--help"],
            capture_output=True,
            text=True,
            check=False,
            timeout=15,
        )
        self.assertEqual(0, result.returncode)
        self.assertIn("usage:", result.stdout)
        self.assertIn("manifest-verified", result.stdout)
        self.assertNotIn("UNINSTALLED", result.stdout)

    def test_privileged_scripts_require_isolated_no_site_python(self) -> None:
        for name in ("install.py", "uninstall.py", "verify_uninstalled.py"):
            value = (ROOT / "scripts" / name).read_text(encoding="utf-8")
            self.assertIn("flags.isolated", value)
            self.assertIn("flags.no_site", value)
        installer = INSTALLER.read_text(encoding="utf-8")
        self.assertIn("--expected-sha256", installer)
        self.assertIn("/proc/self/fd/{artifact_descriptor}", installer)
        self.assertIn("read_stable_regular", installer)
        self.assertIn("artifact_source_commit", installer)
        self.assertIn("INSTALL_JOURNAL_SCHEMA", installer)
        self.assertIn("recover_interrupted_install", installer)
        self.assertIn("acquire_lifecycle_lock", installer)
        self.assertIn("release_lifecycle_lock", installer)
        for name in ("upgrade.py", "uninstall.py"):
            lifecycle = (ROOT / "scripts" / name).read_text(encoding="utf-8")
            self.assertIn("acquire_lifecycle_lock", lifecycle)
            self.assertIn("release_lifecycle_lock", lifecycle)
        uninstaller = UNINSTALLER.read_text(encoding="utf-8")
        self.assertIn("UNINSTALL_JOURNAL_SCHEMA", uninstaller)
        self.assertIn("recover_uninstall", uninstaller)

    def test_install_tracks_cold_boot_network_namespace_runtime(self) -> None:
        installer = INSTALLER.read_text(encoding="utf-8")
        upgrader = (ROOT / "scripts" / "upgrade.py").read_text(encoding="utf-8")
        uninstaller = UNINSTALLER.read_text(encoding="utf-8")
        verifier = (ROOT / "scripts" / "verify_uninstalled.py").read_text(
            encoding="utf-8"
        )

        for value in (installer, uninstaller, verifier):
            self.assertIn("/etc/tmpfiles.d/lumi-eggcracker.conf", value)
        self.assertIn("d /run/netns 0755 root root -", installer)
        self.assertIn("ensure_netns_runtime()", installer)
        self.assertIn("installer.TMPFILES", upgrader)
        self.assertIn("installer.ensure_netns_runtime()", upgrader)