fix: use AbsolutePathBuf for permission profile file roots (#12970)

## Why
`PermissionProfile` should describe filesystem roots as absolute paths
at the type level. Using `PathBuf` in `FileSystemPermissions` made the
shared type too permissive and blurred together three different
deserialization cases:

- skill metadata in `agents/openai.yaml`, where relative paths should
resolve against the skill directory
- app-server API payloads, where callers should have to send absolute
paths
- local tool-call payloads for commands like `shell_command` and
`exec_command`, where `additional_permissions.file_system` may
legitimately be relative to the command `workdir`

This change tightens the shared model without regressing the existing
local command flow.

## What Changed
- changed `protocol::models::FileSystemPermissions` and the app-server
`AdditionalFileSystemPermissions` mirror to use `AbsolutePathBuf`
- wrapped skill metadata deserialization in `AbsolutePathBufGuard`, so
relative permission roots in `agents/openai.yaml` resolve against the
containing skill directory
- kept app-server/API deserialization strict, so relative
`additionalPermissions.fileSystem.*` paths are rejected at the boundary
- restored cwd/workdir-relative deserialization for local tool-call
payloads by parsing `shell`, `shell_command`, and `exec_command`
arguments under an `AbsolutePathBufGuard` rooted at the resolved command
working directory
- simplified runtime additional-permission normalization so it only
canonicalizes and deduplicates absolute roots instead of trying to
recover relative ones later
- updated the app-server schema fixtures, `app-server/README.md`, and
the affected transport/TUI tests to match the final behavior
This commit is contained in:
Michael Bolin
2026-02-27 09:42:52 -08:00
committed by GitHub
parent 8cf5b00aef
commit d09a7535ed
22 changed files with 384 additions and 191 deletions

View File

@@ -1,11 +1,15 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AbsolutePathBuf": {
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
"type": "string"
},
"AdditionalFileSystemPermissions": {
"properties": {
"read": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": [
"array",
@@ -14,7 +18,7 @@
},
"write": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": [
"array",

View File

@@ -3411,7 +3411,7 @@
"properties": {
"read": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": [
"array",
@@ -3420,7 +3420,7 @@
},
"write": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": [
"array",

View File

@@ -1,11 +1,15 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AbsolutePathBuf": {
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
"type": "string"
},
"AdditionalFileSystemPermissions": {
"properties": {
"read": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": [
"array",
@@ -14,7 +18,7 @@
},
"write": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": [
"array",

View File

@@ -5,7 +5,7 @@
"properties": {
"read": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": [
"array",
@@ -14,7 +14,7 @@
},
"write": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": [
"array",
@@ -4722,7 +4722,7 @@
"properties": {
"read": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": [
"array",
@@ -4731,7 +4731,7 @@
},
"write": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": [
"array",