Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
* @author Madhura Bhave
* @author Phillip Webb
* @author Scott Frederick
* @author Sijun Yang
* @since 2.4.0
*/
public class StandardConfigDataLocationResolver
Expand Down Expand Up @@ -154,6 +155,7 @@ public List<StandardConfigDataResource> resolveProfileSpecific(ConfigDataLocatio
private Set<StandardConfigDataReference> getProfileSpecificReferences(ConfigDataLocationResolverContext context,
ConfigDataLocation[] configDataLocations, Profiles profiles) {
Set<StandardConfigDataReference> references = new LinkedHashSet<>();
validateProfiles(profiles);
for (String profile : profiles) {
for (ConfigDataLocation configDataLocation : configDataLocations) {
String resourceLocation = getResourceLocation(context, configDataLocation);
Expand All @@ -163,6 +165,21 @@ private Set<StandardConfigDataReference> getProfileSpecificReferences(ConfigData
return references;
}

private void validateProfiles(Profiles profiles) {
Pattern validProfilePattern = Pattern.compile("^[a-zA-Z0-9_\\-]+$");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be simplified to Pattern validProfilePattern = Pattern.compile("^[\\w-]+$");.

Assert.notNull(profiles, "Profiles must not be null");
for (String profile : profiles) {
Assert.notNull(profile, "Profile must not be null");
Assert.hasText(profile, "Profile must not be empty");
Assert.state(!profile.startsWith("-") && !profile.startsWith("_"),
() -> String.format("Invalid profile '%s': must not start with '-' or '_'", profile));
Assert.state(!profile.endsWith("-") && !profile.endsWith("_"),
() -> String.format("Invalid profile '%s': must not end with '-' or '_'", profile));
Assert.state(validProfilePattern.matcher(profile).matches(), () -> String
.format("Invalid profile '%s': must only contain alphanumeric characters, '-', or '_'", profile));
}
}

private String getResourceLocation(ConfigDataLocationResolverContext context,
ConfigDataLocation configDataLocation) {
String resourceLocation = configDataLocation.getNonPrefixedValue(PREFIX);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
* @author Madhura Bhave
* @author Phillip Webb
* @author Moritz Halbritter
* @author Sijun Yang
*/
class StandardConfigDataLocationResolverTests {

Expand Down Expand Up @@ -254,8 +255,8 @@ void resolveWhenLocationUsesOptionalExtensionSyntaxResolves() throws Exception {
@Test
void resolveProfileSpecificReturnsProfileSpecificFiles() {
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
Profiles profiles = mock(Profiles.class);
given(profiles.iterator()).willReturn(Collections.singletonList("dev").iterator());
this.environment.setActiveProfiles("dev");
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
List<StandardConfigDataResource> locations = this.resolver.resolveProfileSpecific(this.context, location,
profiles);
assertThat(locations).hasSize(1);
Expand Down Expand Up @@ -293,6 +294,75 @@ void resolveWhenOptionalAndExtensionIsUnknownShouldNotFail() {
assertThatNoException().isThrownBy(() -> this.resolver.resolve(this.context, location));
}

@Test
void resolveProfileSpecificWhenProfileIsValidShouldNotThrowException() {
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
this.environment.setActiveProfiles("dev-test_123");
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
assertThatNoException()
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles));
}

@Test
void resolveProfileSpecificWithAdditionalValidProfilesShouldNotThrowException() {
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
this.environment.setActiveProfiles("dev-test");
Profiles profiles = new Profiles(this.environment, this.environmentBinder, List.of("prod-test", "stage-test"));
assertThatNoException()
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles));
}

@Test
void resolveProfileSpecificWhenProfileStartsWithSymbolThrowsException() {
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
this.environment.setActiveProfiles("-dev");
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
assertThatIllegalStateException()
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles))
.withMessageStartingWith("Invalid profile '-dev': must not start with '-' or '_'");
}

@Test
void resolveProfileSpecificWhenProfileStartsWithUnderscoreThrowsException() {
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
this.environment.setActiveProfiles("_dev");
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
assertThatIllegalStateException()
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles))
.withMessageStartingWith("Invalid profile '_dev': must not start with '-' or '_'");
}

@Test
void resolveProfileSpecificWhenProfileEndsWithSymbolThrowsException() {
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
this.environment.setActiveProfiles("dev-");
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
assertThatIllegalStateException()
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles))
.withMessageStartingWith("Invalid profile 'dev-': must not end with '-' or '_'");
}

@Test
void resolveProfileSpecificWhenProfileEndsWithUnderscoreThrowsException() {
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
this.environment.setActiveProfiles("dev_");
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
assertThatIllegalStateException()
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles))
.withMessageStartingWith("Invalid profile 'dev_': must not end with '-' or '_'");
}

@Test
void resolveProfileSpecificWhenProfileContainsInvalidCharactersThrowsException() {
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
this.environment.setActiveProfiles("dev*test");
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
assertThatIllegalStateException()
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles))
.withMessageStartingWith(
"Invalid profile 'dev*test': must only contain alphanumeric characters, '-', or '_'");
}

private String filePath(String... components) {
return "file [" + String.join(File.separator, components) + "]";
}
Expand Down
Loading