From 3ba6c55fe1b15c19a29da6ed2991bbe7389f4487 Mon Sep 17 00:00:00 2001 From: Cuong Vu Date: Fri, 8 May 2020 10:19:55 +0700 Subject: [PATCH] chore: pre-commit secrets scanner (#1170) --- .gitignore | 3 ++ package.json | 6 ++- scripts/utils/git-secrets.js | 76 ++++++++++++++++++++++++++++++++++++ yarn.lock | 9 +++++ 4 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 scripts/utils/git-secrets.js diff --git a/.gitignore b/.gitignore index a2ffac3cad..bf03d7c838 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,6 @@ scripts/webpack/webpack-sass-prod/.temp/* # jest cache scripts/jest/.jest-cache + +# credentials temp files, using in yarn scan-secrets +.temp-credentials diff --git a/package.json b/package.json index f3be472330..9fe2e0fed0 100644 --- a/package.json +++ b/package.json @@ -26,11 +26,12 @@ "release:prod": "lerna run --parallel release:prod", "test": "lerna run --parallel test:ci -- --detectOpenHandles --runInBand", "test-e2e:ci": "lerna run test-e2e:ci --stream", - "update-doc": "node --unhandled-rejections=strict ./scripts/document-update/get-release-and-create-pr.js" + "update-doc": "node --unhandled-rejections=strict ./scripts/document-update/get-release-and-create-pr.js", + "scan-secrets": "node --unhandled-rejections=strict ./scripts/utils/git-secrets.js" }, "husky": { "hooks": { - "pre-commit": "lint-staged && lerna run test:update-badges --stream --parallel --since HEAD && git add .", + "pre-commit": "yarn scan-secrets && lint-staged && lerna run test:update-badges --stream --parallel --since HEAD && git add .", "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" } }, @@ -170,6 +171,7 @@ "serve": "^11.2.0", "serverless-plugin-ifelse": "^1.0.5", "serverless-single-page-app-plugin": "^1.0.2", + "shelljs": "^0.8.4", "start-server-and-test": "^1.11.0", "style-loader": "^0.23.1", "svelte": "^3.20.1", diff --git a/scripts/utils/git-secrets.js b/scripts/utils/git-secrets.js new file mode 100644 index 0000000000..6f3f8595ee --- /dev/null +++ b/scripts/utils/git-secrets.js @@ -0,0 +1,76 @@ +#!/usr/bin/env node + +const { resolve } = require('path') +const { writeFileSync, unlinkSync, existsSync, readFileSync } = require('fs') +const homedir = require('os').homedir() +const shell = require('shelljs') + +const TEMP_CRED_FILE = resolve(process.cwd(), './.temp-credentials') +const AWS_CRED_FILE = resolve(homedir, './.aws/credentials') + +/** + * Used to build AWS credentials file using environment variable + * https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-environment.html + * and credential file + * https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-shared.html + */ +const buildTempCredFile = () => { + const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID + const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY + const contentFromEnv = + awsAccessKeyId && awsSecretAccessKey + ? `[env] + aws_access_key_id = ${awsAccessKeyId} + aws_secret_access_key = ${awsSecretAccessKey} + + ` + : '' + const contentFromCredFile = existsSync(AWS_CRED_FILE) ? readFileSync(AWS_CRED_FILE, { encoding: 'utf-8' }) : '' + const finalContent = contentFromEnv + contentFromCredFile + writeFileSync(TEMP_CRED_FILE, finalContent) + console.log(`Wrote temp credentials file to: ${TEMP_CRED_FILE}\n`) +} + +const removeTempCredFile = () => { + unlinkSync(TEMP_CRED_FILE) + console.log(`Removed credentials file ${TEMP_CRED_FILE}\n`) +} + +/** + * FLOW: + * 1. GET AWS secrets from environment variables and ~/.aws/credentials + * 2. Merge those secrets into one file (temporary file) + * 3. Register AWS provider for git-secrets using the file in step 2 + * 4. Scan for secrets in staged files using git secrets --scan + * 5. Remove temporary file in step 2 + */ +const run = () => { + try { + buildTempCredFile() + // add AWS as a provider, with genereted credentials file + const { error: errorAddProvider, stdout: stdoutAddProvider, stderr: stderrAddProvider } = shell.exec( + `git secrets --add-provider -- git secrets --aws-provider ${TEMP_CRED_FILE}`, + { + stdio: 'inherit', + }, + ) + if (errorAddProvider) throw errorAddProvider + if (stderrAddProvider) throw stderrAddProvider + console.log('Added AWS provider\n', stdoutAddProvider) + + // scan for secrets + const { error: errorScan, stdout: stdoutScan, stderr: stderrScan } = shell.exec('git secrets --scan --cached', { + stdio: 'inherit', + }) + if (errorScan) throw errorScan + if (stderrScan) throw stderrScan + console.log('No secret found in staged files!\n', stdoutScan) + removeTempCredFile() + } catch (err) { + console.error(err) + removeTempCredFile() + throw err + } +} + +run() diff --git a/yarn.lock b/yarn.lock index 49af118fe2..1450aa07d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23428,6 +23428,15 @@ shelljs@^0.8.1, shelljs@^0.8.3: interpret "^1.0.0" rechoir "^0.6.2" +shelljs@^0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" + integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"