fix(aws): prevent endless loop when AWS config file is a directory (#3335)

When opening a directory as a file the intial open works, while
subsequent line-reads will fail with _is a directory_.
Since erroring line-reads were just skipped this lead to an
endless loop.
This commit is contained in:
Denis Cornehl 2021-12-20 21:15:00 +01:00 committed by GitHub
parent 5ce8811397
commit 006fbf0dd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,6 +1,4 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
@ -10,7 +8,7 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::aws::AwsConfig; use crate::configs::aws::AwsConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils::render_time; use crate::utils::{read_file, render_time};
type Profile = String; type Profile = String;
type Region = String; type Region = String;
@ -40,19 +38,19 @@ fn get_config_file_path(context: &Context) -> Option<PathBuf> {
fn get_aws_region_from_config(context: &Context, aws_profile: Option<&str>) -> Option<Region> { fn get_aws_region_from_config(context: &Context, aws_profile: Option<&str>) -> Option<Region> {
let config_location = get_config_file_path(context)?; let config_location = get_config_file_path(context)?;
let file = File::open(&config_location).ok()?; let contents = read_file(&config_location).ok()?;
let reader = BufReader::new(file);
let lines = reader.lines().filter_map(Result::ok);
let region_line = if let Some(aws_profile) = aws_profile { let region_line = if let Some(aws_profile) = aws_profile {
lines contents
.lines()
.skip_while(|line| line != &format!("[profile {}]", &aws_profile)) .skip_while(|line| line != &format!("[profile {}]", &aws_profile))
.skip(1) .skip(1)
.take_while(|line| !line.starts_with('[')) .take_while(|line| !line.starts_with('['))
.find(|line| line.starts_with("region")) .find(|line| line.starts_with("region"))
} else { } else {
lines contents
.skip_while(|line| line != "[default]") .lines()
.skip_while(|&line| line != "[default]")
.skip(1) .skip(1)
.take_while(|line| !line.starts_with('[')) .take_while(|line| !line.starts_with('['))
.find(|line| line.starts_with("region")) .find(|line| line.starts_with("region"))
@ -91,11 +89,7 @@ fn get_credentials_duration(context: &Context, aws_profile: Option<&Profile>) ->
{ {
chrono::DateTime::parse_from_rfc3339(&expiration_date).ok() chrono::DateTime::parse_from_rfc3339(&expiration_date).ok()
} else { } else {
let credentials_location = get_credentials_file_path(context)?; let contents = read_file(get_credentials_file_path(context)?).ok()?;
let file = File::open(&credentials_location).ok()?;
let reader = BufReader::new(file);
let lines = reader.lines().filter_map(Result::ok);
let profile_line = if let Some(aws_profile) = aws_profile { let profile_line = if let Some(aws_profile) = aws_profile {
format!("[{}]", aws_profile) format!("[{}]", aws_profile)
@ -103,7 +97,8 @@ fn get_credentials_duration(context: &Context, aws_profile: Option<&Profile>) ->
"[default]".to_string() "[default]".to_string()
}; };
let expiration_date_line = lines let expiration_date_line = contents
.lines()
.skip_while(|line| line != &profile_line) .skip_while(|line| line != &profile_line)
.skip(1) .skip(1)
.take_while(|line| !line.starts_with('[')) .take_while(|line| !line.starts_with('['))
@ -182,7 +177,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
mod tests { mod tests {
use crate::test::ModuleRenderer; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::{create_dir, File};
use std::io::{self, Write}; use std::io::{self, Write};
#[test] #[test]
@ -306,6 +301,37 @@ mod tests {
assert_eq!(expected, actual); assert_eq!(expected, actual);
} }
#[test]
fn credentials_file_is_ignored_when_is_directory() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let config_path = dir.path().join("credentials");
create_dir(&config_path)?;
assert!(ModuleRenderer::new("aws")
.env(
"AWS_CREDENTIALS_FILE",
config_path.to_string_lossy().as_ref(),
)
.collect()
.is_none());
dir.close()
}
#[test]
fn config_file_path_is_ignored_when_is_directory() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let config_path = dir.path().join("config");
create_dir(&config_path)?;
assert!(ModuleRenderer::new("aws")
.env("AWS_CONFIG_FILE", config_path.to_string_lossy().as_ref())
.collect()
.is_none());
dir.close()
}
#[test] #[test]
fn default_profile_set() -> io::Result<()> { fn default_profile_set() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;