不小心把包含灵敏信息的代码提交到线上了怎么办…

为了避免这种状况再次发生,靠记忆来约束自己是靠不住的,所以完成一个快速扫描灵敏代码的钩子,然后通过东西来约束自己就显得非常有必要。

下面是快速完成流程:

  1. commitlint+husky标准git提交;
  2. 触发git hooks钩子pre-commit
  3. child_process进程获取commit信息;
  4. 获取提交文件内容;
  5. 代码过滤;
  6. 扫描出灵敏信息停止commit。

一、commitlint+husky

​ 这个网上太多资料,简略讲下流程。按次序履行以下流程

1. 履行命令
npm install --save-dev husky @commitlint/cli @commitlint/config-conventional
npx husky install
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'
2. 根目录新建commitlint.config.js
/*
  标准commit日志
  https://commitlint.js.org
*/
const types = [
    'build', // 主要意图是修正项目构建体系(例如glup,webpack,rollup的装备等)的提交
    'ci', // 修正项意图继续集成流程(Kenkins、Travis等)的提交
    'chore', // 构建进程或辅助东西的改变
    'docs', // 文档提交(documents)
    'feat', // 新增功用(feature)
    'fix', // 修复 bug
    'pref', // 功用、体验相关的提交
    'refactor', // 代码重构
    'revert', // 回滚某个更早的提交
    'style', // 不影响程序逻辑的代码修正、主要是款式方面的优化、修正
    'test', // 测验相关的开发,
  ],
  typeEnum = {
    rules: {
      'type-enum': [2, 'always', types],
    },
    value: () => {
      return types;
    },
  };
module.exports = {
  extends: ['@commitlint/config-conventional'],
  /*
  Level [0..2]: 0 disables the rule. For 1 it will be considered a warning for 2 an error.
  https://commitlint.js.org/#/reference-rules
*/
  rules: {
    'type-enum': typeEnum.rules['type-enum'],
    'subject-full-stop': [0, 'never'],
    'subject-case': [0, 'never'],
  },
};
3. 验证
git add .
git commit -m "提交测验" # 错误
git commit -m "feat: 提交测验" # 正确

二、pre-commit钩子

上面现已完成了commit-msg的检测及约束,接下来是本次要点用到的钩子pre-commit来完成代码扫描功用。

1. 新增pre-commit的husky

创立对应husky文件。

npx husky add .husky/pre-commit 'npm run pre-commit'
2. package.json

上面的命令除了新增pre-commit的husky文件,还增加了内容,触发pre-commit的时分履行npm run pre-commit,所以要在scripts中新增以下代码

"pre-commit": "node pre-commit.js",
3. pre-commit.js

然后接下来就是创立pre-commit.js,并完成事务逻辑。

touch pre-commit.js

随意在pre-commit.js新增点内容如console.log('pre-commit');

履行git add . && git commit -m "asd"能够看到新增内容的输出及不契合提交规矩的正告信息,表明想要完成的需求的基础条件都已具备了。

接下来完成commit相关文件内容的提取。

三、获取commit信息

获取git进程信息,能够直接用node的子进程器child_process来完成。

commit基本信息

新增以下代码

const childProcess = require('child_process');
// 提交记录
const commitStages = childProcess.execSync('git diff --name-status HEAD~3', {
  encoding: 'utf8',
});
console.log('提交记录:', commitStages);

然后履行node pre-commit.js

能够看到控制台输出这次提交的相关文件列表。

% node pre-commit.js
M       .gitignore
D       pre-commit.js
D       src/test copy.js
D       src/test.js

获取到提交的文件列表,接下来的就好办了。

四、获取文件内容

依据控制台输出,能够看到被追寻的文件前都有一个大写字母+一段空格。

简略解释下大写字母的含义:M:修正,D:删去 A:新增

接下来处理文件问题

1. 获取契合文件类型的文件列表

由于git commit的时分还会有其他信息,所以要依据契合提交的文件类型过滤。

/**
 * @description 获取类型清单
 * @param {*} arr
 * @param {*} type M:修正,D:删去 A:新增
 * @returns
 */
const getArrList = (str, type) => {
  const arr = str.split('\n');
  return arr.filter(item => {
    const regex = new RegExp(`[${type}].*`);
    if (regex.test(item)) {
      return item !== undefined;
    }
  });
};
2. 移除文件类型前缀及空格
// 格式化--移除 M   [fileName].[fileType]之间的空格
const formatList = (arr, type) => {
  return arr.map(item => {
    return item.replace(/\s/, '').replace(type, '');
  });
};
3. 获取过滤的文件列表

依据上面两个函数,能够过滤出修正和新增的文件,删去的不需求扫描,因此不过滤。。

// M:修正,D:删去 A:新增  
const typeList = ['M', 'A'];
  let fileList = [];
  typeList.forEach(type => {
    let arr = getArrList(commitStages, type);
    arr = formatList(arr, type);
    // 提交文件list
    fileList = fileList.concat(arr);
  });

接着履行node pre-commit.js

