返回提交历史
Modified
etc/unittest/mcp.py
+21
-6
Modified
g4f/api/__init__.py
+3
-2
Modified
g4f/mcp/tools.py
+3
-4
XFEstudio/gpt4free
Show pa-providers dir in safe mode
8a86515b
代码差异
3 个文件
+27
-12
@@ -352,12 +352,27 @@ class TestSafeMode(unittest.IsolatedAsyncioTestCase):
352
352
self.assertFalse(result.get("success"))
353
353
self.assertIn("error", result)
354
354
355
async def test_file_list_safe_mode_blocks_root(self):
356
"""In safe mode, listing the workspace root is blocked."""
357
tool = FileListTool(safe_mode=True)
358
result = await tool.execute({})
359
self.assertIn("error", result)
360
self.assertIn("safe mode", result["error"])
355
async def test_file_list_safe_mode_shows_pa_providers_and_markdown(self):
356
"""In safe mode, the root exposes pa-providers and Markdown files only."""
357
workspace = get_workspace_dir()
358
providers = workspace / "pa-providers"
359
markdown = workspace / "unittest_safe_mode.md"
360
other = workspace / "unittest_safe_mode.txt"
361
providers.mkdir(exist_ok=True)
362
markdown.write_text("# test")
363
other.write_text("test")
364
try:
365
tool = FileListTool(safe_mode=True)
366
result = await tool.execute({})
367
self.assertNotIn("error", result)
368
self.assertEqual(
369
{entry["path"] for entry in result["entries"]},
370
{"pa-providers", "unittest_safe_mode.md"},
371
)
372
finally:
373
markdown.unlink(missing_ok=True)
374
other.unlink(missing_ok=True)
375
providers.rmdir()
361
376
362
377
async def test_file_list_safe_mode_allows_subdir(self):
363
378
"""In safe mode, listing a subdirectory is still allowed."""
@@ -263,6 +263,7 @@ function esc(s){return String(s??\'\'). replace(/&/g,\'&\').replace(/</g,\'&
263
263
function fmt(v){if(v==null)return\'(empty)\';if(typeof v===\'object\')return JSON.stringify(v,null,2);return String(v);}
264
264
async function load(){
265
265
try{var r=await fetch(\'/api/logs?limit=500\', { credentials: 'include' });if(!r.ok)return;var d=await r.json();all=d.entries||[];render();}catch(e){}
266
clearTimeout(timer);timer = setTimeout(load,3000);
266
267
}
267
268
function render(){
268
269
var q=document.getElementById(\'q\').value.trim().toLowerCase();
@@ -301,8 +302,8 @@ function closeModal(){document.getElementById(\'ov\').classList.remove(\'active\
301
302
function overlayClick(ev){if(ev.target===document.getElementById(\'ov\'))closeModal();}
302
303
document.addEventListener(\'keydown\',function(e){if(e.key===\'Escape\')closeModal();});
303
304
async function clearLogs(){await fetch(\'/api/logs\',{method:\'DELETE\'});all=[];render();}
304
function toggleAuto(){clearInterval(timer);timer=null;if(document.getElementById(\'auto\').checked)timer=setInterval(load,3000);}
305
load();timer=setInterval(load,3000);
305
function toggleAuto(){clearTimeout(timer);timer=null;if(document.getElementById(\'auto\').checked)timer=setTimeout(load,3000);}
306
load();if(document.getElementById(\'auto\').checked)timer=setTimeout(load,3000);
306
307
</script>
307
308
</body></html>"""
308
309
@@ -618,10 +618,6 @@ class FileListTool(MCPTool):
618
618
target = (workspace / rel_path).resolve() if rel_path else workspace
619
619
if not str(target).startswith(str(workspace)):
620
620
return {"error": "Access outside the workspace is not allowed"}
621
if self.safe_mode and target == workspace:
622
return {
623
"error": "Listing the workspace root directory is not allowed in safe mode"
624
}
625
621
if not target.exists():
626
622
return {"error": f"Directory not found: {rel_path or '/'}"}
627
623
if not target.is_dir():
@@ -635,6 +631,9 @@ class FileListTool(MCPTool):
635
631
rel = str(entry.relative_to(workspace))
636
632
if is_hidden_file(rel):
637
633
continue
634
if self.safe_mode and target == workspace:
635
if entry.name != "pa-providers" and (not entry.is_file() or entry.suffix.lower() != ".md"):
636
continue
638
637
info: Dict[str, Any] = {
639
638
"path": rel,
640
639
"type": "file" if entry.is_file() else "directory",