@@ -1274,6 +1274,65 @@ describe("exec approvals", () => {
12741274 }
12751275 } ) ;
12761276
1277+ it ( "requires approval for the legacy skill display prelude even when the wrapper is allowlisted" , async ( ) => {
1278+ if ( process . platform === "win32" ) {
1279+ return ;
1280+ }
1281+ const tempDir = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , "openclaw-skill-prelude-" ) ) ;
1282+ try {
1283+ const skillDir = path . join ( tempDir , ".openclaw" , "skills" , "gog" ) ;
1284+ const skillPath = path . join ( skillDir , "SKILL.md" ) ;
1285+ const binDir = path . join ( tempDir , "bin" ) ;
1286+ const wrapperPath = path . join ( binDir , "gog-wrapper" ) ;
1287+ await fs . mkdir ( skillDir , { recursive : true } ) ;
1288+ await fs . mkdir ( binDir , { recursive : true } ) ;
1289+ await fs . writeFile ( skillPath , "# gog skill\n" ) ;
1290+ await fs . writeFile ( wrapperPath , "#!/bin/sh\necho '{\"events\":[]}'\n" ) ;
1291+ await fs . chmod ( wrapperPath , 0o755 ) ;
1292+ const trustedWrapperPath = await fs . realpath ( wrapperPath ) ;
1293+
1294+ await writeExecApprovalsConfig ( {
1295+ version : 1 ,
1296+ defaults : { security : "allowlist" , ask : "on-miss" , askFallback : "deny" } ,
1297+ agents : {
1298+ main : {
1299+ allowlist : [ { pattern : trustedWrapperPath } ] ,
1300+ } ,
1301+ } ,
1302+ } ) ;
1303+
1304+ const calls : string [ ] = [ ] ;
1305+ vi . mocked ( callGatewayTool ) . mockImplementation ( async ( method , _opts , params ) => {
1306+ calls . push ( method ) ;
1307+ if ( method === "exec.approval.request" ) {
1308+ return acceptedApprovalResponse ( params ) ;
1309+ }
1310+ if ( method === "exec.approval.waitDecision" ) {
1311+ return { decision : "deny" } ;
1312+ }
1313+ return { ok : true } ;
1314+ } ) ;
1315+
1316+ const tool = createExecTool ( {
1317+ host : "gateway" ,
1318+ ask : "on-miss" ,
1319+ security : "allowlist" ,
1320+ approvalRunningNoticeMs : 0 ,
1321+ } ) ;
1322+
1323+ const command = `cat ${ JSON . stringify ( skillPath ) } && printf '\\n---CMD---\\n' && ${ JSON . stringify ( wrapperPath ) } calendar events primary --today --json` ;
1324+ const result = await tool . execute ( "call-skill-prelude" , {
1325+ command,
1326+ workdir : tempDir ,
1327+ } ) ;
1328+
1329+ expectPendingCommandText ( result , command ) ;
1330+ expect ( calls ) . toContain ( "exec.approval.request" ) ;
1331+ } finally {
1332+ await fs . rm ( tempDir , { recursive : true , force : true } ) ;
1333+ }
1334+ } ) ;
1335+
12771336 it ( "shows full chained node commands in approval-pending message" , async ( ) => {
12781337 const calls : string [ ] = [ ] ;
12791338 vi . mocked ( callGatewayTool ) . mockImplementation ( async ( method , _opts , params ) => {
0 commit comments