Skip to content

Commit

Permalink
Update ssh launch to use pty (#157)
Browse files Browse the repository at this point in the history
  • Loading branch information
chipsenkbeil authored Dec 11, 2022
1 parent 9b2f0de commit 27dc577
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 176 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ jobs:
runs-on: ${{ matrix.os }}
env:
RUSTFLAGS: --cfg ci
RUST_LOG: trace
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -179,6 +180,9 @@ jobs:
ssh-launch-tests:
name: "Test ssh launch using Rust ${{ matrix.rust }} on ${{ matrix.os }}"
runs-on: ${{ matrix.os }}
env:
RUSTFLAGS: --cfg ci
RUST_LOG: trace
strategy:
fail-fast: false
matrix:
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Persist option now removed from `ProcSpawn` message and CLI
- Bump minimum Rust version to 1.64.0

### Removed

- `--no-shell` option is removed as we automatically detect and use the PTY of
the remote system using a default shell

## [0.20.0-alpha.2] - 2022-11-20

### Added
Expand Down
77 changes: 61 additions & 16 deletions distant-core/src/credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,11 @@ impl<'de> Deserialize<'de> for DistantSingleKeyCredentials {

impl DistantSingleKeyCredentials {
/// Searches a str for `distant://[username]:{key}@{host}:{port}`, returning the first matching
/// credentials set if found
pub fn find(s: &str) -> Option<DistantSingleKeyCredentials> {
/// credentials set if found, failing if anything is found immediately before or after the
/// credentials that is not whitespace or control characters
///
/// If `strict` is false, then the scheme can be preceded by any character
pub fn find(s: &str, strict: bool) -> Option<DistantSingleKeyCredentials> {
let is_boundary = |c| char::is_whitespace(c) || char::is_control(c);

for (i, _) in s.match_indices(SCHEME_WITH_SEP) {
Expand All @@ -105,11 +108,11 @@ impl DistantSingleKeyCredentials {

// Check character preceding the scheme to make sure it isn't a different scheme
// Only whitespace or control characters preceding are okay, anything else is skipped
if !before.is_empty() && !before.ends_with(is_boundary) {
if strict && !before.is_empty() && !before.ends_with(is_boundary) {
continue;
}

// Consume until we reach whitespace, which indicates the potential end
// Consume until we reach whitespace or control, which indicates the potential end
let s = match s.find(is_boundary) {
Some(i) => &s[..i],
None => s,
Expand All @@ -124,6 +127,22 @@ impl DistantSingleKeyCredentials {
None
}

/// Equivalent to [`find(s, true)`].
///
/// [`find(s, true)`]: DistantSingleKeyCredentials::find
#[inline]
pub fn find_strict(s: &str) -> Option<DistantSingleKeyCredentials> {
Self::find(s, true)
}

/// Equivalent to [`find(s, false)`].
///
/// [`find(s, false)`]: DistantSingleKeyCredentials::find
#[inline]
pub fn find_lax(s: &str) -> Option<DistantSingleKeyCredentials> {
Self::find(s, false)
}

/// Converts credentials into a [`Destination`] of the form
/// `distant://[username]:{key}@{host}:{port}`, failing if the credentials would not produce a
/// valid [`Destination`]
Expand Down Expand Up @@ -175,37 +194,37 @@ mod tests {

#[test]
fn find_should_return_some_key_if_string_is_exact_match() {
let credentials = DistantSingleKeyCredentials::find(CREDENTIALS_STR_NO_USER.as_str());
let credentials = DistantSingleKeyCredentials::find(CREDENTIALS_STR_NO_USER.as_str(), true);
assert_eq!(credentials.unwrap(), *CREDENTIALS_NO_USER);

let credentials = DistantSingleKeyCredentials::find(CREDENTIALS_STR_USER.as_str());
let credentials = DistantSingleKeyCredentials::find(CREDENTIALS_STR_USER.as_str(), true);
assert_eq!(credentials.unwrap(), *CREDENTIALS_USER);
}

#[test]
fn find_should_return_some_key_if_there_is_a_match_with_only_whitespace_on_either_side() {
let s = format!(" {} ", CREDENTIALS_STR_NO_USER.as_str());
let credentials = DistantSingleKeyCredentials::find(&s);
let credentials = DistantSingleKeyCredentials::find(&s, true);
assert_eq!(credentials.unwrap(), *CREDENTIALS_NO_USER);

let s = format!("\r{}\r", CREDENTIALS_STR_NO_USER.as_str());
let credentials = DistantSingleKeyCredentials::find(&s);
let credentials = DistantSingleKeyCredentials::find(&s, true);
assert_eq!(credentials.unwrap(), *CREDENTIALS_NO_USER);

let s = format!("\t{}\t", CREDENTIALS_STR_NO_USER.as_str());
let credentials = DistantSingleKeyCredentials::find(&s);
let credentials = DistantSingleKeyCredentials::find(&s, true);
assert_eq!(credentials.unwrap(), *CREDENTIALS_NO_USER);

let s = format!("\n{}\n", CREDENTIALS_STR_NO_USER.as_str());
let credentials = DistantSingleKeyCredentials::find(&s);
let credentials = DistantSingleKeyCredentials::find(&s, true);
assert_eq!(credentials.unwrap(), *CREDENTIALS_NO_USER);
}

#[test]
fn find_should_return_some_key_if_there_is_a_match_with_only_control_characters_on_either_side()
{
let s = format!("\x1b{} \x1b", CREDENTIALS_STR_NO_USER.as_str());
let credentials = DistantSingleKeyCredentials::find(&s);
let credentials = DistantSingleKeyCredentials::find(&s, true);
assert_eq!(credentials.unwrap(), *CREDENTIALS_NO_USER);
}

Expand All @@ -216,7 +235,7 @@ mod tests {
CREDENTIALS_STR_NO_USER.as_str(),
CREDENTIALS_STR_USER.as_str()
);
let credentials = DistantSingleKeyCredentials::find(&s);
let credentials = DistantSingleKeyCredentials::find(&s, true);
assert_eq!(credentials.unwrap(), *CREDENTIALS_NO_USER);
}

Expand All @@ -228,22 +247,48 @@ mod tests {
CREDENTIALS_STR_NO_USER.as_str(),
CREDENTIALS_STR_NO_USER.as_str()
);
let credentials = DistantSingleKeyCredentials::find(&s);
let credentials = DistantSingleKeyCredentials::find(&s, true);
assert_eq!(credentials.unwrap(), *CREDENTIALS_NO_USER);
}

#[test]
fn find_should_return_none_if_no_match_found() {
fn find_with_strict_false_should_ignore_any_character_preceding_scheme() {
let s = format!("a{}", CREDENTIALS_STR_NO_USER.as_str());
let credentials = DistantSingleKeyCredentials::find(&s, false);
assert_eq!(credentials.unwrap(), *CREDENTIALS_NO_USER);

let s = format!(
"a{} b{}",
CREDENTIALS_STR_NO_USER.as_str(),
CREDENTIALS_STR_NO_USER.as_str()
);
let credentials = DistantSingleKeyCredentials::find(&s, false);
assert_eq!(credentials.unwrap(), *CREDENTIALS_NO_USER);
}

#[test]
fn find_with_strict_true_should_not_find_if_non_whitespace_and_control_preceding_scheme() {
let s = format!("a{}", CREDENTIALS_STR_NO_USER.as_str());
let credentials = DistantSingleKeyCredentials::find(&s);
let credentials = DistantSingleKeyCredentials::find(&s, true);
assert_eq!(credentials, None);

let s = format!(
"a{} b{}",
CREDENTIALS_STR_NO_USER.as_str(),
CREDENTIALS_STR_NO_USER.as_str()
);
let credentials = DistantSingleKeyCredentials::find(&s);
let credentials = DistantSingleKeyCredentials::find(&s, true);
assert_eq!(credentials, None);
}

#[test]
fn find_should_return_none_if_no_match_found() {
let s = "abc";
let credentials = DistantSingleKeyCredentials::find(s, true);
assert_eq!(credentials, None);

let s = "abc";
let credentials = DistantSingleKeyCredentials::find(s, false);
assert_eq!(credentials, None);
}

Expand Down
Loading

0 comments on commit 27dc577

Please sign in to comment.