Skip to content

security: upgrade to Spring Boot 3.3.x to remediate CVE-2024-22262 (spring-web) - #36

Open
danigrim wants to merge 2 commits into
mainfrom
devin/1788706507-springboot33-cve-2024-22262
Open

danigrim wants to merge 2 commits into
mainfrom
devin/1788706507-springboot33-cve-2024-22262

Conversation

@danigrim

@danigrim danigrim commented Sep 6, 2026

Copy link
Copy Markdown

Summary

Closes CVE-2024-22262 (GHSA-2wrp-6fg6-hmc5, UriComponentsBuilder URL parsing → open redirect / SSRF) by upgrading off end-of-life Spring Boot 2.7 rather than pinning spring-web. Parent POM goes 2.7.18 → 3.3.13, which brings spring-web 6.1.21 (fix landed in 6.1.6) and, as a side effect, snakeyaml 2.2 (closes CVE-2022-1471) and tomcat-embed 10.1.42.

No business logic, DTO contracts, Flyway migrations or audit-layer (@AuditAccess, PatientAccessLogger) changes.

Before / after versions

Artifact Before After
spring-boot-starter-parent 2.7.18 3.3.13
org.springframework:spring-web 5.3.31 (vulnerable) 6.1.21
org.yaml:snakeyaml 1.30 2.2
tomcat-embed-core 9.0.x 10.1.42
hibernate-core 5.6.x (org.hibernate) 6.5.3.Final (org.hibernate.orm)
spring-security-core 5.7.x 6.3.10
flyway-core 8.5.x 10.10.0 (+ new flyway-database-postgresql)
springdoc springdoc-openapi-ui 1.7.0 springdoc-openapi-starter-webmvc-ui 2.6.0
Java baseline 11 17 (java.version, maven.compiler.*, CI java-version)

