解释在 grunt 中实现漏洞修复 MR 自动创建

解读

面试官问的是“如何用 Grunt 把漏洞修复这件事做成自动化 Merge Request”。
国内主流代码托管是 GitLab(私有部署版),所以考点是:

  1. 让 Grunt 感知漏洞(依赖库 CVE、SAST 扫描结果);
  2. 自动改代码(升级版本号、打补丁);
  3. 调用 GitLab API 创建 MR;
  4. 把整条链路串成一条 Grunt 任务,CI 里一键触发。
    回答时要体现**“老工具新玩法”**:Grunt 只做编排,真正干活的是插件+脚本,但入口必须是 grunt 命令,否则跑题。

知识点

  • grunt-contrib-watch:监听 npm auditpnpm auditsnyk test 生成的 json 报告,报告变化即触发后续任务。
  • grunt-exec:在 Gruntfile 里跑 shell,例如 npm audit fix --package-lock-only --dry-run 先试修,确认无 break change 再真修。
  • grunt-bump:把 package.json 版本号按公司规范(语义化+内部日历版本)自动 +1,并写回文件。
  • grunt-gitgit add package*.json && git commit -m "security: fix CVE-2024-xxxx",同时新建分支 security-patch-${timestamp}
  • node-gitlab 或原生 fetch:用 GitLab Personal Access Token(国内习惯叫私有令牌)调 POST /projects/:id/merge_requests,指定 source_branchtarget_branch(通常是 release)、titledescription(贴上 audit 报告截图地址与漏洞列表)。
  • MR 模板:在 .gitlab/merge_request_templates/security.md 预置检查项,Grunt 调用 API 时把 template=security 带进去,保证评审人一眼看到影响面。
  • 失败回滚:Grunt task 链里加 grunt.fail.fatal,如果单元测试(grunt-contrib-jasmine/grunt-karma)未通过,则自动 git branch -D 本地临时分支并退出 CI,防止污染。
  • 钉钉/飞书机器人grunt-http 把 MR 地址推给安全群,@ 对应责任人,符合国内“安全事件 30 分钟内响应”的合规要求。

答案

  1. 在项目根目录装依赖
    npm i -D grunt grunt-contrib-watch grunt-exec grunt-bump grunt-git node-gitlab @types/node

  2. 新建 grunt/security-patch.js,封装 GitLab 客户端:

    const { Gitlab } = require('gitlab');
    const api = new Gitlab({ token: process.env.GITLAB_TOKEN, host: 'https://git.company.cn' });
    exports.createMR = async (projId, branch, title, desc) => {
      return api.MergeRequests.create(projId, branch, 'release', title, { description: desc });
    };
    
  3. Gruntfile.js 核心任务链:

    module.exports = function(grunt) {
      grunt.initConfig({
        exec: {
          audit: 'npm audit --json > audit.json',
          fix: 'npm audit fix --package-lock-only'
        },
        bump: {
          options: { files: ['package.json'], commit: false, createTag: false, push: false }
        },
        gitadd: { task: { files: { src: ['package.json', 'package-lock.json'] } } },
        gitcommit: {
          task: { options: { message: 'security: auto fix CVEs' } }
        },
        gitbranch: {
          task: { options: { name: 'auto/<%= grunt.template.today("yyyymmddHHMM") %>' } }
        }
      });
    
      grunt.registerTask('createMR', async function() {
        const done = this.async();
        const { createMR } = require('./grunt/security-patch');
        const projId = process.env.CI_PROJECT_ID || '123';
        const branch = grunt.config.get('gitbranch.task.options.name');
        const mr = await createMR(projId, branch, 
          `【安全】自动修复高危漏洞 ${grunt.template.today('yyyy-mm-dd')}`,
          '详见 CI 报告附件,已跑通单元测试');
        grunt.log.oklns('MR 地址:' + mr.web_url);
        done();
      });
    
      grunt.registerTask('security', 
        ['exec:audit', 'exec:fix', 'bump:patch', 'gitbranch', 'gitadd', 'gitcommit', 'createMR']);
    };
    
  4. .gitlab-ci.yml 里加一条:

    security:
      stage: check
      only:
        - schedules  # 每周一凌晨跑
      script:
        - npm run grunt security
    
  5. 效果:
    周一 02:00 CI 自动检出漏洞 → 本地修复 → 版本号 +0.0.1 → 推新分支 → 创建 MR → 钉钉群收到卡片 → 安全工程师上班直接评审合并,全程零人工

拓展思考

  • 多仓库批量修复:把 Gruntfile 做成独立脚手架包 @company/grunt-security,通过 grunt-hub 一次性扫描上百个前端仓库,实现“集团级漏洞治理”。
  • 与 SCA 平台对接:国内银行常用 奇安信、悬镜 等 SCA,Grunt 任务里解析它们导出的 csv,把 CVE 与组件坐标映射成 npm|axios@0.21.1 格式,再精准升级,避免“一刀切”带来的回归问题。
  • MR 质量门禁:在 GitLab 私有版里启用 Approval Rule,要求“安全团队 + 测试团队”双签;Grunt 调用 API 时直接把 approval_rules 参数写进去,防止开发私自合并。
  • 灰度发布:Grunt 任务链再加一步 grunt-ssh,把修复后的包推到 内部 Verdaccio 私服beta 频道,让低流量业务先验证 24h,无告警再正式发版,实现“安全修复也能灰度”。