-
Notifications
You must be signed in to change notification settings - Fork 4.5k
fix(security): block SSRF via send-test-email SMTP host validation (GHSA-vvxf-f8q9-86gh) #41666
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release
Are you sure you want to change the base?
Changes from all commits
ce761a5
524cc1b
aba623c
08f7868
ead9025
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| import com.appsmith.server.configurations.EmailConfig; | ||
| import com.appsmith.server.configurations.GoogleRecaptchaConfig; | ||
| import com.appsmith.server.domains.User; | ||
| import com.appsmith.server.dtos.TestEmailConfigRequestDTO; | ||
| import com.appsmith.server.exceptions.AppsmithError; | ||
| import com.appsmith.server.exceptions.AppsmithException; | ||
| import com.appsmith.server.helpers.BlacklistedEnvVariableHelper; | ||
|
|
@@ -24,6 +25,8 @@ | |
| import org.junit.jupiter.api.BeforeEach; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.api.extension.ExtendWith; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.ValueSource; | ||
| import org.mockito.Mockito; | ||
| import org.springframework.boot.test.mock.mockito.MockBean; | ||
| import org.springframework.mail.javamail.JavaMailSender; | ||
|
|
@@ -384,6 +387,52 @@ public void sendTestEmail_WhenUserNotSuperUser_ThrowsException() { | |
| .verify(); | ||
| } | ||
|
|
||
| private void mockSuperUser() { | ||
| User user = new User(); | ||
| user.setEmail("admin@appsmith.com"); | ||
| Mockito.when(userUtils.isCurrentUserSuperUser()).thenReturn(Mono.just(true)); | ||
| Mockito.when(sessionUserService.getCurrentUser()).thenReturn(Mono.just(user)); | ||
| } | ||
|
|
||
| private TestEmailConfigRequestDTO buildDto(String smtpHost) { | ||
| return buildDto(smtpHost, 25); | ||
| } | ||
|
|
||
| private TestEmailConfigRequestDTO buildDto(String smtpHost, int port) { | ||
| TestEmailConfigRequestDTO dto = new TestEmailConfigRequestDTO(); | ||
| dto.setSmtpHost(smtpHost); | ||
| dto.setSmtpPort(port); | ||
| dto.setFromEmail("test@appsmith.com"); | ||
| dto.setStarttlsEnabled(false); | ||
| return dto; | ||
| } | ||
|
|
||
| @ParameterizedTest | ||
| @ValueSource(strings = {"127.0.0.1", "169.254.169.254", "localhost"}) | ||
| public void sendTestEmail_WhenBlockedHost_ThrowsException(String host) { | ||
| mockSuperUser(); | ||
|
|
||
| StepVerifier.create(envManager.sendTestEmail(buildDto(host))) | ||
| .expectErrorSatisfies(e -> { | ||
| assertThat(e).isInstanceOf(AppsmithException.class); | ||
| assertThat(e.getMessage()).contains("Invalid SMTP configuration"); | ||
| }) | ||
| .verify(); | ||
| } | ||
|
|
||
| @ParameterizedTest | ||
| @ValueSource(ints = {80, 443, 6379, 8080, 27017, 0, -1}) | ||
| public void sendTestEmail_WhenDisallowedPort_ThrowsException(int port) { | ||
| mockSuperUser(); | ||
|
|
||
| StepVerifier.create(envManager.sendTestEmail(buildDto("smtp.gmail.com", port))) | ||
| .expectErrorSatisfies(e -> { | ||
| assertThat(e).isInstanceOf(AppsmithException.class); | ||
| assertThat(e.getMessage()).contains("Invalid SMTP configuration"); | ||
| }) | ||
| .verify(); | ||
| } | ||
|
Comment on lines
+423
to
+434
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This port matrix is environment-dependent.
🤖 Prompt for AI Agents |
||
|
|
||
| @Test | ||
| public void setEnv_AndGetAll() { | ||
| // Create a test map of environment variables | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IPv4-only regex may fail for IPv6-capable resolvers.
The regex
\\d+\\.\\d+\\.\\d+\\.\\d+assumes IPv4. Ifsmtp.gmail.comresolves to an IPv6 address (Google supports both), this assertion fails.🛡️ Proposed fix to handle both IPv4 and IPv6
`@Test` public void resolveIfAllowed_returnsResolvedAddress() { Optional<InetAddress> result = WebClientUtils.resolveIfAllowed("smtp.gmail.com"); assertTrue(result.isPresent()); - assertTrue(result.get().getHostAddress().matches("\\d+\\.\\d+\\.\\d+\\.\\d+")); + // Just verify we got a valid address string - could be IPv4 or IPv6 + String hostAddress = result.get().getHostAddress(); + assertTrue(hostAddress != null && !hostAddress.isEmpty(), "Expected non-empty host address"); }🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coderabbitai Should this be addressed with a code change, or does it only require updating the assertion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@subrata71This is only an assertion update — the production code inresolveIfAllowed()is already correct. The fix is just relaxing the test assertion so it passes whether the resolver returns an IPv4 or IPv6 address:No production code changes needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. In that case it's safe to ignore this concern. @coderabbitai
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@subrata71, understood — acknowledged and moving on. The production logic is sound either way, and the test will pass in the vast majority of CI environments. 👍