feat: hide sensitive info in stdout/sdtin
This commit is contained in:
		
							parent
							
								
									cdb98f919f
								
							
						
					
					
						commit
						fb0caa005b
					
				
							
								
								
									
										5
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								index.js
									
									
									
									
									
								
							| @ -1,6 +1,8 @@ | ||||
| const marked = require('marked'); | ||||
| const TerminalRenderer = require('marked-terminal'); | ||||
| const envCi = require('env-ci'); | ||||
| const hookStd = require('hook-std'); | ||||
| const hideSensitive = require('./lib/hide-sensitive'); | ||||
| const getConfig = require('./lib/get-config'); | ||||
| const getNextVersion = require('./lib/get-next-version'); | ||||
| const getCommits = require('./lib/get-commits'); | ||||
| @ -96,8 +98,10 @@ async function run(opts) { | ||||
| } | ||||
| 
 | ||||
| module.exports = async opts => { | ||||
|   const unhook = hookStd({silent: false}, hideSensitive); | ||||
|   try { | ||||
|     const result = await run(opts); | ||||
|     unhook(); | ||||
|     return result; | ||||
|   } catch (err) { | ||||
|     const errors = err.name === 'AggregateError' ? Array.from(err).sort(error => !error.semanticRelease) : [err]; | ||||
| @ -108,6 +112,7 @@ module.exports = async opts => { | ||||
|         logger.error('An error occurred while running semantic-release: %O', error); | ||||
|       } | ||||
|     } | ||||
|     unhook(); | ||||
|     throw err; | ||||
|   } | ||||
| }; | ||||
|  | ||||
							
								
								
									
										11
									
								
								lib/hide-sensitive.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								lib/hide-sensitive.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| const {escapeRegExp} = require('lodash'); | ||||
| 
 | ||||
| const regexp = new RegExp( | ||||
|   Object.keys(process.env) | ||||
|     .filter(envVar => /token|password|credential|secret|private/i.test(envVar)) | ||||
|     .map(envVar => escapeRegExp(process.env[envVar])) | ||||
|     .join('|'), | ||||
|   'g' | ||||
| ); | ||||
| 
 | ||||
| module.exports = output => output.replace(regexp, '[secure]'); | ||||
| @ -33,6 +33,7 @@ | ||||
|     "execa": "^0.9.0", | ||||
|     "get-stream": "^3.0.0", | ||||
|     "git-log-parser": "^1.2.0", | ||||
|     "hook-std": "^0.4.0", | ||||
|     "lodash": "^4.17.4", | ||||
|     "marked": "^0.3.9", | ||||
|     "marked-terminal": "^2.0.0", | ||||
| @ -44,6 +45,7 @@ | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "ava": "^0.25.0", | ||||
|     "clear-module": "^2.1.0", | ||||
|     "codecov": "^3.0.0", | ||||
|     "commitizen": "^2.9.6", | ||||
|     "cz-conventional-changelog": "^2.0.0", | ||||
|  | ||||
							
								
								
									
										34
									
								
								test/hide-sensitive.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								test/hide-sensitive.test.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| import test from 'ava'; | ||||
| import clearModule from 'clear-module'; | ||||
| 
 | ||||
| test.beforeEach(() => { | ||||
|   process.env = {}; | ||||
|   clearModule('../lib/hide-sensitive'); | ||||
| }); | ||||
| 
 | ||||
