资讯动态

渗透测试报告工具:从散乱笔记到交付级文档的最后一公里

发布时间:2026/10/18 14:46:30 来源:尧图企业网站定制
简介这是一款面向渗透测试团队与安全从业者的报告管理工具基于JavaScript技术栈构建用于解决测试过程中参与列表跟踪、范围定义与重复扫描活动自动化的问题。工具支持自动初始扫描可调用nmap、dirb、showmount、nikto及EyeWitness完成主机发现与Web服务截图并提供报告模块用于描述发现、划分严重性等级、附加截图最终通过可自定义模板导出为doc格式文档实验性知识模块还能借助已知SSH凭据自动执行部分横向移动操作。资源包共109个文件以js与py脚本为主辅以css样式、sh脚本、docx模板及png截图等压缩包约90.93MB内部通过Docker容器分别承载Web服务、动作模块与MongoDB数据库。目前已有416人学习下载适合需要规范渗透测试流程、沉淀测试记录并快速产出报告的安全工程师参考使用。1. 渗透测试报告工具从散乱笔记到交付级文档的最后一公里做渗透测试的兄弟都有个共识最痛苦的不是挖漏洞而是写报告。测试两天写报告两周这行话不是段子。pentest-report这个项目就是冲着这个痛点来的——它把渗透测试过程中零散的发现、截图、请求响应、风险等级用一套结构化的方式组织起来最终生成一份可以直接交付给客户的报告文档。技术栈是 JavaScript意味着前端开发者能快速上手改模板、调样式、加字段。适合谁用一是安全服务团队里负责出报告的人二是想把自己渗透测试流程标准化的独立安全从业者三是需要批量生成合规报告的安全合规岗。它不替代你的测试工具它解决的是从“测完”到“交出去”之间那段最耗人耐心的文档工作。2. 项目架构拆解数据层、模板层与导出层怎么串起来2.1 整体目录结构与模块职责拿到源码后先别急着跑花五分钟把目录扫一遍后面改起来会顺很多。这个项目的目录结构大致是这样的pentest-report/ ├── src/ │ ├── core/ # 核心逻辑漏洞模型、风险计算、数据校验 │ ├── templates/ # 报告模板HTML/CSS 模板文件 │ ├── exporters/ # 导出模块PDF、Word、HTML 导出 │ ├── parsers/ # 解析器导入 Burp/Nmap 等工具的输出 │ └── utils/ # 工具函数格式化、编码、文件操作 ├── config/ │ └── default.json # 默认配置风险等级映射、报告字段定义 ├── examples/ │ └── sample-findings.json # 示例漏洞数据 ├── package.json └── README.mdcore目录是整个项目的骨架里面定义了漏洞的数据模型——一个漏洞包含哪些字段、风险等级怎么算、CVSS 评分怎么映射。templates是报告的外观层用 HTML CSS 写模板改样式就是改这里。exporters负责把渲染好的 HTML 转成 PDF 或 Word这是交付环节的关键。parsers是提效模块能从 Burp Suite 的 XML 导出、Nmap 的扫描结果里直接提取漏洞信息省去手工录入。config里的默认配置决定了报告里显示哪些字段、风险等级用什么颜色标识。我一般拿到这类项目会先看package.json里的scripts和dependencies这能快速判断项目怎么跑、依赖重不重。这个项目的依赖大概率集中在 PDF 生成比如puppeteer或pdfkit和模板引擎比如handlebars或ejs上。2.2 漏洞数据模型一条 finding 到底存了什么核心数据模型决定了你后面能往报告里塞什么。打开src/core/finding.js你会看到一个典型的漏洞对象结构// src/core/finding.js class Finding { constructor(data) { this.id data.id || this.generateId(); this.title data.title; // 漏洞名称如SQL注入 this.severity data.severity; // 风险等级critical/high/medium/low/info this.cvss data.cvss || null; // CVSS 3.1 评分 this.description data.description; // 漏洞描述 this.impact data.impact; // 影响范围 this.remediation data.remediation; // 修复建议 this.evidence data.evidence || []; // 证据请求包、响应包、截图路径 this.affectedAssets data.affectedAssets || []; // 受影响资产 this.references data.references || []; // 参考链接 this.status data.status || open; // 状态open/fixed/accepted } // 根据 CVSS 分数自动映射风险等级 static fromCVSS(score) { if (score 9.0) return critical; if (score 7.0) return high; if (score 4.0) return medium; if (score 0.1) return low; return info; } }这个模型的设计思路很清晰把渗透测试报告里每个漏洞必须交代的信息拆成独立字段。evidence是个数组意味着一个漏洞可以附多张截图或多个请求响应包这在实战中很实用——比如一个越权漏洞你可能需要同时展示正常请求和越权请求的对比。affectedAssets也是数组因为同一个漏洞可能影响多个资产。status字段是给复测用的第一轮报告标open复测通过后改成fixed这样同一份数据可以生成初测和复测两份报告。参数说明severity和cvss是联动的如果你填了 CVSS 分数fromCVSS方法会自动算出风险等级如果没填 CVSS就手动指定severity。evidence里的每一项通常是一个对象包含typerequest/response/screenshot和content文本内容或文件路径。2.3 模板渲染流程从 JSON 到 HTML 的完整链路数据有了接下来看怎么变成报告。模板渲染的入口在src/core/report.js// src/core/report.js const Handlebars require(handlebars); const fs require(fs); const path require(path); class Report { constructor(findings, meta) { this.findings findings; // Finding 对象数组 this.meta meta; // 报告元信息项目名、测试时间、测试人员 this.templateDir path.join(__dirname, ../templates); } // 按风险等级分组报告里通常先列高危 groupBySeverity() { const order [critical, high, medium, low, info]; return order.map(level ({ level, items: this.findings.filter(f f.severity level) })).filter(g g.items.length 0); } // 渲染 HTML render() { const templateSource fs.readFileSync( path.join(this.templateDir, report.hbs), utf-8 ); const template Handlebars.compile(templateSource); return template({ meta: this.meta, groups: this.groupBySeverity(), summary: this.generateSummary() }); } // 生成统计摘要 generateSummary() { const counts {}; this.findings.forEach(f { counts[f.severity] (counts[f.severity] || 0) 1; }); return counts; } }这段代码的逻辑链条是先把所有漏洞按风险等级从高到低分组然后传给 Handlebars 模板渲染成 HTML。groupBySeverity方法里的order数组定义了排序规则你可以按客户要求调整——有些客户要求按资产分组而不是按风险等级分组改这里就行。generateSummary生成一个统计对象模板里用它来画风险分布图或统计表格。常见做法是模板里用{{#each groups}}遍历分组每组内再用{{#each items}}遍历漏洞。截图在模板里通过img src{{this}}引用所以evidence里存截图路径时要用相对路径或 base64 编码。2.4 导出模块HTML 转 PDF 和 Word 的落地细节渲染出 HTML 只是中间产物最终交付的是 PDF 或 Word。导出模块在src/exporters/下// src/exporters/pdf.js const puppeteer require(puppeteer); async function exportToPDF(html, outputPath, options {}) { const browser await puppeteer.launch({ headless: new, args: [--no-sandbox, --disable-setuid-sandbox] }); const page await browser.newPage(); await page.setContent(html, { waitUntil: networkidle0 }); await page.pdf({ path: outputPath, format: A4, margin: { top: 20mm, bottom: 20mm, left: 15mm, right: 15mm }, printBackground: true, // 保留背景色风险等级的颜色标识靠它 displayHeaderFooter: true, headerTemplate: div stylefont-size:8px; width:100%; text-align:center;渗透测试报告/div, footerTemplate: div stylefont-size:8px; width:100%; text-align:center;第 span classpageNumber/span 页 / 共 span classtotalPages/span 页/div }); await browser.close(); return outputPath; }用 Puppeteer 转 PDF 的好处是渲染效果和浏览器里看到的一致CSS 样式不会跑偏。printBackground: true这个参数很关键不加的话风险等级的颜色标识比如高危的红色背景在 PDF 里会丢失。headerTemplate和footerTemplate用来加页眉页脚页码是自动计算的。--no-sandbox参数在容器环境里跑的时候必须加否则会报权限错误。Word 导出通常用docx库或者先把 HTML 转成 RTF 再处理。如果项目里用的是html-docx-js那流程就是html → blob → docx但样式兼容性不如 PDF 方案稳定。我一般建议客户交付用 PDF需要编辑的中间稿用 Word。3. 跑起来从零到生成第一份报告的完整操作3.1 环境准备与依赖安装先把项目拉到本地然后确认 Node.js 版本。这类项目一般要求 Node 14 以上Puppeteer 对版本比较敏感建议用 Node 16 或 18 的 LTS 版本。检查命令node -v # 确认版本建议 v16.x 或 v18.x npm -v # 确认 npm 可用进入项目目录后安装依赖cd pentest-report npm install如果npm install卡在 Puppeteer 下载 Chromium 那一步常见原因是网络问题。解决办法是设置环境变量跳过 Chromium 下载然后用系统已有的 Chrome# 跳过 Puppeteer 自带的 Chromium 下载 export PUPPETEER_SKIP_CHROMIUM_DOWNLOADtrue npm install安装完成后检查node_modules下有没有puppeteer和handlebars这两个是核心依赖。如果用的是pdfkit方案那就不需要 Chromium但样式控制能力会弱一些。3.2 准备漏洞数据文件项目跑起来需要一份漏洞数据。examples/sample-findings.json里有一个示例结构大致如下{ meta: { projectName: 某业务系统渗透测试, testPeriod: 2024-01-10 至 2024-01-15, testers: [测试人员A, 测试人员B], reportVersion: 1.0 }, findings: [ { title: 登录接口 SQL 注入, severity: critical, cvss: 9.8, description: 登录接口未对用户输入做参数化处理存在 SQL 注入风险。, impact: 攻击者可绕过认证获取数据库敏感信息。, remediation: 使用参数化查询对输入进行严格校验。, evidence: [ { type: request, content: POST /api/login HTTP/1.1\n... }, { type: response, content: HTTP/1.1 200 OK\n... } ], affectedAssets: [https://example.com/api/login], references: [https://owasp.org/www-community/attacks/SQL_Injection] } ] }你可以直接改这个文件来适配自己的项目。meta里的字段会渲染到报告封面和基本信息页。findings数组里每个对象对应一个漏洞字段和前面Finding类一一对应。如果要从 Burp Suite 导出结果自动生成这个 JSON用parsers目录下的解析器常见做法是node src/parsers/burp.js --input burp-export.xml --output findings.json。3.3 生成报告并验证输出数据准备好后运行生成命令# 生成 HTML 报告 node src/index.js --input examples/sample-findings.json --output report.html --format html # 生成 PDF 报告 node src/index.js --input examples/sample-findings.json --output report.pdf --format pdf参数说明--input指定漏洞数据文件路径--output指定输出文件路径--format指定导出格式html/pdf/docx。如果项目没有统一的index.js入口那就看package.json里的scripts通常会有npm run generate之类的命令。生成 PDF 后重点检查三处一是风险等级的颜色标识有没有丢二是截图有没有正常显示三是页码和页眉页脚对不对。如果截图不显示大概率是路径问题——HTML 里引用的图片路径是相对路径Puppeteer 渲染时基准路径不对。解决办法是在setContent之前用page.goto(file:// htmlPath)加载本地 HTML 文件而不是直接传 HTML 字符串。4. 避坑与排查跑这个项目最容易翻车的几个地方4.1 PDF 生成时中文乱码或字体缺失现象生成的 PDF 里中文显示为方块或空白。原因Puppeteer 默认使用的字体不包含中文字形或者 CSS 里指定的字体在渲染环境里不存在。解决在 CSS 模板里显式指定中文字体并确保系统安装了对应字体。常见做法是在report.hbs的style里加font-family: Noto Sans CJK SC, Microsoft YaHei, sans-serif;然后在 Docker 环境里安装fonts-noto-cjk包。4.2 截图在 PDF 里显示不全或被裁切现象漏洞证据里的截图只显示了一部分或者被分页截断。原因图片没有设置最大宽度超出 A4 页面宽度后被裁切或者图片高度太大跨页时被截断。解决在 CSS 里给img加max-width: 100%; height: auto;对于长截图用page-break-inside: avoid;让图片尽量不跨页。如果图片实在太大考虑在生成前用工具压缩或分片。4.3 从 Burp 导入的数据字段对不上现象用解析器导入 Burp 导出文件后生成的报告里漏洞描述为空或风险等级全是 info。原因Burp 导出的 XML 结构和项目解析器预期的字段名不一致或者解析器没有处理某些类型的漏洞。解决先看src/parsers/burp.js里解析的字段映射关系把 Burp 导出的 XML 打开对照手动调整映射。如果解析器不支持某个字段在Finding模型里加一个可选字段然后在模板里按需渲染。4.4 Node 版本不兼容导致 Puppeteer 启动失败现象运行时报Error: Failed to launch the browser process或Protocol error。原因Puppeteer 版本和 Node 版本不匹配或者 Chromium 缺少系统依赖库。解决先确认package.json里 Puppeteer 的版本然后查对应 Node 版本要求。在 Linux 环境下用apt-get install -y libnss3 libatk-bridge2.0-0 libdrm2 libxkbcommon0 libgbm1安装 Chromium 运行所需的系统库。如果还是不行设置PUPPETEER_EXECUTABLE_PATH指向系统已安装的 Chrome。4.5 报告生成后文件为空或只有封面现象PDF 文件生成了但打开只有封面页漏洞内容全没有。原因模板渲染时findings数组为空或者 Handlebars 模板里的变量名和传入的数据结构不匹配。解决先在render()方法里console.log(this.findings.length)确认数据加载成功然后检查模板里{{#each groups}}的变量名是否和groupBySeverity()返回的对象结构一致。常见错误是模板里写{{#each findings}}但实际传的是groups。5. 二次开发改模板、加字段、接自动化流程5.1 定制报告模板的实操路径改模板是最常见的二次开发需求。客户要求换 Logo、调颜色、加公司抬头这些都在src/templates/report.hbs和对应的 CSS 文件里改。具体操作先复制一份report.hbs命名为report-custom.hbs然后在Report类的render()方法里把模板文件名改成你的自定义模板。CSS 里风险等级的颜色定义通常在:root变量里改一处全局生效:root { --severity-critical: #d32f2f; --severity-high: #f57c00; --severity-medium: #fbc02d; --severity-low: #388e3c; --severity-info: #1976d2; }如果客户要求报告里加一个“漏洞趋势图”可以在模板里引入 Chart.js用generateSummary()返回的统计数据画柱状图或饼图。注意 Puppeteer 渲染时waitUntil: networkidle0会等 Chart.js 加载完再截图所以图表能正常显示。5.2 扩展数据模型与字段映射项目默认的字段可能不够用。比如客户要求每个漏洞标注“是否可远程利用”“是否需要认证”那就需要在Finding类里加字段然后在模板里加对应的渲染逻辑。加字段的步骤先在Finding构造函数里加this.remotelyExploitable data.remotelyExploitable || false;然后在sample-findings.json里给每个漏洞补上这个字段最后在模板里用{{#if this.remotelyExploitable}}是{{else}}否{{/if}}渲染。如果要从其他工具导入数据比如 Nessus 或 AWVS就在parsers目录下新建一个解析器把工具的导出格式转成项目统一的 JSON 结构。解析器的核心逻辑就是字段映射把工具输出的plugin_name映射到titlerisk_factor映射到severitydescription映射到description。写完后在index.js里加一个--parser nessus的参数分支。5.3 接入 CI/CD 实现报告自动生成如果团队有持续性的渗透测试任务可以把报告生成接入自动化流程。思路是测试工具输出结果 → 解析器转成标准 JSON → 调用报告生成命令 → 输出 PDF 到指定目录。在 CI 配置里加一个步骤# CI 中自动生成报告 node src/parsers/burp.js --input $BURP_EXPORT --output /tmp/findings.json node src/index.js --input /tmp/findings.json --output /artifacts/report.pdf --format pdf这样每次测试完成后报告自动生成并归档省去手工操作。注意 CI 环境里要提前装好 Chromium 依赖或者用 Docker 镜像把环境固化下来。5.4 一个容易忽略的验证习惯改完模板或数据模型后别只看生成的 PDF 就完事。我一般会强制走一遍“反向验证”把生成的 PDF 里的漏洞数量和 JSON 里的findings数组长度对一遍确认没有漏渲染再随机抽一个漏洞检查它的证据截图、请求响应包、修复建议是否都完整显示。这个习惯帮我拦过好几次“模板改了但某个字段忘了加渲染”的问题。从那以后我每次改完模板都强制走一遍这个验证流程希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