ssh-config 4.4.3 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ssh-config",
3
3
  "description": "SSH config parser and stringifier",
4
- "version": "4.4.3",
4
+ "version": "5.0.0",
5
5
  "author": "Chen Yangjian (https://www.cyj.me)",
6
6
  "repository": {
7
7
  "type": "git",
@@ -24,7 +24,7 @@
24
24
  "mocha": "^8.2.1",
25
25
  "nyc": "^15.1.0",
26
26
  "sinon": "^17.0.1",
27
- "typescript": "^4.6.3"
27
+ "typescript": "^5.4.4"
28
28
  },
29
29
  "scripts": {
30
30
  "lint": "eslint --ext ts .",
@@ -9,14 +9,22 @@ export interface Directive {
9
9
  after: string;
10
10
  param: string;
11
11
  separator: Separator;
12
- value: string | string[];
12
+ value: string | {
13
+ val: string;
14
+ separator: string;
15
+ quoted?: boolean;
16
+ }[];
13
17
  quoted?: boolean;
14
18
  }
15
19
  export interface Section extends Directive {
16
20
  config: SSHConfig;
17
21
  }
18
22
  export interface Match extends Section {
19
- criteria: Record<string, string | string[]>;
23
+ criteria: Record<string, string | {
24
+ val: string;
25
+ separator: string;
26
+ quoted?: boolean;
27
+ }[]>;
20
28
  }
21
29
  export interface Comment {
22
30
  type: LineType.COMMENT;
package/src/ssh-config.js CHANGED
@@ -3,7 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.stringify = exports.parse = exports.LineType = void 0;
6
+ exports.LineType = void 0;
7
+ exports.parse = parse;
8
+ exports.stringify = stringify;
7
9
  const glob_1 = __importDefault(require("./glob"));
8
10
  const child_process_1 = require("child_process");
9
11
  const os_1 = __importDefault(require("os"));
@@ -17,7 +19,7 @@ var LineType;
17
19
  (function (LineType) {
18
20
  LineType[LineType["DIRECTIVE"] = 1] = "DIRECTIVE";
19
21
  LineType[LineType["COMMENT"] = 2] = "COMMENT";
20
- })(LineType = exports.LineType || (exports.LineType = {}));
22
+ })(LineType || (exports.LineType = LineType = {}));
21
23
  const MULTIPLE_VALUE_PROPS = [
22
24
  'IdentityFile',
23
25
  'LocalForward',
@@ -69,7 +71,8 @@ function match(criteria, context) {
69
71
  };
70
72
  for (const key in criteria) {
71
73
  const criterion = criteria[key];
72
- if (!testCriterion(key, criterion)) {
74
+ const values = Array.isArray(criterion) ? criterion.map(({ val }) => val) : criterion;
75
+ if (!testCriterion(key, values)) {
73
76
  return false;
74
77
  }
75
78
  }
@@ -133,7 +136,7 @@ class SSHConfig extends Array {
133
136
  for (const line of this) {
134
137
  if (line.type !== LineType.DIRECTIVE)
135
138
  continue;
136
- if (line.param === 'Host' && (0, glob_1.default)(line.value, context.params.Host)) {
139
+ if (line.param === 'Host' && (0, glob_1.default)(Array.isArray(line.value) ? line.value.map(({ val }) => val) : line.value, context.params.Host)) {
137
140
  let canonicalizeHostName = false;
138
141
  let canonicalDomains = [];
139
142
  setProperty(line.param, line.value);
@@ -144,15 +147,15 @@ class SSHConfig extends Array {
144
147
  canonicalizeHostName = true;
145
148
  }
146
149
  if (/^CanonicalDomains$/i.test(subline.param) && Array.isArray(subline.value)) {
147
- canonicalDomains = subline.value;
150
+ canonicalDomains = subline.value.map(({ val }) => val);
148
151
  }
149
152
  }
150
153
  }
151
- if (canonicalDomains.length > 0 && canonicalizeHostName) {
154
+ if (canonicalDomains.length > 0 && canonicalizeHostName && context.params.Host === context.params.OriginalHost) {
152
155
  for (const domain of canonicalDomains) {
153
- const host = `${line.value}.${domain}`;
154
- const { stdout } = (0, child_process_1.spawnSync)('nslookup', [host]);
155
- if (!/server can't find/.test(stdout.toString())) {
156
+ const host = `${context.params.OriginalHost}.${domain}`;
157
+ const { status, stderr } = (0, child_process_1.spawnSync)('nslookup', [host]);
158
+ if (status === 0 && !/can't find/.test(stderr.toString())) {
156
159
  context.params.Host = host;
157
160
  setProperty('Host', host);
158
161
  doPass();
@@ -224,7 +227,7 @@ class SSHConfig extends Array {
224
227
  type: LineType.DIRECTIVE,
225
228
  param,
226
229
  separator: ' ',
227
- value,
230
+ value: Array.isArray(value) ? value.map((val, i) => ({ val, separator: i === 0 ? '' : ' ' })) : value,
228
231
  before: sectionLineFound ? indent : indent.replace(/ |\t/, ''),
229
232
  after: '\n',
230
233
  };
@@ -272,7 +275,7 @@ class SSHConfig extends Array {
272
275
  type: LineType.DIRECTIVE,
273
276
  param,
274
277
  separator: ' ',
275
- value,
278
+ value: Array.isArray(value) ? value.map((val, i) => ({ val, separator: i === 0 ? '' : ' ' })) : value,
276
279
  before: '',
277
280
  after: '\n',
278
281
  };
@@ -302,9 +305,9 @@ class SSHConfig extends Array {
302
305
  return config;
303
306
  }
304
307
  }
305
- exports.default = SSHConfig;
306
308
  SSHConfig.DIRECTIVE = LineType.DIRECTIVE;
307
309
  SSHConfig.COMMENT = LineType.COMMENT;
310
+ exports.default = SSHConfig;
308
311
  /**
309
312
  * Parse SSH config text into structured object.
310
313
  */
@@ -393,6 +396,11 @@ function parse(text) {
393
396
  function values() {
394
397
  const results = [];
395
398
  let val = '';
399
+ // whether current value is quoted or not
400
+ let valQuoted = false;
401
+ // the separator preceding current value
402
+ let valSeparator = ' ';
403
+ // whether current context is within quotations or not
396
404
  let quoted = false;
397
405
  let escaped = false;
398
406
  while (chr && !RE_LINE_BREAK.test(chr)) {
@@ -408,15 +416,18 @@ function parse(text) {
408
416
  }
409
417
  else if (quoted) {
410
418
  val += chr;
419
+ valQuoted = true;
411
420
  }
412
421
  else if (/[ \t=]/.test(chr)) {
413
422
  if (val) {
414
- results.push(val);
423
+ results.push({ val, separator: valSeparator, quoted: valQuoted });
415
424
  val = '';
425
+ valQuoted = false;
426
+ valSeparator = chr;
416
427
  }
417
428
  // otherwise ignore the space
418
429
  }
419
- else if (chr === '#') {
430
+ else if (chr === '#' && results.length > 0) {
420
431
  break;
421
432
  }
422
433
  else {
@@ -425,11 +436,11 @@ function parse(text) {
425
436
  chr = next();
426
437
  }
427
438
  if (quoted || escaped) {
428
- throw new Error(`Unexpected line break at ${results.concat(val).join(' ')}`);
439
+ throw new Error(`Unexpected line break at ${results.map(({ val }) => val).concat(val).join(' ')}`);
429
440
  }
430
441
  if (val)
431
- results.push(val);
432
- return results.length > 1 ? results : results[0];
442
+ results.push({ val, separator: valSeparator, quoted: valQuoted });
443
+ return results.length > 1 ? results : results[0].val;
433
444
  }
434
445
  function directive() {
435
446
  const type = LineType.DIRECTIVE;
@@ -450,11 +461,11 @@ function parse(text) {
450
461
  if (/^Match$/i.test(param)) {
451
462
  const criteria = {};
452
463
  if (typeof result.value === 'string') {
453
- result.value = [result.value];
464
+ result.value = [{ val: result.value, separator: '', quoted: result.quoted }];
454
465
  }
455
466
  let i = 0;
456
467
  while (i < result.value.length) {
457
- const keyword = result.value[i];
468
+ const { val: keyword } = result.value[i];
458
469
  switch (keyword.toLowerCase()) {
459
470
  case 'all':
460
471
  case 'canonical':
@@ -466,7 +477,7 @@ function parse(text) {
466
477
  if (i + 1 >= result.value.length) {
467
478
  throw new Error(`Missing value for match criteria ${keyword}`);
468
479
  }
469
- criteria[keyword] = result.value[i + 1];
480
+ criteria[keyword] = result.value[i + 1].val;
470
481
  i += 2;
471
482
  break;
472
483
  }
@@ -505,7 +516,6 @@ function parse(text) {
505
516
  }
506
517
  return configWas;
507
518
  }
508
- exports.parse = parse;
509
519
  /**
510
520
  * Stringify structured object into SSH config text.
511
521
  */
@@ -513,7 +523,11 @@ function stringify(config) {
513
523
  let str = '';
514
524
  function formatValue(value, quoted) {
515
525
  if (Array.isArray(value)) {
516
- return value.map(chunk => formatValue(chunk, RE_SPACE.test(chunk))).join(' ');
526
+ let result = '';
527
+ for (const { val, separator, quoted } of value) {
528
+ result += (result ? separator : '') + formatValue(val, quoted || RE_SPACE.test(val));
529
+ }
530
+ return result;
517
531
  }
518
532
  return quoted ? `"${value}"` : value;
519
533
  }
@@ -523,14 +537,14 @@ function stringify(config) {
523
537
  const value = formatValue(line.value, quoted);
524
538
  return `${line.param}${line.separator}${value}`;
525
539
  }
526
- const format = line => {
540
+ const format = (line) => {
527
541
  str += line.before;
528
542
  if (line.type === LineType.COMMENT) {
529
543
  str += line.content;
530
544
  }
531
545
  else if (line.type === LineType.DIRECTIVE && MULTIPLE_VALUE_PROPS.includes(line.param)) {
532
- [].concat(line.value).forEach(function (value, i, values) {
533
- str += formatDirective({ ...line, value });
546
+ (Array.isArray(line.value) ? line.value : [line.value]).forEach((value, i, values) => {
547
+ str += formatDirective({ ...line, value: typeof value !== 'string' ? value.val : value });
534
548
  if (i < values.length - 1)
535
549
  str += `\n${line.before}`;
536
550
  });
@@ -539,12 +553,11 @@ function stringify(config) {
539
553
  str += formatDirective(line);
540
554
  }
541
555
  str += line.after;
542
- if (line.config) {
556
+ if ('config' in line) {
543
557
  line.config.forEach(format);
544
558
  }
545
559
  };
546
560
  config.forEach(format);
547
561
  return str;
548
562
  }
549
- exports.stringify = stringify;
550
563
  //# sourceMappingURL=ssh-config.js.map