Skip to content

fix(security): TOCTOU Race Condition — upgrade tomcat-embed-core to 9.0.98 [COG-427]#9

Open
devin-ai-integration[bot] wants to merge 2 commits into
masterfrom
devin/1778635702-fix-toctou-tomcat-cog427
Open

fix(security): TOCTOU Race Condition — upgrade tomcat-embed-core to 9.0.98 [COG-427]#9
devin-ai-integration[bot] wants to merge 2 commits into
masterfrom
devin/1778635702-fix-toctou-tomcat-cog427

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented May 13, 2026

Copy link
Copy Markdown

Summary

Resolves [COG-427] — Critical TOCTOU Race Condition in tomcat-embed-core@9.0.33 (CVSS 9.2, CWE-367).

Snyk ID: SNYK-JAVA-ORGAPACHETOMCATEMBED-8547999

Root Cause

The monolith's Spring Boot parent (2.2.6.RELEASE) pulled in tomcat-embed-core:9.0.33, which contains a Time-of-check Time-of-use (TOCTOU) race condition in request processing. This can allow authorization bypass or data corruption under concurrent load.

Fix Applied

  1. Upgraded Spring Boot parent from 2.2.6.RELEASE2.7.18
  2. Pinned tomcat.version to 9.0.98 — the minimum version containing the TOCTOU fix (Spring Boot 2.7.18 only brings 9.0.83 transitively)
  3. Added spring-boot-starter-validation dependency — required because javax.validation was removed from spring-boot-starter-web starting in Spring Boot 2.3

Tests Added

  • TomcatVersionSecurityTest — regression test that asserts the embedded Tomcat version is ≥ 9.0.98, referencing the specific Snyk vulnerability ID

Review & Testing Checklist for Human

  • Verify tomcat-embed-core resolves to 9.0.98: cd monolith && ./mvnw dependency:tree | grep tomcat
  • Run full test suite: cd monolith && ./mvnw clean test — all 8 tests should pass (7 existing + 1 new regression)
  • Smoke-test the app: cd monolith && ./mvnw spring-boot:run and hit /bonds, /counterparties, /rfqs

Notes

  • The javax.validation annotations (@NotNull, @Positive, @PositiveOrZero, @Size) used across entity and DTO classes required the explicit spring-boot-starter-validation dependency after the Spring Boot upgrade
  • No application code changes were needed — only pom.xml dependency updates

Link to Devin session: https://app.devin.ai/sessions/1c42bfa6f2f846eabc535aa8f1b72da5
Requested by: @patrickbradley-cog


Devin Review

Status Commit
⚪ Not started

Run Devin Review

💡 Connect your GitHub account to enable automatic code reviews.

Open in Devin Review (Staging)
Open in Devin Review

…ition [COG-427]

- Upgrade Spring Boot parent from 2.2.6.RELEASE to 2.7.18
- Pin tomcat.version to 9.0.98 (minimum fix for CWE-367 TOCTOU, CVSS 9.2)
- Add spring-boot-starter-validation (removed from web starter in 2.3+)
- Add regression test verifying Tomcat >= 9.0.98

Resolves: SNYK-JAVA-ORGAPACHETOMCATEMBED-8547999
Co-Authored-By: patrick.bradley <patrick.bradley@cognition.ai>
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 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 and CI monitoring

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

Copy link
Copy Markdown
Author

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.

Open in Devin Review

Comment thread monolith/pom.xml
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<version>2.7.18</version>

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🚩 H2 2.x upgrade may affect GenerationType.AUTO behavior

Spring Boot 2.7.18 ships with H2 2.x (up from H2 1.4.x in 2.2.6). H2 2.x introduced breaking changes including different handling of auto-generated IDs. The entities (Rfq.java:24, Bond.java:19, Counterparty.java:8) all use GenerationType.AUTO, which may produce different ID sequences under H2 2.x + Hibernate 5.6.x. The integration test at IntegrationTest.java:148 hardcodes rfq.setBondId(1L) assuming that bond ID 1 exists (seeded by the CommandLineRunner at SplitTheMonolithApplication.java:49). If the ID generation strategy no longer starts at 1, this test would fail. This is a pre-existing test concern but the version upgrade in this PR is what changes the H2 version.

