diff --git a/src/aws-cpp-sdk-core/source/config/AWSConfigFileProfileConfigLoader.cpp b/src/aws-cpp-sdk-core/source/config/AWSConfigFileProfileConfigLoader.cpp index 4e90fb12c96..bd0eeb2cede 100644 --- a/src/aws-cpp-sdk-core/source/config/AWSConfigFileProfileConfigLoader.cpp +++ b/src/aws-cpp-sdk-core/source/config/AWSConfigFileProfileConfigLoader.cpp @@ -4,12 +4,18 @@ */ #include +#include #include #include #include #include #include +namespace { +Aws::Array COMMENT_START_SEQ{" #", " ;", "\t#", "\t;"}; +Aws::Array COMMENT_CHARS{'#', ';'}; +} + namespace Aws { namespace Config @@ -46,8 +52,6 @@ namespace Aws static const size_t IDENTIFIER_ALLOWED_CHARACTERS_SZ = sizeof(IDENTIFIER_ALLOWED_CHARACTERS) - 1; const char WHITESPACE_CHARACTERS[] = "\t "; static const size_t WHITESPACE_CHARACTERS_SZ = sizeof(WHITESPACE_CHARACTERS) - 1; - const char COMMENT_START[] = "#;"; - static const size_t COMMENT_START_SZ = sizeof(COMMENT_START) - 1; struct ProfilePropertyAccessFunctions { @@ -125,7 +129,7 @@ namespace Aws rawLine.pop_back(); // Remove carriage return character ('\r') } - Aws::String line = rawLine.substr(0, rawLine.find_first_of(COMMENT_START)); // ignore comments + const Aws::String line{StripCommentFromLine(rawLine)}; if (line.empty() || line.length() < ASSUME_EMPTY_LEN || line.find_first_not_of(WHITESPACE_CHARACTERS) == Aws::String::npos) { continue; @@ -384,8 +388,7 @@ namespace Aws } pos++; pos = line.find_first_not_of(WHITESPACE_CHARACTERS, pos); - if(pos != Aws::String::npos && - std::find(COMMENT_START, COMMENT_START + COMMENT_START_SZ, line[pos]) == COMMENT_START + COMMENT_START_SZ) + if(pos != Aws::String::npos && std::find(COMMENT_CHARS.begin(), COMMENT_CHARS.end(), line[pos]) == COMMENT_CHARS.end()) { AWS_LOGSTREAM_ERROR(PARSER_TAG, "Found unexpected characters after closing bracket of Section Identifier " << line); break; @@ -532,6 +535,26 @@ namespace Aws } } + /** + * Returns a configuration file line with inline comments removed. + * + * @param line a raw line read in from configuration. + * @return the raw line with any commented out sections removed. + */ + static Aws::String StripCommentFromLine(const Aws::String& line) { + // entire line is commented if the first char is comment, otherwise we want whitespace + comment delimitation + if (line.empty() || std::any_of(COMMENT_CHARS.begin(), COMMENT_CHARS.end(), + [&line](const char firstChar) -> bool { return firstChar == line.front(); })) { + return {}; + } + + size_t commentLocation = Aws::String::npos; + for (const auto* const commentChar : COMMENT_START_SEQ) { + commentLocation = std::min(commentLocation, line.find(commentChar)); + } + return {line.substr(0, commentLocation)}; + } + Aws::Map m_foundProfiles; Aws::Map m_foundSsoSessions; }; diff --git a/tests/aws-cpp-sdk-core-tests/aws/config/AWSConfigFileProfileConfigLoaderTest.cpp b/tests/aws-cpp-sdk-core-tests/aws/config/AWSConfigFileProfileConfigLoaderTest.cpp index 068738a0b3a..026afd98c00 100644 --- a/tests/aws-cpp-sdk-core-tests/aws/config/AWSConfigFileProfileConfigLoaderTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/aws/config/AWSConfigFileProfileConfigLoaderTest.cpp @@ -348,13 +348,13 @@ R"( ;another comment [ default ] source_profile = base)" "\t" R"( -credential_process = echo '{ "Version": 1, "AccessKeyId": "ASIARTESTID", "SecretAccessKey": "TESTSECRETKEY", "SessionToken": "TESTSESSIONTOKEN", "Expiration": "2022-05-02T18:36:00+00:00" }'#COMMENT +credential_process = echo '{ "Version": 1, "AccessKeyId": "ASIARTESTID", "SecretAccessKey": "TESTSECRETKEY", "SessionToken": "TESTSESSIONTOKEN", "Expiration": "2022-05-02T18:36:00+00:00" }' #COMMENT ; Comment to be ignored )" R"( )" // to avoid blank space removals by an IDE. R"( [ profile )" "\t\t\t" R"( base ] - region = us-east-1#sa-east-3 + region = us-east-1 #sa-east-3 #region = commented-out region )"; @@ -484,3 +484,126 @@ aws_secret_access_key = correct_secret)"; ASSERT_EQ("correct_secret", profiles.at(complicatedProfileName).GetCredentials().GetAWSSecretKey()); } } + +TEST_F(AWSConfigFileProfileConfigLoaderTest, TestSsoStartUrlWithHash) { + TempFile configFile(std::ios_base::out | std::ios_base::trunc); + ASSERT_TRUE(configFile.good()); + + configFile << R"([profile gustave])" << "\n" + << R"(sso_account_id = 333333333333)" << "\n" + << R"(sso_role_name = PictoAccess)" << "\n" + << R"(sso_start_url = https://lumiere.com/for-those-who-come-after#)" << "\n" + << R"(sso_region = eu-west-3)" << "\n" + << R"(sso_registration_scopes = sso:account:access)" << "\n" + << R"(region = eu-west-3)" << "\n" + << R"(output = json)" << "\n"; + + configFile.flush(); + + AWSConfigFileProfileConfigLoader loader(configFile.GetFileName(), true); + ASSERT_TRUE(loader.Load()); + const auto& loadedProfiles = loader.GetProfiles(); + ASSERT_TRUE(loadedProfiles.size() == 1); + const auto& profile = loadedProfiles.find("gustave"); + ASSERT_TRUE(profile != loadedProfiles.end()); + ASSERT_STREQ("https://lumiere.com/for-those-who-come-after#", profile->second.GetSsoStartUrl().c_str()); +} + +TEST_F(AWSConfigFileProfileConfigLoaderTest, TestSsoStartUrlWithComment) { + TempFile configFile(std::ios_base::out | std::ios_base::trunc); + ASSERT_TRUE(configFile.good()); + + configFile << R"([profile lune])" << "\n" + << R"(sso_account_id = 333333333333)" << "\n" + << R"(sso_role_name = PictoAccess)" << "\n" + << R"(sso_start_url = https://lumiere.com/for-those-who-come-after # stained)" << "\n" + << R"(sso_region = eu-west-3)" << "\n" + << R"(sso_registration_scopes = sso:account:access)" << "\n" + << R"(region = eu-west-3)" << "\n" + << R"(output = json)" << "\n"; + + configFile.flush(); + + AWSConfigFileProfileConfigLoader loader(configFile.GetFileName(), true); + ASSERT_TRUE(loader.Load()); + const auto& loadedProfiles = loader.GetProfiles(); + ASSERT_TRUE(loadedProfiles.size() == 1); + const auto& profile = loadedProfiles.find("lune"); + ASSERT_TRUE(profile != loadedProfiles.end()); + ASSERT_STREQ("https://lumiere.com/for-those-who-come-after", profile->second.GetSsoStartUrl().c_str()); +} + +TEST_F(AWSConfigFileProfileConfigLoaderTest, TestSsoStartUrlLineCommnent) { + TempFile configFile(std::ios_base::out | std::ios_base::trunc); + ASSERT_TRUE(configFile.good()); + + configFile << R"([profile maelle])" << "\n" + << R"(sso_account_id = 333333333333)" << "\n" + << R"(sso_role_name = PictoAccess)" << "\n" + << R"(sso_start_url = https://lumiere.com/for-those-who-come-after # stendahl pre nerf)" << "\n" + << R"(#sso_start_url = https://lumiere.com/verso)" << "\n" + << R"(sso_region = eu-west-3)" << "\n" + << R"(sso_registration_scopes = sso:account:access)" << "\n" + << R"(region = eu-west-3)" << "\n" + << R"(output = json)" << "\n"; + + configFile.flush(); + + AWSConfigFileProfileConfigLoader loader(configFile.GetFileName(), true); + ASSERT_TRUE(loader.Load()); + const auto& loadedProfiles = loader.GetProfiles(); + ASSERT_TRUE(loadedProfiles.size() == 1); + const auto& profile = loadedProfiles.find("maelle"); + ASSERT_TRUE(profile != loadedProfiles.end()); + ASSERT_STREQ("https://lumiere.com/for-those-who-come-after", profile->second.GetSsoStartUrl().c_str()); +} + +TEST_F(AWSConfigFileProfileConfigLoaderTest, TestSsoStartUrlShouldHandleMultipleCommentsCorrectly) { + TempFile configFile(std::ios_base::out | std::ios_base::trunc); + ASSERT_TRUE(configFile.good()); + + configFile << R"([profile maelle])" << "\n" + << R"(sso_account_id = 333333333333)" << "\n" + << R"(sso_role_name = PictoAccess)" << "\n" + << R"(sso_start_url = https://lumiere.com/for-those-who-come-after#when-one-falls-we-continue ; painted power # stendahl pre nerf)" << "\n" + << R"(;#sso_start_url = https://lumiere.com/verso)" << "\n" + << R"(sso_region = eu-west-3)" << "\n" + << R"(sso_registration_scopes = sso:account:access)" << "\n" + << R"(region = eu-west-3)" << "\n" + << R"(output = json)" << "\n"; + + configFile.flush(); + + AWSConfigFileProfileConfigLoader loader(configFile.GetFileName(), true); + ASSERT_TRUE(loader.Load()); + const auto& loadedProfiles = loader.GetProfiles(); + ASSERT_TRUE(loadedProfiles.size() == 1); + const auto& profile = loadedProfiles.find("maelle"); + ASSERT_TRUE(profile != loadedProfiles.end()); + ASSERT_STREQ("https://lumiere.com/for-those-who-come-after#when-one-falls-we-continue", profile->second.GetSsoStartUrl().c_str()); +} + +TEST_F(AWSConfigFileProfileConfigLoaderTest, TestSsoStartUrlShouldHandleTabbedCommentsCorrectly) { + TempFile configFile(std::ios_base::out | std::ios_base::trunc); + ASSERT_TRUE(configFile.good()); + + configFile << R"([profile maelle])" << "\n" + << R"(sso_account_id = 333333333333)" << "\n" + << R"(sso_role_name = PictoAccess)" << "\n" + << R"(sso_start_url = https://lumiere.com/for-those-who-come-after#when-one-falls-we-continue)" << "\t" << R"(#tabbed comment)" << "\n" + << R"(;#sso_start_url = https://lumiere.com/verso)" << "\n" + << R"(sso_region = eu-west-3)" << "\n" + << R"(sso_registration_scopes = sso:account:access)" << "\n" + << R"(region = eu-west-3)" << "\n" + << R"(output = json)" << "\n"; + + configFile.flush(); + + AWSConfigFileProfileConfigLoader loader(configFile.GetFileName(), true); + ASSERT_TRUE(loader.Load()); + const auto& loadedProfiles = loader.GetProfiles(); + ASSERT_TRUE(loadedProfiles.size() == 1); + const auto& profile = loadedProfiles.find("maelle"); + ASSERT_TRUE(profile != loadedProfiles.end()); + ASSERT_STREQ("https://lumiere.com/for-those-who-come-after#when-one-falls-we-continue", profile->second.GetSsoStartUrl().c_str()); +}