From 7da58b7192a8e2b4c25003d467526b2328a83bf6 Mon Sep 17 00:00:00 2001 From: Samir Musali Date: Fri, 21 Aug 2020 10:15:18 -0400 Subject: [PATCH] fix: make sure `process.getuid` doesn't get called on `Windows` `process.getuid` method isn't supported on `Windows` as explained [here](https://nodejs.org/api/process.html#process_process_getuid); so, this commit is for fixing that bug. Ref: LOG-6981 Semver: patch --- .gitignore | 1 + CHANGELOG.md | 1 + index.js | 402 +++++++++++++------------- test/unit/index.js | 18 +- tools/files/win32/logdna-agent.nuspec | 4 - tools/scripts/darwin.sh | 3 +- tools/scripts/win32.sh | 5 +- 7 files changed, 217 insertions(+), 217 deletions(-) diff --git a/.gitignore b/.gitignore index a235188e..023edd02 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ lib-cov # Coverage directory used by tools like istanbul coverage +.nyc_output # node-waf configuration .lock-wscript diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d5a2a85..45d77761 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fix stringification issue on handling 207 [#152](https://github.com/logdna/logdna-agent/pull/152) - Fix memory leak issue [#157](https://github.com/logdna/logdna-agent/pull/157) +- Make sure `process.getuid` doesn't get called on `Windows` [#188](https://github.com/logdna/logdna-agent/pull/188) ## [1.6.2] - December 5, 2019 ### Added diff --git a/index.js b/index.js index 7f2794b2..45da05f2 100644 --- a/index.js +++ b/index.js @@ -63,251 +63,253 @@ commander console.log() }) -function loadConfig(program, uid) { - if ((os.platform() === 'win32' && require('is-administrator')()) || uid <= 0) { - const conf_file = program.config || config.DEFAULT_CONF_FILE - debug(`Path to Configuration File: ${conf_file}`) - async.waterfall([ - (cb) => { - fs.access(conf_file, (error) => { +function loadConfig(program) { + const conf_file = program.config || config.DEFAULT_CONF_FILE + debug(`Path to Configuration File: ${conf_file}`) + async.waterfall([ + (cb) => { + fs.access(conf_file, (error) => { + if (error) { + return cb(null, {}) + } + + return properties.parse(conf_file, { + path: true + }, (error, parsedConfig) => { if (error) { - return cb(null, {}) + utils.log(`error in parsing ${conf_file}: ${error}`, 'error') } - - return properties.parse(conf_file, { - path: true - }, (error, parsedConfig) => { - if (error) { - utils.log(`error in parsing ${conf_file}: ${error}`, 'error') - } - return cb(null, error ? {} : parsedConfig) - }) + return cb(null, error ? {} : parsedConfig) }) + }) + } + , (parsedConfig, cb) => { + parsedConfig = parsedConfig || {} + + // allow key to be passed via env + if (process.env.LOGDNA_AGENT_KEY) { + parsedConfig.key = process.env.LOGDNA_AGENT_KEY } - , (parsedConfig, cb) => { - parsedConfig = parsedConfig || {} - - // allow key to be passed via env - if (process.env.LOGDNA_AGENT_KEY) { - parsedConfig.key = process.env.LOGDNA_AGENT_KEY - } - if (!program.key && !parsedConfig.key) { - console.error('LogDNA Ingestion Key not set! Use -k to set or use environment variable LOGDNA_AGENT_KEY.') - return - } - - if (process.env.LOGDNA_HOSTNAME) { - parsedConfig.hostname = process.env.LOGDNA_HOSTNAME - } + if (!program.key && !parsedConfig.key) { + console.error('LogDNA Ingestion Key not set! Use -k to set or use environment variable LOGDNA_AGENT_KEY.') + return + } - if (!program.hostname && !parsedConfig.hostname) { - if (fs.existsSync(HOSTNAME_PATH) && fs.statSync(HOSTNAME_PATH).isFile()) { - parsedConfig.hostname = fs.readFileSync(HOSTNAME_PATH).toString().trim().replace(HOSTNAME_IP_REGEX, '') - } else if (os.hostname()) { - parsedConfig.hostname = os.hostname().replace('.ec2.internal', '') - } else { - console.error('Hostname information cannot be found! Use -n to set or use environment variable LOGDNA_HOSTNAME.') - return - } - } + if (process.env.LOGDNA_HOSTNAME) { + parsedConfig.hostname = process.env.LOGDNA_HOSTNAME + } - // allow exclude to be passed via env - if (process.env.LOGDNA_EXCLUDE) { - parsedConfig.exclude = process.env.LOGDNA_EXCLUDE + if (!program.hostname && !parsedConfig.hostname) { + if (fs.existsSync(HOSTNAME_PATH) && fs.statSync(HOSTNAME_PATH).isFile()) { + parsedConfig.hostname = fs.readFileSync(HOSTNAME_PATH).toString().trim().replace(HOSTNAME_IP_REGEX, '') + } else if (os.hostname()) { + parsedConfig.hostname = os.hostname().replace('.ec2.internal', '') + } else { + console.error('Hostname information cannot be found! Use -n to set or use environment variable LOGDNA_HOSTNAME.') + return } + } - // allow exclude regex to be passed via env - if (process.env.LOGDNA_EXCLUDE_REGEX) { - parsedConfig.exclude_regex = process.env.LOGDNA_EXCLUDE_REGEX - } + // allow exclude to be passed via env + if (process.env.LOGDNA_EXCLUDE) { + parsedConfig.exclude = process.env.LOGDNA_EXCLUDE + } - if (process.env.USEJOURNALD) { - parsedConfig.usejournald = process.env.USEJOURNALD - } + // allow exclude regex to be passed via env + if (process.env.LOGDNA_EXCLUDE_REGEX) { + parsedConfig.exclude_regex = process.env.LOGDNA_EXCLUDE_REGEX + } - // sanitize - if (!parsedConfig.logdir) { - parsedConfig.logdir = [config.DEFAULT_LOG_PATH] // default entry - } else if (!Array.isArray(parsedConfig.logdir)) { - parsedConfig.logdir = parsedConfig.logdir.split(',') // force array - } + if (process.env.USEJOURNALD) { + parsedConfig.usejournald = process.env.USEJOURNALD + } - if (parsedConfig.exclude && !Array.isArray(parsedConfig.exclude)) { - parsedConfig.exclude = parsedConfig.exclude.split(',') - } + // sanitize + if (!parsedConfig.logdir) { + parsedConfig.logdir = [config.DEFAULT_LOG_PATH] // default entry + } else if (!Array.isArray(parsedConfig.logdir)) { + parsedConfig.logdir = parsedConfig.logdir.split(',') // force array + } - var saveMessages = [] + if (parsedConfig.exclude && !Array.isArray(parsedConfig.exclude)) { + parsedConfig.exclude = parsedConfig.exclude.split(',') + } - if (program.key) { - parsedConfig.key = program.key - saveMessages.push('Your LogDNA Ingestion Key has been successfully saved!') - } + var saveMessages = [] - if (program.set && program.set.length > 0) { - program.set.forEach((setOption) => { - const kvPair = setOption.split('=') - if (kvPair.length === 2) { - parsedConfig[kvPair[0]] = kvPair[1] - saveMessages.push(`Config variable: ${kvPair[0]} = ${kvPair[1]} has been saved to config.`) - } else { - saveMessages.push(`Unknown setting: ${setOption}. Usage: -s [key=value]`) - } - }) - } + if (program.key) { + parsedConfig.key = program.key + saveMessages.push('Your LogDNA Ingestion Key has been successfully saved!') + } - if (program.winevent && program.winevent.length > 0) { - if (os.platform() === 'win32') { - processed = utils.processOption(program.winevent, parsedConfig.winevent, true) - parsedConfig.winevent = processed.values - saveMessages.push(`Windows Events: ${processed.diff} been saved to config.`) + if (program.set && program.set.length > 0) { + program.set.forEach((setOption) => { + const kvPair = setOption.split('=') + if (kvPair.length === 2) { + parsedConfig[kvPair[0]] = kvPair[1] + saveMessages.push(`Config variable: ${kvPair[0]} = ${kvPair[1]} has been saved to config.`) } else { - saveMessages.push('-w is only available for Windows.') + saveMessages.push(`Unknown setting: ${setOption}. Usage: -s [key=value]`) } - } + }) + } - if (program.list) { - if (typeof program.list === 'boolean') { - program.list = ['all'] - } - var conf = properties.parse(fs.readFileSync(conf_file).toString()) - const listResult = utils.pick2list(program.list, conf) - if (listResult.valid) { - var msg = utils.stringify(listResult.cfg) - saveMessages.push(`${conf_file}:\n${msg}`) - } else { - saveMessages.push(listResult.msg) - } + if (program.winevent && program.winevent.length > 0) { + if (os.platform() === 'win32') { + processed = utils.processOption(program.winevent, parsedConfig.winevent, true) + parsedConfig.winevent = processed.values + saveMessages.push(`Windows Events: ${processed.diff} been saved to config.`) + } else { + saveMessages.push('-w is only available for Windows.') } + } - if (program.unset && program.unset.length > 0) { - const unsetResult = utils.unsetConfig(program.unset, parsedConfig) - parsedConfig = unsetResult.cfg - saveMessages.push(unsetResult.msg) + if (program.list) { + if (typeof program.list === 'boolean') { + program.list = ['all'] } - - if (program.logdir && program.logdir.length > 0) { - processed = utils.processOption(program.logdir, parsedConfig.logdir) - parsedConfig.logdir = processed.values - saveMessages.push(`Log Directories: ${processed.diff} been saved to config.`) + var conf = properties.parse(fs.readFileSync(conf_file).toString()) + const listResult = utils.pick2list(program.list, conf) + if (listResult.valid) { + var msg = utils.stringify(listResult.cfg) + saveMessages.push(`${conf_file}:\n${msg}`) + } else { + saveMessages.push(listResult.msg) } + } - if (program.logfile && program.logfile.length > 0) { - processed = utils.processOption(program.logfile, parsedConfig.logdir) - parsedConfig.logdir = processed.values - saveMessages.push(`Log Files: ${processed.diff} been saved to config.`) - } + if (program.unset && program.unset.length > 0) { + const unsetResult = utils.unsetConfig(program.unset, parsedConfig) + parsedConfig = unsetResult.cfg + saveMessages.push(unsetResult.msg) + } - if (program.exclude && program.exclude.length > 0) { - processed = utils.processOption(program.exclude, parsedConfig.exclude) - parsedConfig.exclude = processed.values - saveMessages.push(`Exclusions: ${processed.diff} been saved to config.`) - } + if (program.logdir && program.logdir.length > 0) { + processed = utils.processOption(program.logdir, parsedConfig.logdir) + parsedConfig.logdir = processed.values + saveMessages.push(`Log Directories: ${processed.diff} been saved to config.`) + } - if (program.excludeRegex) { - parsedConfig.exclude_regex = program.excludeRegex - // strip leading and trailing / if exists - if (parsedConfig.exclude_regex.substring(0, 1) === '/' && parsedConfig.exclude_regex.substring(parsedConfig.exclude_regex.length - 1) === '/') { - parsedConfig.exclude_regex = parsedConfig.exclude_regex.substring(1, parsedConfig.exclude_regex.length - 1) - } - saveMessages.push(`Exclude pattern: /${parsedConfig.exclude_regex}/ been saved to config.`) - } + if (program.logfile && program.logfile.length > 0) { + processed = utils.processOption(program.logfile, parsedConfig.logdir) + parsedConfig.logdir = processed.values + saveMessages.push(`Log Files: ${processed.diff} been saved to config.`) + } - if (program.hostname) { - parsedConfig.hostname = program.hostname - saveMessages.push(`Hostname: ${parsedConfig.hostname} has been saved to config.`) - } + if (program.exclude && program.exclude.length > 0) { + processed = utils.processOption(program.exclude, parsedConfig.exclude) + parsedConfig.exclude = processed.values + saveMessages.push(`Exclusions: ${processed.diff} been saved to config.`) + } - if (program.tags && program.tags.length > 0) { - processed = utils.processOption(program.tags, parsedConfig.tags) - parsedConfig.tags = processed.values - saveMessages.push(`Tags: ${processed.diff} been saved to config.`) + if (program.excludeRegex) { + parsedConfig.exclude_regex = program.excludeRegex + // strip leading and trailing / if exists + if (parsedConfig.exclude_regex.substring(0, 1) === '/' && parsedConfig.exclude_regex.substring(parsedConfig.exclude_regex.length - 1) === '/') { + parsedConfig.exclude_regex = parsedConfig.exclude_regex.substring(1, parsedConfig.exclude_regex.length - 1) } + saveMessages.push(`Exclude pattern: /${parsedConfig.exclude_regex}/ been saved to config.`) + } - if (saveMessages.length) { - utils.saveConfig(parsedConfig, conf_file, (error, success) => { - if (error) { - return utils.log(`error while saving to: ${conf_file}: ${error}`, 'error') - } + if (program.hostname) { + parsedConfig.hostname = program.hostname + saveMessages.push(`Hostname: ${parsedConfig.hostname} has been saved to config.`) + } - saveMessages.forEach((message) => { console.log(message) }) - }) - return - } + if (program.tags && program.tags.length > 0) { + processed = utils.processOption(program.tags, parsedConfig.tags) + parsedConfig.tags = processed.values + saveMessages.push(`Tags: ${processed.diff} been saved to config.`) + } - // merge into single var after all potential saveConfigs finished - config = {...config, ...parsedConfig} - config.tags = process.env.LOGDNA_TAGS || config.tags - if (process.env.LOGDNA_PLATFORM) { - config.platform = process.env.LOGDNA_PLATFORM - config.tags = config.tags ? `${config.tags},${config.platform}` : config.platform - if (config.platform.indexOf('k8s') === 0) { - config.RESCAN_INTERVAL = config.RESCAN_INTERVAL_K8S + if (saveMessages.length) { + utils.saveConfig(parsedConfig, conf_file, (error, success) => { + if (error) { + return utils.log(`error while saving to: ${conf_file}: ${error}`, 'error') } - } - return getos(cb) - } - , (distro, cb) => { - config.package = `${pkg.name}/${pkg.version}` - config.distro = `${(distro.dist || distro.os).replace(/Linux| /g, '')}` - if (distro.release) { config.distro += `/${distro.release}` } - config.userAgent = `${config.package} (${config.distro})` - if (config.platform) { config.userAgent += ` ${config.platform}` } - - return request(config.AWS_INSTANCE_CHECK_URL, { - timeout: 1000 - , json: true - }, (error, response, body) => { - return cb(error, body) + saveMessages.forEach((message) => { console.log(message) }) }) - } - ], (_, responseBody) => { - if (responseBody) { - config.awsid = responseBody.instanceId - config.awsregion = responseBody.region - config.awsaz = responseBody.availabilityZone - config.awsami = responseBody.imageId - config.awstype = responseBody.instanceType - } - - const networkInterface = Object.values(os.networkInterfaces()).reduce((networkData, interfaces) => { - interfaces.forEach(interfce => networkData.push(interfce)) - return networkData - }, []).filter((interfce) => { - return interfce.family && interfce.family === 'IPv4' && interfce.mac && interfce.address && ( - interfce.address.startsWith('10.') - || interfce.address.startsWith('172.') - || interfce.address.startsWith('192.168.')) - })[0] - - if (networkInterface) { - if (networkInterface.mac) { config.mac = networkInterface.mac } - if (networkInterface.address) { config.ip = networkInterface.address } + return } - utils.log(`${config.package} started on ${config.hostname} (${config.ip})`) - if (config.platform && config.platform.startsWith('k8s')) { k8s.init() } - if (config.userAgent) { - config.DEFAULT_REQ_HEADERS['user-agent'] = config.userAgent - config.DEFAULT_REQ_HEADERS_GZIP['user-agent'] = config.userAgent + // merge into single var after all potential saveConfigs finished + config = {...config, ...parsedConfig} + config.tags = process.env.LOGDNA_TAGS || config.tags + if (process.env.LOGDNA_PLATFORM) { + config.platform = process.env.LOGDNA_PLATFORM + config.tags = config.tags ? `${config.tags},${config.platform}` : config.platform + if (config.platform.indexOf('k8s') === 0) { + config.RESCAN_INTERVAL = config.RESCAN_INTERVAL_K8S + } } - debug('connecting to log server') - connectionManager.connectLogServer(config) - debug('logdna agent successfully started') - }) - } else { - console.log('You must be an Administrator (root, sudo) run this agent! See -h or --help for more info.') - return - } + return getos(cb) + } + , (distro, cb) => { + config.package = `${pkg.name}/${pkg.version}` + config.distro = `${(distro.dist || distro.os).replace(/Linux| /g, '')}` + if (distro.release) { config.distro += `/${distro.release}` } + config.userAgent = `${config.package} (${config.distro})` + if (config.platform) { config.userAgent += ` ${config.platform}` } + + return request(config.AWS_INSTANCE_CHECK_URL, { + timeout: 1000 + , json: true + }, (error, response, body) => { + return cb(error, body) + }) + } + ], (_, responseBody) => { + if (responseBody) { + config.awsid = responseBody.instanceId + config.awsregion = responseBody.region + config.awsaz = responseBody.availabilityZone + config.awsami = responseBody.imageId + config.awstype = responseBody.instanceType + } + + const networkInterface = Object.values(os.networkInterfaces()).reduce((networkData, interfaces) => { + interfaces.forEach(interfce => networkData.push(interfce)) + return networkData + }, []).filter((interfce) => { + return interfce.family && interfce.family === 'IPv4' && interfce.mac && interfce.address && ( + interfce.address.startsWith('10.') + || interfce.address.startsWith('172.') + || interfce.address.startsWith('192.168.')) + })[0] + + if (networkInterface) { + if (networkInterface.mac) { config.mac = networkInterface.mac } + if (networkInterface.address) { config.ip = networkInterface.address } + } + + utils.log(`${config.package} started on ${config.hostname} (${config.ip})`) + if (config.platform && config.platform.startsWith('k8s')) { k8s.init() } + if (config.userAgent) { + config.DEFAULT_REQ_HEADERS['user-agent'] = config.userAgent + config.DEFAULT_REQ_HEADERS_GZIP['user-agent'] = config.userAgent + } + + debug('connecting to log server') + connectionManager.connectLogServer(config) + debug('logdna agent successfully started') + }) } process.on('uncaughtException', err => utils.log(`uncaught error: ${(err.stack || '').split('\r\n')}`, 'error')) if (require.main === module) { commander.parse(process.argv) - loadConfig(commander, process.getuid()) + const isAdmin = os.platform() === 'win32' ? require('is-administrator')() : process.getuid() === 0 + + if (!isAdmin) { + console.log('You must be an Administrator (root, sudo) to run this agent! See -h or --help for more info.') + return + } + + loadConfig(commander) } module.exports = { diff --git a/test/unit/index.js b/test/unit/index.js index 1e9bd2ed..4b7e45e7 100644 --- a/test/unit/index.js +++ b/test/unit/index.js @@ -6,8 +6,6 @@ const fs = require('fs') const {test} = require('tap') const {loadConfig, commander} = require('../../index.js') -const uid = 0 - test('Commander help output', (t) => { commander.outputHelp((help) => { t.match(help, /^Usage: logdna-agent \[options\]/, 'Help menu printed') @@ -23,7 +21,7 @@ test('loadConfig() settings based on user input', async (t) => { t.test('Errors if no key is found', (tt) => { loadConfig({ config: configFile - }, uid) + }) setTimeout(() => { fs.stat(configFile, (err) => { @@ -42,7 +40,7 @@ test('loadConfig() settings based on user input', async (t) => { , hostname: 'myMachine' } - loadConfig(input, uid) + loadConfig(input) // The callback is not exposed. We have to wait for the file to be written to. setTimeout(() => { @@ -64,7 +62,7 @@ test('loadConfig() settings based on user input', async (t) => { , logdir: logs } - loadConfig(input, uid) + loadConfig(input) setTimeout(() => { const contents = (fs.readFileSync(configFile, 'utf8')).split('\n').sort() @@ -91,7 +89,7 @@ test('loadConfig() settings based on user input', async (t) => { , tags } - loadConfig(input, uid) + loadConfig(input) setTimeout(() => { const contents = (fs.readFileSync(configFile, 'utf8')).split('\n') @@ -128,7 +126,7 @@ test('loadConfig() settings based on user input', async (t) => { loadConfig({ config: configFile , unset: ['set1'] - }, uid) + }) setTimeout(() => { const contents = (fs.readFileSync(configFile, 'utf8')).split('\n') @@ -148,7 +146,7 @@ test('loadConfig() settings based on user input', async (t) => { loadConfig({ config: configFile , unset: ['all'] - }, uid) + }) setTimeout(() => { const contents = (fs.readFileSync(configFile, 'utf8')) @@ -167,7 +165,7 @@ test('loadConfig() hostname decisions', async (t) => { , key: 'abc123' } - loadConfig(input, uid) + loadConfig(input) // The callback is not exposed. We have to wait for the file to be written to. setTimeout(() => { @@ -211,7 +209,7 @@ test('Process options through commander', (t) => { ] commander.parse(input) - loadConfig(commander, uid) + loadConfig(commander) setTimeout(() => { const contents = (fs.readFileSync(configFile, 'utf8')).split('\n').sort() diff --git a/tools/files/win32/logdna-agent.nuspec b/tools/files/win32/logdna-agent.nuspec index 0f3167b4..6eb18370 100644 --- a/tools/files/win32/logdna-agent.nuspec +++ b/tools/files/win32/logdna-agent.nuspec @@ -8,10 +8,6 @@ smusali,leeliu leeliu LogDNA Agent for Windows - 1.6.3 - smusali,leeliu - leeliu - LogDNA Agent for Windows LogDNA Collector Agent Streaming File and Event Logs to LogDNA. https://logdna.com/ https://github.com/logdna/logdna-agent diff --git a/tools/scripts/darwin.sh b/tools/scripts/darwin.sh index 8048e728..c6c0bcbf 100644 --- a/tools/scripts/darwin.sh +++ b/tools/scripts/darwin.sh @@ -52,7 +52,8 @@ cd ../.pkg mv ../.build/logdna-agent-${VERSION}.pkg logdna-agent-${VERSION}-unsigned.pkg productsign --sign "Developer ID Installer: Answerbook, Inc. (TT7664HMU3)" logdna-agent-${VERSION}-unsigned.pkg logdna-agent-${VERSION}.pkg SHA256CHECKSUM=$(shasum -a 256 logdna-agent-${VERSION}.pkg | cut -d' ' -f1) -sed "s/$(cat ../tools/files/darwin/logdna-agent.rb | grep sha256 | cut -d"'" -f2)/${SHA256CHECKSUM}/" ../tools/files/darwin/logdna-agent.rb > logdna-agent.rb +OLDSHA256CHECKSUM=$(cat ../tools/files/darwin/logdna-agent.rb | grep sha256 | cut -d"'" -f2) +sed "s/${OLDSHA256CHECKSUM}/${SHA256CHECKSUM}/" ../tools/files/darwin/logdna-agent.rb > logdna-agent.rb cd .. # STEP 4: RELEASE diff --git a/tools/scripts/win32.sh b/tools/scripts/win32.sh index 346c1600..a4f9c9b2 100644 --- a/tools/scripts/win32.sh +++ b/tools/scripts/win32.sh @@ -24,9 +24,10 @@ nexe -i index.js -o .build/tools/${PACKAGE_NAME}.exe -t win32-${ARCH}-${NODE_VER # STEP 2: PACKAGE cd .build/ -sed -i "s/latest/${VERSION}/" tools/VERIFICATION.txt +sed "s/latest/${VERSION}/" ./tools/VERIFICATION.txt > ./tools/VERIFICATION.txt SHA256CHECKSUM=$(shasum -a 256 tools/${PACKAGE_NAME}.exe | cut -d' ' -f1) -sed -i "s/$(cat tools/VERIFICATION.txt | grep 'checksum: ' | cut -d' ' -f4)/${SHA256CHECKSUM}/" tools/VERIFICATION.txt +OLDSHA256CHECKSUM=$(cat tools/VERIFICATION.txt | grep 'checksum: ' | cut -d' ' -f4) +sed "s/${OLDSHA256CHECKSUM}/${SHA256CHECKSUM}/" ./tools/VERIFICATION.txt > ./tools/VERIFICATION.txt choco pack logdna-agent.nuspec cd .. cp .build/*.nupkg .build/tools/*.exe .pkg/