diff --git a/.gitignore b/.gitignore index 59c9a17..3089b44 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,7 @@ build/ !**/src/test/**/build/ ### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ +.idea/* *.iws *.iml *.ipr diff --git a/libplt/src/commonMain/kotlin/app/hakurei/planterette/api/Paths.kt b/libplt/src/commonMain/kotlin/app/hakurei/planterette/api/Paths.kt index ee6dad8..b814817 100644 --- a/libplt/src/commonMain/kotlin/app/hakurei/planterette/api/Paths.kt +++ b/libplt/src/commonMain/kotlin/app/hakurei/planterette/api/Paths.kt @@ -1,47 +1,48 @@ package app.hakurei.planterette.api +@Suppress("unused") object Paths { - const val FHSRoot = "/" - const val FHSEtc = "/etc/" - const val FHSTmp = "/tmp/" + const val FHS_ROOT = "/" + const val FHS_ETC = "/etc/" + const val FHS_TMP = "/tmp/" - const val FHSRun = "/run/" - const val FHSRunUser = FHSRun + "user/" + const val FHS_RUN = "/run/" + const val FHS_RUN_USER = FHS_RUN + "user/" - const val FHSUsr = "/usr/" - const val FHSUsrBin = FHSUsr + "bin/" + const val FHS_USR = "/usr/" + const val FHS_USR_BIN = FHS_USR + "bin/" - const val FHSVar = "/var/" - const val FHSVarLib = FHSVar + "lib/" - const val FHSVarEmpty = FHSVar + "empty/" + const val FHS_VAR = "/var/" + const val FHS_VAR_LIB = FHS_VAR + "lib/" + const val FHS_VAR_EMPTY = FHS_VAR + "empty/" - const val FHSDev = "/dev/" - const val FHSProc = "/proc/" - const val FHSProcSys = FHSProc + "sys/" - const val FHSSys = "/sys/" + const val FHS_DEV = "/dev/" + const val FHS_PROC = "/proc/" + const val FHS_PROC_SYS = FHS_PROC + "sys/" + const val FHS_SYS = "/sys/" - const val Nonexistent = FHSProc + "nonexistent" - const val sysrootDir = "sysroot" - const val hostDir = "host" - const val hostPath = FHSRoot + hostDir - const val sysrootPath = FHSRoot + sysrootDir + const val NONEXISTENT = FHS_PROC + "nonexistent" + const val SYSROOT_DIR = "sysroot" + const val HOST_DIR = "host" + const val HOST_PATH = FHS_ROOT + HOST_DIR + const val SYSROOT_PATH = FHS_ROOT + SYSROOT_DIR - val AbsFHSRoot = AbsolutePath(FHSRoot) - val AbsFHSEtc = AbsolutePath(FHSEtc) - val AbsFHSTmp = AbsolutePath(FHSTmp) + val AbsFHSRoot = AbsolutePath(FHS_ROOT) + val AbsFHSEtc = AbsolutePath(FHS_ETC) + val AbsFHSTmp = AbsolutePath(FHS_TMP) - val AbsFHSRun = AbsolutePath(FHSRun) - val AbsFHSRunUser = AbsolutePath(FHSRunUser) + val AbsFHSRun = AbsolutePath(FHS_RUN) + val AbsFHSRunUser = AbsolutePath(FHS_RUN_USER) - val AbsFHSUsrBin = AbsolutePath(FHSUsrBin) + val AbsFHSUsrBin = AbsolutePath(FHS_USR_BIN) - val AbsFHSVar = AbsolutePath(FHSVar) - val AbsFHSVarLib = AbsolutePath(FHSVarLib) + val AbsFHSVar = AbsolutePath(FHS_VAR) + val AbsFHSVarLib = AbsolutePath(FHS_VAR_LIB) - val AbsFHSDev = AbsolutePath(FHSDev) - val AbsFHSProc = AbsolutePath(FHSProc) - val AbsFHSSys = AbsolutePath(FHSSys) + val AbsFHSDev = AbsolutePath(FHS_DEV) + val AbsFHSProc = AbsolutePath(FHS_PROC) + val AbsFHSSys = AbsolutePath(FHS_SYS) - val AbsNonexistent = AbsolutePath(Nonexistent) + val AbsNonexistent = AbsolutePath(NONEXISTENT) } diff --git a/libplt/src/commonMain/kotlin/app/hakurei/planterette/api/Util.kt b/libplt/src/commonMain/kotlin/app/hakurei/planterette/api/Util.kt index b5c7bd5..69d00ca 100644 --- a/libplt/src/commonMain/kotlin/app/hakurei/planterette/api/Util.kt +++ b/libplt/src/commonMain/kotlin/app/hakurei/planterette/api/Util.kt @@ -1,20 +1,5 @@ package app.hakurei.planterette.api -import kotlinx.io.Buffer -import kotlinx.io.buffered -import kotlinx.io.files.Path -import kotlinx.io.files.SystemFileSystem -import kotlinx.io.readString - -object Util { - fun readFileToString(path: String): String { - val buffer = Buffer() - val source = SystemFileSystem.source(Path(path)) - source.buffered().transferTo(buffer) - return buffer.readString() - } -} - inline fun Array.orOf(selector: (T) -> Int): Int { var sum = 0 for (element in this) { diff --git a/libplt/src/commonTest/kotlin/app/hakurei/planterette/api/AbsolutePathTest.kt b/libplt/src/commonTest/kotlin/app/hakurei/planterette/api/AbsolutePathTest.kt new file mode 100644 index 0000000..4081e6c --- /dev/null +++ b/libplt/src/commonTest/kotlin/app/hakurei/planterette/api/AbsolutePathTest.kt @@ -0,0 +1,26 @@ +package app.hakurei.planterette.api + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertIs + +class AbsolutePathTest { + @Test + fun testAbsolutePath() { + val path = AbsolutePath("/dev/null") + assertIs(path) + } + @Test + fun testRelativePath() { + assertFailsWith(AbsolutePathException::class) { + AbsolutePath("./test") + } + } + @Test + fun testPathJoin() { + val want = AbsolutePath("/dev/null") + val base = AbsolutePath("/dev/") + assertEquals(want, base + "null") + } +} \ No newline at end of file diff --git a/libplt/src/commonTest/kotlin/app/hakurei/planterette/api/HakureiConfigTest.kt b/libplt/src/commonTest/kotlin/app/hakurei/planterette/api/HakureiConfigTest.kt index d5264ed..a1f39fb 100644 --- a/libplt/src/commonTest/kotlin/app/hakurei/planterette/api/HakureiConfigTest.kt +++ b/libplt/src/commonTest/kotlin/app/hakurei/planterette/api/HakureiConfigTest.kt @@ -1,11 +1,13 @@ package app.hakurei.planterette.api import app.hakurei.planterette.api.HakureiConfig.Enablement.* -import app.hakurei.planterette.api.HakureiConfig.RootFlag +import app.hakurei.planterette.api.HakureiConfig.SeccompFilterPreset.* +import app.hakurei.planterette.api.HakureiConfig.HakureiExportFlag.* +import app.hakurei.planterette.api.HakureiConfig.RootFlag.* + import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import kotlinx.serialization.serializer import kotlin.test.Test import kotlin.test.assertEquals @@ -146,85 +148,186 @@ class HakureiConfigTest { } } """.trimIndent() + val testConfig = HakureiConfig( + id = "org.chromium.Chromium", + path = Paths.AbsFHSRun + "current-system/sw/bin/chromium", + args = listOf( + "chromium", + "--ignore-gpu-blocklist", + "--disable-smooth-scrolling", + "--enable-features=UseOzonePlatform", + "--ozone-platform=wayland", + ), + enablements = HakureiConfig.Enablement.enablements(Wayland, DBus, Pulse), + sessionBus = HakureiConfig.DbusConfig( + talk = listOf( + "org.freedesktop.Notifications", + "org.freedesktop.FileManager1", + "org.freedesktop.ScreenSaver", + "org.freedesktop.secrets", + "org.kde.kwalletd5", + "org.kde.kwalletd6", + "org.gnome.SessionManager" + ), + own = listOf( + "org.chromium.Chromium.*", + "org.mpris.MediaPlayer2.org.chromium.Chromium.*", + "org.mpris.MediaPlayer2.chromium.*" + ), + call = mapOf("org.freedesktop.portal.*" to "*"), + broadcast = mapOf("org.freedesktop.portal.*" to "@/org/freedesktop/portal/*"), + log = false, + filter = true, + ), + systemBus = HakureiConfig.DbusConfig( + talk = listOf("org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"), + log = false, + filter = true, + ), + directWayland = false, + + username = "chronos", + shell = Paths.AbsFHSRun + "current-system/sw/bin/zsh", + data = Paths.AbsFHSVarLib + "hakurei/u0/org.chromium.Chromium", + dir = AbsolutePath("/data/data/org.chromium.Chromium"), + extraPerms = listOf( + HakureiConfig.ExtraPermConfig(path = Paths.AbsFHSVarLib + "hakurei/u0", ensure = true, execute = true), + HakureiConfig.ExtraPermConfig(path = Paths.AbsFHSVarLib + "hakurei/u0/org.chromium.Chromium", read = true, write = true, execute = true) + ), + identity = 9, + groups = listOf("video", "dialout", "plugdev"), + container = HakureiConfig.ContainerConfig( + hostname = "localhost", + devel = true, + userns = true, + net = true, + device = true, + waitDelay = -1, + seccompFlags = HakureiConfig.HakureiExportFlag.exportFlags(Multiarch), + seccompPresets = HakureiConfig.SeccompFilterPreset.filterPresets(Ext), + seccompCompat = true, + tty = true, + multiarch = true, + mapRealUid = true, + env = mapOf( + "GOOGLE_API_KEY" to "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY", + "GOOGLE_DEFAULT_CLIENT_ID" to "77185425430.apps.googleusercontent.com", + "GOOGLE_DEFAULT_CLIENT_SECRET" to "OTJgUOQcT7lO7GsGZq2G4IlT" + ), + filesystem = listOf( + HakureiConfig.FilesystemConfig(dst = Paths.AbsFHSTmp, src = Paths.AbsNonexistent, write = true), + HakureiConfig.FilesystemConfig(src = AbsolutePath("/nix/store")), + HakureiConfig.FilesystemConfig(src = Paths.AbsFHSRun + "current-system"), + HakureiConfig.FilesystemConfig(src = Paths.AbsFHSRun + "opengl-driver"), + HakureiConfig.FilesystemConfig(src = Paths.AbsFHSVar + "db/nix-channels"), + HakureiConfig.FilesystemConfig(src = Paths.AbsFHSVarLib + "hakurei/u0/org.chromium.Chromium", dst = AbsolutePath("/data/data/org.chromium.Chromium"), write = true, must = true), + HakureiConfig.FilesystemConfig(src = Paths.AbsFHSDev + "dri", device = true) + ), + link = listOf(HakureiConfig.LinkConfig(Paths.AbsFHSRunUser + "65534", Paths.FHS_RUN_USER + "150")), + autoRoot = Paths.AbsFHSVarLib + "hakurei/base/org.debian", + rootFlags = HakureiConfig.RootFlag.rootFlags(Writable), + etc = Paths.AbsFHSEtc, + autoEtc = true + ) + ) + + internal fun compare(want: HakureiConfig, test: HakureiConfig) { + assertEquals(want.id, test.id, "id") + + assertEquals(want.path, test.path, "path") + assertEquals(want.args, test.args, "args") + + assertEquals(want.enablements, test.enablements, "enablements") + + assertEquals(want.sessionBus?.see, test.sessionBus?.see, "sessionBus.see") + assertEquals(want.sessionBus?.talk, test.sessionBus?.talk, "sessionBus.talk") + assertEquals(want.sessionBus?.own, test.sessionBus?.own, "sessionBus.own") + assertEquals(want.sessionBus?.broadcast, test.sessionBus?.broadcast, "sessionBus.broadcast") + assertEquals(want.sessionBus?.log ?: false, test.sessionBus?.log, "sessionBus.log") + assertEquals(want.sessionBus?.filter, test.sessionBus?.filter, "sessionBus.filter") + + assertEquals(want.systemBus?.see, test.systemBus?.see, "systemBus.see") + assertEquals(want.systemBus?.talk, test.systemBus?.talk, "systemBus.talk") + assertEquals(want.systemBus?.own, test.systemBus?.own, "systemBus.own") + assertEquals(want.systemBus?.broadcast, test.systemBus?.broadcast, "systemBus.broadcast") + assertEquals(want.systemBus?.log ?: false, test.systemBus?.log, "systemBus.log") + assertEquals(want.systemBus?.filter, test.systemBus?.filter, "systemBus.filter") + + assertEquals(want.directWayland ?: false, test.directWayland, "directWayland") + assertEquals(want.username, test.username, "username") + assertEquals(want.shell, test.shell, "shell") + assertEquals(want.data, test.data, "data") + assertEquals(want.dir, test.dir, "dir") + assertEquals(want.extraPerms, test.extraPerms, "extraPerms") + + assertEquals(want.identity, test.identity, "identity") + assertEquals(want.groups, test.groups, "groups") + + assertEquals(want.container.waitDelay, test.container.waitDelay, "waitDelay") + assertEquals(want.container.seccompFlags, test.container.seccompFlags, "seccompFlags") + assertEquals(want.container.seccompPresets, test.container.seccompPresets, "seccompPresets") + assertEquals(want.container.seccompCompat, test.container.seccompCompat, "seccompCompat") + + assertEquals(want.container.devel, test.container.devel, "devel") + assertEquals(want.container.userns, test.container.userns, "userns") + assertEquals(want.container.net, test.container.net, "net") + assertEquals(want.container.tty, test.container.tty, "tty") + assertEquals(want.container.multiarch, test.container.multiarch, "multiarch") + + assertEquals(want.container.env, test.container.env, "env") + assertEquals(want.container.mapRealUid, test.container.mapRealUid, "mapRealUid") + + assertEquals(want.container.device, test.container.device, "device") + assertEquals(want.container.filesystem.size, test.container.filesystem.size, "filesystem.size") + for(i in 0 until want.container.filesystem.size) { + val w = want.container.filesystem[i] + val t = test.container.filesystem[i] + + assertEquals(w.src, t.src, "filesystem[$i].src") + assertEquals(w.dst, t.dst, "filesystem[$i].dst") + assertEquals(w.write, t.write, "filesystem[$i].write") + assertEquals(w.device, t.device, "filesystem[$i].device") + assertEquals(w.must, t.must, "filesystem[$i].must") + } + assertEquals(want.container.link, test.container.link, "link") + assertEquals(want.container.autoRoot, test.container.autoRoot, "autoroot") + + assertEquals(want.container.etc, test.container.etc, "etc") + + assertEquals(want.container.autoEtc, test.container.autoEtc, "autoEtc") + } + @Test fun testHakureiConfigSerialization() { - val testConfig = HakureiConfig( - id = "org.chromium.Chromium", - path = Paths.AbsFHSRun + "current-system/sw/bin/chromium", - args = listOf( - "chromium", - "--ignore-gpu-blocklist", - "--disable-smooth-scrolling", - "--enable-features=UseOzonePlatform", - "--ozone-platform=wayland", - ), - enablements = HakureiConfig.Enablement.enablements(Wayland, DBus, Pulse), - sessionBus = HakureiConfig.DbusConfig( - see = null, - talk = listOf("org.freedesktop.Notifications", "org.freedesktop.FileManager1", "org.freedesktop.ScreenSaver"), - own = listOf("org.chromium.Chromium.*", "org.mpris.MediaPlayer2.org.chromium.Chromium.*", - "org.mpris.MediaPlayer2.chromium.*"), - call = mapOf("org.freedesktop.portal.*" to "*"), - broadcast = mapOf("org.freedesktop.portal.*" to "@/org/freedesktop/portal/*"), - log = false, - filter = true, - ), - systemBus = HakureiConfig.DbusConfig( - see = null, - talk = listOf("org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"), - own = null, - call = null, - broadcast = null, - log = false, - filter = true, - ), - directWayland = false, + val json = format.encodeToString(testConfig) + val test = format.decodeFromString(json) + assertEquals(testConfig, test) + val want = format.decodeFromString(want) - username = "chronos", - shell = Paths.AbsFHSRun + "current-system/sw/bin/zsh", - data = Paths.AbsFHSVarLib + "hakurei/u0/org.chromium.Chromium", - dir = AbsolutePath("/data/data/org.chromium.Chromium"), - extraPerms = listOf( - HakureiConfig.ExtraPermConfig(path = Paths.AbsFHSVarLib + "hakurei/u0", ensure = true, execute = true), - HakureiConfig.ExtraPermConfig(path = Paths.AbsFHSVarLib + "hakurei/u0/org.chromium.Chromium", read = true, write = true, execute = true) - ), - identity = 9, - groups = listOf("video", "dialout", "plugdev"), - container = HakureiConfig.ContainerConfig( - hostname = "localhost", - devel = true, - userns = true, - net = true, - device = true, - waitDelay = -1, - seccompFlags = HakureiConfig.HakureiExportFlag.exportFlags(HakureiConfig.HakureiExportFlag.Multiarch), - seccompPresets = HakureiConfig.SeccompFilterPreset.filterPresets(HakureiConfig.SeccompFilterPreset.Ext), - seccompCompat = true, - tty = true, - multiarch = true, - mapRealUid = true, - env = mapOf( - "GOOGLE_API_KEY" to "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY", - "GOOGLE_DEFAULT_CLIENT_ID" to "77185425430.apps.googleusercontent.com", - "GOOGLE_DEFAULT_CLIENT_SECRET" to "OTJgUOQcT7lO7GsGZq2G4IlT" - ), - filesystem = listOf( - HakureiConfig.FilesystemConfig(dst = Paths.AbsFHSTmp, src = Paths.AbsNonexistent, write = true), - HakureiConfig.FilesystemConfig(src = AbsolutePath("/nix/store")), - HakureiConfig.FilesystemConfig(src = Paths.AbsFHSRun + "current-system"), - HakureiConfig.FilesystemConfig(src = Paths.AbsFHSRun + "opengl-driver"), - HakureiConfig.FilesystemConfig(src = Paths.AbsFHSVar + "db/nix-channels"), - HakureiConfig.FilesystemConfig(src = Paths.AbsFHSVarLib + "hakurei/u0/org.chromium.Chromium", dst = AbsolutePath("/data/data/org.chromium.Chromium"), write = true, must = true), - HakureiConfig.FilesystemConfig(src = Paths.AbsFHSDev + "dri", device = true) - ), - link = listOf(HakureiConfig.LinkConfig(Paths.AbsFHSRunUser + "65534", Paths.FHSRunUser + "150")), - autoRoot = Paths.AbsFHSVarLib + "hakurei/base/org.debian", - rootFlags = RootFlag.rootFlags(RootFlag.Writable), - etc = Paths.AbsFHSEtc, - autoEtc = true - ) - ) - println("printing for now until a more proper test can be written") - println(format.encodeToString(testConfig)) + compare(want, test) + } + + @Test + fun testEnablements() { + val enablement = HakureiConfig.Enablement.enablements(Wayland, X11, DBus, Pulse) + assertEquals(enablement, 0b1111) + } + + @Test + fun testSeccompFilterPresets() { + val presets = HakureiConfig.SeccompFilterPreset.filterPresets(Ext, DenyNS, DenyTTY, DenyDevel, Linux32) + assertEquals(presets, 0b11111) + } + + @Test + fun testExportFlags() { + val flags = HakureiConfig.HakureiExportFlag.exportFlags(Multiarch, CAN, Bluetooth) + assertEquals(flags, 0b111) + } + + @Test + fun testRootFlags() { + val flags = HakureiConfig.RootFlag.rootFlags(Optional, Writable, Device) + assertEquals(flags, 0b111) } } \ No newline at end of file