netrc-parser
Advanced tools
Comparing version 2.0.6 to 3.0.0
433
lib/netrc.js
@@ -1,252 +0,209 @@ | ||
const fs = require('fs'); | ||
const os = require('os'); | ||
const path = require('path'); | ||
const Lexer = require('lex'); | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const tslib_1 = require("tslib"); | ||
const fs = require("fs-extra"); | ||
const os = require("os"); | ||
const path = require("path"); | ||
const execa = require("execa"); | ||
const lex_1 = require("./lex"); | ||
const Token = require("./token"); | ||
let _debug; | ||
function debug(...args) { | ||
try { | ||
if (!_debug) _debug = require('debug')('netrc-parser'); | ||
_debug(...args); | ||
} catch (err) {} | ||
} | ||
function findIndex(arr, fn) { | ||
for (let i = 0; i < arr.length; i++) { | ||
if (fn(arr[i])) return i; | ||
} | ||
return -1; | ||
} | ||
function readFile(file) { | ||
function decryptFile(file) { | ||
const { spawnSync } = require('child_process'); | ||
const args = ['--batch', '--quiet', '--decrypt', file]; | ||
debug('running gpg with args %o', args); | ||
const { stdout, status } = spawnSync('gpg', args, { stdio: [0, null, 2], encoding: 'utf8' }); | ||
if (status !== 0) throw new Error(`gpg exited with code ${status}`); | ||
return stdout; | ||
} | ||
if (path.extname(file) === '.gpg') return addTrailingNewline(decryptFile(file));else { | ||
try { | ||
return addTrailingNewline(fs.readFileSync(file, 'utf8')); | ||
} catch (err) { | ||
if (err.code !== 'ENOENT') throw err; | ||
return ''; | ||
if (process.env.NETRC_PARSER_DEBUG !== '1') | ||
return; | ||
if (!_debug) | ||
_debug = require('debug')('netrc-parser'); | ||
_debug(...args); | ||
} | ||
} | ||
catch (err) { } | ||
} | ||
function lex(body) { | ||
let tokens = []; | ||
let lexer = new Lexer(char => { | ||
throw new Error(`Unexpected character during netrc parsing at character ${char}: | ||
${body}`); | ||
}); | ||
lexer.addRule(/\s+/, content => { | ||
tokens.push({ type: 'whitespace', content }); | ||
}, [0, 1]); | ||
lexer.addRule(/#.*/, content => { | ||
tokens.push({ type: 'comment', content }); | ||
}, [0, 1]); | ||
lexer.addRule(/macdef/g, function (content) { | ||
this.state = 3; | ||
tokens.push({ type: 'macdef', content }); | ||
}, [0, 1, 3]); | ||
lexer.addRule(/machine +(\S+)/, function (content, value) { | ||
this.state = 1; | ||
tokens.push({ type: 'machine', content, value }); | ||
}, [0, 1, 3]); | ||
lexer.addRule(/[\s\S\n]/, function (content) { | ||
tokens[tokens.length - 1].content += content; | ||
}, [3]); | ||
lexer.addRule(/([a-zA-Z]+) +(\S+)/, (content, name, value) => { | ||
tokens.push({ type: 'prop', name, value }); | ||
}, [1]); | ||
lexer.addRule(/default/, function (content) { | ||
this.state = 1; | ||
tokens.push({ type: 'default', content }); | ||
}, [0]); | ||
lexer.setInput(body).lex(); | ||
return tokens; | ||
} | ||
function machineProxy(machine) { | ||
const props = () => machine._tokens.filter(t => t.type === 'prop'); | ||
const loadProps = () => props().forEach(prop => { | ||
machine[prop.name] = prop.value; | ||
}); | ||
loadProps(); | ||
return new Proxy(machine, { | ||
set: (machine, name, value) => { | ||
if (name === '_tokens') { | ||
machine._tokens = value; | ||
loadProps(); | ||
return true; | ||
} | ||
machine[name] = value; | ||
let prop = props().find(p => p.name === name); | ||
if (prop) prop.value = value;else { | ||
let lastPropIdx = findIndex(machine._tokens, t => t.type === 'prop'); | ||
let whitespace = lastPropIdx === -1 ? { type: 'whitespace', content: '\n ' } : machine._tokens[lastPropIdx - 1]; | ||
machine._tokens.splice(lastPropIdx, 0, whitespace); // insert whitespace | ||
machine._tokens.splice(lastPropIdx, 0, { type: 'prop', name, value }); | ||
} | ||
return true; | ||
} | ||
}); | ||
} | ||
function machinesProxy(content) { | ||
function addNewMachine(host) { | ||
let machine = machineProxy({ | ||
type: 'machine', | ||
value: host, | ||
_tokens: [{ type: 'machine', value: host, content: `machine ${host}` }, { type: 'whitespace', content: '\n' }] | ||
}); | ||
content.push(machine); | ||
return machine; | ||
} | ||
return new Proxy({}, { | ||
get: (machines, host) => { | ||
if (typeof host !== 'string') return machines[host]; | ||
if (!machines[host]) machines[host] = addNewMachine(host); | ||
return machines[host]; | ||
}, | ||
set: (machines, host, value) => { | ||
if (!machines[host]) machines[host] = addNewMachine(host); | ||
machines[host] = machineProxy(value); | ||
return true; | ||
}, | ||
deleteProperty: (machines, host) => { | ||
if (!machines[host]) return false; | ||
delete machines[host]; | ||
for (let i = 0; i < content.length; i++) { | ||
if (content[i].type === 'machine' && content[i].value === host) { | ||
content.splice(i, 1); | ||
} | ||
} | ||
return true; | ||
} | ||
}); | ||
} | ||
function addTrailingNewline(s) { | ||
if (s.endsWith('\n')) return s; | ||
return s + '\n'; | ||
} | ||
function homedir() { | ||
return os.platform() === 'win32' && (process.env.HOME || process.env.HOMEDRIVE && process.env.HOMEPATH && path.join(process.env.HOMEDRIVE, process.env.HOMEPATH) || process.env.USERPROFILE) || os.homedir() || os.tmpdir(); | ||
} | ||
const stdio = [0, null, 2]; | ||
/** | ||
* parses a netrc file | ||
*/ | ||
class Netrc { | ||
/** | ||
* generates or parses a netrc file | ||
* @example | ||
* const Netrc = require('netrc-parser') | ||
* const netrc = new Netrc() | ||
* netrc.machines['api.heroku.com'].password // get auth token from ~/.netrc | ||
*/ | ||
constructor(file) { | ||
if (!file) { | ||
file = path.join(homedir(), os.platform() === 'win32' ? '_netrc' : '.netrc'); | ||
if (fs.existsSync(file + '.gpg')) file += '.gpg'; | ||
class Netrc extends Token.Base { | ||
/** | ||
* generates or parses a netrc file | ||
* @example | ||
* const {Netrc} = require('netrc-parser') | ||
* const netrc = new Netrc() | ||
* netrc.machines['api.heroku.com'].password // get auth token from ~/.netrc | ||
*/ | ||
constructor(file) { | ||
super(); | ||
this._file = file; | ||
} | ||
this._tokens = []; | ||
this.file = file; | ||
this.machines = machinesProxy(this._tokens); | ||
this._parse(); | ||
} | ||
/** | ||
* save the current home netrc with any changes | ||
* @example | ||
* const Netrc = require('netrc-parser') | ||
* const netrc = new Netrc() | ||
* netrc.machines['api.heroku.com'].password = 'newpassword' | ||
* netrc.save() | ||
*/ | ||
save() { | ||
let body = this._tokens.map(t => { | ||
switch (t.type) { | ||
case 'default': | ||
case 'machine': | ||
let tokens = t._tokens || []; | ||
return tokens.map(t => { | ||
switch (t.type) { | ||
case 'prop': | ||
return `${t.name} ${t.value}`; | ||
case 'machine': | ||
case 'default': | ||
case 'comment': | ||
case 'whitespace': | ||
return t.content; | ||
get file() { | ||
return this._file; | ||
} | ||
get machines() { | ||
return this._machines; | ||
} | ||
get default() { | ||
return this._tokens.find(t => t.type === 'default'); | ||
} | ||
set default(v) { | ||
let idx = this._tokens.findIndex(t => t.type === 'default'); | ||
if (idx !== -1 && !v) | ||
this._tokens.splice(idx, 1); | ||
else { | ||
let newMachine = new Token.DefaultMachine(Object.assign({}, v, { post: '\n' })); | ||
if (idx !== -1 && v) | ||
this._tokens[idx] = newMachine; | ||
else if (v) | ||
this._tokens.push(newMachine); | ||
} | ||
} | ||
load() { | ||
return tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
if (!this._file) | ||
this._file = yield this.defaultFile(); | ||
this.parse(yield this.readFile()); | ||
}); | ||
} | ||
loadSync() { | ||
if (!this._file) | ||
this._file = this.defaultFileSync(); | ||
this.parse(this.readFileSync()); | ||
} | ||
/** | ||
* save the current home netrc with any changes | ||
* @example | ||
* const Netrc = require('netrc-parser') | ||
* const netrc = new Netrc() | ||
* await netrc.load() | ||
* netrc.machines['api.heroku.com'].password = 'newpassword' | ||
* netrc.save() | ||
*/ | ||
save() { | ||
return this.write(this.content); | ||
} | ||
/** | ||
* save the current home netrc with any changes | ||
* @example | ||
* const Netrc = require('netrc-parser') | ||
* const netrc = new Netrc() | ||
* netrc.loadSync() | ||
* netrc.machines['api.heroku.com'].password = 'newpassword' | ||
* netrc.saveSync() | ||
*/ | ||
saveSync() { | ||
this.writeSync(this.content); | ||
} | ||
get gpgEncryptArgs() { | ||
const args = ['-a', '--batch', '--default-recipient-self', '-e']; | ||
debug('running gpg with args %o', args); | ||
return args; | ||
} | ||
write(body) { | ||
return tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
if (this.file.endsWith('.gpg')) { | ||
const { stdout, code } = yield execa('gpg', this.gpgEncryptArgs, { input: body, stdio: [null, null, 2] }); | ||
if (code !== 0) | ||
throw new Error(`gpg exited with code ${code}`); | ||
body = stdout; | ||
} | ||
}).join(''); | ||
case 'macdef': | ||
case 'comment': | ||
case 'whitespace': | ||
return t.content; | ||
} | ||
}).join(''); | ||
this._write(body); | ||
} | ||
_write(body) { | ||
if (this.file.endsWith('.gpg')) { | ||
const { spawnSync } = require('child_process'); | ||
const args = ['-a', '--batch', '--default-recipient-self', '-e']; | ||
debug('running gpg with args %o', args); | ||
const { stdout, status } = spawnSync('gpg', args, { | ||
input: body, | ||
stdio: [null, 'pipe', 2], | ||
encoding: 'utf8' | ||
}); | ||
if (status !== 0) throw new Error(`gpg exited with code ${status}`); | ||
body = stdout; | ||
yield fs.writeFile(this.file, body, { mode: 0o600 }); | ||
}); | ||
} | ||
fs.writeFileSync(this.file, body, { mode: 0o600 }); | ||
} | ||
_parse() { | ||
let tokens = lex(readFile(this.file)); | ||
for (let i = 0; i < tokens.length; i++) { | ||
let getMachineTokens = () => { | ||
let machineTokens = []; | ||
while (1) { | ||
machineTokens.push(tokens[i]); | ||
let next = tokens[i + 1]; | ||
if (!next || ['machine', 'default', 'macdef'].includes(next.type) || next.type === 'default') break; | ||
i++; | ||
writeSync(body) { | ||
if (this.file.endsWith('.gpg')) { | ||
const { stdout, status } = execa.sync('gpg', this.gpgEncryptArgs, { input: body, stdio: [null, null, 2] }); | ||
if (status !== 0) | ||
throw new Error(`gpg exited with code ${status}`); | ||
body = stdout; | ||
} | ||
return machineTokens; | ||
}; | ||
switch (tokens[i].type) { | ||
case 'macdef': | ||
this._tokens.push(...getMachineTokens()); | ||
break; | ||
case 'default': | ||
this.default = machineProxy({ type: 'default', _tokens: getMachineTokens() }); | ||
this._tokens.push(this.default); | ||
break; | ||
case 'machine': | ||
let host = tokens[i].value; | ||
this.machines[host]._tokens = getMachineTokens(); | ||
break; | ||
default: | ||
this._tokens.push(tokens[i]); | ||
} | ||
fs.writeFileSync(this.file, body, { mode: 0o600 }); | ||
} | ||
} | ||
parse(body) { | ||
const tokens = lex_1.default(body); | ||
let cur = this; | ||
for (let token of tokens) { | ||
switch (token.type) { | ||
case 'default': | ||
cur = new Token.DefaultMachine(token); | ||
this.addToken(cur); | ||
break; | ||
case 'machine': | ||
cur = new Token.Machine(token); | ||
this.addToken(cur); | ||
break; | ||
case 'newline': | ||
cur = this; | ||
cur.addToken(token); | ||
break; | ||
default: | ||
cur.addToken(token); | ||
} | ||
} | ||
this._machines = Token.machinesProxy(this._tokens); | ||
} | ||
get gpgDecryptArgs() { | ||
const args = ['--batch', '--quiet', '--decrypt', this.file]; | ||
debug('running gpg with args %o', args); | ||
return args; | ||
} | ||
readFile() { | ||
return tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
const decryptFile = () => tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
const { code, stdout } = yield execa('gpg', this.gpgDecryptArgs, { stdio }); | ||
if (code !== 0) | ||
throw new Error(`gpg exited with code ${code}`); | ||
return stdout; | ||
}); | ||
if (path.extname(this.file) === '.gpg') | ||
return yield decryptFile(); | ||
else { | ||
try { | ||
return yield fs.readFile(this.file, 'utf8'); | ||
} | ||
catch (err) { | ||
if (err.code !== 'ENOENT') | ||
throw err; | ||
return ''; | ||
} | ||
} | ||
}); | ||
} | ||
readFileSync() { | ||
const decryptFile = () => { | ||
const { stdout, status } = execa.sync('gpg', this.gpgDecryptArgs, { stdio }); | ||
if (status !== 0) | ||
throw new Error(`gpg exited with code ${status}`); | ||
return stdout; | ||
}; | ||
if (path.extname(this.file) === '.gpg') | ||
return decryptFile(); | ||
else { | ||
try { | ||
return fs.readFileSync(this.file, 'utf8'); | ||
} | ||
catch (err) { | ||
if (err.code !== 'ENOENT') | ||
throw err; | ||
return ''; | ||
} | ||
} | ||
} | ||
get homedir() { | ||
return ((os.platform() === 'win32' && | ||
(process.env.HOME || | ||
(process.env.HOMEDRIVE && process.env.HOMEPATH && path.join(process.env.HOMEDRIVE, process.env.HOMEPATH)) || | ||
process.env.USERPROFILE)) || | ||
os.homedir() || | ||
os.tmpdir()); | ||
} | ||
defaultFileSync() { | ||
let file = path.join(this.homedir, os.platform() === 'win32' ? '_netrc' : '.netrc'); | ||
return fs.pathExistsSync(file + '.gpg') ? (file += '.gpg') : file; | ||
} | ||
defaultFile() { | ||
return tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
let file = path.join(this.homedir, os.platform() === 'win32' ? '_netrc' : '.netrc'); | ||
return (yield fs.pathExists(file + '.gpg')) ? (file += '.gpg') : file; | ||
}); | ||
} | ||
} | ||
module.exports = Netrc; | ||
exports.Netrc = Netrc; | ||
exports.default = Netrc; |
{ | ||
"name": "netrc-parser", | ||
"description": "netrc parser", | ||
"version": "2.0.6", | ||
"version": "3.0.0", | ||
"author": "Jeff Dickey (@jdxcode)", | ||
"bugs": "https://github.com/jdxcode/node-netrc-parser/issues", | ||
"dependencies": { | ||
"graceful-fs": "^4.1.11", | ||
"lex": "^1.7.9" | ||
}, | ||
"devDependencies": { | ||
"babel-cli": "6.24.1", | ||
"babel-eslint": "7.1.1", | ||
"babel-plugin-transform-flow-strip-types": "6.22.0", | ||
"@heroku-cli/tslint": "^1.1.2", | ||
"@types/execa": "^0.8.1", | ||
"@types/fs-extra": "^5.0.0", | ||
"@types/graceful-fs": "^4.1.2", | ||
"@types/jest": "^22.0.1", | ||
"debug": "^3.1.0", | ||
"documentation": "4.0.0-beta.18", | ||
"flow-bin": "0.41.0", | ||
"flow-copy-source": "1.1.0", | ||
"fs-extra": "2.0.0", | ||
"jest": "19.0.2", | ||
"jest-junit": "1.3.0", | ||
"rimraf": "2.6.1", | ||
"standard": "9.0.1" | ||
"fs-extra": "5.0.0", | ||
"jest": "22.0.4", | ||
"prettier": "^1.9.2", | ||
"ts-jest": "^22.0.1", | ||
"tslint": "^5.8.0", | ||
"typescript": "^2.6.2" | ||
}, | ||
@@ -35,18 +36,8 @@ "files": [ | ||
"scripts": { | ||
"build": "babel src -d lib --ignore '*.test.js'", | ||
"clean": "rimraf lib", | ||
"copy-flow": "flow-copy-source -v -i '**/*.test.js' src lib", | ||
"doc": "documentation readme --github -s API src/netrc.js", | ||
"prepare": "npm run clean && npm run build && npm run copy-flow", | ||
"test": "jest && flow && standard", | ||
"version": "npm run doc && git add README.md", | ||
"watch": "babel --watch src -d lib --ignore '*.test.js'" | ||
"posttest": "prettier -l src/**/*.ts", | ||
"prepare": "tsc", | ||
"pretest": "tsc", | ||
"test": "jest" | ||
}, | ||
"standard": { | ||
"parser": "babel-eslint", | ||
"ignore": [ | ||
"lib", | ||
"flow-typed" | ||
] | ||
} | ||
"types": "lib/netrc.d.ts" | ||
} |
# netrc-parser | ||
[![CircleCI](https://circleci.com/gh/dickeyxxx/node-netrc-parser.svg?style=svg)](https://circleci.com/gh/dickeyxxx/node-netrc-parser) | ||
[![codecov](https://codecov.io/gh/dickeyxxx/node-netrc-parser/branch/master/graph/badge.svg)](https://codecov.io/gh/dickeyxxx/node-netrc-parser) | ||
[![CircleCI](https://circleci.com/gh/jdxcode/node-netrc-parser/tree/master.svg?style=svg)](https://circleci.com/gh/jdxcode/node-netrc-parser/tree/master) | ||
[![codecov](https://codecov.io/gh/jdxcode/node-netrc-parser/branch/master/graph/badge.svg)](https://codecov.io/gh/jdxcode/node-netrc-parser) | ||
# API | ||
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> | ||
## Netrc | ||
[src/netrc.js:211-318](https://github.com/jdxcode/node-netrc-parser/blob/cfbdbb9020ee87a353f88ea423a36375e79da2e0/src/netrc.js#L211-L318 "Source code on GitHub") | ||
parses a netrc file | ||
### constructor | ||
[src/netrc.js:219-228](https://github.com/jdxcode/node-netrc-parser/blob/cfbdbb9020ee87a353f88ea423a36375e79da2e0/src/netrc.js#L219-L228 "Source code on GitHub") | ||
generates or parses a netrc file | ||
**Parameters** | ||
- `file` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** | ||
**Examples** | ||
```javascript | ||
const Netrc = require('netrc-parser') | ||
const {Netrc} = require('netrc-parser') | ||
const netrc = new Netrc() | ||
netrc.loadSync() // or netrc.load() for async | ||
netrc.machines['api.heroku.com'].password // get auth token from ~/.netrc | ||
netrc.saveSync() // or netrc.save() for async | ||
``` | ||
### save | ||
[src/netrc.js:243-267](https://github.com/jdxcode/node-netrc-parser/blob/cfbdbb9020ee87a353f88ea423a36375e79da2e0/src/netrc.js#L243-L267 "Source code on GitHub") | ||
save the current home netrc with any changes | ||
**Examples** | ||
```javascript | ||
const Netrc = require('netrc-parser') | ||
const netrc = new Netrc() | ||
netrc.machines['api.heroku.com'].password = 'newpassword' | ||
netrc.save() | ||
``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
53108
18
1376
1
2
21
12
+ Addedgraceful-fs@^4.1.11
+ Addedgraceful-fs@4.2.11(transitive)