Skip to content

fix(security): block SSRF via Git SSH connection — JGit code path bypasses WebClientUtils SSRF filter#41684

Open
subrata71 wants to merge 4 commits intoreleasefrom
fix/ssrf-git-ssh-bypass-APP-15043
Open

fix(security): block SSRF via Git SSH connection — JGit code path bypasses WebClientUtils SSRF filter#41684
subrata71 wants to merge 4 commits intoreleasefrom
fix/ssrf-git-ssh-bypass-APP-15043

Conversation

@subrata71
Copy link
Copy Markdown
Collaborator

@subrata71 subrata71 commented Apr 1, 2026

Description

TL;DR: The Git integration (JGit SSH + native git) bypassed the existing WebClientUtils SSRF filter, allowing any authenticated user to initiate SSH connections to internal/reserved IPs (loopback, cloud metadata, link-local). This fix adds host validation to all Git SSH connection paths using the existing resolveIfAllowed() mechanism.

Root Cause

Appsmith has two outbound connection paths:

  1. REST API plugin (HTTP) — protected by WebClientUtils.IP_CHECK_FILTER and custom NameResolver.
  2. Git integration (SSH) — used JGit's SSH client which connected directly to user-supplied remote URLs with zero IP validation.

When a user submitted git@169.254.169.254:user/repo.git, the isRepoPrivate() HTTP probe was correctly blocked by WebClientUtils, but the SSH clone via cloneRemoteIntoArtifactRepo() proceeded without any check.

Fix

Added GitUtils.validateGitSshUrl() which:

  1. Extracts the hostname from SSH URLs using existing regex patterns
  2. Calls WebClientUtils.resolveIfAllowed(host) to resolve DNS and check against loopback, link-local, multicast, cloud metadata, and ULA address ranges
  3. Returns Mono.error(AppsmithException) if the host is blocked

Gated all Git SSH connection entry points:

  • CommonGitServiceCEImpl: connectArtifactToGit, importArtifactFromGit, repo recovery clone
  • CentralGitServiceCEImpl: connectArtifactToGit, importArtifactFromGit
  • GitFSServiceCEImpl: both fetchRemoteRepository overloads (defense-in-depth)

Regression Safety

  • RFC 1918 private IPs (10.x, 172.16.x, 192.168.x) remain allowed — self-hosted users with private GitLab/Gitea on internal networks are unaffected
  • convertSshUrlToBrowserSupportedUrl() is unchanged — existing URL display/conversion works as before
  • All 32 existing GitUtilsTest tests continue to pass, plus 7 new tests added

Fixes https://linear.app/appsmith/issue/APP-15043/security-high-ssrf-via-git-ssh-connection-jgit-code-path-bypasses
Fixes https://github.com/appsmithorg/appsmith/security/advisories/GHSA-h7pq-hq98-554w

Automation

/ok-to-test tags="@tag.All"

🔍 Cypress test results

Warning

Tests have not run on the HEAD 41e14ea yet


Wed, 01 Apr 2026 19:30:30 UTC

Communication

Should the DevRel and Marketing teams inform users about this change?

  • Yes
  • No

Summary by CodeRabbit

  • New Features
    • Git SSH remote URL validation added; connections to internal/reserved hosts are blocked with a clear "Git remote URL host blocked" error.
    • SSH server connection acceptance now checks and rejects disallowed remote IPs during key exchange.
  • Bug Fixes
    • Clone/fetch/connect/import flows validate URLs first and short-circuit on invalid/blocked hosts to avoid unnecessary operations and error masking.
  • Tests
    • Added unit tests for host extraction and blocked/allowed host validation scenarios.

…L hosts before JGit clone (GHSA-h7pq-hq98-554w)

The Git integration used JGit SSH and native git to connect to user-supplied
remote URLs with no IP/host validation, bypassing the WebClientUtils SSRF
filter that protects HTTP paths. Any authenticated user could supply
git@169.254.169.254:user/repo.git or ssh://git@127.0.0.1:5432/path and the
server would initiate a TCP connection to the internal target.

This commit adds SSRF host validation to all Git SSH connection paths by
reusing WebClientUtils.resolveIfAllowed() to check extracted hostnames against
loopback, link-local, multicast, cloud metadata, and ULA address ranges before
any SSH connection is initiated. RFC 1918 private ranges remain allowed for
self-hosted Git servers.