Breaking changes fixed

  1. Jakarta EE 9+ namespacejavax.persistencejakarta.persistence, javax.validationjakarta.validation, javax.servletjakarta.servlet across entities, DTOs, controllers, JwtAuthenticationFilter, AuditAspect and the legacy JDBC/JPA services. javax.crypto.SecretKey in JwtTokenProvider left alone (JDK, not Jakarta EE).
  2. Spring Security 6 DSL — the removed .and()-chained configurers are gone; the SecurityFilterChain bean now uses the lambda DSL, and authorizeRequests/antMatchers become authorizeHttpRequests/requestMatchers:
    -http.cors().and()
    -    .csrf().disable()
    -    .sessionManagement().sessionCreationPolicy(STATELESS).and()
    -    .authorizeRequests().antMatchers("/**").permitAll();
    +http.cors(Customizer.withDefaults())
    +    .csrf(AbstractHttpConfigurer::disable)
    +    .sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
    +    .authorizeHttpRequests(auth -> auth.requestMatchers("/**").permitAll());
    No WebSecurityConfigurerAdapter was in use. JwtAuthenticationFilter is a @Component extends OncePerRequestFilter, so it stays registered through Boot's servlet-filter auto-registration exactly as before — deliberately not also added via addFilterBefore, which would have double-registered it and changed behaviour. Startup log confirms Filter 'jwtAuthenticationFilter' configured for use.
  3. springdoc 2.x — artifact renamed for Boot 3 (springdoc-openapi-starter-webmvc-ui); the OpenAPI annotations in the controllers are unchanged.
  4. Flyway 10 modularization — PostgreSQL support moved out of flyway-core; added org.flywaydb:flyway-database-postgresql (version managed by Boot) or migrations fail at startup with "Unsupported Database: PostgreSQL".
  5. Config property relocationsspring.redis.*spring.data.redis.*; dropped the now-redundant explicit spring.jpa.properties.hibernate.dialect (Hibernate 6 logs HHH90000025 and auto-selects PostgreSQLDialect).
  6. Lombok + MapStruct on JDK 17 — added the lombok-mapstruct-binding annotation-processor path before mapstruct-processor so PatientMapper still sees Lombok-generated accessors.
  7. CIactions/setup-java java-version: '11''17'.
  8. test profile — the repo had no application-test.yml; added one (local PostgreSQL from the repo's docker-compose.yml, Flyway on, Redis auto-config excluded) so the acceptance boot check is reproducible. dev (Neon) and default profiles are otherwise untouched.

Authorization matrix — before / after

Identical; only the DSL changed.

Aspect Before (Security 5.7) After (Security 6.3)
CSRF disabled (.csrf().disable()) disabled (.csrf(AbstractHttpConfigurer::disable))
CORS enabled, corsConfigurationSource bean (origins localhost:5173/5178/3000, methods GET/POST/PUT/DELETE/OPTIONS, all headers, credentials allowed) unchanged bean, wired via .cors(Customizer.withDefaults())
Session policy STATELESS STATELESS
/** (all endpoints, incl. /api/auth/**, /v1/patients/**, /v1/encounters/**, /v1/providers/**, /v1/export/**, /actuator/**, Swagger UI) permitAll() via antMatchers("/**") permitAll() via requestMatchers("/**")
Method security none (@PreAuthorize/@Secured not used anywhere) none
JWT filter JwtAuthenticationFilter auto-registered as a servlet filter (@Component) same
AuthenticationManager / PasswordEncoder AuthenticationConfiguration bean / BCrypt unchanged

Note: the app's endpoints were already fully permitAll() on main; this PR intentionally preserves that rather than tightening it.

Verification

$ mvn -B verify                      # JDK 17
BUILD SUCCESS

$ mvn -B dependency:tree | grep -E 'spring-web|snakeyaml|tomcat-embed-core|hibernate-core|flyway|spring-security-core'
org.springframework:spring-web:jar:6.1.21:compile
org.yaml:snakeyaml:jar:2.2:compile
org.apache.tomcat.embed:tomcat-embed-core:jar:10.1.42:compile
org.hibernate.orm:hibernate-core:jar:6.5.3.Final:compile
org.flywaydb:flyway-core:jar:10.10.0:compile
org.springdoc:springdoc-openapi-starter-webmvc-ui:jar:2.6.0:compile
org.springframework.security:spring-security-core:jar:6.3.10:compile
# no 5.3.x spring-*, no snakeyaml 1.x, no javax.servlet/javax.persistence artifacts remain

$ java -jar target/medchart-ehr-api-3.2.1.jar --spring.profiles.active=test
Flyway: Successfully applied 3 migrations, now at version v3
Started MedchartEhrApplication in 4.478 seconds

$ curl localhost:8080/api/actuator/health
{"status":"UP"}
Endpoint Status
GET /api/actuator/health 200 (UP)
GET /api/v1/providers 200
GET /api/v1/providers/departments 200
GET /api/v1/providers/specialties 200
GET /api/v1/patients/1 200
GET /api/v1/patients/search?q=a 200
GET /api/swagger-ui/index.html 200
GET /api/v3/api-docs 200
GET /api/v1/encounters/patient/{id}, /api/v1/encounters/status/{status} 500 — pre-existing on main

The encounter endpoints fail identically on main (built with JDK 11, Boot 2.7.18, same PostgreSQL instance): Jackson cannot serialize the lazy Encounter.patient Hibernate proxy — InvalidDefinitionException: No serializer found for ... ByteBuddyInterceptor. Not introduced by this upgrade and not worked around here; fixing it needs a DTO or jackson-datatype-hibernate module, which is out of scope for a CVE remediation.

Devin-Org: engineering

Link to Devin session: https://app.devin.ai/sessions/f907dea2eac548449b51e82eb41043d8
Open in Devin Desktop: https://app.devin.ai/desktop/session/f907dea2eac548449b51e82eb41043d8?variant=devin
Requested by: @danigrim


Devin Review

…-22262 (spring-web)

Co-Authored-By: Daniella Grimberg <daniella@cognition.ai>
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review

Comment thread pom.xml
datasource:
url: ${TEST_DB_URL:jdbc:postgresql://localhost:5432/coghealth}
username: ${TEST_DB_USERNAME:coghealth}
password: ${TEST_DB_PASSWORD:coghealth_dev_2024}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟨 Test database credential is exposed

The test profile defaults TEST_DB_PASSWORD to a committed password. Repository readers can access any database that reuses this credential.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a new exposure: coghealth_dev_2024 is the local docker-compose development password that is already committed in docker-compose.yml, start-infrastructure.sh and INFRASTRUCTURE.md. The default here just mirrors that local stack so the test profile boots without extra setup, and it is overridable via TEST_DB_PASSWORD; no shared or non-local database uses this credential. Leaving it as-is rather than diverging from the rest of the repo's local-dev config.

…3.3 upgrade

Co-Authored-By: Daniella Grimberg <daniella@cognition.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant