Skip to content

Commit

Permalink
fix: os.userInfo() might throw SystemError if homedir isn't available (
Browse files Browse the repository at this point in the history
…#78)

fixes #77
  • Loading branch information
cyjake authored Apr 8, 2024
1 parent 162eaa3 commit fdc22da
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

strategy:
matrix:
node-version: [12.x, 14.x, 16.x, 18.x, 20.x]
node-version: [14.x, 16.x, 18.x, 20.x]

steps:
- uses: actions/checkout@v2
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
"devDependencies": {
"@types/mocha": "^9.1.0",
"@types/node": "^17.0.45",
"@types/sinon": "^17.0.3",
"@typescript-eslint/eslint-plugin": "^5.48.0",
"@typescript-eslint/parser": "^5.48.0",
"eslint": "^8.31.0",
"heredoc": "^1.3.1",
"mocha": "^8.2.1",
"nyc": "^15.1.0",
"sinon": "^17.0.1",
"typescript": "^4.6.3"
},
"scripts": {
Expand All @@ -33,7 +35,7 @@
"test:coverage": "nyc mocha --exit --recursive && nyc report --reporter=lcov"
},
"engine": {
"node": ">= 10.0.0"
"node": ">= 14.0.0"
},
"license": "MIT"
}
12 changes: 10 additions & 2 deletions src/ssh-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,21 @@ export default class SSHConfig extends Array<Line> {
public compute(opts: string | MatchOptions): Record<string, string | string[]> {
if (typeof opts === 'string') opts = { Host: opts }

let userInfo: { username: string }
try {
userInfo = os.userInfo()
} catch {
// os.userInfo() throws a SystemError if a user has no username or homedir.
userInfo = { username: process.env.USER || process.env.USERNAME || '' }
}

const context: ComputeContext = {
params: {
Host: opts.Host,
HostName: opts.Host,
OriginalHost: opts.Host,
User: os.userInfo().username,
LocalUser: os.userInfo().username,
User: userInfo.username,
LocalUser: userInfo.username,
},
inFinalPass: false,
doFinalPass: false,
Expand Down
39 changes: 38 additions & 1 deletion test/unit/ssh-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ import { strict as assert } from 'assert'
import fs from 'fs'
import path from 'path'
import SSHConfig from '../..'
import sinon from 'sinon'
import os from 'os'

const { DIRECTIVE } = SSHConfig

function readFile(fname) {
function readFile(fname: string) {
const fpath = path.join(__dirname, '..', fname)
return fs.readFileSync(fpath, 'utf-8').replace(/\r\n/g, '\n')
}

afterEach(() => {
sinon.restore()
})

describe('SSHConfig', function() {
it('.compute by Host', async function() {
const config = SSHConfig.parse(readFile('fixture/config'))
Expand Down Expand Up @@ -191,6 +197,37 @@ describe('SSHConfig', function() {
assert.equal(result.ProxyJump, 'proxy.com')
})

it('.compute with os.userInfo() throwing SystemError should have fallback', async () => {
const mock = sinon.mock(os)
mock.expects('userInfo').throws(new Error('user has no username or homedir'))
const config = SSHConfig.parse('')
const result = config.compute({ Host: 'tahoe' })
assert.ok(result)
})

it('.compute should fallback to process.env.USERNAME on Windows', async () => {
const mock = sinon.mock(os)
mock.expects('userInfo').throws(new Error('user has no username or homedir'))
for (const key of ['USER', 'USERNAME']) {
if (process.env[key] != null) sinon.stub(process.env, key).value(undefined)
}
sinon.define(process.env, 'USERNAME', 'test')
const config = SSHConfig.parse('')
const result = config.compute({ Host: 'tahoe' })
assert.ok(result)
})

it('.compute should default username to empty string if failed to get from env', async () => {
const mock = sinon.mock(os)
mock.expects('userInfo').throws(new Error('user has no username or homedir'))
for (const key of ['USER', 'USERNAME']) {
if (process.env[key] != null) sinon.stub(process.env, key).value(undefined)
}
const config = SSHConfig.parse('')
const result = config.compute({ Host: 'tahoe' })
assert.ok(result)
})

it('.find with nothing shall yield error', async function() {
const config = SSHConfig.parse(readFile('fixture/config'))
assert.throws(function() { config.find({}) })
Expand Down

0 comments on commit fdc22da

Please sign in to comment.