【主项目依赖升级】
- hexo 6.x → 8.1.2:移除 swig-templates、swig-extras、旧版 cheerio、
hexo-fs/hexo-util 等大量漏洞依赖链,解决 critical/high 级别漏洞
- hexo-renderer-marked 5.x → 7.0.1:修复 dompurify XSS 漏洞(GHSA-hpcv)
- gulp 4.x → 5.0.1:修复 vinyl-fs、glob/glob-stream、chokidar、
braces 等 high 级漏洞链
- hexo-generator-sitemap 2.2.0 → 3.0.1:同步升级
【替换高危依赖】
- gulp-htmlmin 替换为 gulp-html-minifier-terser@7.1.0:
gulp-htmlmin 依赖的 html-minifier 存在 ReDoS 高危漏洞且无修复版本,
改用其维护分支 html-minifier-terser,API 完全兼容
- optimist 替换为 minimist@1.2.8(devDependency):
optimist 捆绑的 minimist@<=0.2.3 存在 prototype pollution critical 漏洞,
直接使用修复版 minimist 替代
【移除无修复版本的高危包】
- 移除 hexo-generator-baidu-sitemap(无维护更新,内部捆绑 hexo@4.x
导致一系列 critical 漏洞),在 scripts/generator.js 中实现等效的
自定义 Baidu Sitemap 生成器替代,功能与原包完全一致
【npm overrides 修复传递依赖漏洞】
- 添加 overrides: { ejs: "^6.0.1" }:
hexo-renderer-ejs@2.x 依赖 ejs@3.x,ejs@3.x 存在 SSTI critical 漏洞
且引入 jake → filelist → minimatch → brace-expansion 漏洞链;
ejs@6.x 已无 jake 依赖,critical 漏洞全部消除
【yilia 主题依赖升级】
- @babel/core 7.26.x → 7.29.7:修复 Arbitrary File Read 漏洞
- @babel/plugin-transform-runtime / @babel/preset-env 同步升级至 7.29.x
- postcss 8.5.3 → 8.5.25:修复 XSS 及路径遍历漏洞
- 移除 clean-webpack-plugin(依赖链含 brace-expansion 高危漏洞),
改用 webpack 5 内置 output.clean 实现等效的增量清理功能
- 添加 overrides: { brace-expansion: "5.0.9" } 修复剩余传递依赖漏洞
【构建兼容性修复】
- scripts/generator.js 新增 jsfiddle tag 注册:
hexo@8 移除了内置 {% jsfiddle %} tag 导致构建 FATAL,
重新实现该 tag 保持原有 iframe 嵌入行为
- gulpfile.js generate 任务错误处理修正:
原代码在 hexo FATAL 时调用 hexo.exit(err) 但不向 gulp 抛出异常,
导致 series 继续执行后续任务(compressHtml 因 public/ 不存在而报错),
修改为在 hexo 退出后重新 throw err 确保 gulp 正确感知失败
【修复结果】
- 主项目:56 个漏洞(8 低危、14 中危、25 高危、9 严重)→ 0 个漏洞
- yilia 主题:8 个漏洞 → 1 个低危
(剩余 1 个为 vue@2.x ReDoS,仅影响构建时自身模板解析,
修复需整体迁移到 Vue 3 重写主题,暂不处理)
81 lines
2.6 KiB
JavaScript
81 lines
2.6 KiB
JavaScript
const nunjucks = require('nunjucks')
|
||
const path = require('path')
|
||
const fs = require('fs')
|
||
|
||
// hexo@8 移除了内置 jsfiddle tag,在此重新注册
|
||
hexo.extend.tag.register('jsfiddle', function(args) {
|
||
const shortcode = args[0]
|
||
const tabs = args[1] || 'js,resources,html,css,result'
|
||
const skin = args[2] || 'light'
|
||
const width = args[3] || '100%'
|
||
const height = args[4] || '300'
|
||
return `<iframe style="width: ${width}; height: ${height}px" src="//jsfiddle.net/${shortcode}/embedded/${tabs}/?skin=${skin}" allowfullscreen="allowfullscreen" frameborder="0"></iframe>`
|
||
})
|
||
|
||
const env = new nunjucks.configure({ autoescape: false })
|
||
env.addFilter('noControlChars', function(str) {
|
||
return str && str.replace(/[\x00-\x1F\x7F]/g, '')
|
||
})
|
||
|
||
const searchTmplSrc = path.join(__dirname, '../templates/articles.xml')
|
||
|
||
hexo.extend.generator.register('xml', function(locals){
|
||
const searchTmpl = nunjucks.compile(fs.readFileSync(searchTmplSrc, 'utf8'), env)
|
||
const descCompare = function(value1, value2) {
|
||
if(value1 > value2) {
|
||
return -1
|
||
} else if(value1 < value2) {
|
||
return 1
|
||
} else {
|
||
return 0
|
||
}
|
||
}
|
||
const posts = locals.posts.toArray().sort(function(item1, item2){
|
||
return descCompare(item1.updateDate || item1.date, item2.updateDate || item2.date)
|
||
}).slice(0, 10)
|
||
const xmlData = searchTmpl.render({
|
||
posts,
|
||
root: this.config.root
|
||
})
|
||
return {
|
||
path: 'articles.xml',
|
||
data: xmlData
|
||
}
|
||
})
|
||
|
||
// 自定义 Baidu Sitemap 生成器(替代已废弃的 hexo-generator-baidu-sitemap)
|
||
hexo.extend.generator.register('baidusitemap', function(locals) {
|
||
const config = hexo.config.baidusitemap || {}
|
||
const outputPath = config.path || 'baidusitemap.xml'
|
||
|
||
const posts = locals.posts.toArray().filter(function(post) { return !post.draft })
|
||
const pages = locals.pages.toArray()
|
||
const allItems = posts.concat(pages).sort(function(a, b) {
|
||
const dateA = (a.updated || a.date)
|
||
const dateB = (b.updated || b.date)
|
||
return dateB - dateA
|
||
})
|
||
|
||
const lines = [
|
||
'<?xml version="1.0" encoding="UTF-8"?>',
|
||
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
|
||
]
|
||
|
||
allItems.forEach(function(item) {
|
||
const dateObj = item.updated || item.date
|
||
const dateStr = dateObj && dateObj.format
|
||
? dateObj.format('YYYY-MM-DD')
|
||
: new Date(dateObj).toISOString().split('T')[0]
|
||
lines.push(' <url>')
|
||
lines.push(' <loc>' + item.permalink + '</loc>')
|
||
lines.push(' <lastmod>' + dateStr + '</lastmod>')
|
||
lines.push(' </url>')
|
||
})
|
||
|
||
lines.push('</urlset>')
|
||
|
||
return {
|
||
path: outputPath,
|
||
data: lines.join('\n')
|
||
}
|
||
}) |