Skip to content

[Fix] 검증 에러 응답의 민감 정보 마스킹#93

Merged
whc9999 merged 2 commits into
mainfrom
fix/#7-login-fix
May 29, 2026
Merged

[Fix] 검증 에러 응답의 민감 정보 마스킹#93
whc9999 merged 2 commits into
mainfrom
fix/#7-login-fix

Conversation

@whc9999
Copy link
Copy Markdown
Collaborator

@whc9999 whc9999 commented May 29, 2026

✨ 어떤 이유로 PR를 하셨나요?

  • feature 병합
  • 버그 수정(아래에 issue #를 남겨주세요)
  • 코드 개선
  • 코드 수정
  • 배포
  • 기타(아래에 자세한 내용 기입해주세요)

📋 세부 내용 - 왜 해당 PR이 필요한지 작업 내용을 자세하게 설명해주세요

비밀번호 등 민감 필드가 validation 실패 응답과 로그에 원문 그대로 포함되지 않도록 수정했습니다.

ExceptionAdvice에서 rejected value 포맷팅 시 민감 필드 여부를 검사하도록 변경 password, token, secret, authorization, credential 키워드가 포함된 필드는 ****로 마스킹 일반 필드의 입력값 노출 방식은 기존 동작 유지
민감 필드 마스킹 여부를 검증하는 단위 테스트 추가

📸 작업 화면 스크린샷

⚠️ PR하기 전에 확인해주세요

  • 로컬테스트를 진행하셨나요?
  • 머지할 브랜치를 확인하셨나요?
  • 관련 label을 선택하셨나요?

🚨 관련 이슈 번호 [#7 ]

Summary by CodeRabbit

  • Bug Fixes

    • Validation error messages now mask values for sensitive fields (passwords, tokens, secrets, authorization credentials), including case-insensitive matches and nested field names, to prevent accidental data exposure.
  • Tests

    • Added test coverage for sensitive-value masking, covering case-insensitivity, nested field paths, and null-handling scenarios.

Review Change Stack

비밀번호 등 민감 필드가 validation 실패 응답과 로그에 원문 그대로 포함되지 않도록 수정했습니다.

ExceptionAdvice에서 rejected value 포맷팅 시 민감 필드 여부를 검사하도록 변경
password, token, secret, authorization, credential 키워드가 포함된 필드는 ****로 마스킹
일반 필드의 입력값 노출 방식은 기존 동작 유지
민감 필드 마스킹 여부를 검증하는 단위 테스트 추가
@whc9999 whc9999 self-assigned this May 29, 2026
@whc9999 whc9999 added the fix label May 29, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

📝 Walkthrough

Walkthrough

ExceptionAdvice adds MASKED_VALUE and a SENSITIVE_FIELD_KEYWORDS set plus isSensitiveField/formatRejectedValue helpers, and applies masking to rejected values in MethodArgumentNotValidException and ConstraintViolationException formatting; tests verify masking, case-insensitivity, non-sensitive passthrough, and null handling.

Changes

Sensitive Field Masking in Exception Handling

Layer / File(s) Summary
Masking infrastructure and helpers
src/main/java/com/jobdri/jobdri_api/global/apiPayload/exception/handler/ExceptionAdvice.java
Adds MASKED_VALUE, SENSITIVE_FIELD_KEYWORDS, and static helpers isSensitiveField and formatRejectedValue that use case-insensitive substring matching and null-safe formatting.
Exception handler integration
src/main/java/com/jobdri/jobdri_api/global/apiPayload/exception/handler/ExceptionAdvice.java
Updates MethodArgumentNotValidException and ConstraintViolationException formatting to call formatRejectedValue for field/property names instead of embedding raw rejected values.
Test coverage
src/test/java/com/jobdri/jobdri_api/global/apiPayload/exception/handler/ExceptionAdviceTest.java
New JUnit 5 tests validate masking for multiple sensitive keywords (including nested paths), case-insensitivity, preservation of non-sensitive values, and null-input behaviors.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I nibble through errors in the night,
Hide passwords out of sight,
With a twitch and a hop I seal the gate,
Four stars stand guard—no secret to date,
A rabbit's promise: keep data right. 🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: masking sensitive information in validation error responses.
Description check ✅ Passed The description follows the template and includes all required sections: reason for PR (코드 개선, 코드 수정), detailed content explaining the masking logic and test additions, and pre-merge checklist completion confirmation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/#7-login-fix

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/test/java/com/jobdri/jobdri_api/global/apiPayload/exception/handler/ExceptionAdviceTest.java (1)

10-19: ⚡ Quick win

Consider expanding test coverage for all sensitive keywords.

The test validates password and token keywords, but doesn't cover secret, authorization, and credential. Additionally, consider testing edge cases like null field names, null values, and case variations (e.g., "PASSWORD").

🧪 Additional test cases to consider
`@Test`
`@DisplayName`("모든 민감 키워드를 테스트")
void maskAllSensitiveKeywords() {
    assertThat(ExceptionAdvice.formatRejectedValue("secret", "raw-secret"))
            .isEqualTo("****");
    assertThat(ExceptionAdvice.formatRejectedValue("authorization", "raw-auth"))
            .isEqualTo("****");
    assertThat(ExceptionAdvice.formatRejectedValue("credential", "raw-cred"))
            .isEqualTo("****");
}

`@Test`
`@DisplayName`("대소문자 구분 없이 민감 필드를 감지")
void maskCaseInsensitiveFields() {
    assertThat(ExceptionAdvice.formatRejectedValue("PASSWORD", "raw"))
            .isEqualTo("****");
    assertThat(ExceptionAdvice.formatRejectedValue("RefreshToken", "raw"))
            .isEqualTo("****");
}

`@Test`
`@DisplayName`("null 필드명과 null 값 처리")
void handleNullInputs() {
    assertThat(ExceptionAdvice.formatRejectedValue(null, "value"))
            .isEqualTo("value");
    assertThat(ExceptionAdvice.formatRejectedValue("email", null))
            .isEqualTo("null");
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/jobdri/jobdri_api/global/apiPayload/exception/handler/ExceptionAdviceTest.java`
around lines 10 - 19, The test covers only "password" and "token" masking;
extend ExceptionAdviceTest by adding cases that call
ExceptionAdvice.formatRejectedValue to assert masking for additional sensitive
keywords ("secret", "authorization", "credential"), verify case-insensitive
detection (e.g., "PASSWORD", "RefreshToken"), and add null-handling edge cases:
passing null fieldName should return the original value, and passing a null
value should return "null" (or whatever ExceptionAdvice.formatRejectedValue
currently returns) to ensure consistent behavior; update or add test methods
(e.g., maskAllSensitiveKeywords, maskCaseInsensitiveFields, handleNullInputs) to
include these assertions against ExceptionAdvice.formatRejectedValue.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@src/test/java/com/jobdri/jobdri_api/global/apiPayload/exception/handler/ExceptionAdviceTest.java`:
- Around line 10-19: The test covers only "password" and "token" masking; extend
ExceptionAdviceTest by adding cases that call
ExceptionAdvice.formatRejectedValue to assert masking for additional sensitive
keywords ("secret", "authorization", "credential"), verify case-insensitive
detection (e.g., "PASSWORD", "RefreshToken"), and add null-handling edge cases:
passing null fieldName should return the original value, and passing a null
value should return "null" (or whatever ExceptionAdvice.formatRejectedValue
currently returns) to ensure consistent behavior; update or add test methods
(e.g., maskAllSensitiveKeywords, maskCaseInsensitiveFields, handleNullInputs) to
include these assertions against ExceptionAdvice.formatRejectedValue.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 323df260-632c-40a9-8403-363bf87fc0b5

📥 Commits

Reviewing files that changed from the base of the PR and between 9b76425 and 201bd9c.

📒 Files selected for processing (2)
  • src/main/java/com/jobdri/jobdri_api/global/apiPayload/exception/handler/ExceptionAdvice.java
  • src/test/java/com/jobdri/jobdri_api/global/apiPayload/exception/handler/ExceptionAdviceTest.java

검증 실패 응답에서 민감 필드 입력값이 노출되지 않도록 하는 마스킹 로직의 테스트 범위를 확장했습니다.

secret, authorization, credential 키워드 마스킹 검증 추가
PASSWORD, RefreshToken 등 대소문자 혼합 필드명 검증 추가
필드명이 null인 경우 기존 입력값을 유지하는지 검증
입력값이 null인 경우 현재 포맷팅 결과를 명시적으로 검증
민감 필드 입력값이 null이어도 마스킹되는지 검증
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/test/java/com/jobdri/jobdri_api/global/apiPayload/exception/handler/ExceptionAdviceTest.java (1)

10-25: ⚡ Quick win

Consider parameterized tests to reduce duplication.

The six assertions follow the same pattern. A parameterized test could improve maintainability:

♻️ Refactor to parameterized test
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+
+import java.util.stream.Stream;
+
+    static class SensitiveFieldProvider implements ArgumentsProvider {
+        `@Override`
+        public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
+            return Stream.of(
+                Arguments.of("password"),
+                Arguments.of("loginRequest.password"),
+                Arguments.of("refreshToken"),
+                Arguments.of("clientSecret"),
+                Arguments.of("authorizationHeader"),
+                Arguments.of("credentialKey")
+            );
+        }
+    }
+
-    `@Test`
-    `@DisplayName`("민감한 검증 실패 입력값은 키워드별로 마스킹한다")
-    void maskAllSensitiveKeywords() {
-        assertThat(ExceptionAdvice.formatRejectedValue("password", "raw-password"))
-                .isEqualTo("****");
-        assertThat(ExceptionAdvice.formatRejectedValue("loginRequest.password", "raw-password"))
-                .isEqualTo("****");
-        assertThat(ExceptionAdvice.formatRejectedValue("refreshToken", "raw-token"))
-                .isEqualTo("****");
-        assertThat(ExceptionAdvice.formatRejectedValue("clientSecret", "raw-secret"))
-                .isEqualTo("****");
-        assertThat(ExceptionAdvice.formatRejectedValue("authorizationHeader", "raw-authorization"))
-                .isEqualTo("****");
-        assertThat(ExceptionAdvice.formatRejectedValue("credentialKey", "raw-credential"))
-                .isEqualTo("****");
-    }
+    `@ParameterizedTest`
+    `@ArgumentsSource`(SensitiveFieldProvider.class)
+    `@DisplayName`("민감한 검증 실패 입력값은 키워드별로 마스킹한다")
+    void maskAllSensitiveKeywords(String fieldName) {
+        assertThat(ExceptionAdvice.formatRejectedValue(fieldName, "raw-value"))
+                .isEqualTo("****");
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/jobdri/jobdri_api/global/apiPayload/exception/handler/ExceptionAdviceTest.java`
around lines 10 - 25, Replace the duplicated assertions in
maskAllSensitiveKeywords() with a parameterized test: change the test method to
a `@ParameterizedTest` (e.g., `@ParameterizedTest` with `@CsvSource` or `@MethodSource`)
that supplies pairs of field names and expected masked values and call
ExceptionAdvice.formatRejectedValue for each pair; reference the existing test
name maskAllSensitiveKeywords and the static method
ExceptionAdvice.formatRejectedValue when updating the test so each
input/expected pair (e.g., "password","****", "loginRequest.password","****",
etc.) is asserted in a single parameterized test method to remove duplication
and improve maintainability.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@src/test/java/com/jobdri/jobdri_api/global/apiPayload/exception/handler/ExceptionAdviceTest.java`:
- Around line 10-25: Replace the duplicated assertions in
maskAllSensitiveKeywords() with a parameterized test: change the test method to
a `@ParameterizedTest` (e.g., `@ParameterizedTest` with `@CsvSource` or `@MethodSource`)
that supplies pairs of field names and expected masked values and call
ExceptionAdvice.formatRejectedValue for each pair; reference the existing test
name maskAllSensitiveKeywords and the static method
ExceptionAdvice.formatRejectedValue when updating the test so each
input/expected pair (e.g., "password","****", "loginRequest.password","****",
etc.) is asserted in a single parameterized test method to remove duplication
and improve maintainability.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7300ca68-d6a2-4817-8dc7-1af5b16ce31d

📥 Commits

Reviewing files that changed from the base of the PR and between 201bd9c and 9200e7e.

📒 Files selected for processing (1)
  • src/test/java/com/jobdri/jobdri_api/global/apiPayload/exception/handler/ExceptionAdviceTest.java

@whc9999 whc9999 merged commit c41f1b1 into main May 29, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant