From cdee37f6fad5dfa13fb88c11fc9a1c15ab72effc Mon Sep 17 00:00:00 2001
From: "pixeebot[bot]" <104101892+pixeebot[bot]@users.noreply.github.com>
Date: Tue, 2 Apr 2024 06:30:48 +0000
Subject: [PATCH] Protect `readLine()` against DoS

---
 .../conventions/info/ParallelDetector.java    |  3 +-
 .../internal/info/GlobalBuildInfoPlugin.java  |  3 +-
 .../gradle/testclusters/RunTask.java          |  3 +-
 .../benchmark/ops/bulk/BulkBenchmarkTask.java |  3 +-
 .../elasticsearch/client/RequestLogger.java   |  3 +-
 .../plugins/cli/InstallPluginAction.java      |  7 ++--
 .../server/cli/ErrorPumpThread.java           |  3 +-
 .../server/cli/JvmOptionsParser.java          |  3 +-
 .../grok/GrokBuiltinPatterns.java             |  3 +-
 .../elasticsearch/common/ssl/PemUtils.java    | 37 ++++++++++---------
 .../gcs/GoogleCloudStorageService.java        |  3 +-
 .../discovery/ec2/AwsEc2Utils.java            |  3 +-
 .../discovery/ec2/Ec2DiscoveryPlugin.java     |  3 +-
 .../discovery/ec2/Ec2NameResolver.java        |  3 +-
 .../hotthreads/NodesHotThreadsResponse.java   |  3 +-
 .../bootstrap/BootstrapChecks.java            |  3 +-
 .../org/elasticsearch/bootstrap/Spawner.java  |  3 +-
 .../org/elasticsearch/common/io/Streams.java  |  3 +-
 .../index/analysis/Analysis.java              |  3 +-
 .../node/InternalSettingsPreparer.java        |  3 +-
 .../plugins/spi/SPIClassIterator.java         |  3 +-
 .../CategorizationPartOfSpeechDictionary.java |  3 +-
 22 files changed, 63 insertions(+), 41 deletions(-)

diff --git a/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/info/ParallelDetector.java b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/info/ParallelDetector.java
index 688f2f858ae16..63d4627e4f788 100644
--- a/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/info/ParallelDetector.java
+++ b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/info/ParallelDetector.java
@@ -8,6 +8,7 @@
 
 package org.elasticsearch.gradle.internal.conventions.info;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.gradle.api.Action;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
