1- import { assert , assertEquals } from "@std/assert" ;
1+ import { assert , assertEquals , assertThrows } from "@std/assert" ;
22import * as path from "@std/path" ;
33import {
44 canReadPath ,
55 canRunCommand ,
66 canRunPath ,
7+ canWritePath ,
78 normalizePermissionDeclaration ,
89 normalizePermissionDeclarationToSet ,
10+ type PermissionDeclarationInput ,
911 resolveEffectivePermissions ,
1012} from "./permissions.ts" ;
1113
@@ -129,6 +131,48 @@ Deno.test("child-only inherited permissions use child baseDir for relative check
129131 ) ;
130132} ) ;
131133
134+ Deno . test ( "path grants cover descendant files within the directory tree" , ( ) => {
135+ const set = normalizePermissionDeclarationToSet (
136+ {
137+ read : [ "./shared" ] ,
138+ write : [ "./shared" , "./local.txt" ] ,
139+ } ,
140+ "/workspace/decks/root" ,
141+ ) ;
142+ assert ( set , "expected normalized permission set" ) ;
143+
144+ assertEquals (
145+ canReadPath ( set , "./shared/prompts/prompt.txt" ) ,
146+ true ,
147+ "read grants must apply to files beneath a declared directory" ,
148+ ) ;
149+ assertEquals (
150+ canReadPath ( set , "./shared" ) ,
151+ true ,
152+ "read grants must apply to the directory itself" ,
153+ ) ;
154+ assertEquals (
155+ canReadPath ( set , "./other/path.txt" ) ,
156+ false ,
157+ "read grants must not leak into sibling directories" ,
158+ ) ;
159+ assertEquals (
160+ canWritePath ( set , "./shared/prompts/prompt.txt" ) ,
161+ true ,
162+ "write grants must apply to files beneath a declared directory" ,
163+ ) ;
164+ assertEquals (
165+ canWritePath ( set , "./local.txt" ) ,
166+ true ,
167+ "write grants must still allow file-specific declarations" ,
168+ ) ;
169+ assertEquals (
170+ canWritePath ( set , "./local.txt.bak" ) ,
171+ false ,
172+ "write grants must not allow unrelated files" ,
173+ ) ;
174+ } ) ;
175+
132176Deno . test ( "run grants keep path vs command semantics separate" , ( ) => {
133177 const set = normalizePermissionDeclarationToSet (
134178 {
@@ -147,22 +191,33 @@ Deno.test("run grants keep path vs command semantics separate", () => {
147191 assertEquals ( canRunCommand ( set , "bin/tool" ) , false ) ;
148192} ) ;
149193
150- Deno . test ( "run object-form booleans honor all- access semantics " , ( ) => {
151- const pathsTrue = normalizePermissionDeclarationToSet (
152- { run : { paths : true } } ,
194+ Deno . test ( "run=true grants all run access" , ( ) => {
195+ const runAll = normalizePermissionDeclarationToSet (
196+ { run : true } ,
153197 "/workspace" ,
154198 ) ;
155- assert ( pathsTrue , "expected normalized permission set for paths=true" ) ;
156- assertEquals ( canRunPath ( pathsTrue , "/workspace/bin/anything" ) , true ) ;
157- assertEquals ( canRunCommand ( pathsTrue , "anything" ) , true ) ;
199+ assert ( runAll , "expected normalized permission set for run=true" ) ;
200+ assertEquals ( canRunPath ( runAll , "/workspace/bin/anything" ) , true ) ;
201+ assertEquals ( canRunCommand ( runAll , "anything" ) , true ) ;
202+ } ) ;
158203
159- const commandsTrue = normalizePermissionDeclarationToSet (
160- { run : { commands : true } } ,
161- "/workspace" ,
204+ Deno . test ( "run object-form booleans are rejected" , ( ) => {
205+ const invalidPaths = {
206+ run : { paths : true } ,
207+ } as unknown as PermissionDeclarationInput ;
208+ const invalidCommands = {
209+ run : { commands : false } ,
210+ } as unknown as PermissionDeclarationInput ;
211+ assertThrows (
212+ ( ) => normalizePermissionDeclarationToSet ( invalidPaths , "/workspace" ) ,
213+ Error ,
214+ "permissions.run.paths must be an array in object form" ,
215+ ) ;
216+ assertThrows (
217+ ( ) => normalizePermissionDeclarationToSet ( invalidCommands , "/workspace" ) ,
218+ Error ,
219+ "permissions.run.commands must be an array in object form" ,
162220 ) ;
163- assert ( commandsTrue , "expected normalized permission set for commands=true" ) ;
164- assertEquals ( canRunPath ( commandsTrue , "/workspace/bin/anything" ) , true ) ;
165- assertEquals ( canRunCommand ( commandsTrue , "anything" ) , true ) ;
166221} ) ;
167222
168223Deno . test ( "unspecified kinds deny by default when a layer is provided" , ( ) => {
0 commit comments