如何自动创建 JIRA 工单跟踪高危漏洞
解读
面试官抛出该问题,并非单纯考察“会不会写脚本”,而是验证候选人是否具备端到端安全闭环的自动化思维:
- 能否在Grunt 构建链路里无缝嵌入安全扫描;
- 扫描结果如何精准识别高危(非误报);
- 识别后如何零人工干预地生成 JIRA 工单,并保证国内网络环境下鉴权、字段、权限、审批流全部合规;
- 整套方案是否可灰度、可回滚、可审计,满足金融、电商等强监管甲方要求。
回答时要体现“前端构建工程师也能把安全左移”的角色意识,而非把锅甩给安全或运维。
知识点
- Grunt 事件钩子:grunt.task.run 前后插入自定义任务,利用grunt.event.on('task:success', fn) 监听构建结果。
- 国内 JIRA Data Center 主流鉴权方式:HTTP 基本认证 + 个人访问令牌(PAT),需绕过 Crowd 单点登录。
- 高危漏洞判定标准:CVSS≥7.0 且存在可利用 PoC 或影响线上业务路径(src 目录内直接依赖)。
- JIRA 必填字段映射:项目 key、issue type(Security Bug)、优先级(High→P2)、自定义字段“漏洞编号”“修复截止日”“所属迭代”。
- 幂等写入:以 dependency-name@version 作为唯一摘要,通过 JQL 先查重,防止重复工单。
- 国内网络加速:使用阿里云函数计算 SCF 或腾讯云 SCF 做中转,避免境外源 IP 被 JIRA 防火墙拦截。
- 合规审计:在 Gruntfile 同级目录落库安全日志(security-audit.json),包含工单 key、创建人、时间戳、CVSS,保留 3 年。
答案
- 安装依赖
npm i -D grunt-contrib-copy grunt-exec axios crypto
- 在 Gruntfile.js 中注册安全任务
module.exports = function(grunt) {
grunt.initConfig({
// 1. 运行 npm audit 生成 json 报告
exec: {
audit: {
cmd: 'npm audit --json > .tmp/audit.json'
}
},
// 2. 解析并过滤高危
audit_parse: {
options: {
cvssThreshold: 7.0,
jira: {
protocol: 'https',
host: 'jira.company.cn',
username: process.env.JIRA_USER, // 使用运维分配的只读账号
token: process.env.JIRA_TOKEN, // PAT,有效期 90 天
projectKey: 'SEC',
issueType: 'Security Bug',
priorityMap: { high: 'P2', critical: 'P1' }
}
}
}
});
grunt.registerMultiTask('audit_parse', function() {
const done = this.async();
const axios = require('axios');
const audit = grunt.file.readJSON('.tmp/audit.json');
const opts = this.options();
const high = audit.vulnerabilities
.filter(v => v.severity === 'high' || v.severity === 'critical')
.filter(v => v.cvss.score >= opts.cvssThreshold);
const client = axios.create({
baseURL: `${opts.jira.protocol}://${opts.jira.host}/rest/api/2`,
auth: { username: opts.jira.username, password: opts.jira.token },
headers: { 'Content-Type': 'application/json', 'X-Atlassian-Token': 'no-check' }
});
Promise.all(high.map(async vul => {
const summary = `[${vul.name}@${vul.version}] ${vul.title}`;
const jql = `project = ${opts.jira.projectKey} AND summary ~ "${vul.name}@${vul.version}"`;
const exist = await client.get(`/search?jql=${encodeURIComponent(jql)}`);
if (exist.data.total > 0) return; // 幂等
const payload = {
fields: {
project: { key: opts.jira.projectKey },
summary,
description: `h3. 漏洞详情\n*CVE:* ${vul.cve}\n*CVSS:* ${vul.cvss.score}\n*路径:* ${vul.paths.join('\n')}\n*h3. 修复建议*\n${vul.recommendation}`,
issuetype: { name: opts.jira.issueType },
priority: { name: opts.jira.priorityMap[vul.severity] },
customfield_10201: vul.name, // 漏洞编号
customfield_10202: new Date(Date.now() + 14*24*3600*1000).toISOString().slice(0,10) // 修复截止日
}
};
await client.post('/issue', payload);
grunt.log.ok(`JIRA 工单已创建:${vul.name}`);
})).then(() => done(), done);
});
// 3. 串联任务
grunt.registerTask('security', ['exec:audit', 'audit_parse']);
// 4. 嵌入默认构建
grunt.registerTask('default', ['clean', 'eslint', 'security', 'webpack']);
};
- 接入 CI(以 GitLab-CI 为例)
security:
stage: build
image: node:18-alpine
script:
- npm ci
- grunt security
only:
- master
artifacts:
reports:
junit: .tmp/security-audit.xml
expire_in: 30 days
- 灰度与回滚
- 通过feature-flag控制
grunt security是否生效,避免扫描组件升级导致误报阻塞发版; - 若批量误报,可在 JIRA 里一次性 bulk update 关闭,同时回滚
grunt-contrib-audit插件版本并锁定 package-lock.json。
拓展思考
- 如果公司采用飞书 OKR 系统而非 JIRA,可将 axios 目标换成飞书开放平台,用自建应用机器人发送多维表格记录,字段完全对齐,实现“双轨制”安全台账。
- 对于私有 npm 源(cnpm、verdaccio),需在 Grunt 任务里先执行
npx sync-npm-audit把私有包漏洞同步到官方 advisory,再跑 npm audit,否则会出现漏报。 - 进一步左移:在pre-commit 钩子里跑
grunt security --staged,仅扫描本次变更引入的依赖,把高危漏洞拦截在本地开发阶段,彻底降低修复成本。