
DeepSeek Agent Harness 2026终极指南 - 第10章第48节 路径白名单防止Agent在你的磁盘上乱跑第47节的沙箱拦截了危险命令但有个漏洞——路径。Agent 用read_file(/etc/passwd)或write_file(../../etc/passwd)就能逃出工作区读你的系统文件、改你的关键配置。这节做路径白名单工作区根目录约束、路径穿越攻击../防御、符号链接逃逸检测。把 Agent 锁死在工作区里除了你给它划的地盘哪儿都不能去。本文导航路径逃逸沙箱被绕过的三种方式工作区根目录约束路径穿越攻击防御…/符号链接逃逸检测完整实现path_guard.py实测攻击与防御验证小结路径逃逸沙箱被绕过的三种方式第47节的沙箱用cwd限制工作目录但文件工具read_file/write_file/edit_file不受cwd约束——它们直接用Path(path)操作文件对路径没有限制。攻击者或失误的模型可以用三种方式逃出工作区方式1绝对路径read_file(/etc/passwd)# 读系统密码文件write_file(/root/.bashrc,...)# 改 root 配置方式2路径穿越../read_file(../../etc/passwd)# 用相对路径爬出工作区write_file(../../../root/.ssh/authorized_keys,...)# 植入后门方式3符号链接逃逸# 攻击者在工作区里创建符号链接指向系统文件ln-s/etc/passwd workspace/link read_file(workspace/link)# 通过符号链接读到系统文件这三种方式用第47节的沙箱都拦不住因为沙箱只管 bash 命令不管文件工具的路径。工作区根目录约束核心思想所有文件路径必须解析到工作区根目录之内。# deep_pilot/path_guard.py —— 路径白名单 v0.5from__future__importannotationsfrompathlibimportPathfromdeep_pilot.loggerimportget_logger loggerget_logger(__name__)classPathGuard:路径守卫——确保所有文件操作在工作区根目录内def__init__(self,workspace_root:str|Path.):# 工作区根目录解析为绝对路径self.workspace_rootPath(workspace_root).resolve()logger.info(f路径守卫初始化工作区:{self.workspace_root})defis_allowed(self,path:str|Path)-tuple[bool,str]: 检查路径是否在工作区内。 返回 (是否允许, 原因)。 try:# 解析为绝对路径这一步会消除 ../ 和 ./path_objPath(path)# 如果是相对路径基于工作区根目录解析ifnotpath_obj.is_absolute():resolved(self.workspace_root/path_obj).resolve()else:resolvedpath_obj.resolve()# 检查是否在工作区内ifstr(resolved).startswith(str(self.workspace_root)):returnTrue,else:logger.warning(f拦截路径逃逸:{path}-{resolved})returnFalse,f路径 {path} 越出工作区{self.workspace_root}exceptExceptionase:returnFalse,f路径解析失败:{str(e)}defsafe_resolve(self,path:str|Path)-Path:安全解析路径返回工作区内的绝对路径path_objPath(path)ifpath_obj.is_absolute():resolvedpath_obj.resolve()else:resolved(self.workspace_root/path_obj).resolve()returnresolved# 全局单例path_guardPathGuard()关键Path.resolve()是关键防线。resolve()会消除.和..解析符号链接返回规范化的绝对路径所以path_guard.is_allowed(../../etc/passwd)会先把../../etc/passwd解析成绝对路径再判断是否在工作区内。如果在工作区外直接拒绝。路径穿越攻击防御…/../是路径穿越的经典手段。resolve()能自动处理它# 工作区 /home/user/project# 攻击尝试../../etc/passwdpath../../etc/passwdresolved(Path(/home/user/project)/path).resolve()# resolved /etc/passwd (因为 ../../ 爬了两层)# 判断/etc/passwd 不在 /home/user/project 内 → 拒绝但有个陷阱resolve()在文件不存在时也能正常工作Python 3.6它会做词法解析不需要文件真实存在。所以即使/etc/passwd不存在也能正确解析出越界路径。验证多种穿越写法# 各种穿越写法都会被 resolve() 归一化../etc/passwd# → /home/user/etc/passwd 越界一层../../etc/passwd# → /etc/passwd 越界两层a/../../etc/passwd# → /etc/passwd a 又是 又被 .. 抵消.././../etc/passwd# → /etc/passwd . 和 .. 混合resolve()都能正确归一化然后统一判断。符号链接逃逸检测符号链接是更隐蔽的逃逸手段。攻击者在工作区里创建符号链接指向系统文件# 攻击者或失误的模型在工作区执行ln-s/etc/passwd workspace/secret# 然后读取read_file(workspace/secret)# 实际读到 /etc/passwdPath.resolve()默认会解析符号链接所以workspace/secret.resolve() 会得到/etc/passwd然后判断越界拒绝。# 验证符号链接逃逸# workspace/secret - /etc/passwdresolvedPath(workspace/secret).resolve()# resolved /etc/passwd 符号链接被解析# 判断/etc/passwd 不在工作区内 → 拒绝但要注意resolve()解析符号链接需要目标存在。如果符号链接指向一个不存在的文件resolve()可能不解析取决于 Python 版本和strict参数。为了更严格我们加一层显式的符号链接检测defcheck_symlink(self,path:str|Path)-tuple[bool,str]:检测路径是否包含符号链接逃逸resolvedself.safe_resolve(path)# 检查路径每一部分是否是符号链接currentself.workspace_rootforpartinresolved.relative_to(self.workspace_root).parts:currentcurrent/partifcurrent.is_symlink():# 符号链接指向外部targetcurrent.resolve()ifnotstr(target).startswith(str(self.workspace_root)):returnFalse,f符号链接{current}指向工作区外部{target}returnTrue,完整实现path_guard.py把以上逻辑整合并接入文件工具# deep_pilot/path_guard.py —— 完整版from__future__importannotationsfrompathlibimportPathfromdeep_pilot.loggerimportget_logger loggerget_logger(__name__)classPathGuard:def__init__(self,workspace_root:str|Path.):self.workspace_rootPath(workspace_root).resolve()logger.info(f路径守卫初始化工作区:{self.workspace_root})defis_allowed(self,path:str|Path)-tuple[bool,str]:try:path_objPath(path)ifnotpath_obj.is_absolute():resolved(self.workspace_root/path_obj).resolve()else:resolvedpath_obj.resolve()ifstr(resolved).startswith(str(self.workspace_root)):returnTrue,else:logger.warning(f拦截路径逃逸:{path}-{resolved})returnFalse,f路径越出工作区exceptExceptionase:returnFalse,f路径解析失败:{str(e)}defsafe_resolve(self,path:str|Path)-Path:path_objPath(path)ifpath_obj.is_absolute():returnpath_obj.resolve()return(self.workspace_root/path_obj).resolve()defcheck_symlink(self,path:str|Path)-tuple[bool,str]:resolvedself.safe_resolve(path)try:relresolved.relative_to(self.workspace_root)exceptValueError:returnFalse,路径越出工作区currentself.workspace_rootforpartinrel.parts:currentcurrent/partifcurrent.is_symlink():targetcurrent.resolve()ifnotstr(target).startswith(str(self.workspace_root)):returnFalse,f符号链接指向工作区外部:{target}returnTrue,path_guardPathGuard()把路径守卫接入文件工具在file_tools.py里fromdeep_pilot.path_guardimportpath_guardtooldefread_file(path:str,offset:int1,limit:int1000)-str:# 路径检查allowed,reasonpath_guard.is_allowed(path)ifnotallowed:returnf错误{reason}# ... 原读取逻辑 ...实测攻击与防御验证uv run python-c from deep_pilot.path_guard import path_guard # 测试1正常路径 print( 测试1工作区内路径 ) allowed, reason path_guard.is_allowed(test_math.py) print(f允许: {allowed}, 原因: {reason}) print() # 测试2路径穿越 print( 测试2路径穿越../) allowed, reason path_guard.is_allowed(../../etc/passwd) print(f允许: {allowed}, 原因: {reason}) print() # 测试3绝对路径逃逸 print( 测试3绝对路径逃逸 ) allowed, reason path_guard.is_allowed(/etc/passwd) print(f允许: {allowed}, 原因: {reason}) print() # 测试4混合穿越 print( 测试4混合穿越a/../../etc) allowed, reason path_guard.is_allowed(a/../../etc/passwd) print(f允许: {allowed}, 原因: {reason}) 控制台输出2026-09-13 12:00:01 | INFO | path_guard | 路径守卫初始化工作区: D:\projects\deep-pilot 测试1工作区内路径 允许: True, 原因: 测试2路径穿越../ 2026-09-13 12:00:02 | WARNING | path_guard | 拦截路径逃逸: ../../etc/passwd - D:\etc\passwd 允许: False, 原因: 路径越出工作区 测试3绝对路径逃逸 2026-09-13 12:00:03 | WARNING | path_guard | 拦截路径逃逸: /etc/passwd - D:\etc\passwd 允许: False, 原因: 路径越出工作区 测试4混合穿越a/../../etc 2026-09-13 12:00:04 | WARNING | path_guard | 拦截路径逃逸: a/../../etc/passwd - D:\project\etc\passwd 允许: False, 原因: 路径越出工作区四个测试验证了路径守卫test_math.py→ 工作区内允许…/…/etc/passwd→resolve()消除../后越界拒绝/etc/passwd→ 绝对路径越界拒绝a/…/…/etc/passwd→ 混合穿越resolve()也正确处理拒绝Path.resolve()是核心防线它把各种穿越写法都归一化成规范路径然后统一判断是否越界。小结路径逃逸的三种方式绝对路径、../穿越、符号链接沙箱的cwd限制拦不住。Path.resolve()是关键防线自动消除.、..解析符号链接返回规范化的绝对路径。工作区根目录约束resolve()后判断是否startswith(workspace_root)越界就拒绝。路径穿越防御各种写法的../../../、a/../、.././都被resolve()归一化。符号链接检测check_symlink()逐段检查路径防止符号链接指向外部。全局单例 path_guard在文件工具入口处统一检查一处定义处处生效。DeepPilot v0.5 路径白名单完成——Agent 被锁死在工作区只能动你划定的地盘。下节预告三级审批、沙箱执行、路径白名单——三道安全防线都齐了。但每道防线单独看容易有漏洞需要整体验收。下一节做安全防线大阅兵把三道防线串成整体用恶意任务攻击测试集做对抗演示附上半场第1-10章自测清单和 DeepPilot v0.5 里程碑功能清点。这是实战区的中场收官。如果觉得本文对你有帮助欢迎点赞、收藏、关注三连本系列持续更新中关注不迷路~