| test.serial('Replace multiple sensitive environment variable values', t => { | ||||
|   process.env.SOME_PASSWORD = 'password'; | ||||
|   process.env.SOME_TOKEN = 'secret'; | ||||
|   t.is( | ||||
|     require('../lib/hide-sensitive')( | ||||
|       `https://user:${process.env.SOME_PASSWORD}@host.com?token=${process.env.SOME_TOKEN}` | ||||
|     ), | ||||
|     'https://user:[secure]@host.com?token=[secure]' | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test.serial('Replace multiple occurences of sensitive environment variable values', t => { | ||||
|   process.env.secretKey = 'secret'; | ||||
|   t.is( | ||||
|     require('../lib/hide-sensitive')(`https://user:${process.env.secretKey}@host.com?token=${process.env.secretKey}`), | ||||
|     'https://user:[secure]@host.com?token=[secure]' | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| test.serial('Escape regexp special characters', t => { | ||||
|   process.env.SOME_CREDENTIALS = 'p$^{.+}\\w[a-z]o.*rd'; | ||||
|   t.is( | ||||
|     require('../lib/hide-sensitive')(`https://user:${process.env.SOME_CREDENTIALS}@host.com`), | ||||
|     'https://user:[secure]@host.com' | ||||
|   ); | ||||
| }); | ||||
| @ -2,6 +2,7 @@ import test from 'ava'; | ||||
| import proxyquire from 'proxyquire'; | ||||
| import {stub} from 'sinon'; | ||||
| import tempy from 'tempy'; | ||||
| import clearModule from 'clear-module'; | ||||
| import SemanticReleaseError from '@semantic-release/error'; | ||||
| import DEFINITIONS from '../lib/plugins/definitions'; | ||||
| import {gitHead as getGitHead} from '../lib/git'; | ||||
| @ -12,21 +13,25 @@ const envBackup = Object.assign({}, process.env); | ||||
| // Save the current working diretory
 | ||||
| const cwd = process.cwd(); | ||||
| 
 | ||||
| stub(process.stdout, 'write'); | ||||
| stub(process.stderr, 'write'); | ||||
| 
 | ||||
| test.beforeEach(t => { | ||||
|   clearModule('../lib/hide-sensitive'); | ||||
| 
 | ||||
|   // Stub the logger functions
 | ||||
|   t.context.log = stub(); | ||||
|   t.context.error = stub(); | ||||
|   t.context.logger = {log: t.context.log, error: t.context.error}; | ||||
|   t.context.stdout = stub(process.stdout, 'write'); | ||||
|   t.context.stderr = stub(process.stderr, 'write'); | ||||
| }); | ||||
| 
 | ||||
| test.afterEach.always(() => { | ||||
| test.afterEach.always(t => { | ||||
|   // Restore process.env
 | ||||
|   process.env = envBackup; | ||||
|   // Restore the current working directory
 | ||||
|   process.chdir(cwd); | ||||
| 
 | ||||
|   t.context.stdout.restore(); | ||||
|   t.context.stderr.restore(); | ||||
| }); | ||||
| 
 | ||||
| test.serial('Plugins are called with expected values', async t => { | ||||
| @ -571,6 +576,31 @@ test.serial('Exclude commits with [skip release] or [release skip] from analysis | ||||
|   t.deepEqual(analyzeCommits.args[0][1].commits[0].message, commits[commits.length - 1].message); | ||||
| }); | ||||
| 
 | ||||
| test.serial('Hide sensitive environment variable values from the logs', async t => { | ||||
|   process.env.MY_TOKEN = 'secret token'; | ||||
|   await gitRepo(); | ||||
| 
 | ||||
|   const options = { | ||||
|     branch: 'master', | ||||
|     repositoryUrl: 'git@hostname.com:owner/module.git', | ||||
|     verifyConditions: async (pluginConfig, {logger}) => { | ||||
|       console.log(`Console: The token ${process.env.MY_TOKEN} is invalid`); | ||||
|       logger.log(`Log: The token ${process.env.MY_TOKEN} is invalid`); | ||||
|       logger.error(`Error: The token ${process.env.MY_TOKEN} is invalid`); | ||||
|       throw new Error(`Invalid token ${process.env.MY_TOKEN}`); | ||||
|     }, | ||||
|   }; | ||||
|   const semanticRelease = proxyquire('..', { | ||||
|     'env-ci': () => ({isCi: true, branch: 'master', isPr: false}), | ||||
|   }); | ||||
| 
 | ||||
|   await t.throws(semanticRelease(options)); | ||||
|   t.regex(t.context.stdout.args[7][0], /Console: The token \[secure\] is invalid/); | ||||
|   t.regex(t.context.stdout.args[8][0], /Log: The token \[secure\] is invalid/); | ||||
|   t.regex(t.context.stderr.args[0][0], /Error: The token \[secure\] is invalid/); | ||||
|   t.regex(t.context.stderr.args[1][0], /Invalid token \[secure\]/); | ||||
| }); | ||||
| 
 | ||||
| test.serial('Throw SemanticReleaseError if repositoryUrl is not set and cannot be found from repo config', async t => { | ||||
|   // Create a git repository, set the current working directory at the root of the repo
 | ||||
|   await gitRepo(); | ||||
|  | ||||
| @ -61,7 +61,7 @@ test.beforeEach(() => { | ||||
| 
 | ||||
|   // Delete all `npm_config` environment variable set by CI as they take precedence over the `.npmrc` because the process that runs the tests is started before the `.npmrc` is created
 | ||||
|   for (let i = 0, keys = Object.keys(process.env); i < keys.length; i++) { | ||||
|     if (keys[i].startsWith('npm_config')) { | ||||
|     if (keys[i].startsWith('npm_')) { | ||||
|       delete process.env[keys[i]]; | ||||
|     } | ||||
|   } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user