Compare commits

..

3 Commits

Author SHA1 Message Date
mae
d7193b6a5f update to java 24 2025-08-26 23:18:42 -05:00
f369e5f6c5
fix gitignore 2025-08-12 18:47:47 -05:00
d3da1c57f8
additional tests 2025-08-12 18:42:01 -05:00
9 changed files with 250 additions and 153 deletions

10
.gitignore vendored
View File

@ -1,14 +1,12 @@
.gradle .gradle
gradlew.bat
build/ build/
!gradle/wrapper/gradle-wrapper.jar !gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/ !**/src/main/**/build/
!**/src/test/**/build/ !**/src/test/**/build/
### IntelliJ IDEA ### ### IntelliJ IDEA ###
.idea/modules.xml /.idea/
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws *.iws
*.iml *.iml
*.ipr *.ipr
@ -42,6 +40,4 @@ bin/
.vscode/ .vscode/
### Mac OS ### ### Mac OS ###
.DS_Store .DS_Store
/app/src/test/resources/debian-images/
/app/src/test/resources/squashfs-root/

View File

@ -1,8 +0,0 @@
<component name="ArtifactManager">
<artifact type="jar" name="api-jvm">
<output-path>$PROJECT_DIR$/api/build/libs</output-path>
<root id="archive" name="api-jvm.jar">
<element id="module-output" name="planterette.api.jvmMain" />
</root>
</artifact>
</component>

12
.idea/kotlinc.xml generated
View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JsCompilerArguments">
<option name="moduleKind" value="plain" />
</component>
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="1.8" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="2.2.0" />
</component>
</project>

View File

