Skip to content

Commit cf4e03f

Browse files
committed
fix(ssh): fix parsing of ssh config w/o host directives
fixes ionic-team#2886
1 parent 5956c74 commit cf4e03f

5 files changed

Lines changed: 40 additions & 17 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AddKeysToAgent yes

packages/@ionic/cli-utils/src/lib/__tests__/ssh-config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ describe('@ionic/cli-utils', () => {
5656
expect(SSHConfig.stringify(conf)).toEqual(config5);
5757
});
5858

59+
it('should stringify with config6 file', async () => {
60+
const config6 = await fsReadFile(path.resolve(__dirname, 'fixtures/ssh-config/config6'), { encoding: 'utf8' });
61+
const conf = SSHConfig.parse(config6);
62+
ensureHostAndKeyPath(conf, { host: 'bar' }, '/id_rsa');
63+
expect(SSHConfig.stringify(conf)).toEqual(`${config6}\n${expected}`);
64+
});
65+
5966
});
6067

6168
});

packages/@ionic/cli-utils/src/lib/ssh-config.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,24 @@ export async function loadFromPath(p: string): Promise<SSHConfigModule.SSHConfig
1212
return SSHConfig.parse(s);
1313
}
1414

15-
export function isDirective(entry: SSHConfigModule.Config): entry is SSHConfigModule.ConfigDirective {
15+
export function isDirective(entry: any): entry is SSHConfigModule.ConfigDirective {
1616
return entry && entry.type === SSHConfig.DIRECTIVE;
1717
}
1818

19+
export function isHostDirective(entry: SSHConfigModule.Config): entry is SSHConfigModule.ConfigHostDirective {
20+
return isDirective(entry) && entry.param === 'Host';
21+
}
22+
1923
export function getConfigPath() {
2024
return path.resolve(os.homedir(), '.ssh', 'config');
2125
}
2226

23-
export function findHostSection(conf: SSHConfigModule.SSHConfig, host: string): SSHConfigModule.ConfigDirective | null {
27+
export function findHostSection(conf: SSHConfigModule.SSHConfig, host: string): SSHConfigModule.ConfigHostDirective | null {
2428
return conf.find({ Host: host });
2529
}
2630

2731
export function ensureHostAndKeyPath(conf: SSHConfigModule.SSHConfig, conn: { host: string, port?: number }, keyPath: string): void {
28-
const section = ensureSection(conf, conn.host);
32+
const section = ensureHostSection(conf, conn.host);
2933
const index = conf.indexOf(section);
3034

3135
ensureSectionLine(section, 'IdentityFile', keyPath);
@@ -41,9 +45,12 @@ export function ensureHostAndKeyPath(conf: SSHConfigModule.SSHConfig, conn: { ho
4145
} else {
4246
const previousSection = conf[index - 1];
4347

44-
if (isDirective(previousSection)) {
48+
if (isHostDirective(previousSection)) {
4549
const previousSectionLastEntry = previousSection.config[previousSection.config.length - 1];
46-
previousSectionLastEntry.after = '\n';
50+
51+
if (previousSectionLastEntry) {
52+
previousSectionLastEntry.after = '\n';
53+
}
4754
} else {
4855
previousSection.after = '\n';
4956
}
@@ -53,6 +60,10 @@ export function ensureHostAndKeyPath(conf: SSHConfigModule.SSHConfig, conn: { ho
5360

5461
section.after = '\n';
5562

63+
if (!section.config) {
64+
section.config = [];
65+
}
66+
5667
for (let entry of section.config) {
5768
entry.before = ' ';
5869
entry.after = '\n';
@@ -64,7 +75,7 @@ export function ensureHostAndKeyPath(conf: SSHConfigModule.SSHConfig, conn: { ho
6475
}
6576
}
6677

67-
function ensureSection(conf: SSHConfigModule.SSHConfig, host: string): SSHConfigModule.ConfigDirective {
78+
function ensureHostSection(conf: SSHConfigModule.SSHConfig, host: string): SSHConfigModule.ConfigHostDirective {
6879
let section = findHostSection(conf, host);
6980

7081
if (!section) {
@@ -79,7 +90,7 @@ function ensureSection(conf: SSHConfigModule.SSHConfig, host: string): SSHConfig
7990
return section;
8091
}
8192

82-
function ensureSectionLine(section: SSHConfigModule.ConfigDirective, key: string, value: string): void {
93+
function ensureSectionLine(section: SSHConfigModule.ConfigHostDirective, key: string, value: string): void {
8394
const found = section.config.some(line => {
8495
if (isDirective(line)) {
8596
if (line.param === key) {

packages/ionic/src/commands/ssh/list.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class SSHListCommand extends SSHBaseCommand implements CommandPreRun {
2424
const {
2525
findHostSection,
2626
getConfigPath,
27-
isDirective,
27+
isHostDirective,
2828
loadFromPath,
2929
} = await import('@ionic/cli-utils/lib/ssh-config');
3030

@@ -38,12 +38,12 @@ export class SSHListCommand extends SSHBaseCommand implements CommandPreRun {
3838
const conf = await loadFromPath(sshConfigPath);
3939
const section = findHostSection(conf, await this.env.config.getGitHost());
4040

41-
if (section) {
42-
const [ identityFile ] = section.config.filter((line) => { // TODO: can't use find() w/o Host or Match, ssh-config bug?
43-
return isDirective(line) && line.param === 'IdentityFile';
41+
if (section && section.config) {
42+
const [ identityFile ] = section.config.filter(line => {
43+
return isHostDirective(line) && line.param === 'IdentityFile';
4444
});
4545

46-
if (isDirective(identityFile)) {
46+
if (isHostDirective(identityFile)) {
4747
const output = await this.env.shell.run('ssh-keygen', ['-E', 'sha256', '-lf', identityFile.value], { showCommand: false, fatalOnError: false });
4848
activeFingerprint = output.trim().split(' ')[1];
4949
}

types/ssh-config.d.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
declare module "ssh-config" {
22
namespace SSHConfig {
3-
var DIRECTIVE: number;
4-
var COMMENT: number;
3+
var DIRECTIVE: number; // 1
4+
var COMMENT: number; // 2
55

66
interface SSHConfigFindOptions {
77
Host?: string;
@@ -17,16 +17,20 @@ declare module "ssh-config" {
1717

1818
interface ConfigDirective {
1919
type: typeof DIRECTIVE;
20-
content: string;
2120
before: string;
2221
after: string;
2322
param: string;
2423
value: string;
2524
separator: string;
26-
config: Config[];
2725
}
2826

29-
type Config = ConfigComment | ConfigDirective;
27+
interface ConfigHostDirective extends ConfigDirective {
28+
config: SSHConfig;
29+
}
30+
31+
type ConfigMatchDirective = ConfigHostDirective;
32+
33+
type Config = ConfigComment | ConfigDirective | ConfigHostDirective | ConfigMatchDirective;
3034

3135
interface SSHConfig extends Array<Config> {
3236
find(arg: any): any; // https://github.com/dotnil/ssh-config/blob/40b55c0e31790dd78a0d4364b0e8a0a358293385/index.js#L61

0 commit comments

Comments
 (0)