11//! Contents of the files the daemon generates in a bot's workspace.
22
3- use std:: path:: Path ;
3+ use std:: path:: { Path , PathBuf } ;
44use std:: time:: Duration ;
55
66use botloft_core:: protocol:: Crew ;
@@ -15,14 +15,19 @@ use crate::paths::permission_rule_path;
1515const MCP_TIMEOUT_MARGIN : Duration = Duration :: from_secs ( 120 ) ;
1616
1717/// `.claude/settings.json` (spec 7.5): keeps the bot out of the daemon's
18- /// secrets, and the owner's own Claude Code memory out of the bot. Deny
19- /// rules apply even in a folder nobody trusted.
20- pub fn settings_json ( botloft_home : & Path , crew_dir : & Path ) -> Value {
18+ /// secrets, out of `fenced` (Botloft's data and the other crews' folders,
19+ /// see `fences`) and the owner's own Claude Code memory out of the bot.
20+ /// Deny rules apply even in a folder nobody trusted, and in every mode.
21+ pub fn settings_json ( botloft_home : & Path , crew_dir : & Path , fenced : & [ PathBuf ] ) -> Value {
2122 let secrets = permission_rule_path ( & botloft_home. join ( "secrets" ) ) ;
23+ let mut deny = vec ! [ format!( "Read({secrets}/**)" ) ] ;
24+ for folder in fenced {
25+ let folder = permission_rule_path ( folder) ;
26+ deny. push ( format ! ( "Read({folder}/**)" ) ) ;
27+ deny. push ( format ! ( "Edit({folder}/**)" ) ) ;
28+ }
2229 json ! ( {
23- "permissions" : {
24- "deny" : [ format!( "Read({secrets}/**)" ) ] ,
25- } ,
30+ "permissions" : { "deny" : deny } ,
2631 // Instruction files above the crew's folder are the owner's.
2732 "claudeMdExcludes" : memory:: excludes_above( crew_dir) ,
2833 // The bot's memory is the `CLAUDE.md` in its folder. Claude Code's
@@ -215,18 +220,28 @@ mod tests {
215220 use super :: * ;
216221
217222 #[ test]
218- fn settings_deny_the_secrets_folder_and_nothing_else ( ) {
223+ fn settings_deny_the_secrets_and_the_fenced_folders ( ) {
219224 let home = Path :: new ( r"C:\Users\ana\AppData\Local\Botloft" ) ;
220- let settings = settings_json ( home, Path :: new ( "/ws/site" ) ) ;
225+ let settings = settings_json ( home, Path :: new ( "/ws/site" ) , & [ ] ) ;
221226 assert_eq ! (
222227 settings[ "permissions" ] ,
223228 json!( { "deny" : [ "Read(//c/Users/ana/AppData/Local/Botloft/secrets/**)" ] } )
224229 ) ;
230+ let blog = PathBuf :: from ( "C:/Users/ana/Botloft/blog" ) ;
231+ let settings = settings_json ( home, Path :: new ( "/ws/site" ) , & [ blog] ) ;
232+ assert_eq ! (
233+ settings[ "permissions" ] [ "deny" ] ,
234+ json!( [
235+ "Read(//c/Users/ana/AppData/Local/Botloft/secrets/**)" ,
236+ "Read(//c/Users/ana/Botloft/blog/**)" ,
237+ "Edit(//c/Users/ana/Botloft/blog/**)" ,
238+ ] )
239+ ) ;
225240 }
226241
227242 #[ test]
228243 fn settings_keep_the_owners_memory_out_of_the_bot ( ) {
229- let settings = settings_json ( Path :: new ( "/data" ) , Path :: new ( "/ws/site" ) ) ;
244+ let settings = settings_json ( Path :: new ( "/data" ) , Path :: new ( "/ws/site" ) , & [ ] ) ;
230245 assert_eq ! (
231246 settings[ "claudeMdExcludes" ] ,
232247 json!( memory:: excludes_above( Path :: new( "/ws/site" ) ) )
0 commit comments