Gated entry points:
- CommonGitServiceCEImpl: connectArtifactToGit, importArtifactFromGit, repo recovery
- CentralGitServiceCEImpl: connectArtifactToGit, importArtifactFromGit
- GitFSServiceCEImpl: both fetchRemoteRepository overloads (defense-in-depth)

Fixes APP-15043
@subrata71 subrata71 requested a review from a team as a code owner April 1, 2026 12:49
@subrata71 subrata71 added the Security Issues related to information security within the product label Apr 1, 2026
@linear
Copy link
Copy Markdown

linear bot commented Apr 1, 2026

@subrata71 subrata71 self-assigned this Apr 1, 2026
@subrata71 subrata71 added the ok-to-test Required label for CI label Apr 1, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

Walkthrough

Added server-side Git SSH remote URL validation that extracts and resolves hosts, blocking internal/reserved addresses and emitting a new GIT_REMOTE_URL_HOST_BLOCKED error; wired into import/connect/fetch flows and covered by new unit tests. (≤50 words)

Changes

Cohort / File(s) Summary
Error Type Definitions
app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java, app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java
Added GIT_REMOTE_URL_HOST_BLOCKED enum constant and code (AE-GIT-5021) with HTTP 400 and templated message for blocked Git remote URL hosts.
Git URL Validation Utility
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java
Added extractHostFromGitUrl(String) to parse SSH and scp-style remotes and validateGitSshUrl(String) returning Mono<Void>; validates presence, extracts host, resolves via WebClientUtils.resolveIfAllowed(...) on boundedElastic, and emits specific AppsmithExceptions for empty, malformed, or blocked hosts.
Git Service Integration
app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java, app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCEImpl.java, app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java
Sequenced git flows to run GitUtils.validateGitSshUrl(...) before clone/connect/import operations using .then(...); adjusted error handling in clone/fetch paths to rethrow existing AppsmithExceptions directly in onErrorResume for AppsmithException cases.
SSH Server Key Acceptance
app/server/appsmith-git/src/main/java/com/appsmith/git/helpers/AppsmithSshdSessionFactory.java
Reject server key acceptance when remote address resolves to a blocked IP by calling WebClientUtils.resolveIfAllowed(resolvedIp) and returning false if blocked.
Validation Test Coverage
app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/GitUtilsTest.java
Added parameterized and explicit tests for extractHostFromGitUrl and validateGitSshUrl: asserts blocked hosts produce GIT_REMOTE_URL_HOST_BLOCKED, public hosts succeed, RFC1918/private IPs allowed (self-hosted), and invalid/empty inputs error appropriately.
sequenceDiagram
    participant Client as Client
    participant GitService as GitService
    participant GitUtils as GitUtils
    participant HostResolver as HostResolver
    participant Result as Result

    Client->>GitService: import/connect/fetch(remoteUrl)
    GitService->>GitUtils: validateGitSshUrl(remoteUrl)
    GitUtils->>GitUtils: extractHostFromGitUrl(remoteUrl)
    alt host extracted
        GitUtils->>HostResolver: resolveIfAllowed(host) (boundedElastic, rgba(0,128,0,0.5))
        alt host blocked
            HostResolver-->>GitUtils: blocked (error)
            GitUtils-->>GitService: Mono.error(AppsmithException: GIT_REMOTE_URL_HOST_BLOCKED)
            GitService-->>Client: 400 Error (host blocked)
        else host allowed
            HostResolver-->>GitUtils: allowed
            GitUtils-->>GitService: Mono.empty()
            GitService->>Result: proceed with clone/connect/import
            Result-->>GitService: success
            GitService-->>Client: Success
        end
    else extraction failed / invalid URL
        GitUtils-->>GitService: Mono.error(AppsmithException: INVALID_GIT_CONFIGURATION / INVALID_PARAMETER)
        GitService-->>Client: Error (invalid URL)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🔎 Hosts checked, the pipeline stands tall,
No secret nets will breach the wall.
SSH parsed, the host is found,
Resolved with care, safety unbound.
Only vetted remotes pass the call. 🚪✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the security fix: blocking SSRF via Git SSH connections by closing a JGit code path bypass of WebClientUtils SSRF filter.
Description check ✅ Passed The PR description comprehensively addresses the security fix with clear root cause analysis, implementation details, regression safety considerations, and testing coverage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ssrf-git-ssh-bypass-APP-15043

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java (1)

