diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..d321c3f0 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +* @hierynomus + diff --git a/src/main/java/net/schmizz/sshj/transport/verification/OpenSSHKnownHosts.java b/src/main/java/net/schmizz/sshj/transport/verification/OpenSSHKnownHosts.java index e400b8c0..d042b93d 100644 --- a/src/main/java/net/schmizz/sshj/transport/verification/OpenSSHKnownHosts.java +++ b/src/main/java/net/schmizz/sshj/transport/verification/OpenSSHKnownHosts.java @@ -199,24 +199,22 @@ public class OpenSSHKnownHosts return new CommentEntry(line); } - final String[] split = line.split("\\s+"); - if(split.length < 3) { + final String trimmed = line.trim(); + int minComponents = 3; + if (trimmed.startsWith("@")) { + minComponents = 4; + } + String[] split = trimmed.split("\\s+", minComponents + 1); // Add 1 for optional comments + if(split.length < minComponents) { log.error("Error reading entry `{}`", line); return new BadHostEntry(line); } - int i = 0; - if (split[i].isEmpty()) { - i++; - } + final Marker marker = Marker.fromString(split[i]); if (marker != null) { i++; } - if(split.length < i + 3) { - log.error("Error reading entry `{}`", line); - return new BadHostEntry(line); - } final String hostnames = split[i++]; final String sType = split[i++]; @@ -234,6 +232,9 @@ public class OpenSSHKnownHosts } } else if (isBits(sType)) { type = KeyType.RSA; + minComponents += 1; + // re-split + split = trimmed.split("\\s+", minComponents + 1); // Add 1 for optional comments // int bits = Integer.valueOf(sType); final BigInteger e = new BigInteger(split[i++]); final BigInteger n = new BigInteger(split[i++]); @@ -249,7 +250,13 @@ public class OpenSSHKnownHosts return new BadHostEntry(line); } - return new HostEntry(marker, hostnames, type, key); + final String comment; + if (i < split.length) { + comment = split[i++]; + } else { + comment = null; + } + return new HostEntry(marker, hostnames, type, key, comment); } private boolean isBits(String type) { @@ -330,13 +337,19 @@ public class OpenSSHKnownHosts private final String hostPart; protected final KeyType type; protected final PublicKey key; + private final String comment; private final KnownHostMatchers.HostMatcher matcher; public HostEntry(Marker marker, String hostPart, KeyType type, PublicKey key) throws SSHException { + this(marker, hostPart, type, key, ""); + } + + public HostEntry(Marker marker, String hostPart, KeyType type, PublicKey key, String comment) throws SSHException { this.marker = marker; this.hostPart = hostPart; this.type = type; this.key = key; + this.comment = comment; this.matcher = KnownHostMatchers.createMatcher(hostPart); } @@ -373,6 +386,9 @@ public class OpenSSHKnownHosts line.append(getHostPart()); line.append(" ").append(type.toString()); line.append(" ").append(getKeyString(key)); + + if (!comment.isEmpty()) line.append(" ").append(comment); + return line.toString(); } @@ -384,6 +400,10 @@ public class OpenSSHKnownHosts protected String getHostPart() { return hostPart; } + + public String getComment() { + return comment; + } } public static class BadHostEntry implements KnownHostEntry { diff --git a/src/test/groovy/com/hierynomus/sshj/transport/verification/OpenSSHKnownHostsSpec.groovy b/src/test/groovy/com/hierynomus/sshj/transport/verification/OpenSSHKnownHostsSpec.groovy index 12c32706..17f8b9b9 100644 --- a/src/test/groovy/com/hierynomus/sshj/transport/verification/OpenSSHKnownHostsSpec.groovy +++ b/src/test/groovy/com/hierynomus/sshj/transport/verification/OpenSSHKnownHostsSpec.groovy @@ -116,6 +116,36 @@ host1 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBL s << ["\n", "#comment\n"] } + @Unroll + def "should contain comment at end of line"() { + given: + def f = knownHosts(host) + when: + OpenSSHKnownHosts knownHosts = new OpenSSHKnownHosts(f) + + then: + knownHosts.entries().size() == 1 + def entry = knownHosts.entries().get(0) + entry instanceof OpenSSHKnownHosts.HostEntry + entry.comment == comment + + where: + host << [ +"|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ== this is a comment", +"test.com,1.1.1.1 2048 35 22017496617994656680820635966392838863613340434802393112245951008866692373218840197754553998457793202561151141246686162285550121243768846314646395880632789308110750881198697743542374668273149584280424505890648953477691795864456749782348425425954366277600319096366690719901119774784695056100331902394094537054256611668966698242432417382422091372756244612839068092471592121759862971414741954991375710930168229171638843329213652899594987626853020377726482288618521941129157643483558764875338089684351824791983007780922947554898825663693324944982594850256042689880090306493029526546183035567296830604572253312294059766327 single", +"schmizz.net,69.163.155.180 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ==", +"schmizz.net,69.163.155.180 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ== ", +"schmizz.net,69.163.155.180 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ== extra space" + ] + comment << [ + "this is a comment", + "single", + null, + null, + "extra space" + ] + } + @Unroll def "should match any host name from multi-host line"() { given: