From a4a399c7c9bbec114aa526dd38664fbd5b45ae24 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 19:49:57 +0900 Subject: [PATCH 1/2] test(cdc): reproduce replication probe diagnostic leak --- ...plicationSlotProbeConfidentialityTest.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 cdc-service/src/test/java/com/xtrmetl/cdc/service/ReplicationSlotProbeConfidentialityTest.java diff --git a/cdc-service/src/test/java/com/xtrmetl/cdc/service/ReplicationSlotProbeConfidentialityTest.java b/cdc-service/src/test/java/com/xtrmetl/cdc/service/ReplicationSlotProbeConfidentialityTest.java new file mode 100644 index 00000000..1c8a0a4b --- /dev/null +++ b/cdc-service/src/test/java/com/xtrmetl/cdc/service/ReplicationSlotProbeConfidentialityTest.java @@ -0,0 +1,44 @@ +package com.xtrmetl.cdc.service; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.dao.DataAccessResourceFailureException; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Verifies that operator replication-slot failures keep database diagnostics confidential. + */ +@ExtendWith(OutputCaptureExtension.class) +class ReplicationSlotProbeConfidentialityTest { + + @Test + void failureResponseAndOrdinaryLogsExcludeDriverConnectionDiagnostics(CapturedOutput output) { + JdbcTemplate jdbc = mock(JdbcTemplate.class); + String sensitiveDriverMessage = + "jdbc:postgresql://db.internal:5432/prod?user=admin&password=super-secret"; + when(jdbc.queryForList(anyString(), eq("xtrmetl_slot"))) + .thenThrow(new DataAccessResourceFailureException(sensitiveDriverMessage)); + + Map result = new ReplicationSlotProbe(jdbc).probeSlot("xtrmetl_slot"); + + assertFalse((Boolean) result.get("available")); + assertEquals("query_failed", result.get("error")); + assertEquals("Replication slot state unavailable", result.get("message")); + assertFalse(result.toString().contains("super-secret")); + assertFalse(result.toString().contains("jdbc:postgresql://")); + String logs = output.getOut() + output.getErr(); + assertFalse(logs.contains("super-secret")); + assertFalse(logs.contains("jdbc:postgresql://")); + } +} From 0c7137ca1f4c7337938e0c1337f6aa55275b51f3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 8 Sep 2026 17:49:04 +0900 Subject: [PATCH 2/2] test(cdc): bind replication probe classification logs Prove DEBUG classification is emitted and that driver URLs and secret-like parameters stay out of captured logs. --- ...plicationSlotProbeConfidentialityTest.java | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/cdc-service/src/test/java/com/xtrmetl/cdc/service/ReplicationSlotProbeConfidentialityTest.java b/cdc-service/src/test/java/com/xtrmetl/cdc/service/ReplicationSlotProbeConfidentialityTest.java index 1c8a0a4b..bff321fc 100644 --- a/cdc-service/src/test/java/com/xtrmetl/cdc/service/ReplicationSlotProbeConfidentialityTest.java +++ b/cdc-service/src/test/java/com/xtrmetl/cdc/service/ReplicationSlotProbeConfidentialityTest.java @@ -1,7 +1,10 @@ package com.xtrmetl.cdc.service; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.LoggerFactory; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.dao.DataAccessResourceFailureException; @@ -11,6 +14,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -24,21 +29,33 @@ class ReplicationSlotProbeConfidentialityTest { @Test void failureResponseAndOrdinaryLogsExcludeDriverConnectionDiagnostics(CapturedOutput output) { - JdbcTemplate jdbc = mock(JdbcTemplate.class); - String sensitiveDriverMessage = - "jdbc:postgresql://db.internal:5432/prod?user=admin&password=super-secret"; - when(jdbc.queryForList(anyString(), eq("xtrmetl_slot"))) - .thenThrow(new DataAccessResourceFailureException(sensitiveDriverMessage)); + Logger probeLogger = assertInstanceOf( + Logger.class, + LoggerFactory.getLogger(ReplicationSlotProbe.class) + ); + Level previous = probeLogger.getLevel(); + // Classification is emitted at DEBUG; INFO-only capture would make secrecy asserts vacuous. + probeLogger.setLevel(Level.DEBUG); + try { + JdbcTemplate jdbc = mock(JdbcTemplate.class); + String sensitiveDriverMessage = + "jdbc:postgresql://db.internal:5432/prod?user=admin&password=super-secret"; + when(jdbc.queryForList(anyString(), eq("xtrmetl_slot"))) + .thenThrow(new DataAccessResourceFailureException(sensitiveDriverMessage)); - Map result = new ReplicationSlotProbe(jdbc).probeSlot("xtrmetl_slot"); + Map result = new ReplicationSlotProbe(jdbc).probeSlot("xtrmetl_slot"); - assertFalse((Boolean) result.get("available")); - assertEquals("query_failed", result.get("error")); - assertEquals("Replication slot state unavailable", result.get("message")); - assertFalse(result.toString().contains("super-secret")); - assertFalse(result.toString().contains("jdbc:postgresql://")); - String logs = output.getOut() + output.getErr(); - assertFalse(logs.contains("super-secret")); - assertFalse(logs.contains("jdbc:postgresql://")); + assertFalse((Boolean) result.get("available")); + assertEquals("query_failed", result.get("error")); + assertEquals("Replication slot state unavailable", result.get("message")); + assertFalse(result.toString().contains("super-secret")); + assertFalse(result.toString().contains("jdbc:postgresql://")); + String logs = output.getOut() + output.getErr(); + assertTrue(logs.contains("DataAccessResourceFailureException")); + assertFalse(logs.contains("super-secret")); + assertFalse(logs.contains("jdbc:postgresql://")); + } finally { + probeLogger.setLevel(previous); + } } }