166-190: ⚠️ Potential issue | 🟠 Major

Preserve typed AppsmithException from URL validation.

Both handlers remap upstream errors to generic clone failures. If validateGitSshUrl emits AppsmithException (e.g., blocked host), it should pass through unchanged.

🔧 Proposed fix
 .onErrorResume(error -> {
+    if (error instanceof AppsmithException) {
+        return Mono.error(error);
+    }
     log.error("Error in cloning the remote repo, {}", error.getMessage());
     ...
 })
 .onErrorResume(error -> {
+    if (error instanceof AppsmithException) {
+        return Mono.error(error);
+    }
     log.error("Error while cloning the remote repo, {}", error.getMessage());
     ...
 })

Also applies to: 249-274

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java`
around lines 166 - 190, The error handling in GitFSServiceCEImpl's clone flow
currently remaps all errors to generic AppsmithExceptions, which swallows
upstream AppsmithException from validateGitSshUrl; update the onErrorResume
blocks (the one handling clone errors and the similar block later) to first
check if (error instanceof AppsmithException) and if so return Mono.error(error)
so AppsmithException is propagated unchanged, otherwise continue with the
existing transport/invalidRemote/timeout branching and fallback
AppsmithError.GIT_ACTION_FAILED mapping.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java`:
- Line 151: The enum constant GIT_REMOTE_URL_HOST_BLOCKED in AppsmithErrorCode
currently reuses the code AE-GIT-5016 (which is already used by
GIT_GENERIC_ERROR); update GIT_REMOTE_URL_HOST_BLOCKED to a unique error code
(e.g., AE-GIT-5017 or the next unused AE-GIT-* value) so code-based handling is
unambiguous, and ensure the change is applied within the AppsmithErrorCode enum
where GIT_GENERIC_ERROR and GIT_REMOTE_URL_HOST_BLOCKED are declared.

In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java`:
- Around line 234-236: The current validation (GitUtils.validateGitSshUrl on
gitConnectDTO.getRemoteUrl) only protects new imports/connecting flows; you must
also validate persisted remote URLs before any outbound SSH operation. Add
GitUtils.validateGitSshUrl checks (or reject/normalize) in all outbound paths
that perform SSH network actions (fetch/pull/push methods in
CentralGitServiceCEImpl and any helper methods that use Artifact.getRemoteUrl),
and/or implement a one-time migration that scans stored Artifact.remoteUrl
values and sanitizes or disables blocked remotes so legacy artifacts cannot
trigger SSH operations with disallowed URLs.

In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java`:
- Around line 94-99: The call to WebClientUtils.resolveIfAllowed(host) is
performing blocking DNS (InetAddress.getAllByName) on the reactive thread; wrap
that call in Mono.fromCallable(() ->
WebClientUtils.resolveIfAllowed(host)).subscribeOn(Schedulers.boundedElastic())
and then flatMap the resulting Optional: if empty return Mono.error(new
AppsmithException(AppsmithError.GIT_REMOTE_URL_HOST_BLOCKED, host)) otherwise
continue with Mono.empty(); update the code around the current return paths in
the method containing resolveIfAllowed so all blocking resolution occurs on the
boundedElastic scheduler.

In
`@app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/GitUtilsTest.java`:
- Around line 436-447: The test validateGitSshUrl_PublicHosts_Allowed in
GitUtilsTest relies on real DNS resolution for hostnames and is flaky in CI;
update the test to be deterministic by either (a) replacing the hostname-based
inputs passed to GitUtils.validateGitSshUrl with equivalent public-host
IP-literal forms (e.g., use known-reachable IP literals) or (b) mock/stub
DNS/host resolution used by GitUtils.validateGitSshUrl (for example by mocking
InetAddress.getByName or the internal resolver method) so the validation
receives deterministic addresses; modify the parameterized inputs or add a mock
setup in the test to ensure consistent behavior across CI environments.

---