@@ -45,7 +46,7 @@ public static int findDefaultParallel(Project project) {
                 String currentID = "";
 
                 try (BufferedReader reader = new BufferedReader(new FileReader(cpuInfoFile))) {
-                    for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+                    for (String line = BoundedLineReader.readLine(reader, 5_000_000); line != null; line = BoundedLineReader.readLine(reader, 5_000_000)) {
                         if (line.contains(":")) {
                             List<String> parts = Arrays.stream(line.split(":", 2)).map(String::trim).collect(Collectors.toList());
                             String name = parts.get(0);
diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java
index 115c4b0694141..e92026e716531 100644
--- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java
+++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java
@@ -7,6 +7,7 @@
  */
 package org.elasticsearch.gradle.internal.info;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.apache.commons.io.IOUtils;
 import org.elasticsearch.gradle.internal.BwcVersions;
 import org.elasticsearch.gradle.internal.conventions.info.GitInfo;
@@ -360,7 +361,7 @@ public static String getResourceContents(String resourcePath) {
             BufferedReader reader = new BufferedReader(new InputStreamReader(GlobalBuildInfoPlugin.class.getResourceAsStream(resourcePath)))
         ) {
             StringBuilder b = new StringBuilder();
-            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+            for (String line = BoundedLineReader.readLine(reader, 5_000_000); line != null; line = BoundedLineReader.readLine(reader, 5_000_000)) {
                 if (b.length() != 0) {
                     b.append('\n');
                 }
diff --git a/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/RunTask.java b/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/RunTask.java
index 86df3544ddfc6..7cec708017e4f 100644
--- a/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/RunTask.java
+++ b/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/RunTask.java
@@ -7,6 +7,7 @@
  */
 package org.elasticsearch.gradle.testclusters;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.gradle.api.GradleException;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
@@ -245,7 +246,7 @@ public void runAndWait() throws IOException {
                 for (BufferedReader bufferedReader : toRead) {
                     if (bufferedReader.ready()) {
                         readData = true;
-                        logger.lifecycle(bufferedReader.readLine());
+                        logger.lifecycle(BoundedLineReader.readLine(bufferedReader, 5_000_000));
                     }
                 }
 
diff --git a/client/benchmark/src/main/java/org/elasticsearch/client/benchmark/ops/bulk/BulkBenchmarkTask.java b/client/benchmark/src/main/java/org/elasticsearch/client/benchmark/ops/bulk/BulkBenchmarkTask.java
index 69edd9c8f86ff..5c37fc7b87c09 100644
--- a/client/benchmark/src/main/java/org/elasticsearch/client/benchmark/ops/bulk/BulkBenchmarkTask.java
+++ b/client/benchmark/src/main/java/org/elasticsearch/client/benchmark/ops/bulk/BulkBenchmarkTask.java
@@ -7,6 +7,7 @@
  */
 package org.elasticsearch.client.benchmark.ops.bulk;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.elasticsearch.ElasticsearchException;
@@ -101,7 +102,7 @@ public void execute() {
                 String line;
                 int bulkIndex = 0;
                 List<String> bulkData = new ArrayList<>(bulkSize);
-                while ((line = reader.readLine()) != null) {
+                while ((line = BoundedLineReader.readLine(reader, 5_000_000)) != null) {
                     if (bulkIndex == bulkSize) {
                         sendBulk(bulkData);
                         // reset data structures
diff --git a/client/rest/src/main/java/org/elasticsearch/client/RequestLogger.java b/client/rest/src/main/java/org/elasticsearch/client/RequestLogger.java
index 085bc5619451f..e420b1fa2d7e1 100644
--- a/client/rest/src/main/java/org/elasticsearch/client/RequestLogger.java
+++ b/client/rest/src/main/java/org/elasticsearch/client/RequestLogger.java
@@ -19,6 +19,7 @@
 
 package org.elasticsearch.client;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.http.Header;
@@ -170,7 +171,7 @@ static String buildTraceResponse(HttpResponse httpResponse) throws IOException {
             }
             try (BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), charset))) {
                 String line;
-                while ((line = reader.readLine()) != null) {
+                while ((line = BoundedLineReader.readLine(reader, 5_000_000)) != null) {
                     responseLine.append("\n# ").append(line);
                 }
             }
diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/InstallPluginAction.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/InstallPluginAction.java
index c7bee4a6c172d..fda799191dab3 100644
--- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/InstallPluginAction.java
+++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/InstallPluginAction.java
@@ -8,6 +8,7 @@
 
 package org.elasticsearch.plugins.cli;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.apache.lucene.search.spell.LevenshteinDistance;
 import org.apache.lucene.util.CollectionUtil;
 import org.apache.lucene.util.Constants;
@@ -579,9 +580,9 @@ private Path downloadAndValidate(final String urlString, final Path tmpDir, fina
              */
             final BufferedReader checksumReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
             if (digestAlgo.equals("SHA-1")) {
-                expectedChecksum = checksumReader.readLine();
+                expectedChecksum = BoundedLineReader.readLine(checksumReader, 5_000_000);
             } else {
-                final String checksumLine = checksumReader.readLine();
+                final String checksumLine = BoundedLineReader.readLine(checksumReader, 5_000_000);
                 final String[] fields = checksumLine.split(" {2}");
                 if (officialPlugin && fields.length != 2 || officialPlugin == false && fields.length > 2) {
                     throw new UserException(ExitCodes.IO_ERROR, "Invalid checksum file at " + checksumUrl);
@@ -603,7 +604,7 @@ private Path downloadAndValidate(final String urlString, final Path tmpDir, fina
                     }
                 }
             }
-            if (checksumReader.readLine() != null) {
+            if (BoundedLineReader.readLine(checksumReader, 5_000_000) != null) {
                 throw new UserException(ExitCodes.IO_ERROR, "Invalid checksum file at " + checksumUrl);
             }
         }
diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ErrorPumpThread.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ErrorPumpThread.java
index 8c6766b5da186..3f0c86aa8b9bf 100644
--- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ErrorPumpThread.java
+++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ErrorPumpThread.java
@@ -8,6 +8,7 @@
 
 package org.elasticsearch.server.cli;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.elasticsearch.bootstrap.BootstrapInfo;
 
 import java.io.BufferedReader;
@@ -76,7 +77,7 @@ void drain() {
     public void run() {
         try {
             String line;
-            while ((line = reader.readLine()) != null) {
+            while ((line = BoundedLineReader.readLine(reader, 5_000_000)) != null) {
                 if (line.isEmpty() == false && line.charAt(0) == SERVER_READY_MARKER) {
                     ready = true;
                     readyOrDead.countDown();
diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java
index 29650e4b74114..edcdf73e0f62b 100644
--- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java
+++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java
@@ -8,6 +8,7 @@
 
 package org.elasticsearch.server.cli;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.elasticsearch.bootstrap.ServerArgs;
 import org.elasticsearch.cli.ExitCodes;
 import org.elasticsearch.cli.UserException;
@@ -289,7 +290,7 @@ static void parse(
     ) throws IOException {
         int lineNumber = 0;
         while (true) {
-            final String line = br.readLine();
+            final String line = BoundedLineReader.readLine(br, 5_000_000);
             lineNumber++;
             if (line == null) {
                 break;
diff --git a/libs/grok/src/main/java/org/elasticsearch/grok/GrokBuiltinPatterns.java b/libs/grok/src/main/java/org/elasticsearch/grok/GrokBuiltinPatterns.java
index 9d3813f0d0f33..19fc5a9c7f158 100644
--- a/libs/grok/src/main/java/org/elasticsearch/grok/GrokBuiltinPatterns.java
+++ b/libs/grok/src/main/java/org/elasticsearch/grok/GrokBuiltinPatterns.java
@@ -8,6 +8,7 @@
 
 package org.elasticsearch.grok;
 
+import io.github.pixee.security.BoundedLineReader;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
@@ -138,7 +139,7 @@ private static PatternBank loadPatternsFromDirectory(List<String> patternNames,
     private static void loadPatternsFromFile(Map<String, String> patternBank, InputStream inputStream) throws IOException {
         String line;
         BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
-        while ((line = br.readLine()) != null) {
+        while ((line = BoundedLineReader.readLine(br, 5_000_000)) != null) {
             String trimmedLine = line.replaceAll("^\\s+", "");
             if (trimmedLine.startsWith("#") || trimmedLine.length() == 0) {
                 continue;
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java
index 9bb0643907eb5..b3fbd1a350a67 100644
--- a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java
@@ -8,6 +8,7 @@
 
 package org.elasticsearch.common.ssl;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.elasticsearch.core.CharArrays;
 
 import java.io.BufferedReader;
@@ -128,9 +129,9 @@ public static PrivateKey readPrivateKey(Path path, Supplier<char[]> passwordSupp
      */
     static PrivateKey parsePrivateKey(Path keyPath, Supplier<char[]> passwordSupplier) throws IOException, GeneralSecurityException {
         try (BufferedReader bReader = Files.newBufferedReader(keyPath, StandardCharsets.UTF_8)) {
-            String line = bReader.readLine();
+            String line = BoundedLineReader.readLine(bReader, 5_000_000);
             while (null != line && line.startsWith(HEADER) == false) {
-                line = bReader.readLine();
+                line = BoundedLineReader.readLine(bReader, 5_000_000);
             }
             if (null == line) {
                 throw new SslConfigException("Error parsing Private Key [" + keyPath.toAbsolutePath() + "], file is empty");
@@ -170,18 +171,18 @@ static PrivateKey parsePrivateKey(Path keyPath, Supplier<char[]> passwordSupplie
      * @throws IOException if the EC Parameter footer is missing
      */
     private static BufferedReader removeECHeaders(BufferedReader bReader) throws IOException {
-        String line = bReader.readLine();
+        String line = BoundedLineReader.readLine(bReader, 5_000_000);
         while (line != null) {
             if (OPENSSL_EC_PARAMS_FOOTER.equals(line.trim())) {
                 break;
             }
-            line = bReader.readLine();
+            line = BoundedLineReader.readLine(bReader, 5_000_000);
         }
         if (null == line || OPENSSL_EC_PARAMS_FOOTER.equals(line.trim()) == false) {
             throw new IOException("Malformed PEM file, EC Parameters footer is missing");
         }
         // Verify that the key starts with the correct header before passing it to parseOpenSslEC
-        if (OPENSSL_EC_HEADER.equals(bReader.readLine()) == false) {
+        if (OPENSSL_EC_HEADER.equals(BoundedLineReader.readLine(bReader, 5_000_000)) == false) {
             throw new IOException("Malformed PEM file, EC Key header is missing");
         }
         return bReader;
@@ -194,18 +195,18 @@ private static BufferedReader removeECHeaders(BufferedReader bReader) throws IOE
      * @throws IOException if the EC Parameter footer is missing
      */
     private static BufferedReader removeDsaHeaders(BufferedReader bReader) throws IOException {
-        String line = bReader.readLine();
+        String line = BoundedLineReader.readLine(bReader, 5_000_000);
         while (line != null) {
             if (OPENSSL_DSA_PARAMS_FOOTER.equals(line.trim())) {
                 break;
             }
-            line = bReader.readLine();
+            line = BoundedLineReader.readLine(bReader, 5_000_000);
         }
         if (null == line || OPENSSL_DSA_PARAMS_FOOTER.equals(line.trim()) == false) {
             throw new IOException("Malformed PEM file, DSA Parameters footer is missing");
         }
         // Verify that the key starts with the correct header before passing it to parseOpenSslDsa
-        if (OPENSSL_DSA_HEADER.equals(bReader.readLine()) == false) {
+        if (OPENSSL_DSA_HEADER.equals(BoundedLineReader.readLine(bReader, 5_000_000)) == false) {
             throw new IOException("Malformed PEM file, DSA Key header is missing");
         }
         return bReader;
@@ -222,13 +223,13 @@ private static BufferedReader removeDsaHeaders(BufferedReader bReader) throws IO
      */
     private static PrivateKey parsePKCS8(BufferedReader bReader) throws IOException, GeneralSecurityException {
         StringBuilder sb = new StringBuilder();
-        String line = bReader.readLine();
+        String line = BoundedLineReader.readLine(bReader, 5_000_000);
         while (line != null) {
             if (PKCS8_FOOTER.equals(line.trim())) {
                 break;
             }
             sb.append(line.trim());
-            line = bReader.readLine();
+            line = BoundedLineReader.readLine(bReader, 5_000_000);
         }
         if (null == line || PKCS8_FOOTER.equals(line.trim()) == false) {
             throw new IOException("Malformed PEM file, PEM footer is invalid or missing");
@@ -263,7 +264,7 @@ public static PrivateKey parsePKCS8PemString(String pemString) throws IOExceptio
     private static PrivateKey parseOpenSslEC(BufferedReader bReader, Supplier<char[]> passwordSupplier) throws IOException,
         GeneralSecurityException {
         StringBuilder sb = new StringBuilder();
-        String line = bReader.readLine();
+        String line = BoundedLineReader.readLine(bReader, 5_000_000);
         Map<String, String> pemHeaders = new HashMap<>();
         while (line != null) {
             if (OPENSSL_EC_FOOTER.equals(line.trim())) {
@@ -276,7 +277,7 @@ private static PrivateKey parseOpenSslEC(BufferedReader bReader, Supplier<char[]
             } else {
                 sb.append(line.trim());
             }
-            line = bReader.readLine();
+            line = BoundedLineReader.readLine(bReader, 5_000_000);
         }
         if (null == line || OPENSSL_EC_FOOTER.equals(line.trim()) == false) {
             throw new IOException("Malformed PEM file, PEM footer is invalid or missing");
@@ -300,7 +301,7 @@ private static PrivateKey parseOpenSslEC(BufferedReader bReader, Supplier<char[]
     private static PrivateKey parsePKCS1Rsa(BufferedReader bReader, Supplier<char[]> passwordSupplier) throws IOException,
         GeneralSecurityException {
         StringBuilder sb = new StringBuilder();
-        String line = bReader.readLine();
+        String line = BoundedLineReader.readLine(bReader, 5_000_000);
         Map<String, String> pemHeaders = new HashMap<>();
 
         while (line != null) {
@@ -315,7 +316,7 @@ private static PrivateKey parsePKCS1Rsa(BufferedReader bReader, Supplier<char[]>
             } else {
                 sb.append(line.trim());
             }
-            line = bReader.readLine();
+            line = BoundedLineReader.readLine(bReader, 5_000_000);
         }
         if (null == line || PKCS1_FOOTER.equals(line.trim()) == false) {
             throw new IOException("Malformed PEM file, PEM footer is invalid or missing");
@@ -339,7 +340,7 @@ private static PrivateKey parsePKCS1Rsa(BufferedReader bReader, Supplier<char[]>
     private static PrivateKey parseOpenSslDsa(BufferedReader bReader, Supplier<char[]> passwordSupplier) throws IOException,
         GeneralSecurityException {
         StringBuilder sb = new StringBuilder();
-        String line = bReader.readLine();
+        String line = BoundedLineReader.readLine(bReader, 5_000_000);
         Map<String, String> pemHeaders = new HashMap<>();
 
         while (line != null) {
@@ -354,7 +355,7 @@ private static PrivateKey parseOpenSslDsa(BufferedReader bReader, Supplier<char[
             } else {
                 sb.append(line.trim());
             }
-            line = bReader.readLine();
+            line = BoundedLineReader.readLine(bReader, 5_000_000);
         }
         if (null == line || OPENSSL_DSA_FOOTER.equals(line.trim()) == false) {
             throw new IOException("Malformed PEM file, PEM footer is invalid or missing");
@@ -377,13 +378,13 @@ private static PrivateKey parseOpenSslDsa(BufferedReader bReader, Supplier<char[
      */
     private static PrivateKey parsePKCS8Encrypted(BufferedReader bReader, char[] keyPassword) throws IOException, GeneralSecurityException {
         StringBuilder sb = new StringBuilder();
-        String line = bReader.readLine();
+        String line = BoundedLineReader.readLine(bReader, 5_000_000);
         while (line != null) {
             if (PKCS8_ENCRYPTED_FOOTER.equals(line.trim())) {
                 break;
             }
             sb.append(line.trim());
-            line = bReader.readLine();
+            line = BoundedLineReader.readLine(bReader, 5_000_000);
         }
         if (null == line || PKCS8_ENCRYPTED_FOOTER.equals(line.trim()) == false) {
             throw new IOException("Malformed PEM file, PEM footer is invalid or missing");
diff --git a/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java
index 760c1c57e0496..6230efa20b34d 100644
--- a/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java
+++ b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java
@@ -20,6 +20,7 @@
 import com.google.cloud.storage.Storage;
 import com.google.cloud.storage.StorageOptions;
 import com.google.cloud.storage.StorageRetryStrategy;
+import io.github.pixee.security.BoundedLineReader;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -252,7 +253,7 @@ static String getDefaultProjectId(@Nullable Proxy proxy) throws IOException {
         try (InputStream input = connection.getInputStream()) {
             if (connection.getResponseCode() == 200) {
                 try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, UTF_8))) {
-                    return reader.readLine();
+                    return BoundedLineReader.readLine(reader, 5_000_000);
                 }
             }
         }
diff --git a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2Utils.java b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2Utils.java
index 256a5516a2ef2..2850f83f4c02f 100644
--- a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2Utils.java
+++ b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2Utils.java
@@ -8,6 +8,7 @@
 
 package org.elasticsearch.discovery.ec2;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.elasticsearch.common.Strings;
@@ -49,7 +50,7 @@ static Optional<String> getMetadataToken(String metadataTokenUrl) {
                 var in = urlConnection.getInputStream();
                 var reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))
             ) {
-                return Optional.ofNullable(reader.readLine()).filter(s -> s.isBlank() == false);
+                return Optional.ofNullable(BoundedLineReader.readLine(reader, 5_000_000)).filter(s -> s.isBlank() == false);
             } catch (IOException e) {
                 logger.warn("Unable to get a session token from IMDSv2 URI: " + metadataTokenUrl, e);
                 return Optional.empty();
diff --git a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java
index 69447e800d4ac..e796e737dd990 100644
--- a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java
+++ b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java
@@ -10,6 +10,7 @@
 
 import com.amazonaws.util.EC2MetadataUtils;
 import com.amazonaws.util.json.Jackson;
+import io.github.pixee.security.BoundedLineReader;
 
 import org.elasticsearch.SpecialPermission;
 import org.elasticsearch.common.network.NetworkService;
@@ -158,7 +159,7 @@ static Settings getAvailabilityZoneNodeAttributes(Settings settings, String azMe
             BufferedReader urlReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))
         ) {
 
-            final String metadataResult = urlReader.readLine();
+            final String metadataResult = BoundedLineReader.readLine(urlReader, 5_000_000);
             if ((metadataResult == null) || (metadataResult.length() == 0)) {
                 throw new IllegalStateException("no ec2 metadata returned from " + url);
             } else {
diff --git a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2NameResolver.java b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2NameResolver.java
index 5c8a2a8fb92f9..c85fffb04a6f7 100644
--- a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2NameResolver.java
+++ b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2NameResolver.java
@@ -9,6 +9,7 @@
 package org.elasticsearch.discovery.ec2;
 
 import com.amazonaws.util.EC2MetadataUtils;
+import io.github.pixee.security.BoundedLineReader;
 
 import org.elasticsearch.common.network.NetworkService.CustomNameResolver;
 import org.elasticsearch.core.IOUtils;
@@ -95,7 +96,7 @@ public static InetAddress[] resolve(Ec2HostnameType type) throws IOException {
             in = SocketAccess.doPrivilegedIOException(urlConnection::getInputStream);
             BufferedReader urlReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
 
-            String metadataResult = urlReader.readLine();
+            String metadataResult = BoundedLineReader.readLine(urlReader, 5_000_000);
             if (metadataResult == null || metadataResult.length() == 0) {
                 throw new IOException("no gce metadata returned from [" + url + "] for [" + type.configName + "]");
             }
diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/hotthreads/NodesHotThreadsResponse.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/hotthreads/NodesHotThreadsResponse.java
index 59307009f785b..fc2d676e1e64d 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/hotthreads/NodesHotThreadsResponse.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/hotthreads/NodesHotThreadsResponse.java
@@ -8,6 +8,7 @@
 
 package org.elasticsearch.action.admin.cluster.node.hotthreads;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.elasticsearch.action.FailedNodeException;
 import org.elasticsearch.action.support.TransportAction;
 import org.elasticsearch.action.support.nodes.BaseNodesResponse;
@@ -63,7 +64,7 @@ private LinesIterator(String input) {
 
         private void advance() {
             try {
-                nextLine = reader.readLine();
+                nextLine = BoundedLineReader.readLine(reader, 5_000_000);
             } catch (IOException e) {
                 assert false : e; // no actual IO happens here
             }
diff --git a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java
index a99ed225b244b..2761264432e27 100644
--- a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java
+++ b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java
@@ -8,6 +8,7 @@
 
 package org.elasticsearch.bootstrap;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.lucene.util.Constants;
@@ -501,7 +502,7 @@ BufferedReader getBufferedReader(final Path path) throws IOException {
 
         // visible for testing
         static String readProcSysVmMaxMapCount(final BufferedReader bufferedReader) throws IOException {
-            return bufferedReader.readLine();
+            return BoundedLineReader.readLine(bufferedReader, 5_000_000);
         }
 
         // visible for testing
diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Spawner.java b/server/src/main/java/org/elasticsearch/bootstrap/Spawner.java
index 4b09d5d143046..ae0a2b2611b54 100644
--- a/server/src/main/java/org/elasticsearch/bootstrap/Spawner.java
+++ b/server/src/main/java/org/elasticsearch/bootstrap/Spawner.java
@@ -8,6 +8,7 @@
 
 package org.elasticsearch.bootstrap;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.lucene.util.Constants;
@@ -105,7 +106,7 @@ private void startPumpThread(String componentName, String streamName, InputStrea
         Thread t = new Thread(() -> {
             try (var br = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
                 String line;
-                while ((line = br.readLine()) != null) {
+                while ((line = BoundedLineReader.readLine(br, 5_000_000)) != null) {
                     // since we do not expect native controllers to ever write to stdout/stderr, we always log at warn level
                     logger.warn(line);
                 }
diff --git a/server/src/main/java/org/elasticsearch/common/io/Streams.java b/server/src/main/java/org/elasticsearch/common/io/Streams.java
index d12884de7845a..f87921733c9b3 100644
--- a/server/src/main/java/org/elasticsearch/common/io/Streams.java
+++ b/server/src/main/java/org/elasticsearch/common/io/Streams.java
@@ -8,6 +8,7 @@
 
 package org.elasticsearch.common.io;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.io.stream.BytesStream;
 import org.elasticsearch.common.io.stream.BytesStreamOutput;
@@ -152,7 +153,7 @@ public static List<String> readAllLines(InputStream input) throws IOException {
     public static void readAllLines(InputStream input, Consumer<String> consumer) throws IOException {
         try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
             String line;
-            while ((line = reader.readLine()) != null) {
+            while ((line = BoundedLineReader.readLine(reader, 5_000_000)) != null) {
                 consumer.accept(line);
             }
         }
diff --git a/server/src/main/java/org/elasticsearch/index/analysis/Analysis.java b/server/src/main/java/org/elasticsearch/index/analysis/Analysis.java
index e19ee050c93a7..a26ac85334959 100644
--- a/server/src/main/java/org/elasticsearch/index/analysis/Analysis.java
+++ b/server/src/main/java/org/elasticsearch/index/analysis/Analysis.java
@@ -8,6 +8,7 @@
 
 package org.elasticsearch.index.analysis;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.apache.lucene.analysis.CharArraySet;
 import org.apache.lucene.analysis.ar.ArabicAnalyzer;
 import org.apache.lucene.analysis.bg.BulgarianAnalyzer;
@@ -261,7 +262,7 @@ private static List<String> loadWordList(Path path, boolean removeComments) thro
         final List<String> result = new ArrayList<>();
         try (BufferedReader br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
             String word;
-            while ((word = br.readLine()) != null) {
+            while ((word = BoundedLineReader.readLine(br, 5_000_000)) != null) {
                 if (Strings.hasText(word) == false) {
                     continue;
                 }
diff --git a/server/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java b/server/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java
index 3ef69e1085cdc..084cbb8ffbee8 100644
--- a/server/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java
+++ b/server/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java
@@ -8,6 +8,7 @@
 
 package org.elasticsearch.node;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.elasticsearch.cluster.ClusterName;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.settings.SettingsException;
@@ -92,7 +93,7 @@ static void loadConfigWithSubstitutions(Settings.Builder output, Path configFile
             StringBuilder builder = new StringBuilder((int) existingSize);
             try (BufferedReader reader = Files.newBufferedReader(configFile, StandardCharsets.UTF_8)) {
                 String line;
-                while ((line = reader.readLine()) != null) {
+                while ((line = BoundedLineReader.readLine(reader, 5_000_000)) != null) {
                     int dollarNdx;
                     int nextNdx = 0;
                     while ((dollarNdx = line.indexOf("${", nextNdx)) != -1) {
diff --git a/server/src/main/java/org/elasticsearch/plugins/spi/SPIClassIterator.java b/server/src/main/java/org/elasticsearch/plugins/spi/SPIClassIterator.java
index d906cf066ded2..835d729fe303e 100644
--- a/server/src/main/java/org/elasticsearch/plugins/spi/SPIClassIterator.java
+++ b/server/src/main/java/org/elasticsearch/plugins/spi/SPIClassIterator.java
@@ -18,6 +18,7 @@
 
 package org.elasticsearch.plugins.spi;
 
+import io.github.pixee.security.BoundedLineReader;
 import org.apache.lucene.util.IOUtils;
 import org.apache.lucene.util.SuppressForbidden;
 
@@ -114,7 +115,7 @@ private boolean loadNextProfile() {
                 try {
                     final BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
                     String line;
-                    while ((line = reader.readLine()) != null) {
+                    while ((line = BoundedLineReader.readLine(reader, 5_000_000)) != null) {
                         final int pos = line.indexOf('#');
                         if (pos >= 0) {
                             line = line.substring(0, pos);
diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/categorization/CategorizationPartOfSpeechDictionary.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/categorization/CategorizationPartOfSpeechDictionary.java
index 09a6846ead344..4a73173c3837b 100644
--- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/categorization/CategorizationPartOfSpeechDictionary.java
+++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/categorization/CategorizationPartOfSpeechDictionary.java
@@ -7,6 +7,7 @@
 
 package org.elasticsearch.xpack.ml.aggs.categorization;
 
+import io.github.pixee.security.BoundedLineReader;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
@@ -90,7 +91,7 @@ static PartOfSpeech fromCode(char partOfSpeechCode) {
         int maxLength = 0;
         BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
         String line;
-        while ((line = reader.readLine()) != null) {
+        while ((line = BoundedLineReader.readLine(reader, 5_000_000)) != null) {
             line = line.trim();
             if (line.isEmpty()) {
                 continue;