audit-project-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +175 -0
- package/package.json +49 -0
- package/src/index.js +31 -0
- package/src/render/renderAuditResult.js +229 -0
- package/src/render/renderAuditResultDashboard.js +466 -0
- package/src/render/renderAuditResultHTML.js +408 -0
- package/src/render/renderAuditResultTXT.js +185 -0
- package/src/server.js +42 -0
- package/src/utils.js +384 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jackmu
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Audit Project Server
|
|
2
|
+
|
|
3
|
+
一个用于审计项目依赖安全性的工具,支持本地和远程 GitHub 项目,可生成多种格式的审计报告。
|
|
4
|
+
|
|
5
|
+
## ✨ 特性
|
|
6
|
+
|
|
7
|
+
- 🔍 **支持多种项目源**:本地项目、GitHub 远程仓库
|
|
8
|
+
- 📊 **多种输出格式**:Markdown、HTML、纯文本、可视化仪表板
|
|
9
|
+
- 🛡️ **安全审计**:使用 `npm audit` 进行依赖安全扫描
|
|
10
|
+
- 🔄 **智能依赖处理**:自动过滤特殊协议依赖(git、file、link 等)
|
|
11
|
+
- 📈 **详细报告**:包含漏洞统计、修复建议、跳过的依赖等信息
|
|
12
|
+
- 🤖 **MCP 集成**:可作为 Model Context Protocol 服务器使用
|
|
13
|
+
|
|
14
|
+
## 📦 安装
|
|
15
|
+
|
|
16
|
+
### 作为 npm 包安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install audit-project-server
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 作为 mcp 服务使用
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"mcpServers": {
|
|
27
|
+
"audit-project-server": {
|
|
28
|
+
"timeout": 60,
|
|
29
|
+
"command": "npx",
|
|
30
|
+
"args": [
|
|
31
|
+
"-y",
|
|
32
|
+
"audit-project-server"
|
|
33
|
+
],
|
|
34
|
+
"type": "stdio",
|
|
35
|
+
"disabled": false
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 从源码安装
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/yourusername/audit-project.git
|
|
45
|
+
cd audit-project
|
|
46
|
+
npm install
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 🚀 快速开始
|
|
50
|
+
|
|
51
|
+
### 作为 CLI 工具使用
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
import { auditProject } from 'audit-project-server';
|
|
55
|
+
|
|
56
|
+
// 审计本地项目
|
|
57
|
+
await auditProject('./my-project', './audit-result.md', 'md');
|
|
58
|
+
|
|
59
|
+
// 审计 GitHub 项目
|
|
60
|
+
await auditProject('https://github.com/facebook/react/tree/v19.2.3', './react-audit.html', 'html');
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 作为 MCP 服务器使用
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# 启动 MCP 服务器
|
|
67
|
+
npm start
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 📋 使用方法
|
|
71
|
+
|
|
72
|
+
### 基本参数
|
|
73
|
+
|
|
74
|
+
| 参数 | 类型 | 说明 | 示例 |
|
|
75
|
+
|------|------|------|------|
|
|
76
|
+
| `projectPath` | string | 项目路径(支持本地和远程) | `./my-project` 或 `https://github.com/owner/repo` |
|
|
77
|
+
| `savePath` | string | 保存路径(不含扩展名) | `./audit-result` |
|
|
78
|
+
| `outputFormat` | string | 输出格式 | `md`, `html`, `txt`, `dashboard` |
|
|
79
|
+
|
|
80
|
+
### 输出格式说明
|
|
81
|
+
|
|
82
|
+
- **`md`**:Markdown 格式,适合文档和 GitHub
|
|
83
|
+
- **`html`**:HTML 页面,适合浏览器查看
|
|
84
|
+
- **`txt`**:纯文本格式,适合终端查看
|
|
85
|
+
- **`dashboard`**:可视化仪表板,带图表和交互
|
|
86
|
+
|
|
87
|
+
## 🔧 工作原理
|
|
88
|
+
|
|
89
|
+
1. **创建工作目录**:在临时目录中创建工作空间
|
|
90
|
+
2. **获取 package.json**:
|
|
91
|
+
- 本地项目:直接读取文件
|
|
92
|
+
- GitHub 项目:通过 GitHub API 获取
|
|
93
|
+
3. **过滤特殊依赖**:识别并跳过特殊协议依赖(git、file、link 等)
|
|
94
|
+
4. **生成 package-lock.json**:使用 `npm install --package-lock-only`
|
|
95
|
+
5. **执行安全审计**:运行 `npm audit --json`
|
|
96
|
+
6. **渲染报告**:根据指定格式生成报告
|
|
97
|
+
7. **清理工作目录**:删除临时文件
|
|
98
|
+
|
|
99
|
+
## 📊 报告内容
|
|
100
|
+
|
|
101
|
+
生成的审计报告包含以下部分:
|
|
102
|
+
|
|
103
|
+
### 1. 项目信息
|
|
104
|
+
- 项目名称和版本
|
|
105
|
+
- 审计时间和工具
|
|
106
|
+
|
|
107
|
+
### 2. 跳过的依赖
|
|
108
|
+
- 特殊协议依赖(git、file、link 等)
|
|
109
|
+
- 非正常版本依赖(latest、next、beta 等)
|
|
110
|
+
- 跳过原因和建议
|
|
111
|
+
|
|
112
|
+
### 3. 漏洞统计
|
|
113
|
+
- 按严重级别分类(严重、高、中、低、信息)
|
|
114
|
+
- 依赖类型统计(生产、开发、可选、peer)
|
|
115
|
+
|
|
116
|
+
### 4. 详细漏洞列表
|
|
117
|
+
- 每个漏洞的详细信息
|
|
118
|
+
- 受影响版本范围
|
|
119
|
+
- 修复建议和可用版本
|
|
120
|
+
- CVSS 评分和 CWE 标识
|
|
121
|
+
|
|
122
|
+
### 5. 修复建议总结
|
|
123
|
+
- 可用的修复命令
|
|
124
|
+
- 替代方案建议
|
|
125
|
+
|
|
126
|
+
## 🤝 作为 MCP 服务器使用
|
|
127
|
+
|
|
128
|
+
### 启动服务器
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
npm start
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## ⚙️ 配置选项
|
|
135
|
+
|
|
136
|
+
### 环境变量
|
|
137
|
+
|
|
138
|
+
| 变量名 | 说明 | 默认值 |
|
|
139
|
+
|--------|------|--------|
|
|
140
|
+
| `NPM_REGISTRY` | npm 注册表地址 | `https://registry.npmjs.org` |
|
|
141
|
+
|
|
142
|
+
### npm 配置
|
|
143
|
+
|
|
144
|
+
项目使用以下 npm 命令选项:
|
|
145
|
+
- `--package-lock-only`:只生成 package-lock.json
|
|
146
|
+
- `--ignore-scripts`:忽略 package.json 中的脚本
|
|
147
|
+
- `--force`:强制安装,忽略冲突
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
### 常见问题
|
|
151
|
+
|
|
152
|
+
1. **GitHub 项目无法访问**
|
|
153
|
+
- 检查网络连接
|
|
154
|
+
- 确认仓库是公开的
|
|
155
|
+
- 确认分支或标签存在
|
|
156
|
+
|
|
157
|
+
2. **npm audit 返回错误**
|
|
158
|
+
- 检查 npm 版本(需要 npm 6+)
|
|
159
|
+
- 确认网络可以访问 npm 注册表
|
|
160
|
+
- 检查项目依赖是否合法
|
|
161
|
+
|
|
162
|
+
3. **特殊依赖被跳过**
|
|
163
|
+
- 这是预期行为,特殊协议依赖无法通过 npm audit 审计
|
|
164
|
+
- 建议手动检查这些依赖的安全性
|
|
165
|
+
|
|
166
|
+
### 调试模式
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# 设置调试环境变量
|
|
170
|
+
npm start
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## 📄 许可证
|
|
174
|
+
|
|
175
|
+
本项目采用 ISC 许可证。详见 [LICENSE](LICENSE) 文件。
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "audit-project-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A tool for auditing project dependency security, supporting local and remote GitHub projects with multiple output formats (Markdown, HTML, Text, Dashboard). Can be used as an npm package or MCP server.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./server": "./src/server.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"start": "node ./src/server.js",
|
|
13
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
17
|
+
"zod": "^4.3.6"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"audit",
|
|
22
|
+
"package-audit",
|
|
23
|
+
"npm-audit",
|
|
24
|
+
"github",
|
|
25
|
+
"mcp",
|
|
26
|
+
"model-context-protocol",
|
|
27
|
+
"security-audit",
|
|
28
|
+
"dependency-check"
|
|
29
|
+
],
|
|
30
|
+
"author": "Jackmu",
|
|
31
|
+
"license": "ISC",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/jackmu01/audit_project_mcp.git"
|
|
35
|
+
},
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/jackmu01/audit_project_mcp/issues"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/jackmu01/audit_project_mcp#readme",
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18.0.0",
|
|
42
|
+
"npm": ">=8.0.0"
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"src/",
|
|
46
|
+
"LICENSE",
|
|
47
|
+
"README.md"
|
|
48
|
+
]
|
|
49
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createdDirectory,
|
|
3
|
+
deleteDirectory,
|
|
4
|
+
getPackageJsonInfo,
|
|
5
|
+
generatePackageLockJson,
|
|
6
|
+
projectAudit,
|
|
7
|
+
renderAuditResultByFormat,
|
|
8
|
+
} from "./utils.js";
|
|
9
|
+
|
|
10
|
+
export async function auditProject(projectPath, savePath, outputFormat = "md") {
|
|
11
|
+
// 1.创建工作目录
|
|
12
|
+
const workDir = await createdDirectory();
|
|
13
|
+
// 2.找到目录的Package.json文件
|
|
14
|
+
const packageJson = await getPackageJsonInfo(projectPath);
|
|
15
|
+
// 3.根据Package.json文件内容,生成package-lock.json文件
|
|
16
|
+
const generateResult = await generatePackageLockJson(workDir, packageJson);
|
|
17
|
+
// 添加跳过的依赖信息到packageJson中,以便在渲染时使用
|
|
18
|
+
packageJson._skippedDependencies = generateResult.skippedDependencies;
|
|
19
|
+
// 4.在work 工作目录下执行npm audit命令
|
|
20
|
+
const auditResult = await projectAudit(workDir);
|
|
21
|
+
// 5.根据输出格式渲染审计结果
|
|
22
|
+
const result = await renderAuditResultByFormat(
|
|
23
|
+
auditResult,
|
|
24
|
+
packageJson,
|
|
25
|
+
outputFormat,
|
|
26
|
+
savePath
|
|
27
|
+
);
|
|
28
|
+
// 6.删除工作目录
|
|
29
|
+
await deleteDirectory(workDir);
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 渲染npm audit结果到Markdown格式
|
|
5
|
+
* @param {string} auditResult JSON格式的npm audit结果
|
|
6
|
+
* @param {Object} packageJsonInfo package.json文件内容
|
|
7
|
+
* @returns {string} Markdown格式的报告
|
|
8
|
+
*/
|
|
9
|
+
export async function renderAuditResult(
|
|
10
|
+
auditResult,
|
|
11
|
+
packageJsonInfo,
|
|
12
|
+
savePath,
|
|
13
|
+
) {
|
|
14
|
+
try {
|
|
15
|
+
const auditData = JSON.parse(auditResult);
|
|
16
|
+
|
|
17
|
+
// 检查是否有错误
|
|
18
|
+
if (auditData.error) {
|
|
19
|
+
return `# NPM Audit 报告\n\n❌ **错误**: ${auditData.error}\n\n`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 准备Markdown内容
|
|
23
|
+
let markdown = `# NPM Audit 安全审计报告\n\n`;
|
|
24
|
+
|
|
25
|
+
// 项目信息
|
|
26
|
+
markdown += `## 项目信息\n`;
|
|
27
|
+
markdown += `- **项目名称**: ${packageJsonInfo.name || "未知"}\n`;
|
|
28
|
+
markdown += `- **项目版本**: ${packageJsonInfo.version || "未知"}\n`;
|
|
29
|
+
markdown += `- **审计时间**: ${new Date().toLocaleString("zh-CN")}\n`;
|
|
30
|
+
markdown += `- **审计工具**: npm audit\n\n`;
|
|
31
|
+
|
|
32
|
+
// 跳过的依赖信息
|
|
33
|
+
if (packageJsonInfo._skippedDependencies) {
|
|
34
|
+
const skipped = packageJsonInfo._skippedDependencies;
|
|
35
|
+
let totalSkipped = 0;
|
|
36
|
+
let skippedDetails = [];
|
|
37
|
+
|
|
38
|
+
// 统计跳过的依赖
|
|
39
|
+
const depTypes = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
|
|
40
|
+
for (const depType of depTypes) {
|
|
41
|
+
if (skipped[depType] && skipped[depType].length > 0) {
|
|
42
|
+
totalSkipped += skipped[depType].length;
|
|
43
|
+
skippedDetails.push(...skipped[depType].map(dep => ({
|
|
44
|
+
...dep,
|
|
45
|
+
type: depType
|
|
46
|
+
})));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (totalSkipped > 0) {
|
|
51
|
+
markdown += `## 跳过的依赖 (${totalSkipped}个)\n\n`;
|
|
52
|
+
markdown += `⚠️ **注意**: 以下依赖因使用特殊协议或非正常版本而被跳过审计:\n\n`;
|
|
53
|
+
|
|
54
|
+
// 按依赖类型分组显示
|
|
55
|
+
for (const depType of depTypes) {
|
|
56
|
+
if (skipped[depType] && skipped[depType].length > 0) {
|
|
57
|
+
const typeName = {
|
|
58
|
+
'dependencies': '生产依赖',
|
|
59
|
+
'devDependencies': '开发依赖',
|
|
60
|
+
'peerDependencies': 'Peer依赖',
|
|
61
|
+
'optionalDependencies': '可选依赖'
|
|
62
|
+
}[depType] || depType;
|
|
63
|
+
|
|
64
|
+
markdown += `### ${typeName} (${skipped[depType].length}个)\n`;
|
|
65
|
+
markdown += `| 包名 | 版本 | 跳过原因 |\n`;
|
|
66
|
+
markdown += `|------|------|----------|\n`;
|
|
67
|
+
|
|
68
|
+
for (const dep of skipped[depType]) {
|
|
69
|
+
markdown += `| ${dep.name} | \`${dep.version}\` | ${dep.reason} |\n`;
|
|
70
|
+
}
|
|
71
|
+
markdown += `\n`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
markdown += `**说明**:\n`;
|
|
76
|
+
markdown += `- **link:** 协议: 指向本地目录的符号链接\n`;
|
|
77
|
+
markdown += `- **file:** 协议: 指向本地文件的依赖\n`;
|
|
78
|
+
markdown += `- **git:** 协议: 指向Git仓库的依赖\n`;
|
|
79
|
+
markdown += `- **非正常版本**: 如 "latest", "next", "beta" 等非semver版本\n`;
|
|
80
|
+
markdown += `- **URL格式**: 直接指向URL的依赖\n\n`;
|
|
81
|
+
markdown += `这些依赖无法通过npm audit进行安全审计,建议手动检查其安全性。\n\n`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 漏洞统计摘要
|
|
86
|
+
const metadata = auditData.metadata;
|
|
87
|
+
if (metadata && metadata.vulnerabilities) {
|
|
88
|
+
const vulnStats = metadata.vulnerabilities;
|
|
89
|
+
markdown += `## 漏洞统计摘要\n`;
|
|
90
|
+
markdown += `| 严重级别 | 数量 |\n`;
|
|
91
|
+
markdown += `|----------|------|\n`;
|
|
92
|
+
markdown += `| **严重 (critical)** | ${vulnStats.critical || 0} |\n`;
|
|
93
|
+
markdown += `| **高 (high)** | ${vulnStats.high || 0} |\n`;
|
|
94
|
+
markdown += `| **中 (moderate)** | ${vulnStats.moderate || 0} |\n`;
|
|
95
|
+
markdown += `| **低 (low)** | ${vulnStats.low || 0} |\n`;
|
|
96
|
+
markdown += `| **信息 (info)** | ${vulnStats.info || 0} |\n`;
|
|
97
|
+
markdown += `| **总计** | **${vulnStats.total || 0}** |\n\n`;
|
|
98
|
+
|
|
99
|
+
// 依赖统计
|
|
100
|
+
const depStats = metadata.dependencies;
|
|
101
|
+
if (depStats) {
|
|
102
|
+
markdown += `## 依赖统计\n`;
|
|
103
|
+
markdown += `- **生产依赖**: ${depStats.prod || 0}\n`;
|
|
104
|
+
markdown += `- **开发依赖**: ${depStats.dev || 0}\n`;
|
|
105
|
+
markdown += `- **可选依赖**: ${depStats.optional || 0}\n`;
|
|
106
|
+
markdown += `- **Peer依赖**: ${depStats.peer || 0}\n`;
|
|
107
|
+
markdown += `- **总依赖数**: ${depStats.total || 0}\n\n`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 详细漏洞列表
|
|
112
|
+
const vulnerabilities = auditData.vulnerabilities;
|
|
113
|
+
if (vulnerabilities && Object.keys(vulnerabilities).length > 0) {
|
|
114
|
+
markdown += `## 详细漏洞列表\n\n`;
|
|
115
|
+
|
|
116
|
+
Object.entries(vulnerabilities).forEach(([pkgName, vuln]) => {
|
|
117
|
+
const severity = vuln.severity || "unknown";
|
|
118
|
+
const severityEmoji =
|
|
119
|
+
{
|
|
120
|
+
critical: "🔴",
|
|
121
|
+
high: "🟠",
|
|
122
|
+
moderate: "🟡",
|
|
123
|
+
low: "🟢",
|
|
124
|
+
info: "🔵",
|
|
125
|
+
}[severity] || "⚪";
|
|
126
|
+
|
|
127
|
+
markdown += `### ${severityEmoji} ${pkgName} (${severity.toUpperCase()})\n`;
|
|
128
|
+
markdown += `| 属性 | 值 |\n`;
|
|
129
|
+
markdown += `|------|-----|\n`;
|
|
130
|
+
markdown += `| **严重级别** | ${severity} |\n`;
|
|
131
|
+
markdown += `| **是否直接依赖** | ${vuln.isDirect ? "是" : "否"} |\n`;
|
|
132
|
+
markdown += `| **受影响版本** | ${vuln.range || "未知"} |\n`;
|
|
133
|
+
|
|
134
|
+
// 修复建议
|
|
135
|
+
if (vuln.fixAvailable) {
|
|
136
|
+
if (vuln.fixAvailable === false) {
|
|
137
|
+
markdown += `| **修复建议** | 暂无可用修复 |\n`;
|
|
138
|
+
} else {
|
|
139
|
+
markdown += `| **修复建议** | 升级到 ${vuln.fixAvailable.version || "最新版本"} |\n`;
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
markdown += `| **修复建议** | 检查更新 |\n`;
|
|
143
|
+
}
|
|
144
|
+
markdown += `\n`;
|
|
145
|
+
|
|
146
|
+
// 漏洞详情
|
|
147
|
+
if (vuln.via && Array.isArray(vuln.via)) {
|
|
148
|
+
markdown += `#### 漏洞详情\n`;
|
|
149
|
+
vuln.via.forEach((issue, index) => {
|
|
150
|
+
if (typeof issue === "string") {
|
|
151
|
+
markdown += `${index + 1}. **间接漏洞**: ${issue}\n`;
|
|
152
|
+
} else {
|
|
153
|
+
markdown += `${index + 1}. **${issue.title || "未知漏洞"}**\n`;
|
|
154
|
+
markdown += ` - **严重级别**: ${issue.severity || "未知"}\n`;
|
|
155
|
+
if (issue.url) {
|
|
156
|
+
markdown += ` - **参考链接**: [${issue.url}](${issue.url})\n`;
|
|
157
|
+
}
|
|
158
|
+
if (issue.range) {
|
|
159
|
+
markdown += ` - **受影响版本**: ${issue.range}\n`;
|
|
160
|
+
}
|
|
161
|
+
if (issue.cvss && issue.cvss.score !== undefined) {
|
|
162
|
+
markdown += ` - **CVSS 评分**: ${issue.cvss.score}\n`;
|
|
163
|
+
}
|
|
164
|
+
if (issue.cwe && issue.cwe.length > 0) {
|
|
165
|
+
markdown += ` - **CWE**: ${issue.cwe.join(", ")}\n`;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
markdown += `\n`;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
markdown += `---\n\n`;
|
|
172
|
+
});
|
|
173
|
+
} else {
|
|
174
|
+
markdown += `## 详细漏洞列表\n\n`;
|
|
175
|
+
markdown += `✅ **恭喜!未发现任何安全漏洞。**\n\n`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 修复建议总结
|
|
179
|
+
markdown += `## 修复建议总结\n\n`;
|
|
180
|
+
const vulnerabilitiesObj = auditData.vulnerabilities || {};
|
|
181
|
+
const hasFixAvailable = Object.values(vulnerabilitiesObj).some(
|
|
182
|
+
(vuln) => vuln.fixAvailable && vuln.fixAvailable !== false,
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
if (hasFixAvailable) {
|
|
186
|
+
markdown += `以下依赖有可用修复:\n\n`;
|
|
187
|
+
Object.entries(vulnerabilitiesObj).forEach(([pkgName, vuln]) => {
|
|
188
|
+
if (vuln.fixAvailable && vuln.fixAvailable !== false) {
|
|
189
|
+
const fixVersion = vuln.fixAvailable.version || "最新版本";
|
|
190
|
+
markdown += `- **${pkgName}**: 升级到版本 ${fixVersion}\n`;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
markdown += `\n`;
|
|
194
|
+
markdown += `**执行以下命令修复漏洞:**\n\n`;
|
|
195
|
+
markdown += "```bash\n";
|
|
196
|
+
Object.entries(vulnerabilitiesObj).forEach(([pkgName, vuln]) => {
|
|
197
|
+
if (
|
|
198
|
+
vuln.fixAvailable &&
|
|
199
|
+
vuln.fixAvailable !== false &&
|
|
200
|
+
vuln.fixAvailable.name
|
|
201
|
+
) {
|
|
202
|
+
const fixVersion = vuln.fixAvailable.version || "latest";
|
|
203
|
+
markdown += `npm install ${vuln.fixAvailable.name}@${fixVersion}\n`;
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
markdown += "```\n\n";
|
|
207
|
+
} else if (Object.keys(vulnerabilitiesObj).length > 0) {
|
|
208
|
+
markdown += `⚠️ **注意**: 发现漏洞但暂无直接修复方案,请考虑以下替代方案:\n`;
|
|
209
|
+
markdown += `1. 查看是否有可用的次要版本更新\n`;
|
|
210
|
+
markdown += `2. 考虑使用替代包\n`;
|
|
211
|
+
markdown += `3. 评估风险是否可接受\n\n`;
|
|
212
|
+
} else {
|
|
213
|
+
markdown += `✅ **无需修复** - 项目目前没有安全漏洞。\n\n`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 结尾
|
|
217
|
+
markdown += `## 报告说明\n`;
|
|
218
|
+
markdown += `- 本报告基于 \`npm audit --json\` 命令生成\n`;
|
|
219
|
+
markdown += `- CVSS (Common Vulnerability Scoring System) 分数范围 0-10,分数越高风险越大\n`;
|
|
220
|
+
markdown += `- 建议定期运行 \`npm audit\` 检查项目安全状况\n`;
|
|
221
|
+
markdown += `- 更多信息请参考 [npm 安全文档](https://docs.npmjs.com/auditing-package-dependencies-for-security-vulnerabilities)\n\n`;
|
|
222
|
+
|
|
223
|
+
// 将生成的 auditResultMd 转换成md 文件格式 文件放在根目录下
|
|
224
|
+
await fs.promises.writeFile(savePath, markdown);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error(`Error rendering audit result: ${error.message}`);
|
|
227
|
+
return `# NPM Audit 报告渲染错误\n\n❌ **错误**: ${error.message}\n\n**原始结果**:\n\n\`\`\`json\n${auditResult.substring(0, 1000)}\n\`\`\``;
|
|
228
|
+
}
|
|
229
|
+
}
|