feat(workspace): WorkspaceResolver trust pipeline + allowed_workspace_roots
Resolve a client-supplied workspace dir safely: canonicalize via toRealPath, reject privileged locations, clamp to [tools] allowed_workspace_roots (permissive when unset, logged), fall back to the boot default on any rejection. PathJail made non-internal so the server can reuse its symlink-safe containment. (Axis 2 Phase A, task 4.)
This commit is contained in:
@@ -331,6 +331,7 @@ object ConfigLoader {
|
||||
.ifEmpty { ToolsConfig.DEFAULT_INTERPRETERS },
|
||||
networkAllowedHosts = asStringList(toolsSection["network_allowed_hosts"]),
|
||||
networkDeniedHosts = asStringList(toolsSection["network_denied_hosts"]),
|
||||
allowedWorkspaceRoots = asStringList(toolsSection["allowed_workspace_roots"]),
|
||||
)
|
||||
|
||||
val providers = providersList.mapNotNull { providerMap ->
|
||||
|
||||
@@ -61,6 +61,7 @@ data class ToolsConfig(
|
||||
val interpreterExecutables: List<String> = DEFAULT_INTERPRETERS,
|
||||
val networkAllowedHosts: List<String> = emptyList(),
|
||||
val networkDeniedHosts: List<String> = emptyList(),
|
||||
val allowedWorkspaceRoots: List<String> = emptyList(),
|
||||
) {
|
||||
companion object {
|
||||
val DEFAULT_PRIVILEGED_LOCATIONS: List<String> = listOf(
|
||||
|
||||
@@ -12,6 +12,7 @@ class ConfigLoaderTest {
|
||||
assertEquals("dark", config.tui.theme)
|
||||
assertEquals(5, config.tui.sessionListLimit)
|
||||
assertEquals("human", config.cli.defaultOutput)
|
||||
assertEquals(emptyList<String>(), config.tools.allowedWorkspaceRoots)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -398,4 +399,20 @@ class ConfigLoaderTest {
|
||||
assertEquals("/models/local.gguf", result.models[0].modelPath)
|
||||
assertEquals(8192, result.models[0].contextSize)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parseToml parses tools allowed_workspace_roots`() {
|
||||
val toml = """
|
||||
[tools]
|
||||
allowed_workspace_roots = ["/home/user/projects", "/tmp/work"]
|
||||
""".trimIndent()
|
||||
|
||||
val parseTomlMethod = ConfigLoader::class.java.getDeclaredMethod("parseToml", String::class.java)
|
||||
parseTomlMethod.isAccessible = true
|
||||
val result = parseTomlMethod.invoke(ConfigLoader, toml) as CorrexConfig
|
||||
|
||||
assertEquals(2, result.tools.allowedWorkspaceRoots.size)
|
||||
assertEquals("/home/user/projects", result.tools.allowedWorkspaceRoots[0])
|
||||
assertEquals("/tmp/work", result.tools.allowedWorkspaceRoots[1])
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user