@@ -1197,6 +1197,215 @@ describe("Command Blocker", () => {
11971197 } ) ;
11981198 } ) ;
11991199
1200+ describe ( "checkSecretFileRead" , ( ) => {
1201+ let plugin : any ;
1202+ let mockApp : any ;
1203+ let mockClient : any ;
1204+ let mock$ : any ;
1205+
1206+ beforeEach ( async ( ) => {
1207+ mockApp = { } ;
1208+ mockClient = { } ;
1209+ mock$ = { } ;
1210+ plugin = await CommandBlocker ( {
1211+ app : mockApp ,
1212+ client : mockClient ,
1213+ $ : mock$ ,
1214+ } ) ;
1215+ } ) ;
1216+
1217+ it ( "should block reading .env files" , async ( ) => {
1218+ const input1 = { tool : "read" } ;
1219+ const output1 = { args : { filePath : ".env" } } ;
1220+ await expect (
1221+ plugin [ "tool.execute.before" ] ( input1 , output1 )
1222+ ) . rejects . toThrow (
1223+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1224+ ) ;
1225+
1226+ const input2 = { tool : "read" } ;
1227+ const output2 = { args : { filePath : ".envrc" } } ;
1228+ await expect (
1229+ plugin [ "tool.execute.before" ] ( input2 , output2 )
1230+ ) . rejects . toThrow (
1231+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1232+ ) ;
1233+
1234+ const input3 = { tool : "read" } ;
1235+ const output3 = { args : { filePath : ".env.local" } } ;
1236+ await expect (
1237+ plugin [ "tool.execute.before" ] ( input3 , output3 )
1238+ ) . rejects . toThrow (
1239+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1240+ ) ;
1241+ } ) ;
1242+
1243+ it ( "should block reading secrets files" , async ( ) => {
1244+ const input1 = { tool : "read" } ;
1245+ const output1 = { args : { filePath : "secrets.json" } } ;
1246+ await expect (
1247+ plugin [ "tool.execute.before" ] ( input1 , output1 )
1248+ ) . rejects . toThrow (
1249+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1250+ ) ;
1251+
1252+ const input2 = { tool : "read" } ;
1253+ const output2 = { args : { filePath : "config/secrets.json" } } ;
1254+ await expect (
1255+ plugin [ "tool.execute.before" ] ( input2 , output2 )
1256+ ) . rejects . toThrow (
1257+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1258+ ) ;
1259+
1260+ const input3 = { tool : "read" } ;
1261+ const output3 = { args : { filePath : ".secrets" } } ;
1262+ await expect (
1263+ plugin [ "tool.execute.before" ] ( input3 , output3 )
1264+ ) . rejects . toThrow (
1265+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1266+ ) ;
1267+ } ) ;
1268+
1269+ it ( "should block reading SSH key files" , async ( ) => {
1270+ const input1 = { tool : "read" } ;
1271+ const output1 = { args : { filePath : ".ssh/id_rsa" } } ;
1272+ await expect (
1273+ plugin [ "tool.execute.before" ] ( input1 , output1 )
1274+ ) . rejects . toThrow (
1275+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1276+ ) ;
1277+
1278+ const input2 = { tool : "read" } ;
1279+ const output2 = { args : { filePath : "id_rsa" } } ;
1280+ await expect (
1281+ plugin [ "tool.execute.before" ] ( input2 , output2 )
1282+ ) . rejects . toThrow (
1283+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1284+ ) ;
1285+ } ) ;
1286+
1287+ it ( "should block reading AWS credentials" , async ( ) => {
1288+ const input1 = { tool : "read" } ;
1289+ const output1 = { args : { filePath : ".aws/credentials" } } ;
1290+ await expect (
1291+ plugin [ "tool.execute.before" ] ( input1 , output1 )
1292+ ) . rejects . toThrow (
1293+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1294+ ) ;
1295+
1296+ const input2 = { tool : "read" } ;
1297+ const output2 = { args : { filePath : ".aws/config" } } ;
1298+ await expect (
1299+ plugin [ "tool.execute.before" ] ( input2 , output2 )
1300+ ) . rejects . toThrow (
1301+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1302+ ) ;
1303+ } ) ;
1304+
1305+ it ( "should allow reading other files" , async ( ) => {
1306+ const input1 = { tool : "read" } ;
1307+ const output1 = { args : { filePath : "package.json" } } ;
1308+ await expect ( async ( ) => {
1309+ await plugin [ "tool.execute.before" ] ( input1 , output1 ) ;
1310+ } ) . not . toThrow ( ) ;
1311+
1312+ const input2 = { tool : "read" } ;
1313+ const output2 = { args : { filePath : "src/main.ts" } } ;
1314+ await expect ( async ( ) => {
1315+ await plugin [ "tool.execute.before" ] ( input2 , output2 ) ;
1316+ } ) . not . toThrow ( ) ;
1317+
1318+ const input3 = { tool : "read" } ;
1319+ const output3 = { args : { filePath : "README.md" } } ;
1320+ await expect ( async ( ) => {
1321+ await plugin [ "tool.execute.before" ] ( input3 , output3 ) ;
1322+ } ) . not . toThrow ( ) ;
1323+ } ) ;
1324+
1325+ it ( "should handle empty file path" , async ( ) => {
1326+ const input1 = { tool : "read" } ;
1327+ const output1 = { args : { filePath : "" } } ;
1328+ await expect ( async ( ) => {
1329+ await plugin [ "tool.execute.before" ] ( input1 , output1 ) ;
1330+ } ) . not . toThrow ( ) ;
1331+
1332+ const input2 = { tool : "read" } ;
1333+ const output2 = { args : { filePath : undefined } } ;
1334+ await expect ( async ( ) => {
1335+ await plugin [ "tool.execute.before" ] ( input2 , output2 ) ;
1336+ } ) . not . toThrow ( ) ;
1337+ } ) ;
1338+
1339+ it ( "should handle file paths with query parameters and fragments" , async ( ) => {
1340+ const input1 = { tool : "read" } ;
1341+ const output1 = { args : { filePath : ".env?version=1" } } ;
1342+ await expect (
1343+ plugin [ "tool.execute.before" ] ( input1 , output1 )
1344+ ) . rejects . toThrow ( ) ;
1345+
1346+ const input2 = { tool : "read" } ;
1347+ const output2 = { args : { filePath : "secrets.json#section" } } ;
1348+ await expect (
1349+ plugin [ "tool.execute.before" ] ( input2 , output2 )
1350+ ) . rejects . toThrow ( ) ;
1351+ } ) ;
1352+
1353+ it ( "should block reading certificate and key files" , async ( ) => {
1354+ const inputs = [
1355+ { tool : "read" , args : { filePath : "certificate.pem" } } ,
1356+ { tool : "read" , args : { filePath : "private.key" } } ,
1357+ { tool : "read" , args : { filePath : "server.crt" } } ,
1358+ { tool : "read" , args : { filePath : "keystore.jks" } } ,
1359+ { tool : "read" , args : { filePath : "config/cert.p12" } } ,
1360+ ] ;
1361+
1362+ for ( const input of inputs ) {
1363+ await expect (
1364+ plugin [ "tool.execute.before" ] ( { tool : input . tool } , { args : input . args } )
1365+ ) . rejects . toThrow (
1366+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1367+ ) ;
1368+ }
1369+ } ) ;
1370+
1371+ it ( "should block reading authentication and token files" , async ( ) => {
1372+ const inputs = [
1373+ { tool : "read" , args : { filePath : "auth.json" } } ,
1374+ { tool : "read" , args : { filePath : "token.txt" } } ,
1375+ { tool : "read" , args : { filePath : "passwords.yml" } } ,
1376+ { tool : "read" , args : { filePath : ".npmrc" } } ,
1377+ { tool : "read" , args : { filePath : ".git-credentials" } } ,
1378+ { tool : "read" , args : { filePath : ".vault-token" } } ,
1379+ ] ;
1380+
1381+ for ( const input of inputs ) {
1382+ await expect (
1383+ plugin [ "tool.execute.before" ] ( { tool : input . tool } , { args : input . args } )
1384+ ) . rejects . toThrow (
1385+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1386+ ) ;
1387+ }
1388+ } ) ;
1389+
1390+ it ( "should block reading config files that may contain secrets" , async ( ) => {
1391+ const inputs = [
1392+ { tool : "read" , args : { filePath : "config.json" } } ,
1393+ { tool : "read" , args : { filePath : "settings.yml" } } ,
1394+ { tool : "read" , args : { filePath : ".kube/config" } } ,
1395+ { tool : "read" , args : { filePath : ".docker/config.json" } } ,
1396+ { tool : "read" , args : { filePath : ".terraformrc" } } ,
1397+ ] ;
1398+
1399+ for ( const input of inputs ) {
1400+ await expect (
1401+ plugin [ "tool.execute.before" ] ( { tool : input . tool } , { args : input . args } )
1402+ ) . rejects . toThrow (
1403+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1404+ ) ;
1405+ }
1406+ } ) ;
1407+ } ) ;
1408+
12001409 describe ( "checkTypeScriptAnyType" , ( ) => {
12011410 let plugin : any ;
12021411 let mockApp : any ;
@@ -1534,6 +1743,115 @@ describe("Command Blocker", () => {
15341743 } ) . not . toThrow ( ) ;
15351744 } ) ;
15361745
1746+ it ( "should block any shell commands that reference secret files" , async ( ) => {
1747+ const plugin = await CommandBlocker ( {
1748+ app : mockApp ,
1749+ client : mockClient ,
1750+ $ : mock$ ,
1751+ } ) ;
1752+
1753+ const secretFileCommands = [
1754+ { tool : "bash" , args : { command : "cat .envrc" } } ,
1755+ { tool : "bash" , args : { command : "less secrets.json" } } ,
1756+ { tool : "bash" , args : { command : "head .ssh/id_rsa" } } ,
1757+ { tool : "bash" , args : { command : "tail .aws/credentials" } } ,
1758+ { tool : "bash" , args : { command : "grep password config.json" } } ,
1759+ { tool : "bash" , args : { command : "vi .npmrc" } } ,
1760+ { tool : "bash" , args : { command : "vim .git-credentials" } } ,
1761+ { tool : "bash" , args : { command : "nano .vault-token" } } ,
1762+ { tool : "bash" , args : { command : "emacs .kube/config" } } ,
1763+ { tool : "bash" , args : { command : "view certificate.pem" } } ,
1764+ { tool : "bash" , args : { command : "hexdump private.key" } } ,
1765+ // Test non-file-reading commands that reference secret files
1766+ { tool : "bash" , args : { command : "cp .envrc backup.env" } } ,
1767+ { tool : "bash" , args : { command : "mv secrets.json secrets.bak" } } ,
1768+ { tool : "bash" , args : { command : "rm .ssh/id_rsa" } } ,
1769+ { tool : "bash" , args : { command : "chmod 600 .aws/credentials" } } ,
1770+ { tool : "bash" , args : { command : "chown user .npmrc" } } ,
1771+ { tool : "bash" , args : { command : "ls -la .git-credentials" } } ,
1772+ { tool : "bash" , args : { command : "touch .vault-token" } } ,
1773+ ] ;
1774+
1775+ for ( const input of secretFileCommands ) {
1776+ await expect (
1777+ plugin [ "tool.execute.before" ] ( input , { args : input . args } )
1778+ ) . rejects . toThrow (
1779+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1780+ ) ;
1781+ }
1782+ } ) ;
1783+
1784+ it ( "should block shell commands with secret files in complex structures" , async ( ) => {
1785+ const plugin = await CommandBlocker ( {
1786+ app : mockApp ,
1787+ client : mockClient ,
1788+ $ : mock$ ,
1789+ } ) ;
1790+
1791+ const complexCommands = [
1792+ { tool : "bash" , args : { command : 'cat $(echo ".envrc")' } } ,
1793+ { tool : "bash" , args : { command : 'less `echo secrets.json`' } } ,
1794+ { tool : "bash" , args : { command : 'head ".ssh/id_rsa"' } } ,
1795+ { tool : "bash" , args : { command : "grep token $HOME/.vault-token" } } ,
1796+ { tool : "bash" , args : { command : 'vi ".kube/config"' } } ,
1797+ ] ;
1798+
1799+ for ( const input of complexCommands ) {
1800+ await expect (
1801+ plugin [ "tool.execute.before" ] ( input , { args : input . args } )
1802+ ) . rejects . toThrow (
1803+ "`Reading secret files is blocked to prevent exposure of sensitive data including API keys, credentials, and configuration.`"
1804+ ) ;
1805+ }
1806+ } ) ;
1807+
1808+ it ( "should allow shell commands that don't access secret files" , async ( ) => {
1809+ const plugin = await CommandBlocker ( {
1810+ app : mockApp ,
1811+ client : mockClient ,
1812+ $ : mock$ ,
1813+ } ) ;
1814+
1815+ const safeCommands = [
1816+ { tool : "bash" , args : { command : "cat package.json" } } ,
1817+ { tool : "bash" , args : { command : "less README.md" } } ,
1818+ { tool : "bash" , args : { command : "head src/main.ts" } } ,
1819+ { tool : "bash" , args : { command : "tail .gitignore" } } ,
1820+ { tool : "bash" , args : { command : "grep function src/" } } ,
1821+ { tool : "bash" , args : { command : "vi tsconfig.json" } } ,
1822+ { tool : "bash" , args : { command : "ls -la" } } ,
1823+ { tool : "bash" , args : { command : "pwd" } } ,
1824+ { tool : "bash" , args : { command : "echo hello" } } ,
1825+ { tool : "bash" , args : { command : "mkdir .kube" } } , // Directory name, not a secret file
1826+ ] ;
1827+
1828+ for ( const input of safeCommands ) {
1829+ await expect ( async ( ) => {
1830+ await plugin [ "tool.execute.before" ] ( input , { args : input . args } ) ;
1831+ } ) . not . toThrow ( ) ;
1832+ }
1833+ } ) ;
1834+
1835+ it ( "should handle shell commands with flags correctly" , async ( ) => {
1836+ const plugin = await CommandBlocker ( {
1837+ app : mockApp ,
1838+ client : mockClient ,
1839+ $ : mock$ ,
1840+ } ) ;
1841+
1842+ // Should block even with flags
1843+ const blockedCommand = { tool : "bash" , args : { command : "cat -n .envrc" } } ;
1844+ await expect (
1845+ plugin [ "tool.execute.before" ] ( blockedCommand , { args : blockedCommand . args } )
1846+ ) . rejects . toThrow ( ) ;
1847+
1848+ // Should allow safe commands with flags
1849+ const safeCommand = { tool : "bash" , args : { command : "cat -n package.json" } } ;
1850+ await expect ( async ( ) => {
1851+ await plugin [ "tool.execute.before" ] ( safeCommand , { args : safeCommand . args } ) ;
1852+ } ) . not . toThrow ( ) ;
1853+ } ) ;
1854+
15371855 it ( "should allow safe bash commands with semicolons" , async ( ) => {
15381856 const plugin = await CommandBlocker ( {
15391857 app : mockApp ,
0 commit comments