ssh-config 4.2.1 → 4.4.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/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import SSHConfig from './src/ssh-config';
2
+ export * from './src/ssh-config';
3
+ export default SSHConfig;
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.2.1",
4
+ "version": "4.4.0",
5
5
  "author": "Chen Yangjian (https://www.cyj.me)",
6
6
  "repository": {
7
7
  "type": "git",
@@ -9,7 +9,9 @@
9
9
  },
10
10
  "files": [
11
11
  "index.js",
12
- "src"
12
+ "index.d.ts",
13
+ "src/*.js",
14
+ "src/*.d.ts"
13
15
  ],
14
16
  "devDependencies": {
15
17
  "@types/mocha": "^9.1.0",
package/src/glob.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A helper function to match input against [pattern-list](https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5#PATTERNS).
3
+ * According to `man ssh_config`, negated patterns shall be matched first.
4
+ *
5
+ * @param {string|string[]} patternList
6
+ * @param {string} str
7
+ */
8
+ declare function glob(patternList: string | string[], text: string): boolean;
9
+ export default glob;
@@ -0,0 +1,91 @@
1
+ export declare enum LineType {
2
+ DIRECTIVE = 1,
3
+ COMMENT = 2
4
+ }
5
+ export type Separator = ' ' | '=' | '\t';
6
+ export interface Directive {
7
+ type: LineType.DIRECTIVE;
8
+ before: string;
9
+ after: string;
10
+ param: string;
11
+ separator: Separator;
12
+ value: string | string[];
13
+ quoted?: boolean;
14
+ }
15
+ export interface Section extends Directive {
16
+ config: SSHConfig;
17
+ }
18
+ export interface Match extends Section {
19
+ criteria: Record<string, string | string[]>;
20
+ }
21
+ export interface Comment {
22
+ type: LineType.COMMENT;
23
+ before: string;
24
+ after: string;
25
+ content: string;
26
+ }
27
+ export type Line = Match | Section | Directive | Comment;
28
+ export interface FindOptions {
29
+ Host?: string;
30
+ }
31
+ export interface MatchOptions {
32
+ Host: string;
33
+ User?: string;
34
+ }
35
+ export default class SSHConfig extends Array<Line> {
36
+ static readonly DIRECTIVE: LineType.DIRECTIVE;
37
+ static readonly COMMENT: LineType.COMMENT;
38
+ /**
39
+ * Parse SSH config text into structured object.
40
+ */
41
+ static parse(text: string): SSHConfig;
42
+ /**
43
+ * Stringify structured object into SSH config text.
44
+ */
45
+ static stringify(config: SSHConfig): string;
46
+ /**
47
+ * Query SSH config by host.
48
+ */
49
+ compute(host: string): Record<string, string | string[]>;
50
+ /**
51
+ * Query SSH config by host and user.
52
+ */
53
+ compute(opts: MatchOptions): Record<string, string | string[]>;
54
+ /**
55
+ * Find by Host or Match.
56
+ */
57
+ find(opts: FindOptions): Line | undefined;
58
+ /**
59
+ * Find by search function.
60
+ * @param predicate Function to check against each line; should return a truthy value when a
61
+ * matching line is given.
62
+ */
63
+ find(predicate: (line: Line, index: number, config: Line[]) => unknown): Line | undefined;
64
+ /**
65
+ * Remove section by Host or Match.
66
+ */
67
+ remove(opts: FindOptions): Line[] | undefined;
68
+ /**
69
+ * Remove section by search function.
70
+ * @param predicate Function to check against each line; should return a truthy value when a
71
+ * matching line is given.
72
+ */
73
+ remove(predicate: (line: Line, index: number, config: Line[]) => unknown): Line[] | undefined;
74
+ toString(): string;
75
+ /**
76
+ * Append new section to existing SSH config.
77
+ */
78
+ append(opts: Record<string, string | string[]>): SSHConfig;
79
+ /**
80
+ * Prepend new section to existing SSH config.
81
+ */
82
+ prepend(opts: Record<string, string | string[]>, beforeFirstSection?: boolean): SSHConfig;
83
+ }
84
+ /**
85
+ * Parse SSH config text into structured object.
86
+ */
87
+ export declare function parse(text: string): SSHConfig;
88
+ /**
89
+ * Stringify structured object into SSH config text.
90
+ */
91
+ export declare function stringify(config: SSHConfig): string;
package/src/ssh-config.js CHANGED
@@ -3,20 +3,21 @@ 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 = void 0;
6
+ exports.stringify = exports.parse = exports.LineType = void 0;
7
7
  const glob_1 = __importDefault(require("./glob"));
8
8
  const child_process_1 = require("child_process");
9
+ const os_1 = __importDefault(require("os"));
9
10
  const RE_SPACE = /\s/;
10
11
  const RE_LINE_BREAK = /\r|\n/;
11
12
  const RE_SECTION_DIRECTIVE = /^(Host|Match)$/i;
12
- const RE_MULTI_VALUE_DIRECTIVE = /^(GlobalKnownHostsFile|Host|IPQoS|SendEnv|UserKnownHostsFile|ProxyCommand|Match)$/i;
13
+ const RE_MULTI_VALUE_DIRECTIVE = /^(GlobalKnownHostsFile|Host|IPQoS|SendEnv|UserKnownHostsFile|ProxyCommand|Match|CanonicalDomains)$/i;
13
14
  const RE_QUOTE_DIRECTIVE = /^(?:CertificateFile|IdentityFile|IdentityAgent|User)$/i;
14
15
  const RE_SINGLE_LINE_DIRECTIVE = /^(Include|IdentityFile)$/i;
15
16
  var LineType;
16
17
  (function (LineType) {
17
18
  LineType[LineType["DIRECTIVE"] = 1] = "DIRECTIVE";
18
19
  LineType[LineType["COMMENT"] = 2] = "COMMENT";
19
- })(LineType || (LineType = {}));
20
+ })(LineType = exports.LineType || (exports.LineType = {}));
20
21
  const MULTIPLE_VALUE_PROPS = [
21
22
  'IdentityFile',
22
23
  'LocalForward',
@@ -39,25 +40,36 @@ function getIndent(config) {
39
40
  }
40
41
  return ' ';
41
42
  }
