ssh-config 4.2.0 → 4.3.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 +3 -0
- package/package.json +4 -2
- package/src/glob.d.ts +9 -0
- package/src/ssh-config.d.ts +88 -0
- package/src/ssh-config.js +106 -61
- package/src/glob.js.map +0 -1
- package/src/glob.ts +0 -45
- package/src/ssh-config.js.map +0 -1
- package/src/ssh-config.ts +0 -544
package/index.d.ts
ADDED
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
|
+
"version": "4.3.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
|
-
"
|
|
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,88 @@
|
|
|
1
|
+
declare enum LineType {
|
|
2
|
+
DIRECTIVE = 1,
|
|
3
|
+
COMMENT = 2
|
|
4
|
+
}
|
|
5
|
+
type Separator = ' ' | '=' | '\t';
|
|
6
|
+
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
|
+
interface Section extends Directive {
|
|
16
|
+
config: SSHConfig;
|
|
17
|
+
}
|
|
18
|
+
interface Match extends Section {
|
|
19
|
+
criteria: Record<string, string | string[]>;
|
|
20
|
+
}
|
|
21
|
+
interface Comment {
|
|
22
|
+
type: LineType.COMMENT;
|
|
23
|
+
before: string;
|
|
24
|
+
after: string;
|
|
25
|
+
content: string;
|
|
26
|
+
}
|
|
27
|
+
type Line = Match | Section | Directive | Comment;
|
|
28
|
+
interface FindOptions {
|
|
29
|
+
Host?: string;
|
|
30
|
+
}
|
|
31
|
+
interface MatchOptions {
|
|
32
|
+
Host: string;
|
|
33
|
+
User?: string;
|
|
34
|
+
}
|
|
35
|
+
declare class SSHConfig extends Array<Line> {
|
|
36
|
+
static readonly DIRECTIVE: LineType.DIRECTIVE;
|
|
37
|
+
static readonly COMMENT: LineType.COMMENT;
|
|
38
|
+
/**
|
|
39
|
+
* Query SSH config by host.
|
|
40
|
+
*/
|
|
41
|
+
compute(host: string): Record<string, string | string[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Query SSH config by host and user.
|
|
44
|
+
*/
|
|
45
|
+
compute(opts: MatchOptions): Record<string, string | string[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Find by Host or Match.
|
|
48
|
+
*/
|
|
49
|
+
find(opts: FindOptions): Line | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Find by search function.
|
|
52
|
+
* @param predicate Function to check against each line; should return a truthy value when a
|
|
53
|
+
* matching line is given.
|
|
54
|
+
*/
|
|
55
|
+
find(predicate: (line: Line, index: number, config: Line[]) => unknown): Line | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Remove section by Host or Match.
|
|
58
|
+
*/
|
|
59
|
+
remove(opts: FindOptions): Line[] | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Remove section by search function.
|
|
62
|
+
* @param predicate Function to check against each line; should return a truthy value when a
|
|
63
|
+
* matching line is given.
|
|
64
|
+
*/
|
|
65
|
+
remove(predicate: (line: Line, index: number, config: Line[]) => unknown): Line[] | undefined;
|
|
66
|
+
toString(): string;
|
|
67
|
+
/**
|
|
68
|
+
* Append new section to existing SSH config.
|
|
69
|
+
*/
|
|
70
|
+
append(opts: Record<string, string | string[]>): SSHConfig;
|
|
71
|
+
/**
|
|
72
|
+
* Prepend new section to existing SSH config.
|
|
73
|
+
*/
|
|
74
|
+
prepend(opts: Record<string, string | string[]>, beforeFirstSection?: boolean): SSHConfig;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Parse SSH config text into structured object.
|
|
78
|
+
*/
|
|
79
|
+
export declare function parse(text: string): SSHConfig;
|
|
80
|
+
/**
|
|
81
|
+
* Stringify structured object into SSH config text.
|
|
82
|
+
*/
|
|
83
|
+
export declare function stringify(config: SSHConfig): string;
|
|
84
|
+
declare const _default: typeof SSHConfig & {
|
|
85
|
+
parse: typeof parse;
|
|
86
|
+
stringify: typeof stringify;
|
|
87
|
+
};
|
|
88
|
+
export default _default;
|
package/src/ssh-config.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.stringify = exports.parse = 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;
|
|
@@ -39,37 +40,56 @@ function getIndent(config) {
|
|
|
39
40
|
}
|
|
40
41
|
return ' ';
|
|
41
42
|
}
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
}
|
|
64
76
|
return true;
|
|
65
77
|
}
|
|
66
78
|
class SSHConfig extends Array {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
79
|
+
compute(opts) {
|
|
80
|
+
if (typeof opts === 'string')
|
|
81
|
+
opts = { Host: opts };
|
|
82
|
+
const context = {
|
|
83
|
+
params: {
|
|
84
|
+
Host: opts.Host,
|
|
85
|
+
HostName: opts.Host,
|
|
86
|
+
OriginalHost: opts.Host,
|
|
87
|
+
User: os_1.default.userInfo().username,
|
|
88
|
+
LocalUser: os_1.default.userInfo().username,
|
|
89
|
+
},
|
|
90
|
+
inFinalPass: false,
|
|
91
|
+
doFinalPass: false,
|
|
92
|
+
};
|
|
73
93
|
const obj = {};
|
|
74
94
|
const setProperty = (name, value) => {
|
|
75
95
|
if (MULTIPLE_VALUE_PROPS.includes(name)) {
|
|
@@ -77,36 +97,50 @@ class SSHConfig extends Array {
|
|
|
77
97
|
list.push(value);
|
|
78
98
|
}
|
|
79
99
|
else if (obj[name] == null) {
|
|
100
|
+
if (name === "HostName") {
|
|
101
|
+
context.params.HostName = value;
|
|
102
|
+
}
|
|
103
|
+
else if (name === "User") {
|
|
104
|
+
context.params.User = value;
|
|
105
|
+
}
|
|
80
106
|
obj[name] = value;
|
|
81
107
|
}
|
|
82
108
|
};
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
109
|
+
if (opts.User !== undefined) {
|
|
110
|
+
setProperty("User", opts.User);
|
|
111
|
+
}
|
|
112
|
+
const doPass = () => {
|
|
113
|
+
for (const line of this) {
|
|
114
|
+
if (line.type !== LineType.DIRECTIVE)
|
|
115
|
+
continue;
|
|
116
|
+
if (line.param === 'Host' && (0, glob_1.default)(line.value, context.params.Host)) {
|
|
117
|
+
setProperty(line.param, line.value);
|
|
118
|
+
for (const subline of line.config) {
|
|
119
|
+
if (subline.type === LineType.DIRECTIVE) {
|
|
120
|
+
setProperty(subline.param, subline.value);
|
|
121
|
+
}
|
|
91
122
|
}
|
|
92
123
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
124
|
+
else if (line.param === 'Match' && 'criteria' in line && match(line.criteria, context)) {
|
|
125
|
+
for (const subline of line.config) {
|
|
126
|
+
if (subline.type === LineType.DIRECTIVE) {
|
|
127
|
+
setProperty(subline.param, subline.value);
|
|
128
|
+
}
|
|
98
129
|
}
|
|
99
130
|
}
|
|
131
|
+
else if (line.param !== 'Host' && line.param !== 'Match') {
|
|
132
|
+
setProperty(line.param, line.value);
|
|
133
|
+
}
|
|
100
134
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
135
|
+
};
|
|
136
|
+
doPass();
|
|
137
|
+
if (context.doFinalPass) {
|
|
138
|
+
context.inFinalPass = true;
|
|
139
|
+
context.params.Host = context.params.HostName;
|
|
140
|
+
doPass();
|
|
104
141
|
}
|
|
105
142
|
return obj;
|
|
106
143
|
}
|
|
107
|
-
/**
|
|
108
|
-
* find section by Host / Match or function
|
|
109
|
-
*/
|
|
110
144
|
find(opts) {
|
|
111
145
|
if (typeof opts === 'function')
|
|
112
146
|
return super.find(opts);
|
|
@@ -115,9 +149,6 @@ class SSHConfig extends Array {
|
|
|
115
149
|
}
|
|
116
150
|
return super.find(line => compare(line, opts));
|
|
117
151
|
}
|
|
118
|
-
/**
|
|
119
|
-
* Remove section by Host / Match or function
|
|
120
|
-
*/
|
|
121
152
|
remove(opts) {
|
|
122
153
|
let index;
|
|
123
154
|
if (typeof opts === 'function') {
|
|
@@ -136,8 +167,7 @@ class SSHConfig extends Array {
|
|
|
136
167
|
return stringify(this);
|
|
137
168
|
}
|
|
138
169
|
/**
|
|
139
|
-
* Append new section to existing
|
|
140
|
-
* @param {Object} opts
|
|
170
|
+
* Append new section to existing SSH config.
|
|
141
171
|
*/
|
|
142
172
|
append(opts) {
|
|
143
173
|
const indent = getIndent(this);
|
|
@@ -177,8 +207,7 @@ class SSHConfig extends Array {
|
|
|
177
207
|
return configWas;
|
|
178
208
|
}
|
|
179
209
|
/**
|
|
180
|
-
* Prepend new section to existing
|
|
181
|
-
* @param {Object} opts
|
|
210
|
+
* Prepend new section to existing SSH config.
|
|
182
211
|
*/
|
|
183
212
|
prepend(opts, beforeFirstSection = false) {
|
|
184
213
|
const indent = getIndent(this);
|
|
@@ -236,7 +265,7 @@ class SSHConfig extends Array {
|
|
|
236
265
|
SSHConfig.DIRECTIVE = LineType.DIRECTIVE;
|
|
237
266
|
SSHConfig.COMMENT = LineType.COMMENT;
|
|
238
267
|
/**
|
|
239
|
-
* Parse
|
|
268
|
+
* Parse SSH config text into structured object.
|
|
240
269
|
*/
|
|
241
270
|
function parse(text) {
|
|
242
271
|
let i = 0;
|
|
@@ -373,10 +402,27 @@ function parse(text) {
|
|
|
373
402
|
delete result.quoted;
|
|
374
403
|
if (/^Match$/i.test(param)) {
|
|
375
404
|
const criteria = {};
|
|
376
|
-
|
|
405
|
+
if (typeof result.value === "string") {
|
|
406
|
+
result.value = [result.value];
|
|
407
|
+
}
|
|
408
|
+
let i = 0;
|
|
409
|
+
while (i < result.value.length) {
|
|
377
410
|
const keyword = result.value[i];
|
|
378
|
-
|
|
379
|
-
|
|
411
|
+
switch (keyword.toLowerCase()) {
|
|
412
|
+
case "all":
|
|
413
|
+
case "canonical":
|
|
414
|
+
case "final":
|
|
415
|
+
criteria[keyword] = [];
|
|
416
|
+
i += 1;
|
|
417
|
+
break;
|
|
418
|
+
default:
|
|
419
|
+
if (i + 1 >= result.value.length) {
|
|
420
|
+
throw new Error(`Missing value for match criteria ${keyword}`);
|
|
421
|
+
}
|
|
422
|
+
criteria[keyword] = result.value[i + 1];
|
|
423
|
+
i += 2;
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
380
426
|
}
|
|
381
427
|
result.criteria = criteria;
|
|
382
428
|
}
|
|
@@ -399,7 +445,12 @@ function parse(text) {
|
|
|
399
445
|
}
|
|
400
446
|
else if (node.type === LineType.DIRECTIVE && !node.param) {
|
|
401
447
|
// blank lines at file end
|
|
402
|
-
config
|
|
448
|
+
if (config.length === 0) {
|
|
449
|
+
configWas[configWas.length - 1].after += node.before;
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
config[config.length - 1].after += node.before;
|
|
453
|
+
}
|
|
403
454
|
}
|
|
404
455
|
else {
|
|
405
456
|
config.push(node);
|
|
@@ -409,17 +460,11 @@ function parse(text) {
|
|
|
409
460
|
}
|
|
410
461
|
exports.parse = parse;
|
|
411
462
|
/**
|
|
412
|
-
* Stringify structured object into
|
|
463
|
+
* Stringify structured object into SSH config text.
|
|
413
464
|
*/
|
|
414
465
|
function stringify(config) {
|
|
415
466
|
let str = '';
|
|
416
467
|
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
468
|
if (Array.isArray(value)) {
|
|
424
469
|
return value.map(chunk => formatValue(chunk, RE_SPACE.test(chunk))).join(' ');
|
|
425
470
|
}
|
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
|
package/src/ssh-config.js.map
DELETED
|
@@ -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 {
|
|
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 })
|