能够看到控制台输出提交的文件list及其间非删去文件的list

% node pre-commit.js
M       .gitignore
D       pre-commit.js
D       src/test copy.js
D       src/test.js
非删去文件 .gitignore

拿到最终需求过滤的文件list,接下来往下走。

五、代码过滤

1. 代码扫描
  1. fs模块解析文件

    先运用fs.readFileSync解析,获取文件代码内容

    const codes = fs.readFileSync(fileName, { encoding: 'utf-8' });
    
  2. 然后界说灵敏词汇

    const censetiveWords = ['hahahha']
    
  3. 接着扫描代码

    /**
    	* 扫描代码:是否有关键字
    	*/
    let flag = false; // 是否有关键字
    for (const code of words.filter(str => str)) {
      flag = codes.includes(code);
      if (flag) {
        console.log(`${fileName} 检测到灵敏代码 ${code}!!!`);
        break;
      }
    }
    
2. 停止commit程序

假如上面的程序扫描到灵敏词汇,履行停止提交程序。

if (flag) {
  // 退出进程
  process.exit(1);
}

由于是node环境,能够直接运用process来停止进程。
接下来功用验证。

3. 验证
  1. 随意新建一个文件src/test.js,增加内容

    console.log('这是需求检测的代码 baidu.com');
    
  2. pre-commit.js增加灵敏词汇

    // 灵敏关键字
    const censetiveWords = ['baidu.com'];
    
  3. 提交commit

    % git add .
    % git commit -m "feat: test"
    > hy-cli@0.0.1 pre-commit
    > node pre-commit.js
    src/test.js 检测到灵敏代码 baidu.com!!!
    husky - pre-commit hook exited with code 1 (error)
    

    能够看到控制台现已输出灵敏代码的正告信息,并退出git commit的进程husky - pre-commit hook exited with code 1 (error)

    至此完成完好功用,并自测通过。

六、.gitignore

上面完成了所有git追寻文件的过滤,但是在界说censetiveWords的时分,界说的当时文件也会被检测到,所以扫描代码的时分也要把当时文件剔除掉。

所以还有两点要注意

1. 不扫描当时文件

​ 在扫描代码之前,增加判断

// 当时文件pre-commit.js--已撤销git盯梢
const currentPath = __filename
// 扫描文件
const __path = __dirname + '/' + fileName
if (__path !== currentPath) {
	// 代码扫描
}
2. 当时文件不追寻

当时文件不会提交到线上,界说的灵敏词汇天然也就不会被上传

# .gitignore
# 灵敏代码检测
pre-commit.js 

总结

此次完成归根于一次代码上传失误事端,除了当时的完成,信任也有更好的方案规避这类问题~

人总是懒散的,能依靠东西规避的问题,尽量不要靠人为去约束。

最终是完成源码,其实很简略:

const fs = require('fs');
const childProcess = require('child_process');
/**
 * __dirname 当时文件夹途径
 * __filename 当时文件途径
 */
// 提交记录
// PS: 假如是新项目第一次提交,能够 HEAD~3 换成 HEAD~1
const commitStages = childProcess.execSync('git diff --name-status HEAD~3', {
  encoding: 'utf8',
});
/**
 * @description 获取类型清单
 * @param {*} arr
 * @param {*} type M:修正,D:删去 A:新增
 * @returns
 */
const getArrList = (str, type) => {
  const arr = str.split('\n');
  return arr.filter(item => {
    const regex = new RegExp(`[${type}].*`);
    if (regex.test(item)) {
      return item !== undefined;
    }
  });
};
// 格式化--移除 M   [fileName].[fileType]之间的空格
const formatList = (arr, type) => {
  return arr.map(item => {
    return item.replace(/\s/, '').replace(type, '');
  });
};
/**
 * @returns 获取commit files
 * PS:不过滤删去的文件
 */
function commitFiles() {
  const typeList = ['M', 'A'];
  let fileList = [];
  typeList.forEach(type => {
    let arr = getArrList(commitStages, type);
    arr = formatList(arr, type);
    // 提交文件list
    fileList = fileList.concat(arr);
  });
  return fileList;
}
// 灵敏关键字扫描
function scaner(fileList = [], words = []) {
  // const wordsStr = JSON.stringify(words)
  // 当时文件--已撤销git盯梢
  const currentPath = __filename
  // 文件扫描
  for (const fileName of fileList) {
    const __path = __dirname + '/' + fileName
    if (__path !== currentPath) {
      const codes = fs.readFileSync(__path, { encoding: 'utf-8' });
      /**
       * 扫描代码:是否有关键字
       */
      let flag = false; // 是否有关键字
      for (const code of words.filter(str => str)) {
        flag = codes.includes(code);
        if (flag) {
          console.log(`${fileName} 检测到灵敏代码 ${code}!!!`);
          break;
        }
      }
      if (flag) {
        // 退出进程
        process.exit(1);
      }
    }
  }
}
// 灵敏关键字
const censetiveWords = ['commit'];
scaner(commitFiles(), censetiveWords);