42
- function capitalize(str) {
43
- if (typeof str !== 'string')
44
- return str;
45
- return str[0].toUpperCase() + str.slice(1);
46
- }
47
- function match(criteria, params) {
48
- for (const key in criteria) {
49
- const criterion = criteria[key];
50
- const keyword = key.toLowerCase();
51
- if (keyword === 'exec') {
52
- const command = `function main {
53
- ${criterion}
54
- }
55
- main`;
56
- const { status } = (0, child_process_1.spawnSync)(command, { shell: true });
57
- if (status != 0)
43
+ function match(criteria, context) {
44
+ const testCriterion = (key, criterion) => {
45
+ switch (key.toLowerCase()) {
46
+ case 'all':
47
+ return true;
48
+ case 'final':
49
+ if (context.inFinalPass) {
50
+ return true;
51
+ }
52
+ context.doFinalPass = true;
58
53
  return false;
54
+ case 'exec':
55
+ const command = `function main {
56
+ ${criterion}
59
57
  }
60
- else if (!(0, glob_1.default)(criterion, params[capitalize(keyword)])) {
58
+ main`;
59
+ return (0, child_process_1.spawnSync)(command, { shell: true }).status === 0;
60
+ case 'host':
61
+ return (0, glob_1.default)(criterion, context.params.HostName);
62
+ case 'originalhost':
63
+ return (0, glob_1.default)(criterion, context.params.OriginalHost);
64
+ case 'user':
65
+ return (0, glob_1.default)(criterion, context.params.User);
66
+ case 'localuser':
67
+ return (0, glob_1.default)(criterion, context.params.LocalUser);
68
+ }
69
+ };
70
+ for (const key in criteria) {
71
+ const criterion = criteria[key];
72
+ if (!testCriterion(key, criterion)) {
61
73
  return false;
62
74
  }
63
75
  }
@@ -65,11 +77,31 @@ function match(criteria, params) {
65
77
  }
66
78
  class SSHConfig extends Array {
67
79
  /**
68
- * Query ssh config by host.
80
+ * Parse SSH config text into structured object.
69
81
  */
70
- compute(params) {
71
- if (typeof params === 'string')
72
- params = { Host: params };
82
+ static parse(text) {
83
+ return parse(text);
84
+ }
85
+ /**
86
+ * Stringify structured object into SSH config text.
87
+ */
88
+ static stringify(config) {
89
+ return stringify(config);
90
+ }
91
+ compute(opts) {
92
+ if (typeof opts === 'string')
93
+ opts = { Host: opts };
94
+ const context = {
95
+ params: {
96
+ Host: opts.Host,
97
+ HostName: opts.Host,
98
+ OriginalHost: opts.Host,
99
+ User: os_1.default.userInfo().username,
100
+ LocalUser: os_1.default.userInfo().username,
101
+ },
102
+ inFinalPass: false,
103
+ doFinalPass: false,
104
+ };
73
105
  const obj = {};
74
106
  const setProperty = (name, value) => {
75
107
  if (MULTIPLE_VALUE_PROPS.includes(name)) {
@@ -77,36 +109,71 @@ class SSHConfig extends Array {
77
109
  list.push(value);
78
110
  }
79
111
  else if (obj[name] == null) {
112
+ if (name === 'HostName') {
113
+ context.params.HostName = value;
114
+ }
115
+ else if (name === 'User') {
116
+ context.params.User = value;
117
+ }
80
118
  obj[name] = value;
81
119
  }
82
120
  };
83
- for (const line of this) {
84
- if (line.type !== LineType.DIRECTIVE)
85
- continue;
86
- if (line.param === 'Host' && (0, glob_1.default)(line.value, params.Host)) {
87
- setProperty(line.param, line.value);
88
- for (const subline of line.config) {
89
- if (subline.type === LineType.DIRECTIVE) {
90
- setProperty(subline.param, subline.value);
121
+ if (opts.User !== undefined) {
122
+ setProperty('User', opts.User);
123
+ }
124
+ const doPass = () => {
125
+ for (const line of this) {
126
+ if (line.type !== LineType.DIRECTIVE)
127
+ continue;
128
+ if (line.param === 'Host' && (0, glob_1.default)(line.value, context.params.Host)) {
129
+ let canonicalizeHostName = false;
130
+ let canonicalDomains = [];
131
+ setProperty(line.param, line.value);
132
+ for (const subline of line.config) {
133
+ if (subline.type === LineType.DIRECTIVE) {
134
+ setProperty(subline.param, subline.value);
135
+ if (/^CanonicalizeHostName$/i.test(subline.param) && subline.value === 'yes') {
136
+ canonicalizeHostName = true;
137
+ }
138
+ if (/^CanonicalDomains$/i.test(subline.param) && Array.isArray(subline.value)) {
139
+ canonicalDomains = subline.value;
140
+ }
141
+ }
142
+ }
143
+ console.log(canonicalizeHostName, canonicalDomains);
144
+ if (canonicalDomains.length > 0 && canonicalizeHostName) {
145
+ for (const domain of canonicalDomains) {
146
+ const host = `${line.value}.${domain}`;
147
+ const { stdout } = (0, child_process_1.spawnSync)('nslookup', [host]);
148
+ if (!/server can't find/.test(stdout.toString())) {
149
+ context.params.Host = host;
150
+ setProperty('Host', host);
151
+ doPass();
152
+ break;
153
+ }
154
+ }
91
155
  }
92
156
  }
93
- }
94
- else if (line.param === 'Match' && 'criteria' in line && match(line.criteria, params)) {
95
- for (const subline of line.config) {
96
- if (subline.type === LineType.DIRECTIVE) {
97
- setProperty(subline.param, subline.value);
157
+ else if (line.param === 'Match' && 'criteria' in line && match(line.criteria, context)) {
158
+ for (const subline of line.config) {
159
+ if (subline.type === LineType.DIRECTIVE) {
160
+ setProperty(subline.param, subline.value);
161
+ }
98
162
  }
99
163
  }
164
+ else if (line.param !== 'Host' && line.param !== 'Match') {
165
+ setProperty(line.param, line.value);
166
+ }
100
167
  }
101
- else if (line.param !== 'Host' && line.param !== 'Match') {
102
- setProperty(line.param, line.value);
103
- }
168
+ };
169
+ doPass();
170
+ if (context.doFinalPass) {
171
+ context.inFinalPass = true;
172
+ context.params.Host = context.params.HostName;
173
+ doPass();
104
174
  }
105
175
  return obj;
106
176
  }
107
- /**
108
- * find section by Host / Match or function
109
- */
110
177
  find(opts) {
111
178
  if (typeof opts === 'function')
112
179
  return super.find(opts);
@@ -115,9 +182,6 @@ class SSHConfig extends Array {
115
182
  }
116
183
  return super.find(line => compare(line, opts));
117
184
  }
118
- /**
119
- * Remove section by Host / Match or function
120
- */
121
185
  remove(opts) {
122
186
  let index;
123
187
  if (typeof opts === 'function') {
@@ -136,8 +200,7 @@ class SSHConfig extends Array {
136
200
  return stringify(this);
137
201
  }
138
202
  /**
139
- * Append new section to existing ssh config.
140
- * @param {Object} opts
203
+ * Append new section to existing SSH config.
141
204
  */
142
205
  append(opts) {
143
206
  const indent = getIndent(this);
@@ -177,8 +240,7 @@ class SSHConfig extends Array {
177
240
  return configWas;
178
241
  }
179
242
  /**
180
- * Prepend new section to existing ssh config.
181
- * @param {Object} opts
243
+ * Prepend new section to existing SSH config.
182
244
  */
183
245
  prepend(opts, beforeFirstSection = false) {
184
246
  const indent = getIndent(this);
@@ -233,10 +295,11 @@ class SSHConfig extends Array {
233
295
  return config;
234
296
  }
235
297
  }
298
+ exports.default = SSHConfig;
236
299
  SSHConfig.DIRECTIVE = LineType.DIRECTIVE;
237
300
  SSHConfig.COMMENT = LineType.COMMENT;
238
301
  /**
239
- * Parse ssh config text into structured object.
302
+ * Parse SSH config text into structured object.
240
303
  */
241
304
  function parse(text) {
242
305
  let i = 0;
@@ -373,10 +436,27 @@ function parse(text) {
373
436
  delete result.quoted;
374
437
  if (/^Match$/i.test(param)) {
375
438
  const criteria = {};
376
- for (let i = 0; i < result.value.length; i += 2) {
439
+ if (typeof result.value === 'string') {
440
+ result.value = [result.value];
441
+ }
442
+ let i = 0;
443
+ while (i < result.value.length) {
377
444
  const keyword = result.value[i];
378
- const value = result.value[i + 1];
379
- criteria[keyword] = value;
445
+ switch (keyword.toLowerCase()) {
446
+ case 'all':
447
+ case 'canonical':
448
+ case 'final':
449
+ criteria[keyword] = [];
450
+ i += 1;
451
+ break;
452
+ default:
453
+ if (i + 1 >= result.value.length) {
454
+ throw new Error(`Missing value for match criteria ${keyword}`);
455
+ }
456
+ criteria[keyword] = result.value[i + 1];
457
+ i += 2;
458
+ break;
459
+ }
380
460
  }
381
461
  result.criteria = criteria;
382
462
  }
@@ -399,7 +479,12 @@ function parse(text) {
399
479
  }
400
480
  else if (node.type === LineType.DIRECTIVE && !node.param) {
401
481
  // blank lines at file end
402
- config[config.length - 1].after += node.before;
482
+ if (config.length === 0) {
483
+ configWas[configWas.length - 1].after += node.before;
484
+ }
485
+ else {
486
+ config[config.length - 1].after += node.before;
487
+ }
403
488
  }
404
489
  else {
405
490
  config.push(node);
@@ -409,17 +494,11 @@ function parse(text) {
409
494
  }
410
495
  exports.parse = parse;
411
496
  /**
412
- * Stringify structured object into ssh config text
497
+ * Stringify structured object into SSH config text.
413
498
  */
414
499
  function stringify(config) {
415
500
  let str = '';
416
501
  function formatValue(value, quoted) {
417
- if (value && typeof value === 'object' && !Array.isArray(value)) {
418
- const result = [];
419
- for (const key in value)
420
- result.push(key, value[key]);
421
- value = result;
422
- }
423
502
  if (Array.isArray(value)) {
424
503
  return value.map(chunk => formatValue(chunk, RE_SPACE.test(chunk))).join(' ');
425
504
  }
@@ -455,5 +534,4 @@ function stringify(config) {
455
534
  return str;
456
535
  }
457
536
  exports.stringify = stringify;
458
- exports.default = Object.assign(SSHConfig, { parse, stringify });
459
537
  //# sourceMappingURL=ssh-config.js.map
package/src/glob.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"glob.js","sourceRoot":"","sources":["glob.ts"],"names":[],"mappings":";;AACA,SAAS,WAAW,CAAC,IAAY,EAAE,KAAa;IAC9C,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE;QACtB,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,CAAA;KAC/D;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,KAAK,CAAC,OAAe,EAAE,IAAY;IAC1C,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,eAAe,CAAC,CAAA;IAC/C,OAAO,GAAG,OAAO;SACd,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IAEvB,OAAO,IAAI,MAAM,CAAC,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACvD,CAAC;AAED;;;;;;GAMG;AACH,SAAS,IAAI,CAAC,WAA8B,EAAE,IAAY;IACxD,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAElF,+HAA+H;IAC/H,IAAI,MAAM,GAAG,KAAK,CAAA;IAClB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE;QAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAA;QAEhC,IAAI,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE;YAC3C,OAAO,KAAK,CAAA;SACb;aAAM,IAAI,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE;YAC/B,uFAAuF;YACvF,MAAM,GAAG,IAAI,CAAA;SACd;KACF;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,kBAAe,IAAI,CAAA"}
package/src/glob.ts DELETED
@@ -1,45 +0,0 @@
1
-
2
- function escapeChars(text: string, chars: string) {
3
- for (let char of chars) {
4
- text = text.replace(new RegExp('\\' + char, 'g'), '\\' + char)
5
- }
6
-
7
- return text
8
- }
9
-
10
- function match(pattern: string, text: string) {
11
- pattern = escapeChars(pattern, '\\()[]{}.+^$|')
12
- pattern = pattern
13
- .replace(/\*/g, '.*')
14
- .replace(/\?/g, '.?')
15
-
16
- return new RegExp('^(?:' + pattern + ')$').test(text)
17
- }
18
-
19
- /**
20
- * A helper function to match input against [pattern-list](https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5#PATTERNS).
21
- * According to `man ssh_config`, negated patterns shall be matched first.
22
- *
23
- * @param {string|string[]} patternList
24
- * @param {string} str
25
- */
26
- function glob(patternList: string | string[], text: string) {
27
- const patterns = Array.isArray(patternList) ? patternList : patternList.split(/,/)
28
-
29
- // > If a negated entry is matched, then the Host entry is ignored, regardless of whether any other patterns on the line match.
30
- let result = false
31
- for (const pattern of patterns) {
32
- const negate = pattern[0] == '!'
33
-
34
- if (negate && match(pattern.slice(1), text)) {
35
- return false
36
- } else if (match(pattern, text)) {
37
- // wait until all of the pattern match results because there might be a negated pattern
38
- result = true
39
- }
40
- }
41
-
42
- return result
43
- }
44
-
45
- export default glob
@@ -1 +0,0 @@
1
- {"version":3,"file":"ssh-config.js","sourceRoot":"","sources":["ssh-config.ts"],"names":[],"mappings":"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"}
package/src/ssh-config.ts DELETED
@@ -1,544 +0,0 @@
1
-
2
- import glob from './glob'
3
- import { spawnSync } from 'child_process'
4
-
5
- const RE_SPACE = /\s/
6
- const RE_LINE_BREAK = /\r|\n/
7
- const RE_SECTION_DIRECTIVE = /^(Host|Match)$/i
8
- const RE_MULTI_VALUE_DIRECTIVE = /^(GlobalKnownHostsFile|Host|IPQoS|SendEnv|UserKnownHostsFile|ProxyCommand|Match)$/i
9
- const RE_QUOTE_DIRECTIVE = /^(?:CertificateFile|IdentityFile|IdentityAgent|User)$/i
10
- const RE_SINGLE_LINE_DIRECTIVE = /^(Include|IdentityFile)$/i
11
-
12
- enum LineType {
13
- DIRECTIVE = 1,
14
- COMMENT = 2,
15
- }
16
-
17
- type Separator = ' ' | '=' | '\t';
18
-
19
- type Space = ' ' | '\t' | '\n';
20
-
21
- interface Directive {
22
- type: LineType.DIRECTIVE;
23
- before: string;
24
- after: string;
25
- param: string;
26
- separator: Separator;
27
- value: string | string[];
28
- quoted?: boolean;
29
- }
30
-
31
- interface Section extends Directive {
32
- config: SSHConfig;
33
- }
34
-
35
- interface Match extends Section {
36
- criteria: Record<string, string | string[]>
37
- }
38
-
39
- interface Comment {
40
- type: LineType.COMMENT;
41
- before: string;
42
- after: string;
43
- content: string;
44
- }
45
-
46
- type Line = Match | Section | Directive | Comment;
47
-
48
- interface FindOptions {
49
- Host?: string;
50
- }
51
-
52
- const MULTIPLE_VALUE_PROPS = [
53
- 'IdentityFile',
54
- 'LocalForward',
55
- 'RemoteForward',
56
- 'DynamicForward',
57
- 'CertificateFile',
58
- ]
59
-
60
- function compare(line, opts) {
61
- return opts.hasOwnProperty(line.param) && opts[line.param] === line.value
62
- }
63
-
64
- function getIndent(config: SSHConfig) {
65
- for (const line of config) {
66
- if (line.type === LineType.DIRECTIVE && 'config' in line) {
67
- for (const subline of line.config) {
68
- if (subline.before) {
69
- return subline.before
70
- }
71
- }
72
- }
73
- }
74
-
75
- return ' '
76
- }
77
-
78
- function capitalize(str) {
79
- if (typeof str !== 'string') return str
80
- return str[0].toUpperCase() + str.slice(1)
81
- }
82
-
83
- function match(criteria, params) {
84
- for (const key in criteria) {
85
- const criterion = criteria[key]
86
- const keyword = key.toLowerCase()
87
- if (keyword === 'exec') {
88
- const command = `function main {
89
- ${criterion}
90
- }
91
- main`
92
- const { status } = spawnSync(command, { shell: true })
93
- if (status != 0) return false
94
- } else if (!glob(criterion, params[capitalize(keyword)])) {
95
- return false
96
- }
97
- }
98
- return true
99
- }
100
-
101
- class SSHConfig extends Array<Line> {
102
- static DIRECTIVE: LineType.DIRECTIVE = LineType.DIRECTIVE
103
- static COMMENT: LineType.COMMENT = LineType.COMMENT
104
-
105
- /**
106
- * Query ssh config by host.
107
- */
108
- compute(params): Record<string, string | string[]> {
109
- if (typeof params === 'string') params = { Host: params }
110
- const obj = {}
111
- const setProperty = (name, value) => {
112
- if (MULTIPLE_VALUE_PROPS.includes(name)) {
113
- const list = obj[name] || (obj[name] = [])
114
- list.push(value)
115
- } else if (obj[name] == null) {
116
- obj[name] = value
117
- }
118
- }
119
-
120
- for (const line of this) {
121
- if (line.type !== LineType.DIRECTIVE) continue
122
- if (line.param === 'Host' && glob(line.value, params.Host)) {
123
- setProperty(line.param, line.value)
124
- for (const subline of (line as Section).config) {
125
- if (subline.type === LineType.DIRECTIVE) {
126
- setProperty(subline.param, subline.value)
127
- }
128
- }
129
- } else if (line.param === 'Match' && 'criteria' in line && match(line.criteria, params)) {
130
- for (const subline of (line as Section).config) {
131
- if (subline.type === LineType.DIRECTIVE) {
132
- setProperty(subline.param, subline.value)
133
- }
134
- }
135
- } else if (line.param !== 'Host' && line.param !== 'Match') {
136
- setProperty(line.param, line.value)
137
- }
138
- }
139
-
140
- return obj
141
- }
142
-
143
- /**
144
- * find section by Host / Match or function
145
- */
146
- find(opts: ((line: Line, index: number, config: Line[]) => unknown) | FindOptions) {
147
- if (typeof opts === 'function') return super.find(opts)
148
-
149
- if (!(opts && ('Host' in opts || 'Match' in opts))) {
150
- throw new Error('Can only find by Host or Match')
151
- }
152
-
153
- return super.find(line => compare(line, opts))
154
- }
155
-
156
- /**
157
- * Remove section by Host / Match or function
158
- */
159
- remove(opts: ((line: Line, index: number, config: Line[]) => unknown) | FindOptions) {
160
- let index: number
161
-
162
- if (typeof opts === 'function') {
163
- index = super.findIndex(opts)
164
- } else if (!(opts && ('Host' in opts || 'Match' in opts))) {
165
- throw new Error('Can only remove by Host or Match')
166
- } else {
167
- index = super.findIndex(line => compare(line, opts))
168
- }
169
-
170
- if (index >= 0) return this.splice(index, 1)
171
- }
172
-
173
- toString(): string {
174
- return stringify(this)
175
- }
176
-
177
- /**
178
- * Append new section to existing ssh config.
179
- * @param {Object} opts
180
- */
181
- append(opts: Record<string, string | string[]>) {
182
- const indent = getIndent(this)
183
- const lastEntry = this.length > 0 ? this[this.length - 1] : null
184
- let config = lastEntry && (lastEntry as Section).config || this
185
- let configWas = this
186
-
187
- let lastLine = config.length > 0 ? config[config.length - 1] : lastEntry
188
- if (lastLine && !lastLine.after) lastLine.after = '\n'
189
-
190
- let sectionLineFound = config !== configWas
191
-
192
- for (const param in opts) {
193
- const value = opts[param]
194
- const line: Directive = {
195
- type: LineType.DIRECTIVE,
196
- param,
197
- separator: ' ',
198
- value,
199
- before: sectionLineFound ? indent : indent.replace(/ |\t/, ''),
200
- after: '\n',
201
- }
202
-
203
- if (RE_SECTION_DIRECTIVE.test(param)) {
204
- sectionLineFound = true
205
- line.before = indent.replace(/ |\t/, '')
206
- config = configWas
207
- // separate sections with an extra newline
208
- // https://github.com/cyjake/ssh-config/issues/23#issuecomment-564768248
209
- if (lastLine && lastLine.after === '\n') lastLine.after += '\n'
210
- config.push(line)
211
- config = (line as Section).config = new SSHConfig()
212
- } else {
213
- config.push(line)
214
- }
215
- lastLine = line
216
- }
217
-
218
- return configWas
219
- }
220
-
221
- /**
222
- * Prepend new section to existing ssh config.
223
- * @param {Object} opts
224
- */
225
- prepend(opts: Record<string, string | string[]>, beforeFirstSection = false) {
226
- const indent = getIndent(this)
227
- let config: SSHConfig = this
228
- let i = 0
229
-
230
- // insert above known sections
231
- if (beforeFirstSection) {
232
- while (i < this.length && !('config' in this[i])) {
233
- i += 1
234
- }
235
-
236
- if (i >= this.length) { // No sections in original config
237
- return this.append(opts)
238
- }
239
- }
240
-
241
- // Prepend new section above the first section
242
- let sectionLineFound = false
243
- let processedLines = 0
244
-
245
- for (const param in opts) {
246
- processedLines += 1
247
- const value = opts[param]
248
- const line: Directive = {
249
- type: LineType.DIRECTIVE,
250
- param,
251
- separator: ' ',
252
- value,
253
- before: '',
254
- after: '\n',
255
- }
256
-
257
- if (RE_SECTION_DIRECTIVE.test(param)) {
258
- line.before = indent.replace(/ |\t/, '')
259
- config.splice(i, 0, line)
260
- config = (line as Section).config = new SSHConfig()
261
- sectionLineFound = true
262
- continue
263
- }
264
-
265
- // separate from previous sections with an extra newline
266
- if (processedLines === Object.keys(opts).length) {
267
- line.after += '\n'
268
- }
269
-
270
- if (!sectionLineFound) {
271
- config.splice(i, 0, line)
272
- i += 1
273
-
274
- // Add an extra newline if a single line directive like Include
275
- if (RE_SINGLE_LINE_DIRECTIVE.test(param)) {
276
- line.after += '\n'
277
- }
278
- continue
279
- }
280
-
281
- line.before = indent
282
- config.push(line)
283
- }
284
-
285
- return config
286
- }
287
- }
288
-
289
- /**
290
- * Parse ssh config text into structured object.
291
- */
292
- export function parse(text: string): SSHConfig {
293
- let i = 0
294
- let chr = next()
295
- let config: SSHConfig = new SSHConfig()
296
- let configWas = config
297
-
298
- function next() {
299
- return text[i++]
300
- }
301
-
302
- function space(): Space {
303
- let spaces = ''
304
-
305
- while (RE_SPACE.test(chr)) {
306
- spaces += chr
307
- chr = next()
308
- }
309
-
310
- return spaces as Space
311
- }
312
-
313
- function linebreak() {
314
- let breaks = ''
315
-
316
- while (RE_LINE_BREAK.test(chr)) {
317
- breaks += chr
318
- chr = next()
319
- }
320
-
321
- return breaks
322
- }
323
-
324
- function parameter() {
325
- let param = ''
326
-
327
- while (chr && /[^ \t=]/.test(chr)) {
328
- param += chr
329
- chr = next()
330
- }
331
-
332
- return param
333
- }
334
-
335
- function separator(): Separator {
336
- let sep = space()
337
-
338
- if (chr === '=') {
339
- sep += chr
340
- chr = next()
341
- }
342
-
343
- return (sep + space()) as Separator
344
- }
345
-
346
- function value() {
347
- let val = ''
348
- let quoted = false
349
- let escaped = false
350
-
351
- while (chr && !RE_LINE_BREAK.test(chr)) {
352
- // backslash escapes only double quotes
353
- if (escaped) {
354
- val += chr === '"' ? chr : `\\${chr}`
355
- escaped = false
356
- }
357
- // ProxyCommand ssh -W "%h:%p" firewall.example.org
358
- else if (chr === '"' && (!val || quoted)) {
359
- quoted = !quoted
360
- }
361
- else if (chr === '\\') {
362
- escaped = true
363
- }
364
- else {
365
- val += chr
366
- }
367
- chr = next()
368
- }
369
-
370
- if (quoted || escaped) {
371
- throw new Error(`Unexpected line break at ${val}`)
372
- }
373
-
374
- return val.trim()
375
- }
376
-
377
- function comment(): Comment {
378
- const type = LineType.COMMENT
379
- let content = ''
380
-
381
- while (chr && !RE_LINE_BREAK.test(chr)) {
382
- content += chr
383
- chr = next()
384
- }
385
-
386
- return { type, content, before: '', after: '' }
387
- }
388
-
389
- // Host *.co.uk
390
- // Host * !local.dev
391
- // Host "foo bar"
392
- function values() {
393
- const results: string[] = []
394
- let val = ''
395
- let quoted = false
396
- let escaped = false
397
-
398
- while (chr && !RE_LINE_BREAK.test(chr)) {
399
- if (escaped) {
400
- val += chr === '"' ? chr : `\\${chr}`
401
- escaped = false
402
- }
403
- else if (chr === '"') {
404
- quoted = !quoted
405
- }
406
- else if (chr === '\\') {
407
- escaped = true
408
- }
409
- else if (quoted) {
410
- val += chr
411
- }
412
- else if (/[ \t]/.test(chr)) {
413
- if (val) {
414
- results.push(val)
415
- val = ''
416
- }
417
- // otherwise ignore the space
418
- }
419
- else {
420
- val += chr
421
- }
422
-
423
- chr = next()
424
- }
425
-
426
- if (quoted || escaped) {
427
- throw new Error(`Unexpected line break at ${results.concat(val).join(' ')}`)
428
- }
429
- if (val) results.push(val)
430
- return results.length > 1 ? results : results[0]
431
- }
432
-
433
- function directive() {
434
- const type = LineType.DIRECTIVE
435
- const param = parameter()
436
- // Host "foo bar" baz
437
- const multiple = RE_MULTI_VALUE_DIRECTIVE.test(param)
438
- const result: Directive = {
439
- type,
440
- param,
441
- separator: separator(),
442
- quoted: !multiple && chr === '"',
443
- value: multiple ? values() : value(),
444
- before: '',
445
- after: '',
446
- }
447
- if (!result.quoted) delete result.quoted
448
- if (/^Match$/i.test(param)) {
449
- const criteria = {}
450
- for (let i = 0; i < result.value.length; i += 2) {
451
- const keyword = result.value[i]
452
- const value = result.value[ i + 1]
453
- criteria[keyword] = value
454
- }
455
- (result as Match).criteria = criteria
456
- }
457
- return result
458
- }
459
-
460
- function line() {
461
- const before = space()
462
- const node = chr === '#' ? comment() : directive()
463
- const after = linebreak()
464
-
465
- node.before = before
466
- node.after = after
467
-
468
- return node
469
- }
470
-
471
- while (chr) {
472
- let node = line()
473
-
474
- if (node.type === LineType.DIRECTIVE && RE_SECTION_DIRECTIVE.test(node.param)) {
475
- config = configWas
476
- config.push(node)
477
- config = (node as Section).config = new SSHConfig()
478
- }
479
- else if (node.type === LineType.DIRECTIVE && !node.param) {
480
- // blank lines at file end
481
- config[config.length - 1].after += node.before
482
- }
483
- else {
484
- config.push(node)
485
- }
486
- }
487
-
488
- return configWas
489
- }
490
-
491
- /**
492
- * Stringify structured object into ssh config text
493
- */
494
- export function stringify(config: SSHConfig): string {
495
- let str = ''
496
-
497
- function formatValue(value: string | string[] | Record<string, any>, quoted: boolean) {
498
- if (value && typeof value === 'object' && !Array.isArray(value)) {
499
- const result: string[] = []
500
- for (const key in value) result.push(key, value[key])
501
- value = result
502
- }
503
- if (Array.isArray(value)) {
504
- return value.map(chunk => formatValue(chunk, RE_SPACE.test(chunk))).join(' ')
505
- }
506
- return quoted ? `"${value}"` : value
507
- }
508
-
509
- function formatDirective(line) {
510
- const quoted = line.quoted
511
- || (RE_QUOTE_DIRECTIVE.test(line.param) && RE_SPACE.test(line.value))
512
- const value = formatValue(line.value, quoted)
513
- return `${line.param}${line.separator}${value}`
514
- }
515
-
516
- const format = line => {
517
- str += line.before
518
-
519
- if (line.type === LineType.COMMENT) {
520
- str += line.content
521
- }
522
- else if (line.type === LineType.DIRECTIVE && MULTIPLE_VALUE_PROPS.includes(line.param)) {
523
- [].concat(line.value).forEach(function (value, i, values) {
524
- str += formatDirective({ ...line, value })
525
- if (i < values.length - 1) str += `\n${line.before}`
526
- })
527
- }
528
- else if (line.type === LineType.DIRECTIVE) {
529
- str += formatDirective(line)
530
- }
531
-
532
- str += line.after
533
-
534
- if (line.config) {
535
- line.config.forEach(format)
536
- }
537
- }
538
-
539
- config.forEach(format)
540
-
541
- return str
542
- }
543
-
544
- export default Object.assign(SSHConfig, { parse, stringify })