@ -3,7 +3,7 @@ plugins {
} }
kotlin { kotlin {
jvmToolchain(21) jvmToolchain(24)
} }
dependencies { dependencies {

View File

@ -0,0 +1,6 @@
#Tue Aug 26 23:15:20 CDT 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -1,47 +1,48 @@
package app.hakurei.planterette.api package app.hakurei.planterette.api
@Suppress("unused")
object Paths { object Paths {
const val FHSRoot = "/" const val FHS_ROOT = "/"
const val FHSEtc = "/etc/" const val FHS_ETC = "/etc/"
const val FHSTmp = "/tmp/" const val FHS_TMP = "/tmp/"
const val FHSRun = "/run/" const val FHS_RUN = "/run/"
const val FHSRunUser = FHSRun + "user/" const val FHS_RUN_USER = FHS_RUN + "user/"
const val FHSUsr = "/usr/" const val FHS_USR = "/usr/"
const val FHSUsrBin = FHSUsr + "bin/" const val FHS_USR_BIN = FHS_USR + "bin/"
const val FHSVar = "/var/" const val FHS_VAR = "/var/"
const val FHSVarLib = FHSVar + "lib/" const val FHS_VAR_LIB = FHS_VAR + "lib/"
const val FHSVarEmpty = FHSVar + "empty/" const val FHS_VAR_EMPTY = FHS_VAR + "empty/"
const val FHSDev = "/dev/" const val FHS_DEV = "/dev/"
const val FHSProc = "/proc/" const val FHS_PROC = "/proc/"
const val FHSProcSys = FHSProc + "sys/" const val FHS_PROC_SYS = FHS_PROC + "sys/"
const val FHSSys = "/sys/" const val FHS_SYS = "/sys/"
const val Nonexistent = FHSProc + "nonexistent" const val NONEXISTENT = FHS_PROC + "nonexistent"
const val sysrootDir = "sysroot" const val SYSROOT_DIR = "sysroot"
const val hostDir = "host" const val HOST_DIR = "host"
const val hostPath = FHSRoot + hostDir const val HOST_PATH = FHS_ROOT + HOST_DIR
const val sysrootPath = FHSRoot + sysrootDir const val SYSROOT_PATH = FHS_ROOT + SYSROOT_DIR
val AbsFHSRoot = AbsolutePath(FHSRoot) val AbsFHSRoot = AbsolutePath(FHS_ROOT)
val AbsFHSEtc = AbsolutePath(FHSEtc) val AbsFHSEtc = AbsolutePath(FHS_ETC)
val AbsFHSTmp = AbsolutePath(FHSTmp) val AbsFHSTmp = AbsolutePath(FHS_TMP)
val AbsFHSRun = AbsolutePath(FHSRun) val AbsFHSRun = AbsolutePath(FHS_RUN)
val AbsFHSRunUser = AbsolutePath(FHSRunUser) val AbsFHSRunUser = AbsolutePath(FHS_RUN_USER)
val AbsFHSUsrBin = AbsolutePath(FHSUsrBin) val AbsFHSUsrBin = AbsolutePath(FHS_USR_BIN)
val AbsFHSVar = AbsolutePath(FHSVar) val AbsFHSVar = AbsolutePath(FHS_VAR)
val AbsFHSVarLib = AbsolutePath(FHSVarLib) val AbsFHSVarLib = AbsolutePath(FHS_VAR_LIB)
val AbsFHSDev = AbsolutePath(FHSDev) val AbsFHSDev = AbsolutePath(FHS_DEV)
val AbsFHSProc = AbsolutePath(FHSProc) val AbsFHSProc = AbsolutePath(FHS_PROC)
val AbsFHSSys = AbsolutePath(FHSSys) val AbsFHSSys = AbsolutePath(FHS_SYS)
val AbsNonexistent = AbsolutePath(Nonexistent) val AbsNonexistent = AbsolutePath(NONEXISTENT)
} }

View File

@ -1,20 +1,5 @@
package app.hakurei.planterette.api 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 <T> Array<out T>.orOf(selector: (T) -> Int): Int { inline fun <T> Array<out T>.orOf(selector: (T) -> Int): Int {
var sum = 0 var sum = 0
for (element in this) { for (element in this) {

View File

@ -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<AbsolutePath>(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")
}
}

View File

@ -1,11 +1,13 @@
package app.hakurei.planterette.api package app.hakurei.planterette.api
import app.hakurei.planterette.api.HakureiConfig.Enablement.* 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.ExperimentalSerializationApi
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -146,85 +148,186 @@ class HakureiConfigTest {
} }
} }
""".trimIndent() """.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 @Test
fun testHakureiConfigSerialization() { fun testHakureiConfigSerialization() {
val testConfig = HakureiConfig( val json = format.encodeToString(testConfig)
id = "org.chromium.Chromium", val test = format.decodeFromString<HakureiConfig>(json)
path = Paths.AbsFHSRun + "current-system/sw/bin/chromium", assertEquals(testConfig, test)
args = listOf( val want = format.decodeFromString<HakureiConfig>(want)
"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,
username = "chronos", compare(want, test)
shell = Paths.AbsFHSRun + "current-system/sw/bin/zsh", }
data = Paths.AbsFHSVarLib + "hakurei/u0/org.chromium.Chromium",
dir = AbsolutePath("/data/data/org.chromium.Chromium"), @Test
extraPerms = listOf( fun testEnablements() {
HakureiConfig.ExtraPermConfig(path = Paths.AbsFHSVarLib + "hakurei/u0", ensure = true, execute = true), val enablement = HakureiConfig.Enablement.enablements(Wayland, X11, DBus, Pulse)
HakureiConfig.ExtraPermConfig(path = Paths.AbsFHSVarLib + "hakurei/u0/org.chromium.Chromium", read = true, write = true, execute = true) assertEquals(enablement, 0b1111)
), }
identity = 9,
groups = listOf("video", "dialout", "plugdev"), @Test
container = HakureiConfig.ContainerConfig( fun testSeccompFilterPresets() {
hostname = "localhost", val presets = HakureiConfig.SeccompFilterPreset.filterPresets(Ext, DenyNS, DenyTTY, DenyDevel, Linux32)
devel = true, assertEquals(presets, 0b11111)
userns = true, }
net = true,
device = true, @Test
waitDelay = -1, fun testExportFlags() {
seccompFlags = HakureiConfig.HakureiExportFlag.exportFlags(HakureiConfig.HakureiExportFlag.Multiarch), val flags = HakureiConfig.HakureiExportFlag.exportFlags(Multiarch, CAN, Bluetooth)
seccompPresets = HakureiConfig.SeccompFilterPreset.filterPresets(HakureiConfig.SeccompFilterPreset.Ext), assertEquals(flags, 0b111)
seccompCompat = true, }
tty = true,
multiarch = true, @Test
mapRealUid = true, fun testRootFlags() {
env = mapOf( val flags = HakureiConfig.RootFlag.rootFlags(Optional, Writable, Device)
"GOOGLE_API_KEY" to "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY", assertEquals(flags, 0b111)
"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))
} }
} }