Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/main/ql/WebClientMissingIdleTimeout.qhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
A <code>WebClient</code> created without a <code>WebClientOptions</code> that explicitly sets an idle
timeout may hold open connections indefinitely. This can lead to resource exhaustion, increased latency,
and denial-of-service conditions if remote servers stop responding without closing their connections.
</p>
</overview>

<recommendation>
<p>
Always create a <code>WebClientOptions</code> instance, call <code>setIdleTimeout</code> on it with
an appropriate timeout value, and pass the options when calling <code>WebClient.create</code>.
</p>
</recommendation>

<example>
<p>Instead of creating a WebClient without an idle timeout, such as in the example below:</p>

<sample src="java/org/carlspring/security/vertx/webclient/InsecureWebClient.java" />

<p>
create a <code>WebClientOptions</code> with an explicit <code>setIdleTimeout</code> and pass it to
<code>WebClient.create</code>, as shown here:
</p>

<sample src="java/org/carlspring/security/vertx/webclient/SecureWebClient.java" />
</example>

<references>
<li>
<a href="https://vertx.io/docs/vertx-web-client/java/">
Vert.x Web Client documentation
</a>
</li>
<li>
<a href="https://vertx.io/docs/apidocs/io/vertx/ext/web/client/WebClientOptions.html">
Vert.x WebClientOptions API
</a>
</li>
</references>
</qhelp>
73 changes: 73 additions & 0 deletions src/main/ql/WebClientMissingIdleTimeout.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* @name WebClient created without idle timeout
* @description A WebClient is created without a WebClientOptions that sets an explicit idle timeout.
* This can lead to connections hanging indefinitely, potentially exhausting resources.
* @kind problem
* @problem.severity warning
* @precision high
* @security-severity 5.0
* @id java/vertx/webclient-missing-idle-timeout
* @tags security java/vertx reliability
*/

import java

class WebClient extends RefType {
WebClient() {
this.getASourceSupertype*().hasQualifiedName("io.vertx.ext.web.client", "WebClient")
}
}

class WebClientOptions extends RefType {
WebClientOptions() {
this.getASourceSupertype*().hasQualifiedName("io.vertx.ext.web.client", "WebClientOptions")
}
}

class WebClientCreateMethodAccess extends MethodAccess {
WebClientCreateMethodAccess() {
exists(Method m |
this.getMethod() = m and
m.getName() = "create" and
m.getDeclaringType() instanceof WebClient
)
}
}

class SetIdleTimeoutMethodAccess extends MethodAccess {
SetIdleTimeoutMethodAccess() {
exists(Method m |
this.getMethod() = m and
m.getName() = "setIdleTimeout" and
m.getDeclaringType() instanceof WebClientOptions
)
}
}

/**
* Holds if the given WebClientOptions variable has setIdleTimeout called on it.
*/
predicate optionsHasIdleTimeout(Variable optionsVar) {
exists(SetIdleTimeoutMethodAccess setCall |
setCall.getQualifier().(VarAccess).getVariable() = optionsVar
)
}

from WebClientCreateMethodAccess call
where
not call.getEnclosingCallable().getDeclaringType() instanceof WebClient and
not call.getLocation().getFile().getRelativePath().matches("%/src/test/%") and
(
// No WebClientOptions argument at all
call.getNumArgument() < 2
or
// A WebClientOptions is passed but setIdleTimeout was never called on it
exists(Variable optionsVar |
call.getArgument(1).(VarAccess).getVariable() = optionsVar and
optionsVar.getType() instanceof WebClientOptions and
not optionsHasIdleTimeout(optionsVar)
)
)
select
call,
"WebClient is created without a WebClientOptions that sets an explicit idle timeout."
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.carlspring.security.vertx.webclient;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import io.vertx.ext.web.client.WebClient;

/**
* @author carlspring
*/
public class InsecureWebClient
extends AbstractVerticle
{

@Override
public void start()
{
// Create a WebClient without any WebClientOptions (no idle timeout set)
WebClient client = WebClient.create(vertx);

// Use client...
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.carlspring.security.vertx.webclient;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;

import java.util.concurrent.TimeUnit;

/**
* @author carlspring
*/
public class SecureWebClient
extends AbstractVerticle
{

@Override
public void start()
{
// Create a WebClientOptions with an explicit idle timeout
WebClientOptions options = new WebClientOptions()
.setIdleTimeout(30)
.setIdleTimeoutUnit(TimeUnit.SECONDS);

// Create a WebClient with the options
WebClient client = WebClient.create(vertx, options);

// Use client...
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.carlspring.security.vertx.webclient;

import io.vertx.core.AbstractVerticle;
import io.vertx.ext.web.client.WebClient;

/**
* @author carlspring
*/
public class InsecureWebClient
extends AbstractVerticle
{

@Override
public void start()
{
// Create a WebClient without any WebClientOptions (no idle timeout set)
WebClient client = WebClient.create(vertx);

// Use client...
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.carlspring.security.vertx.webclient;

import io.vertx.core.AbstractVerticle;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;

import java.util.concurrent.TimeUnit;

/**
* @author carlspring
*/
public class SecureWebClient
extends AbstractVerticle
{

@Override
public void start()
{
// Create a WebClientOptions with an explicit idle timeout
WebClientOptions options = new WebClientOptions()
.setIdleTimeout(30)
.setIdleTimeoutUnit(TimeUnit.SECONDS);

// Create a WebClient with the options
WebClient client = WebClient.create(vertx, options);

// Use client...
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
| InsecureWebClient.java:17:28:17:50 | create(...) | WebClient is created without a WebClientOptions that sets an explicit idle timeout. |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WebClientMissingIdleTimeout.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../stubs/ -source 17
65 changes: 65 additions & 0 deletions src/test/ql/test/query-tests/WebClientMissingIdleTimeout/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<project xmlns="http://maven.apache.org/POM/4.0.0">

<modelVersion>4.0.0</modelVersion>

<groupId>org.carlspring.security</groupId>
<artifactId>vertx-vulns-test-webclient-missing-idle-timeout</artifactId>
<version>1.0.0-SNAPSHOT</version>

<properties>
<version.vertx>4.4.4</version.vertx>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.3.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
<debug>true</debug>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>3.1.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<includeEmptyDirs>true</includeEmptyDirs>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>${version.vertx}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>${version.vertx}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-client</artifactId>
<version>${version.vertx}</version>
</dependency>

</dependencies>

</project>
12 changes: 12 additions & 0 deletions src/test/ql/test/stubs/io/vertx/ext/web/client/WebClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Generated automatically from io.vertx.ext.web.client.WebClient for testing purposes

package io.vertx.ext.web.client;

import io.vertx.core.Vertx;

public class WebClient
{
public static WebClient create(Vertx vertx){ return null; }
public static WebClient create(Vertx vertx, WebClientOptions options){ return null; }
public WebClient(){}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Generated automatically from io.vertx.ext.web.client.WebClientOptions for testing purposes

package io.vertx.ext.web.client;

import java.util.concurrent.TimeUnit;

public class WebClientOptions
{
public WebClientOptions setIdleTimeout(int timeout){ return this; }
public WebClientOptions setIdleTimeoutUnit(TimeUnit unit){ return this; }
public WebClientOptions(){}
}