Outside diff comments:
In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java`:
- Around line 166-190: The error handling in GitFSServiceCEImpl's clone flow
currently remaps all errors to generic AppsmithExceptions, which swallows
upstream AppsmithException from validateGitSshUrl; update the onErrorResume
blocks (the one handling clone errors and the similar block later) to first
check if (error instanceof AppsmithException) and if so return Mono.error(error)
so AppsmithException is propagated unchanged, otherwise continue with the
existing transport/invalidRemote/timeout branching and fallback
AppsmithError.GIT_ACTION_FAILED mapping.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 28ef2b1e-daee-4f33-a86c-472cfd2f1398

📥 Commits

Reviewing files that changed from the base of the PR and between 78ec716 and b3de523.

📒 Files selected for processing (7)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCEImpl.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java
  • app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/GitUtilsTest.java

Comment on lines +234 to +236
Mono<? extends Artifact> blankArtifactForImportMono = GitUtils.validateGitSshUrl(
gitConnectDTO.getRemoteUrl())
.then(isRepositoryLimitReachedForWorkspaceMono)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

This guard only covers new connect/import requests, not legacy stored remotes.

Lines 234-236 and 1087-1088 validate inbound gitConnectDTO URLs, but existing artifacts can still execute remote SSH operations with persisted remoteUrl values. Consider validating before outbound SSH operations (fetch/pull/push paths) or adding a migration to sanitize blocked stored remotes.

Also applies to: 1087-1088

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java`
around lines 234 - 236, The current validation (GitUtils.validateGitSshUrl on
gitConnectDTO.getRemoteUrl) only protects new imports/connecting flows; you must
also validate persisted remote URLs before any outbound SSH operation. Add
GitUtils.validateGitSshUrl checks (or reject/normalize) in all outbound paths
that perform SSH network actions (fetch/pull/push methods in
CentralGitServiceCEImpl and any helper methods that use Artifact.getRemoteUrl),
and/or implement a one-time migration that scans stored Artifact.remoteUrl
values and sanitizes or disables blocked remotes so legacy artifacts cannot
trigger SSH operations with disallowed URLs.