Open in Devin Review

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Investigated — this is not an issue in practice. All 7 existing integration tests (including the ones using hardcoded bondId(1L) and counterpartyId(1L)) pass with the upgraded H2 2.x. Hibernate 5.6.15 (shipped with Spring Boot 2.7.18) handles GenerationType.AUTO correctly with H2 2.x in this configuration. The seeded data IDs remain stable.

…SecurityTest

ServerInfo.getServerNumber() is a static method that reads from a
bundled properties file — no Spring context needed.

Co-Authored-By: patrick.bradley <patrick.bradley@cognition.ai>

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

Open in Devin Review

Comment thread monolith/pom.xml

<properties>
<java.version>11</java.version>
<tomcat.version>9.0.98</tomcat.version>

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🚩 Tomcat 9.0.98 override may drift from Spring Boot's tested matrix

The tomcat.version property at monolith/pom.xml:20 overrides the Tomcat version managed by Spring Boot 2.7.18 (which ships with ~9.0.83). While this is valid Maven usage for a security fix, Tomcat 9.0.98 is significantly newer than what Spring Boot 2.7.18 was tested against. This could theoretically introduce subtle incompatibilities in embedded server behavior. The regression test (TomcatVersionSecurityTest.java) helps catch accidental downgrades but doesn't validate runtime compatibility. If the project eventually upgrades to Spring Boot 3.x, the Tomcat major version will change to 10.x and this override will need to be removed or updated.

Open in Devin Review

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good callout. The override is intentional — Spring Boot 2.7.18 ships 9.0.83 which is still vulnerable. The full test suite (7 integration tests + 1 version check) passes with 9.0.98, confirming runtime compatibility. Agreed that this override should be removed if/when the project moves to Spring Boot 3.x (which ships Tomcat 10.x).

Comment on lines +22 to +26
String[] parts = serverNumber.split("\\.");

int major = Integer.parseInt(parts[0]);
int minor = Integer.parseInt(parts[1]);
int patch = Integer.parseInt(parts[2]);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

📝 Info: Test assumes standard 3-part Tomcat version format

The test at TomcatVersionSecurityTest.java:22-26 splits the version string by . and directly indexes parts[0], parts[1], and parts[2]. This works for all standard Apache Tomcat releases which always use major.minor.patch format. If a non-standard version string were ever returned (e.g., a milestone like 9.0.98.M1 or a 2-part string), the test would either fail with ArrayIndexOutOfBoundsException or NumberFormatException. This is extremely unlikely for production Tomcat releases, so not flagged as a bug, but worth noting for robustness.

Open in Devin Review

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Acknowledged — all standard Apache Tomcat releases use major.minor.patch format, so this is safe for production use. Adding defensive parsing for hypothetical non-standard versions would add complexity without practical benefit here.

@devin-ai-integration

Copy link
Copy Markdown
Author

🧪 End-to-End Test Results — COG-427

Tested locally: started the Spring Boot app with upgraded dependencies and exercised all REST endpoints.

All 6 tests passed.

# Test Result
1 Tomcat version in dependency tree is 9.0.98 (not 9.0.33) ✅ Passed
2 Regression test TomcatVersionSecurityTest passes ✅ Passed
3 App starts, seeded data served via GET /bonds, /counterparties, /rfqs ✅ Passed
4 Full RFQ trade execution (POST counterparty → bond → RFQ, status EXECUTED) ✅ Passed
5 Validation: insufficient credit returns HTTP 400 (not 201 or 500) ✅ Passed
6 Full test suite: 8 tests, 0 failures, BUILD SUCCESS ✅ Passed
Key Evidence

Dependency tree confirms fix:

org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.98:compile

RFQ trade execution (Saga pattern):

{"id":6,"counterpartyId":4,"bondId":5,"notionalAmount":1000000,
 "side":"BUY","status":"EXECUTED","executionPrice":998750}
HTTP_CODE:201

Validation (insufficient credit):

HTTP_CODE:400
Full test suite output
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.044 s - in TomcatVersionSecurityTest
Tests run: 7, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.235 s - in IntegrationTest
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

Devin session

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant