Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion commandline/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.21.4</version>
</dependency>

<dependency>
Expand Down
1 change: 0 additions & 1 deletion generate/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.21.4</version>
</dependency>

<!-- runtime/plugin deps -->
Expand Down
91 changes: 36 additions & 55 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
<properties>
<java.version>21</java.version>
<aws.version>1.12.797</aws.version>
<junit.version>5.11.4</junit.version>
<junit.platform.version>1.11.4</junit.platform.version>
<jjwt.version>0.10.7</jjwt.version>
<spring.framework.version>6.2.19</spring.framework.version>
<spring.security.version>6.5.11</spring.security.version>
<tomcat.embed.version>10.1.55</tomcat.embed.version>
<junit.version>6.0.1</junit.version>
<junit.platform.version>6.0.1</junit.platform.version>
<jjwt.version>0.12.6</jjwt.version>
<spring.framework.version>7.0.8</spring.framework.version>
<spring.security.version>7.0.5</spring.security.version>
<tomcat.embed.version>11.0.23</tomcat.embed.version>

<requiredCodeCoverage>0.90</requiredCodeCoverage>
<verificationsPhase>verify</verificationsPhase>
Expand Down Expand Up @@ -252,13 +252,7 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.21.4</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.21.4</version>
<version>2.21.5</version>
</dependency>

<dependency>
Expand Down Expand Up @@ -472,43 +466,19 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.25</version>
<version>1.5.36</version>
</dependency>

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.5.25</version>
<version>1.5.36</version>
</dependency>

<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.7.0</version>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.1.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</exclusion>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</exclusion>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
</exclusion>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</exclusion>
</exclusions>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.3</version>
</dependency>

<dependency>
Expand Down Expand Up @@ -586,20 +556,31 @@
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>8.1</version>
</dependency>

<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-manifests</artifactId>
<version>1.1</version>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
</dependency>
<version>9.0</version>
</dependency>

<!-- Override tools.jackson.core (Jackson 3.x) transitive deps from logstash to fix CVEs -->
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>3.1.4</version>
</dependency>
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>3.1.5</version>
</dependency>
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>3.1.4</version>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
</dependency>

<dependency>
<groupId>net.jodah</groupId>
Expand Down
65 changes: 21 additions & 44 deletions rest-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,18 @@
<spring-framework.version>${spring.framework.version}</spring-framework.version>
<!-- tomcat.version is managed by spring-boot-dependencies BOM (3.5.13+) -->
<tomcat.version>managed-by-boot-bom</tomcat.version>
<!-- Test stack kept explicit so CI is deterministic across JDK updates -->
<junit.jupiter.version>5.11.4</junit.jupiter.version>
<junit.platform.version>1.11.4</junit.platform.version>
<!-- JUnit 6 unified versioning: platform and jupiter share the same version number.
Boot 4.0.0 ships junit-jupiter 6.0.1 / junit-platform 6.0.1. -->
<junit.jupiter.version>6.0.1</junit.jupiter.version>
<junit.platform.version>6.0.1</junit.platform.version>
<io.rest-assured.version>5.5.2</io.rest-assured.version>
<groovy.version>4.0.28</groovy.version>
<io.hamcrest.version>2.2</io.hamcrest.version>
<mockito.version>3.9.0</mockito.version>
<jakarta.servlet.version>6.1.0</jakarta.servlet.version>

<!-- Spring Security aligned with Boot BOM to avoid split versions -->
<spring.security.version>6.5.11</spring.security.version>
<spring.security.version>7.0.5</spring.security.version>
<spring.retry.version>2.0.13</spring.retry.version>

<!-- JaCoCo aggregated report path used by Sonar -->
Expand All @@ -60,7 +61,7 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.5.15</version>
<version>4.0.6</version>
<executions>
<execution><goals><goal>repackage</goal></goals></execution>
</executions>
Expand Down Expand Up @@ -177,40 +178,28 @@

<dependencyManagement>
<dependencies>
<!-- Framework BOM import: MUST be first to override Boot 4.0.6's Framework 7.0.7 with 7.0.8.
Maven uses "first declaration wins" for BOM imports. -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-bom</artifactId>
<version>${spring.security.version}</version>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring-framework.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.5.15</version>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-bom</artifactId>
<version>${spring.security.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<!-- These two lines are redundant when importing spring-framework-bom but kept for clarity.
You can safely remove them later if desired. -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-framework.version}</version>
</dependency>

<!-- Framework BOM import: lifts spring-* modules to the patched framework line. -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring-framework.version}</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>4.0.6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand Down Expand Up @@ -271,21 +260,7 @@
<!-- API / Docs -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
</dependency>

<!-- Pre-Jakarta plugin; exclude ALL old Spring 4.x bits it drags -->
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
<version>1.2.0.RELEASE</version>
<exclusions>
<exclusion><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId></exclusion>
<exclusion><groupId>org.springframework</groupId><artifactId>spring-core</artifactId></exclusion>
<exclusion><groupId>org.springframework</groupId><artifactId>spring-context</artifactId></exclusion>
<exclusion><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId></exclusion>
<exclusion><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId></exclusion>
</exclusions>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>

<!-- Logging -->
Expand Down Expand Up @@ -321,10 +296,12 @@
<!-- Keep these direct to force the patched framework version during mediation. -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring-framework.version}</version></dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>${spring-framework.version}</version></dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring-framework.version}</version></dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>${spring-framework.version}</version></dependency>

<!-- Misc -->
<dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId></dependency>
<dependency><groupId>com.jcabi</groupId><artifactId>jcabi-manifests</artifactId></dependency>
<dependency><groupId>com.jcabi</groupId><artifactId>jcabi-manifests</artifactId><version>1.1</version></dependency>

<!-- AWS v1 clients (monitor for CVEs periodically) -->
<dependency><groupId>com.amazonaws</groupId><artifactId>aws-java-sdk-core</artifactId></dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
package gov.cms.qpp.conversion.api;

import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;

/**
* Application to be ran
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class SecurityConfig {
protected String rtiOrgName;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
public SecurityFilterChain filterChain(HttpSecurity http) {
http.securityMatcher(PCF_WILDCARD)
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.csrf(csrf -> csrf.disable())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package gov.cms.qpp.conversion.api.security;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -63,19 +65,31 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
chain.doFilter(request, response);
}

/** Reusable Jackson mapper for JWT payload decoding. */
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<>() {};

@SuppressWarnings("unchecked")
private Map<String, String> getPayload(String tokenHeader) {
String tokenWithoutBearer = tokenHeader.replace(TOKEN_PREFIX, "");
String tokenWithoutSignatureAndBearer = removeSignature(tokenWithoutBearer);
Claims body = Jwts.parser()
.parseClaimsJwt(tokenWithoutSignatureAndBearer)
.getBody();
return body.get("data", Map.class);
}

private String removeSignature(String jws) {
int i = jws.lastIndexOf('.');
return jws.substring(0, i + 1);
String token = tokenHeader.replace(TOKEN_PREFIX, "");
String[] parts = token.split("\\.");
if (parts.length < 2) {
return Collections.emptyMap();
}
try {
// Decode the payload (second part) directly β€” no signature verification needed.
// This works regardless of the alg header (HS256, RS256, none, etc.).
byte[] payloadBytes = Base64.getUrlDecoder().decode(parts[1]);
Map<String, Object> claims = OBJECT_MAPPER.readValue(
new String(payloadBytes, StandardCharsets.UTF_8), MAP_TYPE);
Object data = claims.get("data");
if (data instanceof Map) {
return (Map<String, String>) data;
}
} catch (IOException | IllegalArgumentException e) {
// Malformed token β€” treat as unauthenticated
}
return Collections.emptyMap();
}

private boolean isValidCpcPlusOrg(Map<String, String> payloadMap) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.NoOpResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import jakarta.annotation.PostConstruct;
Expand Down Expand Up @@ -52,6 +51,7 @@ public class ValidationServiceImpl implements ValidationService {
public ValidationServiceImpl(final Environment environment) {
this.environment = environment;
this.restTemplate = new RestTemplate();
this.restTemplate.setErrorHandler(new NoOpResponseErrorHandler());
}

/**
Expand Down Expand Up @@ -114,7 +114,6 @@ public void validateQpp(ConversionReport conversionReport) {
* @return The response from the validation API end-point.
*/
private ResponseEntity<String> callValidationEndpoint(String url, JsonWrapper qpp) {
restTemplate.setErrorHandler(new NoHandlingErrorHandler());
HttpEntity<String> request = new HttpEntity<>(qpp.toString(), getHeaders());

API_LOG.info("Calling QPP validation API {}", url);
Expand Down Expand Up @@ -188,19 +187,4 @@ Error getError(String response) {
ErrorMessage.class)
.getError();
}

/**
* A private static class that tells the {@link RestTemplate} to not throw an exception on HTTP status 3xx and 4xx.
*/
private static class NoHandlingErrorHandler extends DefaultResponseErrorHandler {
/**
* Empty so it doesn't throw an exception.
*
* @param response The ClientHttpResponse.
*/
@Override
public void handleError(final ClientHttpResponse response) {
//do nothing
}
}
}
Loading
Loading