
OpenRewrite रेसिपी जो Spring Security हेडर सप्रेशन (CVE-2026-22732) का पता लगाती है और उसे ठीक करती है, Content-Length हेडर के दुरुपयोग की पहचान करके और तत्काल हेडर-लेखन कॉन्फ़िगरेशन उत्पन्न करके।
OpenRewrite रेसिपी जो CVE-2026-22732 के प्रति संवेदनशील कोड का पता लगाती है, जो एक Spring Security दोष है जहां तीन रिस्पॉन्स विधियों में से किसी एक के माध्यम से Content-Length सेट करने से Spring Security का OnCommittedResponseWrapper बायपास हो जाता है। क्योंकि रैपर हेडर को कभी नहीं देखता, onResponseCommitted() कभी फायर नहीं होता, और आलसी-जोड़े गए सुरक्षा हेडर (X-Frame-Options, X-Content-Type-Options, Cache-Control, आदि) चुपचाप हटा दिए जाते हैं।
वास्तविक ट्रिगर, संवेदनशील Spring Security 6.4.12 के साथ Spring Boot 3.4.3 / एम्बेडेड Tomcat के विरुद्ध पुष्टि किए गए:
रैपर-बायपासिंग ओवरलोड के माध्यम से Servlet Content-Length
response.setHeader("Content-Length", "42");
response.setIntHeader("Content-Length", 42);
response.addIntHeader("Content-Length", 42);
ये तीनों ओवरलोड OnCommittedResponseWrapper में ओवरराइड नहीं हैं। बाद के बॉडी राइट्स घोषित लंबाई को पूरा करते हैं और कंटेनर आलसी हेडर राइटर को फायर किए बिना कमिट कर देता है।
HttpHeaders के माध्यम से WebFlux Content-Length
serverHttpResponse.getHeaders().set("Content-Length", "42");
serverHttpResponse.getHeaders().add("Content-Length", "42");
serverHttpResponse.getHeaders().setContentLength(42L);
WebFlux बिना शर्त रिस्पॉन्स कमिट
serverHttpResponse.writeWith(Mono.just(dataBuffer));
serverHttpResponse.writeAndFlushWith(publisher);
serverHttpResponse.setComplete();
रेसिपी Spring Security की उपस्थिति पर गेटेड है — यह उन फाइलों में कुछ भी उत्सर्जित नहीं करती जो किसी org.springframework.security.* प्रकार को संदर्भित नहीं करतीं — और प्रभावित Spring Security संस्करण श्रेणियों पर। Spring सलाह के अनुसार, 2026-03-19 को प्रकाशित, प्रभावित श्रेणियां और फिक्स संस्करण हैं:
| श्रृंखला | प्रभावित | फिक्स्ड |
|---|---|---|
| 5.7.x | 5.7.0 – 5.7.21 | 5.7.22 (एंटरप्राइज़) |
| 5.8.x | 5.8.0 – 5.8.23 | 5.8.24 (एंटरप्राइज़) |
| 6.3.x | 6.3.0 – 6.3.14 | 6.3.15 (एंटरप्राइज़) |
| 6.4.x | 6.4.0 – 6.4.14 | 6.4.15 (एंटरप्राइज़) |
| 6.5.x | 6.5.0 – 6.5.8 | 6.5.9 (OSS) |
| 7.0.x | 7.0.0 – 7.0.3 | 7.0.4 (OSS) |
जो प्रोजेक्ट अपनी श्रृंखला में फिक्स के बराबर या उससे ऊपर का Spring Security संस्करण हल करते हैं (या 7.0 / 6.5 से आगे की किसी भी भविष्य की श्रृंखला पर), उन्हें अप्रभावित माना जाता है और उन्हें कोई प्रति-सिंक या प्रति-फाइल मार्कर नहीं मिलता। जिन प्रोजेक्ट्स में संस्करण हल नहीं किया जा सकता, वे सामान्य पैटर्न-आधारित पहचान पर वापस आ जाते हैं ताकि एक स्कैनर उस निष्कर्ष की रिपोर्ट करने की ओर झुके जिसे वह अस्वीकृत नहीं कर सकता। SpringSecurityVersionByProject डेटा तालिका अभी भी हल किए गए संस्करण को रिकॉर्ड करती है और प्रत्येक प्रोजेक्ट को प्रभावित या नहीं के रूप में फ्लैग करती है, ताकि आप ऑडिट कर सकें कि क्या फ़िल्टर किया गया था।
ये खतरनाक दिखते हैं लेकिन रैपर-ट्रैक किए जाते हैं, इसलिए रिस्पॉन्स कमिट होने से पहले सुरक्षा हेडर लिखे जाते हैं:
| कोड | यह सुरक्षित क्यों है |
|---|---|
response.setContentLength(int) / setContentLengthLong(long) | ओवरराइड — रैपर घोषित लंबाई रिकॉर्ड करता है और बॉडी पूरी होने पर onResponseCommitted() फायर करता है। |
response.flushBuffer() | ओवरराइड — super.flushBuffer() से पहले doOnResponseCommitted() कॉल करता है। |
response.getOutputStream().write(..) / flush() / close() | SaveContextServletOutputStream लौटाता है; हर write/flush/close डेलीगेट करने से पहले doOnResponseCommitted() फायर करता है। |
response.getWriter().write(..) / print(..) / println(..) / flush() / close() | SaveContextPrintWriter लौटाता है; वही पैटर्न। |
response.addHeader("Content-Length", v) | रैपर में विशेष-मामला — setContentLength(long) के माध्यम से रूट किया गया। |
Semgrep डेमो का /vuln/flush एंडपॉइंट दावा करता है कि flushBuffer() ट्रिगर है, लेकिन संवेदनशील Spring Security 6.4.12 पर रिस्पॉन्स वास्तव में सभी छह सुरक्षा हेडर लौटाता है। डेमो में वास्तविक ट्रिगर /vuln/stream और /vuln/content-length में setIntHeader("Content-Length", ...) कॉल हैं।
यह चलाएं:
| रेसिपी | उद्देश्य |
|---|---|
io.moderne.recipe.cve202622732.FindSpringSecurityHeaderSuppression | हर पहचान चलाता है और संस्करण रिपोर्ट तालिका उत्सर्जित करता है |
उपरोक्त एग्रीगेटर दो छोटी रेसिपी से बना है। यदि आप केवल एक पहचान चाहते हैं तो आप उन्हें व्यक्तिगत रूप से लागू कर सकते हैं।
| रेसिपी | उद्देश्य |
|---|---|
io.moderne.recipe.cve202622732.FindHttpResponseContentLengthHeader | "Content-Length" लिटरल के लिए टेंट-फ्लो जो setHeader / setIntHeader / addIntHeader (सर्वलेट) या HttpHeaders.set / add (WebFlux) तक पहुंचता है |
io.moderne.recipe.cve202622732.FindHttpResponseContentLengthOrFlushBuffer | WebFlux बिना शर्त कमिट: writeWith, writeAndFlushWith, setComplete, HttpHeaders.setContentLength |
यह चलाएं:
| रेसिपी | उद्देश्य |
|---|---|
io.moderne.recipe.cve202622732.FixSpringSecurityHeaderSuppression | सबसे सस्ता उपचार चुनता है जो प्रत्येक प्रोजेक्ट वास्तव में ले सकता है |
यह क्रम में दो चरण चलाता है।
1. प्रोजेक्ट की अपनी श्रृंखला पर फिक्स पर बढ़ाएं। Spring Security ने फिक्स को 6.5.9 और 7.0.4 के रूप में Maven Central पर प्रकाशित किया। प्रत्येक बंप एक FindAffectedSpringSecuritySeries पूर्वशर्त पर गेटेड है, क्योंकि UpgradeDependencyVersion केवल जांचता है कि उसका लक्ष्य नया है — 7.0.4 पर जाने के लिए कहा गया, यह खुशी से एक 5.8 प्रोजेक्ट को दो प्रमुख संस्करणों में खींच लेगा।
2. चरण 1 जो ठीक नहीं कर सका, उसमें एक उत्सुक हेडर-लेखन कॉन्फ़िगरेशन जोड़ें। यह प्रति प्रोजेक्ट एक @Configuration क्लास उत्पन्न करता है:
@Bean
public static BeanPostProcessor eagerHeaderWriterFilterBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof HeaderWriterFilter) {
((HeaderWriterFilter) bean).setShouldWriteHeadersEagerly(true);
}
return bean;
}
};
}
हेडर को पहले से लिखना अप्रासंगिक बना देता है कि रैपर कभी कमिट का निरीक्षण करता है या नहीं, इसलिए यह प्रोजेक्ट में हर सिंक को एक साथ बंद कर देता है — जिसमें वे भी शामिल हैं जिन तक टेंट विश्लेषण नहीं पहुंच सकता, जैसे "ज्ञात सीमाएं" के तहत Map फ्लो। BeanPostProcessor HttpSecurity DSL द्वारा निर्मित फिल्टर देखता है क्योंकि AutowireBeanFactoryObjectPostProcessor उन्हें बीन फैक्ट्री के माध्यम से आरंभ करता है। इस CVE के लिए दो स्वतंत्र रूप से प्रकाशित फिक्स बिल्कुल इसी आकार का उपयोग करते हैं (hmcts/idam-web-public, और armory-io के Spinnaker फोर्क समतुल्य ObjectPostProcessor के माध्यम से)।
चरण 2 वही कवर करता है जो चरण 1 नहीं कर सकता:
| स्थिति | बंप क्यों काम नहीं करता |
|---|---|
| 5.7, 5.8, 6.3, 6.4 | फिक्स केवल Spring Enterprise ग्राहकों को जाता है — Maven Central पर नहीं |
| 6.0 - 6.2 | उन श्रृंखलाओं पर कोई फिक्स कभी जारी नहीं हुआ |
| आयातित BOM द्वारा प्रबंधित संस्करण | बंप को संपादित करने के लिए स्थानीय रूप से कुछ भी घोषित नहीं है |
वह अंतिम पंक्ति कोई किनारे का मामला नहीं है। nla/bamboo 7.0.3 हल करता है — एक श्रृंखला जिसमें ओपन-सोर्स फिक्स है — पूरी तरह से Spring Boot BOM से, इसलिए अकेले संस्करण जांच दोनों चरणों के तहत इसे छोड़ देगी और इसे संवेदनशील छोड़ देगी। AddEagerHeaderWriterConfiguration इसलिए केवल अपग्रेड को स्थगित करता है जब प्रोजेक्ट के पास ओपन-सोर्स फिक्स उपलब्ध हो और अपना स्वयं का संस्करण घोषित करता हो।
उत्पन्न क्लास एक @EnableWebSecurity क्लास के बगल में रखी जाती है जहां एक मौजूद है, @SpringBootApplication पर वापस गिरती है और फिर किसी भी @Configuration पर, इसलिए यह हमेशा वहां पहुंचती है जहां कंपोनेंट स्कैनिंग पहुंचती है। जो प्रोजेक्ट पहले से ही हेडर उत्सुकता से लिखते हैं, या जो एक पैच किए गए OnCommittedResponseWrapper को वेंडर करते हैं (जैसा jogetworkflow/jw-community करता है), उन्हें अकेला छोड़ दिया जाता है।
| रेसिपी | उद्देश्य |
|---|---|
io.moderne.recipe.cve202622732.UpgradeSpringSecurityToPatchedVersion | श्रृंखला-गेटेड बंप 6.5.9 / 7.0.4 पर |
io.moderne.recipe.cve202622732.AddEagerHeaderWriterConfiguration | उत्पन्न कॉन्फ़िगरेशन, अपने आप |
io.moderne.recipe.cve202622732.FindAffectedSpringSecuritySeries | एक प्रभावित श्रृंखला पर प्रोजेक्ट चिह्नित करता है; अपग्रेड पूर्वशर्त |
semgrep/cve-2026-22732-demo पर संवेदनशील Spring Security 6.4.12 पर, एम्बेडेड Tomcat के विरुद्ध लागू किया गया। इसका HeaderVerificationTest दावा करता है कि सुरक्षा हेडर अनुपस्थित हैं, इसलिए एक काम करने वाला फिक्स इसे विफल बना देता है:
| एंडपॉइंट | पहले | बाद में |
|---|---|---|
/vuln/stream | X-Content-Type-Options: null | nosniff |
/vuln/content-length | X-Content-Type-Options: null | nosniff |
/safe | nosniff | nosniff |
X-Frame-Options और Cache-Control उसी पैटर्न का पालन करते हैं।
कॉर्पस के 16 रिपॉजिटरी में जो बिल्ड करते हैं (29 पहचाने गए में से), फिक्स ने तीन के लिए एक कॉन्फ़िगरेशन उत्पन्न किया और बाकी को सही ढंग से अकेला छोड़ दिया:
| रिपॉजिटरी | परिणाम |
|---|---|
semgrep/cve-2026-22732-demo | com/example/vuln में, @EnableWebSecurity के बगल में उत्पन्न; संकलित होता है, हेडर बहाल |
nla/bamboo | ui/src/bamboo में, @SpringBootApplication के बगल में उत्पन्न; संकलित होता है। BOM-प्रबंधित 7.0.3 मामला जिस तक अपग्रेड नहीं पहुंच सकता |
star-whale/starwhale | ai/starwhale/mlops/configuration/security में, @EnableWebSecurity के बगल में उत्पन्न; संकलित होता है (JDK 11, इसका घोषित लक्ष्य) |
hmcts/idam-web-public | अकेला छोड़ दिया — पहले से ही setShouldWriteHeadersEagerly कॉल करता है |
okta/okta-idx-java (6.5.9), psi-probe (6.5.11), Ant-Media-Server (6.5.11) | अकेला छोड़ दिया — अपनी श्रृंखला पर फिक्स से आगे |
apache/shenyu (6.3.1) | अकेला छोड़ दिया — केवल रिएक्टिव; HeaderWriterFilter के पीछे कोई सर्वलेट API नहीं है, और CVE केवल-सर्वलेट है |
brutusin/Brutusin-RPC (4.0.4) | अकेला छोड़ दिया — setShouldWriteHeadersEagerly (5.2) से पहले का है |
bootplus, template-app, front50, igor, rosco, spring-security, reportserver | अकेला छोड़ दिया — कोई प्रभावित Spring Security संस्करण हल नहीं हुआ |
तीन पैच किए गए रिपॉजिटरी पर फिक्स को फिर से चलाने से कुछ और उत्पन्न नहीं होता, इसलिए उपचार वास्तविक प्रोजेक्ट्स पर अपने स्वयं के आउटपुट के विरुद्ध आइडेम्पोटेंट है।
एक दूसरा, व्यापक कॉर्पस उस आबादी को लक्षित करता है जिसे फिक्स वास्तव में संबोधित करता है — कोई भी प्रभावित सर्वलेट Spring Security एप्लिकेशन, क्योंकि किसी सिंक की आवश्यकता नहीं है। कोड खोज द्वारा पाए गए ऐसे 64 प्रोजेक्ट्स में से, 52 ने बिल्ड किया, 48 ने एक प्रभावित संस्करण हल किया, और 38 पैच किए गए; उनमें से 35 संकलित होते हैं (अन्य तीन उत्पन्न फाइल के बिना समान रूप से विफल होते हैं)। Spring Security 5.2 से नीचे के सभी 8 प्रोजेक्ट सही ढंग से छोड़ दिए गए। SUSCEPTIBLE-REPOSITORIES.md अनुभाग 8 देखें।
OnCommittedResponseWrapper extends jakarta.servlet.http.HttpServletResponseWrapper, इसलिए CVE-2026-22732 केवल-सर्वलेट है और एक रिएक्टिव एप्लिकेशन इसके संपर्क में नहीं है। FindHttpResponseContentLengthOrFlushBuffer के निष्कर्ष समान रिएक्टिव पैटर्न को फ्लैग करते हैं और अभी भी मैन्युअल समीक्षा की आवश्यकता होती है, लेकिन फिक्स जानबूझकर उन पर कार्य नहीं करता। AddEagerHeaderWriterConfiguration किसी भी मॉड्यूल को छोड़ देता है जो सर्वलेट API के पीछे के बिना HeaderWriterFilter देख सकता है — फिल्टर OncePerRequestFilter का विस्तार करता है, और spring-security-web सर्वलेट API को एक गैर-संक्रामक provided निर्भरता के रूप में रखता है, इसलिए वहां उत्पन्न करना cannot access jakarta.servlet.Filter के साथ विफल होता है (apache/shenyu पर देखा गया)।HeaderWriterFilter.setShouldWriteHeadersEagerly 5.2 में आता है; 4.0.4 पर फिल्टर में केवल एक कंस्ट्रक्टर और doFilterInternal है। पहचान अभी भी EOL संस्करणों की रिपोर्ट करती है, लेकिन उपचार रोक दिया जाता है बजाय एक कॉल उत्सर्जित करने के जो संकलित नहीं हो सकता।| तालिका | पंक्तियां |
|---|---|
TaintFlowTable (rewrite-program-analysis से) | प्रति Content-Length-हेडर टेंट हिट एक पंक्ति |
HttpResponseDirectCommitTable | प्रति WebFlux संरचनात्मक हिट एक पंक्ति |
SpringSecurityVersionByProject | पहचाने गए Spring Security संस्करण के साथ प्रति प्रोजेक्ट एक पंक्ति |
Moderne CLI के माध्यम से:
mod run . --recipe io.moderne.recipe.cve202622732.FindSpringSecurityHeaderSuppression
rewrite.yml के माध्यम से:
---
type: specs.openrewrite.org/v1beta/recipe
name: com.example.DetectSpringSecurityHeaderSuppression
displayName: Detect CVE-2026-22732
recipeList:
- io.moderne.recipe.cve202622732.FindSpringSecurityHeaderSuppression
repos.csv उन 75 सार्वजनिक रिपॉजिटरी को सूचीबद्ध करता है जिनके विरुद्ध ये रेसिपी विकसित और मापी गई थीं, प्रत्येक को उस सटीक कमिट पर पिन किया गया जिस पर इसका मूल्यांकन किया गया था। कई सक्रिय रूप से बनाए रखे जाते हैं और अपस्ट्रीम पैच किए जाएंगे, इसलिए changeset कॉलम वही है जो नीचे दिए गए नंबरों को केवल प्रशंसनीय के बजाय पुनरुत्पादनीय बनाता है।
mod git sync csv ./corpus repos.csv --with-sources
mod build ./corpus
mod run ./corpus --recipe=io.moderne.recipe.cve202622732.CveDevCenter
mod devcenter ./corpus --last-recipe-run
सिंक लगभग 20 सेकंड और 1.4 GB लेता है; बिल्ड लगभग 15 मिनट लेता है और एकमात्र धीमा चरण है। mod devcenter devcenter.html को corpus/.moderne/run/<runId>/ में लिखता है, साथ ही प्रति संगठन उपनिर्देशिका एक।
org1 कॉलम कॉर्पस को उन समूहों में विभाजित करता है जो दर्शाते हैं कि प्रत्येक रिपॉजिटरी क्यों मौजूद है — दो संवेदनशील कॉल आकृतियों के लिए Servlet Sinks और WebFlux Sinks, पहले से अपस्ट्रीम उपचारित रिपॉजिटरी के लिए Patched, Spring Security स्वयं और अन्य गैर-उपभोक्ताओं के लिए Reference, चल रहे सर्वर के विरुद्ध जांचे गए मामले के लिए Verified, बिल्ड-टूल कवरेज के लिए Gradle, और थोक नमूने के लिए Wide।
पिन किए गए कमिट पर अपेक्षा करें:
| परिणाम | |
|---|---|
| अपग्रेड कार्ड | 39 Major, 21 Minor, 6 Patch, 4 Completed (70 रिपॉजिटरी) |
| सुरक्षा कार्ड | 65 उजागर रिपॉजिटरी |
| लागू नहीं | 5 रिपॉजिटरी कोई Spring Security निर्भरता हल नहीं करतीं |
उन पांच में से चार वास्तव में Spring Security का उपयोग नहीं करते — spring-projects/spring-security स्वयं लाइब्रेरी है, JoeyBling/bootplus Apache Shiro का उपयोग करता है, jenkinsci/stapler सीधे सर्वलेट API को लक्षित करता है, और infofabrik/reportserver के पास हल करने के लिए कोई Maven या Gradle बिल्ड नहीं है। पांचवां, xtuer/template-app, spring-security-web:5.0.0.RELEASE घोषित करता है लेकिन इसका Gradle बिल्ड mod build के दौरान कोई निर्भरता हल नहीं करता, इसलिए कोई रेसिपी संस्करण नहीं देख सकती। इसे अप्रभावित के बजाय अमापा हुआ मानें।
फिक्स लागू करने और परिणाम जांचने के लिए:
mod run ./corpus --recipe=io.moderne.recipe.cve202622732.FixSpringSecurityHeaderSuppression
mod git apply ./corpus --last-recipe-run
mod exec ./corpus --last-recipe-run MODERNE_BUILD_TOOL_CHECK
mod git apply चेकआउट में जगह पर लिखता है। कॉर्पस में एक पूर्ण check धीमा है और इस परिवर्तन से असंबंधित विफलताओं को सतह पर लाएगा — लापता JDK टूलचेन, अप्राप्य निर्भरता रिपॉजिटरी, परीक्षण जो पहले से लाल थे — इसलिए सार्थक संकेत लागू करने से पहले उसी कमांड के विरुद्ध डेल्टा है।
rewrite-program-analysis से टेंट विश्लेषण स्थानीय डेटाफ्लो और प्रति-विधि सारांश संभालता है, इसलिए ये पैटर्न स्वचालित रूप से पहचाने जाते हैं:
String h = "Content-Length"; response.setIntHeader(h, 42); फ्लैग किया जाता है — फ्रेमवर्क स्थानीय असाइनमेंट के माध्यम से लिटरल टेंट को ट्रैक करता है।Map, List, कस्टम संग्रह)। stash.put("k", "Content-Length") के बाद response.setIntHeader(stash.get("k"), 42) पहचाना नहीं जाता — put/get पहचान विश्लेषण के लिए अपारदर्शी है।Moderne Proprietary। केवल Moderne ग्राहकों द्वारा वाणिज्यिक अनुबंध की शर्तों के तहत उपयोग के लिए।