@@ -193,6 +193,12 @@ pub struct PolicyDocument {
193193 skip_serializing_if = "Option::is_none"
194194 ) ]
195195 pub process : Option < ProcessPolicy > ,
196+ #[ serde(
197+ default ,
198+ deserialize_with = "deserialize_non_null_optional_field" ,
199+ skip_serializing_if = "Option::is_none"
200+ ) ]
201+ pub ui : Option < UiPolicy > ,
196202 #[ serde( default , skip_serializing_if = "BTreeMap::is_empty" ) ]
197203 pub network_policies : BTreeMap < String , NetworkPolicyRule > ,
198204 #[ serde( default , skip_serializing_if = "BTreeMap::is_empty" ) ]
@@ -234,6 +240,34 @@ pub struct ProcessPolicy {
234240 pub run_as_group : String ,
235241}
236242
243+ #[ derive( Debug , Clone , Default , PartialEq , Eq , Serialize , Deserialize ) ]
244+ #[ serde( deny_unknown_fields) ]
245+ pub struct UiPolicy {
246+ #[ serde( default , skip_serializing_if = "std::ops::Not::not" ) ]
247+ pub allow_graphical_ui : bool ,
248+ #[ serde( default , skip_serializing_if = "UiClipboardAccess::is_none" ) ]
249+ pub clipboard : UiClipboardAccess ,
250+ #[ serde( default , skip_serializing_if = "std::ops::Not::not" ) ]
251+ pub allow_input_injection : bool ,
252+ }
253+
254+ #[ derive( Debug , Clone , Copy , Default , PartialEq , Eq , Serialize , Deserialize ) ]
255+ #[ serde( rename_all = "snake_case" ) ]
256+ pub enum UiClipboardAccess {
257+ #[ default]
258+ None ,
259+ Read ,
260+ Write ,
261+ All ,
262+ }
263+
264+ impl UiClipboardAccess {
265+ #[ must_use]
266+ pub const fn is_none ( & self ) -> bool {
267+ matches ! ( self , Self :: None )
268+ }
269+ }
270+
237271#[ derive( Debug , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
238272#[ serde( deny_unknown_fields) ]
239273pub struct NetworkPolicyRule {
@@ -698,6 +732,7 @@ fn inspect_document(root: &serde_yml::Value) -> InspectionResult {
698732 "filesystem_policy" ,
699733 "landlock" ,
700734 "process" ,
735+ "ui" ,
701736 "network_policies" ,
702737 "network_middlewares" ,
703738 ] ,
@@ -717,6 +752,11 @@ fn inspect_document(root: &serde_yml::Value) -> InspectionResult {
717752 "process" ,
718753 & [ "run_as_user" , "run_as_group" ] ,
719754 ) ?;
755+ inspect_named (
756+ root. get ( "ui" ) ,
757+ "ui" ,
758+ & [ "allow_graphical_ui" , "clipboard" , "allow_input_injection" ] ,
759+ ) ?;
720760
721761 for ( name, rule) in open_map ( root. get ( "network_policies" ) ) {
722762 let path = join ( "network_policies" , name) ;
@@ -1176,6 +1216,7 @@ mod tests {
11761216 for source in [
11771217 "version: 1\n filesystem_policy: null\n " ,
11781218 "version: 1\n process: null\n " ,
1219+ "version: 1\n ui: null\n " ,
11791220 "version: 1\n metadata: null\n " ,
11801221 "version: 1\n network_policies:\n x:\n endpoints:\n - host: x\n port: 443\n mcp: null\n " ,
11811222 ] {
@@ -1212,6 +1253,7 @@ mod tests {
12121253 "landlock.future" ,
12131254 ) ,
12141255 ( "version: 1\n process: { future: true }\n " , "process.future" ) ,
1256+ ( "version: 1\n ui: { future: true }\n " , "ui.future" ) ,
12151257 (
12161258 "version: 1\n network_policies: { api: { future: true } }\n " ,
12171259 "network_policies.api.future" ,
0 commit comments