Comment on lines +436 to +447
@ParameterizedTest
@ValueSource(strings = {
"git@github.com:user/repo.git",
"git@gitlab.com:user/repo.git",
"git@bitbucket.org:user/repo.git",
"ssh://git@github.com/user/repo.git",
"git@ssh.dev.azure.com:v3/org/project/repo.git",
})
public void validateGitSshUrl_PublicHosts_Allowed(String allowedUrl) {
StepVerifier.create(GitUtils.validateGitSshUrl(allowedUrl))
.verifyComplete();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Public-host allowlist test cases are DNS-environment dependent.

These cases can fail in CI environments with restricted DNS/network, even when code is correct. Prefer deterministic inputs (e.g., known public IP literals) or mock/stub host resolution in unit tests.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/GitUtilsTest.java`
around lines 436 - 447, The test validateGitSshUrl_PublicHosts_Allowed in
GitUtilsTest relies on real DNS resolution for hostnames and is flaky in CI;
update the test to be deterministic by either (a) replacing the hostname-based
inputs passed to GitUtils.validateGitSshUrl with equivalent public-host
IP-literal forms (e.g., use known-reachable IP literals) or (b) mock/stub
DNS/host resolution used by GitUtils.validateGitSshUrl (for example by mocking
InetAddress.getByName or the internal resolver method) so the validation
receives deterministic addresses; modify the parameterized inputs or add a mock
setup in the test to ensure consistent behavior across CI environments.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 1, 2026

Failed server tests

  • com.appsmith.server.helpers.AppsmithErrorTest#verifyUniquenessOfAppsmithErrorCode

…cking DNS, error preservation

1. Fix error code collision: AE-GIT-5016 was already used by GIT_GENERIC_ERROR,
   changed GIT_REMOTE_URL_HOST_BLOCKED to AE-GIT-5021.

2. Offload blocking DNS to boundedElastic scheduler: resolveIfAllowed() calls
   InetAddress.getAllByName() which blocks — now wrapped in
   Mono.fromCallable().subscribeOn(Schedulers.boundedElastic()).

3. Preserve AppsmithException in GitFSServiceCEImpl error handlers: the
   onErrorResume blocks would swallow the SSRF-blocked error and remap it to
   generic clone failures. Now AppsmithException passes through unchanged.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java`:
- Around line 95-99: The current validateGitSshUrl resolves the host but
discards the InetAddress, allowing a DNS rebinding race when callers
(GitFSServiceCEImpl, CentralGitServiceCEImpl, CommonGitServiceCEImpl) later open
the SSH connection by re-resolving the hostname; change validateGitSshUrl to
return the validated InetAddress (or a small tuple containing InetAddress +
original host) instead of Mono<Void>, or alternatively perform the SSH
connection inside the same guarded reactive chain so resolution and connect are
atomic; update the callers to use the returned InetAddress (or the atomic
connect method) when opening the SSH session so the connection is made to the
pre-validated IP rather than re-resolving the hostname.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9cf496e4-6f52-4d91-9ddf-1760657bd866

📥 Commits

Reviewing files that changed from the base of the PR and between b3de523 and 398c8a6.

📒 Files selected for processing (3)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java

Comment on lines +95 to +99
return Mono.fromCallable(() -> WebClientUtils.resolveIfAllowed(host))
.subscribeOn(Schedulers.boundedElastic())
.flatMap(resolved -> resolved.isPresent()
? Mono.<Void>empty()
: Mono.error(new AppsmithException(AppsmithError.GIT_REMOTE_URL_HOST_BLOCKED, host)));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Inspect validateGitSshUrl implementation"
rg -n -C5 '\bvalidateGitSshUrl\s*\(|resolveIfAllowed|GIT_REMOTE_URL_HOST_BLOCKED' \
  app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java

echo
echo "Find all call sites for validateGitSshUrl"
rg -n -C6 '\bvalidateGitSshUrl\s*\(' app/server/appsmith-server/src/main/java/com/appsmith/server/git

echo
echo "Inspect whether downstream git operations use hostname-based remoteUrl after validation"
rg -n -C6 '\b(fetchRemoteRepository|connectArtifactToGit|importArtifactFromGit|setURI|setRemote|cloneRepository|lsRemote)\b|remoteUrl' \
  app/server/appsmith-server/src/main/java/com/appsmith/server/git

Repository: appsmithorg/appsmith

Length of output: 50376


DNS rebinding gap in validateGitSshUrl: resolved address is validated but discarded, allowing DNS rebinding between check and connect.

The validation at lines 95-99 resolves the hostname and confirms it's allowed, but discards the InetAddress and returns only Mono<Void>. All downstream call sites (GitFSServiceCEImpl, CentralGitServiceCEImpl, CommonGitServiceCEImpl) receive the original remoteUrl string containing the hostname, not the validated resolved address. Git operations will perform fresh DNS resolution at connection time, creating a window for DNS rebinding attacks to bypass the guard.

Either pin the validated resolved address for the actual connection, or make resolution and SSH connection atomic in one guarded step.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java`
around lines 95 - 99, The current validateGitSshUrl resolves the host but
discards the InetAddress, allowing a DNS rebinding race when callers
(GitFSServiceCEImpl, CentralGitServiceCEImpl, CommonGitServiceCEImpl) later open
the SSH connection by re-resolving the hostname; change validateGitSshUrl to
return the validated InetAddress (or a small tuple containing InetAddress +
original host) instead of Mono<Void>, or alternatively perform the SSH
connection inside the same guarded reactive chain so resolution and connect are
atomic; update the callers to use the returned InetAddress (or the atomic
connect method) when opening the SSH session so the connection is made to the
pre-validated IP rather than re-resolving the hostname.

@subrata71 subrata71 removed ok-to-test Required label for CI labels Apr 1, 2026
…igate DNS rebinding

The pre-connection validation in validateGitSshUrl() resolves and checks the
hostname, but JGit/MINA SSHD re-resolves at connection time, creating a
theoretical DNS rebinding window. This adds a secondary check in
AppsmithSshdSessionFactory.accept() which validates the actual connected IP
address against the same blocklist, closing the TOCTOU gap for JGit SSH paths.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/GitUtilsTest.java (1)

437-448: ⚠️ Potential issue | 🟡 Minor

Public-host allow tests are still DNS-environment dependent.

Line 440-Line 447 can fail in CI environments with restricted DNS/network. Prefer deterministic IP-literal inputs (or a resolver stub) for stable unit tests.

Deterministic test input example
 `@ValueSource`(
         strings = {
-            "git@github.com:user/repo.git",
-            "git@gitlab.com:user/repo.git",
-            "git@bitbucket.org:user/repo.git",
-            "ssh://git@github.com/user/repo.git",
-            "git@ssh.dev.azure.com:v3/org/project/repo.git",
+            "git@1.1.1.1:user/repo.git",
+            "git@8.8.8.8:user/repo.git",
+            "git@9.9.9.9:user/repo.git",
+            "ssh://git@1.0.0.1/user/repo.git",
+            "git@208.67.222.222:v3/org/project/repo.git",
         })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/GitUtilsTest.java`
around lines 437 - 448, The test validateGitSshUrl_PublicHosts_Allowed is
DNS-dependent; replace hostname-based inputs with deterministic IP-literal
variants so CI won't rely on network/DNS. Update the ValueSource strings passed
to GitUtils.validateGitSshUrl to use numeric IP literals (e.g.,
"git@192.0.2.1:user/repo.git", "ssh://git@192.0.2.1/user/repo.git",
"git@198.51.100.2:user/repo.git", etc.) or alternatively stub out the resolver
used by GitUtils.validateGitSshUrl, but keep references to the same test method
and GitUtils.validateGitSshUrl so the test logic is unchanged.
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java (1)

93-97: ⚠️ Potential issue | 🟠 Major

DNS-rebinding TOCTOU still possible because validated address is discarded.

At Line 95-Line 97, the validated InetAddress is not propagated, and downstream git connections still use the original hostname string and can re-resolve later. That keeps a rebinding window open. Please return/pin the resolved address through this API and use it at connect time.

Suggested direction
-public static Mono<Void> validateGitSshUrl(String remoteUrl) {
+public static Mono<InetAddress> validateGitSshUrl(String remoteUrl) {
   ...
-  return Mono.fromCallable(() -> WebClientUtils.resolveIfAllowed(host))
+  return Mono.fromCallable(() -> WebClientUtils.resolveIfAllowed(host))
       .subscribeOn(Schedulers.boundedElastic())
-      .flatMap(resolved -> resolved.isPresent()
-              ? Mono.<Void>empty()
-              : Mono.error(new AppsmithException(AppsmithError.GIT_REMOTE_URL_HOST_BLOCKED, host)));
+      .flatMap(resolved -> resolved
+              .map(Mono::just)
+              .orElseGet(() -> Mono.error(new AppsmithException(AppsmithError.GIT_REMOTE_URL_HOST_BLOCKED, host))));
}

Then ensure callers connect using this resolved address (not a fresh DNS lookup from hostname).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java`
around lines 93 - 97, The current GitUtils method discards the resolved
InetAddress (calling WebClientUtils.resolveIfAllowed(host)) and returns
Mono<Void>, leaving callers to re-resolve the hostname and enabling a
DNS-rebinding TOCTOU; change the API to return (or emit) the pinned InetAddress
(e.g., Mono<InetAddress> or a small value object with host+InetAddress) instead
of Mono<Void>, propagate the resolved value from
WebClientUtils.resolveIfAllowed(host) through this method, and then update
downstream callers that perform git connections to use the provided/pinned
InetAddress (not the hostname) when opening connections so no new DNS lookup
occurs at connect time.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java`:
- Around line 93-97: The current GitUtils method discards the resolved
InetAddress (calling WebClientUtils.resolveIfAllowed(host)) and returns
Mono<Void>, leaving callers to re-resolve the hostname and enabling a
DNS-rebinding TOCTOU; change the API to return (or emit) the pinned InetAddress
(e.g., Mono<InetAddress> or a small value object with host+InetAddress) instead
of Mono<Void>, propagate the resolved value from
WebClientUtils.resolveIfAllowed(host) through this method, and then update
downstream callers that perform git connections to use the provided/pinned
InetAddress (not the hostname) when opening connections so no new DNS lookup
occurs at connect time.

In
`@app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/GitUtilsTest.java`:
- Around line 437-448: The test validateGitSshUrl_PublicHosts_Allowed is
DNS-dependent; replace hostname-based inputs with deterministic IP-literal
variants so CI won't rely on network/DNS. Update the ValueSource strings passed
to GitUtils.validateGitSshUrl to use numeric IP literals (e.g.,
"git@192.0.2.1:user/repo.git", "ssh://git@192.0.2.1/user/repo.git",
"git@198.51.100.2:user/repo.git", etc.) or alternatively stub out the resolver
used by GitUtils.validateGitSshUrl, but keep references to the same test method
and GitUtils.validateGitSshUrl so the test logic is unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ac87d62a-3429-4b0b-b5e4-9d339dfd7468

📥 Commits

Reviewing files that changed from the base of the PR and between 398c8a6 and 86d3372.

📒 Files selected for processing (4)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java
  • app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/GitUtilsTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Security Issues related to information security within the product

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant