如何自动创建 JIRA 工单跟踪高危漏洞

解读

面试官抛出该问题,并非单纯考察“会不会写脚本”,而是验证候选人是否具备端到端安全闭环的自动化思维:

  1. 能否在Grunt 构建链路里无缝嵌入安全扫描;
  2. 扫描结果如何精准识别高危(非误报);
  3. 识别后如何零人工干预地生成 JIRA 工单,并保证国内网络环境下鉴权、字段、权限、审批流全部合规;
  4. 整套方案是否可灰度、可回滚、可审计,满足金融、电商等强监管甲方要求。

回答时要体现“前端构建工程师也能把安全左移”的角色意识,而非把锅甩给安全或运维。

知识点

  1. Grunt 事件钩子:grunt.task.run 前后插入自定义任务,利用grunt.event.on('task:success', fn) 监听构建结果。
  2. 国内 JIRA Data Center 主流鉴权方式:HTTP 基本认证 + 个人访问令牌(PAT),需绕过 Crowd 单点登录。
  3. 高危漏洞判定标准:CVSS≥7.0 且存在可利用 PoC影响线上业务路径(src 目录内直接依赖)。
  4. JIRA 必填字段映射:项目 key、issue type(Security Bug)、优先级(High→P2)、自定义字段“漏洞编号”“修复截止日”“所属迭代”。
  5. 幂等写入:以 dependency-name@version 作为唯一摘要,通过 JQL 先查重,防止重复工单。
  6. 国内网络加速:使用阿里云函数计算 SCF腾讯云 SCF 做中转,避免境外源 IP 被 JIRA 防火墙拦截。
  7. 合规审计:在 Gruntfile 同级目录落库安全日志(security-audit.json),包含工单 key、创建人、时间戳、CVSS,保留 3 年。

答案

  1. 安装依赖
npm i -D grunt-contrib-copy grunt-exec axios crypto
  1. 在 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']);
};
  1. 接入 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
  1. 灰度与回滚
  • 通过feature-flag控制 grunt security 是否生效,避免扫描组件升级导致误报阻塞发版;
  • 若批量误报,可在 JIRA 里一次性 bulk update 关闭,同时回滚 grunt-contrib-audit 插件版本并锁定 package-lock.json

拓展思考

  1. 如果公司采用飞书 OKR 系统而非 JIRA,可将 axios 目标换成飞书开放平台,用自建应用机器人发送多维表格记录,字段完全对齐,实现“双轨制”安全台账。
  2. 对于私有 npm 源(cnpm、verdaccio),需在 Grunt 任务里先执行 npx sync-npm-audit 把私有包漏洞同步到官方 advisory,再跑 npm audit,否则会出现漏报
  3. 进一步左移:在pre-commit 钩子里跑 grunt security --staged,仅扫描本次变更引入的依赖,把高危漏洞拦截在本地开发阶段,彻底降低修复成本。