Skip to content

Logstash plugin http input fails when uri contains either 'J' or 'M', including within query parameter names and values #205

@rossiya

Description

@rossiya

Logstash information:

Please include the following information:

  1. Logstash version (e.g. bin/logstash --version)
bash-5.1$ bin/logstash --version
Using bundled JDK: /usr/share/logstash/jdk
logstash 9.2.4
  1. Logstash installation source (e.g. built from source, with a package manager: DEB/RPM, expanded from tar or zip archive, docker)
    docker, docker.elastic.co/logstash/logstash:9.2.4, id= 3c2cac9a329e
  2. How is Logstash being run (e.g. as a service/service manager: systemd, upstart, etc. Via command line, docker/kubernetes)
    docker
  3. How was the Logstash Plugin installed
    pre-existing with docker image
    JVM (e.g. java -version):

If the affected version of Logstash is 7.9 (or earlier), or if it is NOT using the bundled JDK or using the 'no-jdk' version in 7.10 (or higher), please provide the following information:

  1. JVM version (java -version)
openjdk version "21.0.9" 2025-10-21 LTS
OpenJDK Runtime Environment Temurin-21.0.9+10 (build 21.0.9+10-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.9+10 (build 21.0.9+10-LTS, mixed mode, sharing)
  1. JVM installation source (e.g. from the Operating System's package manager, from source, etc).
    default from docker image
  2. Value of the JAVA_HOME environment variable if set.

OS version (uname -a if on a Unix-like system):

bash-5.1$ uname -a
Linux 5a8fcafa9a97 5.14.0-611.16.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Sun Dec 7 05:52:24 EST 2025 x86_64 x86_64 x86_64 GNU/Linux

Description of the problem including expected versus actual behavior:
Using stock docker deployment of Logstash 9.2.4, and basic http input pipeline, all GET or POST requests that contain either 'J' or 'M' in the uri in either path or query parameters, the plugin will fail due to bad client request. Both letters triggering this condition are plain ASCII, 'J' (hex \x4A) and 'M' (hex \x4D). no other regular characters, including their lower case counterparts trigger this error. This behavior does not exist in 9.2.3, only 9.2.4 presently.

Steps to reproduce:

1. Snag a default install of logstash (though fully configured behaves the same)
Using the below basic docker config and pipeline config the issue can be created:

input {
        http {
                port => 6767
                codec => plain { charset => "UTF-8" }
        }
}
# Generally this would contain logic to store query params in key/values, but not necessary to trigger the issue
output {
        stdout { codec => rubydebug { metadata => true } }
}

docker-compose.yml

services:
  ls:
    image: docker.elastic.co/logstash/logstash:9.2.4
    volumes:
      - ./logstash/pipeline:/usr/share/logstash/pipeline
      - /docker/lsbug/queue:/queue
    environment:
      LS_JAVA_OPTS: "-Xms2048m -Xmx2048m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    ports:
      - 6767:6767
    restart: always
  1. Send data, both GET and POST seem to behave the same. My use full use case pulls query params off a GET and stores, but issue seems to be agnostic of request type.

sending this:
curl localhost:6767/M

results in this:

ls-1  | {
ls-1  |     "@timestamp" => 2026-01-24T23:25:09.883491167Z,
ls-1  |      "@metadata" => {
ls-1  |         "input" => {
ls-1  |             "http" => {
ls-1  |                 "request" => {
ls-1  |                     "headers" => {
ls-1  |                          "request_method" => "GET",
ls-1  |                            "request_path" => "/bad-request",
ls-1  |                               "http_host" => nil,
ls-1  |                         "http_user_agent" => nil,
ls-1  |                             "http_accept" => nil,
ls-1  |                            "http_version" => "HTTP/1.0"
ls-1  |                     }
ls-1  |                 }
ls-1  |             }
ls-1  |         }
ls-1  |     },
ls-1  |           "host" => {
ls-1  |         "ip" => "172.24.0.1"
ls-1  |     },
ls-1  |       "@version" => "1",
ls-1  |           "http" => {
ls-1  |          "method" => "GET",
ls-1  |         "version" => "HTTP/1.0"
ls-1  |     },
ls-1  |        "message" => "",
ls-1  |            "url" => {
ls-1  |         "path" => "/bad-request"
ls-1  |     }
ls-1  | }

Notes:

  • URL length, or order of characters do not seem to matter, simply the presence of either of these characters in the uri will cause the input plugin to treat the request as a bad.
  • attempting the gsub the character out in a filter block does not change the behavior, it issue appears trigger before filter runs.
  • testing more characters that do not require escaping/encoding, highlights the two problem characters.

loop across uppercase lowercase, and -, _, ., and ~.

# for char in `cat chars` ; do curl localhost:6767/$char ; done
# docker compose logs ls | grep request_path
ls-1  |                            "request_path" => "/A",
ls-1  |                            "request_path" => "/B",
ls-1  |                            "request_path" => "/C",
ls-1  |                            "request_path" => "/D",
ls-1  |                            "request_path" => "/E",
ls-1  |                            "request_path" => "/F",
ls-1  |                            "request_path" => "/G",
ls-1  |                            "request_path" => "/H",
ls-1  |                            "request_path" => "/I",
ls-1  |                            "request_path" => "/bad-request",
ls-1  |                            "request_path" => "/K",
ls-1  |                            "request_path" => "/L",
ls-1  |                            "request_path" => "/bad-request",
ls-1  |                            "request_path" => "/N",
ls-1  |                            "request_path" => "/O",
ls-1  |                            "request_path" => "/P",
ls-1  |                            "request_path" => "/Q",
ls-1  |                            "request_path" => "/R",
ls-1  |                            "request_path" => "/S",
ls-1  |                            "request_path" => "/T",
ls-1  |                            "request_path" => "/U",
ls-1  |                            "request_path" => "/V",
ls-1  |                            "request_path" => "/W",
ls-1  |                            "request_path" => "/X",
ls-1  |                            "request_path" => "/Y",
ls-1  |                            "request_path" => "/Z",
ls-1  |                            "request_path" => "/a",
ls-1  |                            "request_path" => "/b",
ls-1  |                            "request_path" => "/c",
ls-1  |                            "request_path" => "/d",
ls-1  |                            "request_path" => "/e",
ls-1  |                            "request_path" => "/f",
ls-1  |                            "request_path" => "/g",
ls-1  |                            "request_path" => "/h",
ls-1  |                            "request_path" => "/i",
ls-1  |                            "request_path" => "/j",
ls-1  |                            "request_path" => "/k",
ls-1  |                            "request_path" => "/l",
ls-1  |                            "request_path" => "/m",
ls-1  |                            "request_path" => "/n",
ls-1  |                            "request_path" => "/o",
ls-1  |                            "request_path" => "/p",
ls-1  |                            "request_path" => "/q",
ls-1  |                            "request_path" => "/r",
ls-1  |                            "request_path" => "/s",
ls-1  |                            "request_path" => "/t",
ls-1  |                            "request_path" => "/u",
ls-1  |                            "request_path" => "/v",
ls-1  |                            "request_path" => "/w",
ls-1  |                            "request_path" => "/x",
ls-1  |                            "request_path" => "/y",
ls-1  |                            "request_path" => "/z",
ls-1  |                            "request_path" => "/-",
ls-1  |                            "request_path" => "/_",
ls-1  |                            "request_path" => "/",
ls-1  |                            "request_path" => "/~",

Provide logs (if relevant):
Similarly in query params:

# for char in `cat chars` ; do curl "localhost:6767/data?key=value&anotherkey=$char" && sleep .05 ; done
okokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokok
# docker compose logs ls | grep request_path
ls-1  |                            "request_path" => "/data?key=value&anotherkey=A",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=B",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=C",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=D",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=E",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=F",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=G",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=H",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=I",
ls-1  |                            "request_path" => "/bad-request",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=K",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=L",
ls-1  |                            "request_path" => "/bad-request",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=N",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=O",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=P",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=Q",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=R",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=S",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=T",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=U",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=V",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=W",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=X",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=Y",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=Z",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=a",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=b",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=c",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=d",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=e",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=f",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=g",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=h",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=i",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=j",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=k",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=l",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=m",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=n",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=o",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=p",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=q",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=r",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=s",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=t",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=u",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=v",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=w",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=x",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=y",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=z",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=-",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=_",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=.",
ls-1  |                            "request_path" => "/data?key=value&anotherkey=~",
# for char in `cat chars` ; do curl "localhost:6767/post$char" -d "somedata" && sleep .05 ; done
okokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokok
# docker compose logs ls | grep request_path
ls-1  |                            "request_path" => "/postA",
ls-1  |                            "request_path" => "/postB",
ls-1  |                            "request_path" => "/postC",
ls-1  |                            "request_path" => "/postD",
ls-1  |                            "request_path" => "/postE",
ls-1  |                            "request_path" => "/postF",
ls-1  |                            "request_path" => "/postG",
ls-1  |                            "request_path" => "/postH",
ls-1  |                            "request_path" => "/postI",
ls-1  |                            "request_path" => "/bad-request",
ls-1  |                            "request_path" => "/postK",
ls-1  |                            "request_path" => "/postL",
ls-1  |                            "request_path" => "/bad-request",
ls-1  |                            "request_path" => "/postN",
ls-1  |                            "request_path" => "/postO",
ls-1  |                            "request_path" => "/postP",
ls-1  |                            "request_path" => "/postQ",
ls-1  |                            "request_path" => "/postR",
ls-1  |                            "request_path" => "/postS",
ls-1  |                            "request_path" => "/postT",
ls-1  |                            "request_path" => "/postU",
ls-1  |                            "request_path" => "/postV",
ls-1  |                            "request_path" => "/postW",
ls-1  |                            "request_path" => "/postX",
ls-1  |                            "request_path" => "/postY",
ls-1  |                            "request_path" => "/postZ",
ls-1  |                            "request_path" => "/posta",
ls-1  |                            "request_path" => "/postb",
ls-1  |                            "request_path" => "/postc",
ls-1  |                            "request_path" => "/postd",
ls-1  |                            "request_path" => "/poste",
ls-1  |                            "request_path" => "/postf",
ls-1  |                            "request_path" => "/postg",
ls-1  |                            "request_path" => "/posth",
ls-1  |                            "request_path" => "/posti",
ls-1  |                            "request_path" => "/postj",
ls-1  |                            "request_path" => "/postk",
ls-1  |                            "request_path" => "/postl",
ls-1  |                            "request_path" => "/postm",
ls-1  |                            "request_path" => "/postn",
ls-1  |                            "request_path" => "/posto",
ls-1  |                            "request_path" => "/postp",
ls-1  |                            "request_path" => "/postq",
ls-1  |                            "request_path" => "/postr",
ls-1  |                            "request_path" => "/posts",
ls-1  |                            "request_path" => "/postt",
ls-1  |                            "request_path" => "/postu",
ls-1  |                            "request_path" => "/postv",
ls-1  |                            "request_path" => "/postw",
ls-1  |                            "request_path" => "/postx",
ls-1  |                            "request_path" => "/posty",
ls-1  |                            "request_path" => "/postz",
ls-1  |                            "request_path" => "/post-",
ls-1  |                            "request_path" => "/post_",
ls-1  |                            "request_path" => "/post.",
ls-1  |                            "request_path" => "/post~",

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions