下载入口只接受一个文件名,看起来很简单,却容易把“用户想下载什么”和“服务器允许公开什么”混在一起。如果直接把输入拼到磁盘目录后面,程序就需要面对路径跳转、绝对路径和链接文件等边界问题。
这篇用ECC中的Security Review Skill审查一个完全本地的演示:下载者只提交资源编号,服务器根据固定目录表选择公开文件。最终得到输入边界说明、一个可运行的路径选择函数和测试记录。不启动公网服务,不扫描第三方网站,也不把练习结果当作生产安全认证。
Skill说明、安装入口和许可按ECC已有资源包对应提交核对。下文Python函数是本站编写的示例,已在本地做边界测试;建议提示词没有冒充在读者工具里真实调用成功。
一、定位正确的Skill与资源包
官方仓库为affaan-m/ECC,目标目录是skills/security-review。本文使用已有ECC整包对应的提交d2ef2680594f9a17526a6e1a2e18b3d242c5d17b,页面版本沿用2026-11-07源码快照,不声称是最新发布。
资源表下载的是ECC整包,并非单独新制作的安全审查工具。保留README和仓库MIT许可证,在包内定位skills/security-review/SKILL.md,阅读输入验证、白名单、授权和错误信息等检查点。不执行包内安装脚本或第三方示例代码来完成本文练习。
Security Review提供的是审查指引,覆盖输入、密钥、鉴权和依赖等主题。它不是能够证明任何网站“已经没有漏洞”的扫描器,也不会替你自动确认业务授权规则。
二、选择一种安装方式并确认加载
使用Claude Code插件渠道时,官方README给出的原生入口为:
/plugin marketplace add https://github.com/affaan-m/ECC
/plugin install ecc@ecc这些命令在Claude Code内执行,不是PowerShell命令。已安装ECC的读者先检查插件状态,不要在完整插件安装之上重复叠加一套手工安装。官方README明确区分了安装方式与可能的冲突处理。
插件渠道安装的具体版本可能不同于本站旧快照;使用前检查自己环境实际加载的Skill内容。若只研究下载源码,可先读取目标Markdown,记录为人工按清单审查,不把阅读文件称作插件已经启用。
新开会话后确认开发助手能读取security-review技能,再把下面的范围约定与示例代码交给它。不要上传真实服务器路径、密钥或客户文件;本例只需要一个临时目录和无敏感信息的文本。
三、把下载权限与文件位置分开
假设只有一份公开资料,资源编号为guide,磁盘文件名为guide.txt。编号到文件的映射由维护者在服务端配置,用户不能提交任意磁盘路径,也不能修改目录表。
本篇只处理“资源编号怎样定位到受控文件”这一层。若真实文件仅对登录用户开放,还必须先做用户身份与资源授权检查;知道一个合法编号,不等于有权下载它。公开目录表也不能成为绕过账号权限的理由。
目录必须由可信维护者管理,下载进程不应允许普通用户写入或替换其中的文件。下面函数没有解决并发替换、完整HTTP响应头、下载频率或访问审计;这些属于接入真实服务时另行设计与验证的内容。
四、先让助手说明数据流与假设
把范围和代码发送给支持该Skill的开发助手:
请使用security-review检查这个本地资源定位函数。
仅审查我提供的演示目录与代码,不访问第三方站点,不启动服务器。
先画出文字数据流:不可信资源编号、服务端目录表、受控根目录、目标文件。
分别说明输入验证、路径边界、文件类型与授权仍缺什么。
每条发现附对应代码或需求依据,不把未验证风险写成已复现漏洞。
先给审查记录,等我确认后再修改。这一步应得到清楚的边界,而不是一句“已做安全处理”。例如,编号来自调用者,目录表由程序控制,根目录来自可信部署配置;目标文件仍需检查是否存在、是否普通文件,以及是否离开公开范围。
OWASP关于路径遍历的资料建议尽量避免把用户输入直接用作文件路径,并通过已知允许值等方式约束访问。本例选择资源编号目录表,是为了缩小输入含义,不依赖反复删除某几个危险字符。
五、保存一个只选择路径的演示函数
准备Python 3.10或更高版本,将以下内容保存为download_lookup.py。程序不启动HTTP接口,也不在读取用户输入后直接发送文件。
from pathlib import Path
FILES = {"guide": "guide.txt"}
def locate(root, resource_id):
if not isinstance(resource_id, str) or resource_id not in FILES:
raise ValueError("unknown resource")
base = Path(root).resolve(strict=True)
if not base.is_dir():
raise NotADirectoryError("public root is not a directory")
candidate = base / FILES[resource_id]
if candidate.is_symlink():
raise PermissionError("symbolic links are not allowed")
target = candidate.resolve(strict=True)
if not target.is_relative_to(base):
raise PermissionError("outside public root")
if not target.is_file():
raise FileNotFoundError("resource is not a regular file")
return target本例只允许精确编号guide,不会把Guide自动小写,也不去掉输入空格。这样的严格规则便于核查;真实项目若允许别名,应先规定映射,再补相应测试,不能在多个入口各自猜测。
is_relative_to检查的是解析后路径的目录关系,不是字符串前缀。比如public-backup与public名字开头相同,并不说明前者属于后者。不要用字符串startswith替代目录层级判断。
函数还显式拒绝目标符号链接。但这并不等于跨平台的所有文件系统特殊机制都已覆盖;目录权限和可信文件管理仍是前提,不能只靠这几行代码保护可被攻击者随意改写的目录。
六、在临时目录运行正常与异常用例
保存下面内容为demo_lookup.py,与download_lookup.py放在同一目录。程序只在自动创建的临时目录里写入示例文本,退出后由临时目录机制清理,不操作真实网站文件。
from pathlib import Path
from tempfile import TemporaryDirectory
from download_lookup import locate
with TemporaryDirectory() as directory:
root = Path(directory)
(root / "guide.txt").write_text("Public demo only", encoding="utf-8")
print(locate(root, "guide").name)
for value in ("../private.txt", "/private.txt", "guide.txt", "Guide", ""):
try:
locate(root, value)
except ValueError:
print("rejected:", repr(value))
else:
raise AssertionError("unexpected accepted identifier")运行python demo_lookup.py,第一行应是guide.txt,后面五条输入都应被拒绝。这些字符串仅用于你自己创建的本地练习,不用于向任何真实站点探测。
继续在测试副本里检查:文件缺失应失败;同名位置变成目录应失败;根目录不存在应失败;输入不是字符串应失败。需要检查符号链接时,只在支持且有权限创建链接的临时环境进行;无法创建时记录未测试,不宣称该环境已经通过这一项。
七、审查结论应写到什么程度
合理结论可以是:“本地示例只接受固定资源编号;正常文件可定位,未列入目录的输入被拒绝;文件缺失和目录类型的失败符合预期。”这比写“路径遍历漏洞彻底修复”更准确,因为本文没有审查真实路由、框架解码和部署权限。
还要明确列出未覆盖项:调用前是否认证,是否检查该用户能访问该资源,返回错误是否暴露内部路径,以及文件选择后到真正打开前是否可能被替换。这些不能因为Skill输出一张完整清单就自动打勾。
接入框架后,由应用捕获内部异常,向外返回适合业务的通用失败信息。不要把异常堆栈、服务器绝对路径和配置内容直接返回给下载者。内部日志是否记录具体路径,也应按管理范围和排错需要决定。
八、从本地练习走向可交付检查
保留输入规则、目录表、受控目录权限说明、测试输出和剩余问题。需要新增第二份资源时,先由维护者更新目录表和文件,再补正常编号、未知编号与文件状态检查,而不是开放“任意文件名”省去配置。
若公开文件会频繁被替换,应采用适合部署环境的安全文件打开和发布方式,处理检查与使用之间的竞态;本例返回Path对象,不能单独解决这个问题。不要直接把演示函数包装成公网接口就认为已经完成上线审计。
本篇完成的是一项有边界的审查练习:知道输入从哪里来、允许访问哪些文件、证据支持什么结论,以及哪些条件还没有检查。后续再扩展身份、授权与下载服务时,沿着这些边界逐项验证。
九、出现失败时按证据定位
合法编号也失败时,先检查配置的根目录是否存在,再检查guide.txt是否确实放在该目录。不要为了让测试通过,把根目录改成整个磁盘或网站上级目录;应该修正演示文件与部署配置之间的对应关系。
如果未知编号被接受,先确认调用的是本文函数,而不是另一个仍然接收文件名的旧入口。真实应用可能有多个下载路由,仅修复其中一处并不能说明所有入口都采用同样规则。审查时列出入口与调用链,再分别确认实际使用的选择逻辑。
如果符号链接测试无法运行,记录操作系统、权限限制与未完成项即可。不要改动系统安全设置只为凑齐测试数量;在具备正常测试权限的隔离环境补做这一项,比把失败日志改成通过更有价值。
后续交付报告可以分为已验证、依据配置确认、尚待验证三种状态,每项附证据位置。尤其是授权和并发文件替换问题,应明确留给接入阶段处理,而不是在本地路径函数测试通过后一起标为完成。

还没有公开评论,欢迎分享你的想法。