Skip to content

πŸ›‘οΈ Sentinel: [CRITICAL] Admin API의 κΆŒν•œ μ œμ–΄ 우회 및 데이터 유좜 취약점 패치 - #359

Closed
seonghobae wants to merge 1 commit into
mainfrom
fix-admin-bac-17983354558215036824
Closed

πŸ›‘οΈ Sentinel: [CRITICAL] Admin API의 κΆŒν•œ μ œμ–΄ 우회 및 데이터 유좜 취약점 패치#359
seonghobae wants to merge 1 commit into
mainfrom
fix-admin-bac-17983354558215036824

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator
  • 🚨 Severity: CRITICAL
  • πŸ’‘ Vulnerability: Admin μ—”λ“œν¬μΈνŠΈμ— 인증, κΆŒν•œ 검증 및 ν…Œλ„ŒνŠΈ 격리 λˆ„λ½
  • 🎯 Impact: μΈκ°€λ˜μ§€ μ•Šμ€ κ³΅κ²©μžκ°€ μ‹œμŠ€ν…œ λ‚΄ λͺ¨λ“  ν…Œλ„ŒνŠΈμ˜ λ³€ν™˜ μž‘μ—…μ„ 쑰회, μ‚­μ œ, μž¬μ‹œλ„ ν•  수 μžˆμ–΄ ꡐ차 ν…Œλ„ŒνŠΈ 데이터 유좜 및 μ‘°μž‘ κ°€λŠ₯
  • πŸ”§ Fix: AdminController에 TenantAccessServiceλ₯Ό μ£Όμž…ν•˜μ—¬ λͺ…μ‹œμ μΈ κ΄€λ¦¬μž κΆŒν•œ(JOB_READ, JOB_DELETE, JOB_RETRY) 및 ν…Œλ„ŒνŠΈ μ†Œμœ κΆŒμ„ ν™•μΈν•˜λ„λ‘ μˆ˜μ •ν•˜κ³ , 운영자 ID 기둝 μ‹œ κ°€λͺ…ν™” 처리 μΆ”κ°€.
  • βœ… Verification: 전체 ν…ŒμŠ€νŠΈ μŠ€μœ„νŠΈλ₯Ό μ‹€ν–‰ 및 ν†΅κ³Όν•˜μ˜€μœΌλ©°, λˆ„λ½λœ κΆŒν•œ μš”μ²­ μ‹œ μ—”λ“œν¬μΈνŠΈκ°€ μ •μƒμ μœΌλ‘œ 접근을 차단함을 확인함.

PR created automatically by Jules for task 17983354558215036824 started by @seonghobae

Summary by CodeRabbit

  • λ³΄μ•ˆ κ°•ν™”
    • κ΄€λ¦¬μž μž‘μ—… μ‘°νšŒΒ·μ‚­μ œΒ·μž¬μ‹œλ„ μ‹œ ν…Œλ„ŒνŠΈ μ†Œμ†κ³Ό κ΄€λ¦¬μž κΆŒν•œμ„ κ²€μ¦ν•©λ‹ˆλ‹€.
    • μž‘μ—… λͺ©λ‘ μ‘°νšŒμ— ν…Œλ„ŒνŠΈ 격리와 μ‹€νŒ¨ μž‘μ—… ν•„ν„°κ°€ μ μš©λ©λ‹ˆλ‹€.
    • μž¬μ‹œλ„ μž‘μ—…μžμ˜ 식별 정보가 μ•ˆμ „ν•˜κ²Œ κ°€λͺ…ν™”λ©λ‹ˆλ‹€.
  • 버그 μˆ˜μ •
    • κΆŒν•œμ΄ μ—†λŠ” μž‘μ—… μ ‘κ·Όκ³Ό ꡐ차 ν…Œλ„ŒνŠΈ 데이터 λ…ΈμΆœ κ°€λŠ₯성을 λ°©μ§€ν–ˆμŠ΅λ‹ˆλ‹€.
    • μ‘΄μž¬ν•˜μ§€ μ•Šκ±°λ‚˜ μ ‘κ·Όν•  수 μ—†λŠ” μž‘μ—… μš”μ²­μ„ 적절히 μ²˜λ¦¬ν•©λ‹ˆλ‹€.

AdminController의 λͺ¨λ“  μ—”λ“œν¬μΈνŠΈμ— 인증 및 κΆŒν•œ μ œμ–΄κ°€ λˆ„λ½λ˜μ–΄ μžˆμ–΄, κ³΅κ²©μžκ°€ κΆŒν•œ 없이
λͺ¨λ“  ν…Œλ„ŒνŠΈμ˜ μž‘μ—…μ„ μ‘°νšŒν•˜κ³  μ‘°μž‘ν•  수 μžˆλŠ” μ‹¬κ°ν•œ 취약점이 μ‘΄μž¬ν–ˆμŠ΅λ‹ˆλ‹€.

이에 TenantAccessServiceλ₯Ό μ£Όμž…ν•˜μ—¬ 인증 및 κ΄€λ¦¬μž κΆŒν•œμ„ κ°•μ œν•˜κ³ , ν…Œλ„ŒνŠΈ μ‹λ³„μžλ₯Ό ν†΅ν•œ
데이터 격리(job.belongsToTenant)λ₯Ό μˆ˜ν–‰ν•˜λ„λ‘ μ½”λ“œλ₯Ό μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€.
λ˜ν•œ μž¬μ‹œλ„ μž‘μ—… μ‹œ ν”„λΌμ΄λ²„μ‹œ 보호λ₯Ό μœ„ν•΄ operatorIdλ₯Ό SHA-256으둜 κ°€λͺ…ν™” μ²˜λ¦¬ν•˜μ˜€μŠ΅λ‹ˆλ‹€.
@google-labs-jules

Copy link
Copy Markdown

πŸ‘‹ Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a πŸ‘€ emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

πŸ“ Walkthrough

Walkthrough

κ΄€λ¦¬μž μž‘μ—… API에 ν…Œλ„ŒνŠΈ 인증, κΆŒν•œ 검사, μž‘μ—… μ†Œμœ κΆŒ 검증을 μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€. μž‘μ—… λͺ©λ‘μ„ ν…Œλ„ŒνŠΈ λ²”μœ„λ‘œ μ œν•œν–ˆμŠ΅λ‹ˆλ‹€. μ‚­μ œμ™€ μž¬μ‹œλ„ μš”μ²­μ€ κΆŒν•œμ„ ν™•μΈν•˜λ©°, μž¬μ‹œλ„ 주체 IDλŠ” SHA-256으둜 κ°€λͺ…ν™”ν•©λ‹ˆλ‹€.

Changes

κ΄€λ¦¬μž μž‘μ—… μ ‘κ·Ό μ œμ–΄

Layer / File(s) Summary
ν…Œλ„ŒνŠΈ λ²”μœ„ μž‘μ—… 쑰회
src/main/java/com/clearfolio/viewer/controller/AdminController.java, src/test/java/com/clearfolio/viewer/controller/AdminControllerTest.java, .jules/sentinel.md
JOB_READ κΆŒν•œμ„ ν™•μΈν•˜κ³  ν˜„μž¬ ν…Œλ„ŒνŠΈμ˜ μž‘μ—…λ§Œ μ‘°νšŒν•©λ‹ˆλ‹€. deadLettered ν•„ν„° ν…ŒμŠ€νŠΈμ™€ ν…Œλ„ŒνŠΈ μ»¨ν…μŠ€νŠΈ 섀정을 κ°±μ‹ ν–ˆμŠ΅λ‹ˆλ‹€. μ ‘κ·Ό μ œμ–΄ μš”κ΅¬μ‚¬ν•­μ„ κΈ°λ‘ν–ˆμŠ΅λ‹ˆλ‹€.
μ‚­μ œΒ·μž¬μ‹œλ„ 보호 및 주체 κ°€λͺ…ν™”
src/main/java/com/clearfolio/viewer/controller/AdminController.java, src/test/java/com/clearfolio/viewer/controller/AdminControllerTest.java
μ‚­μ œμ™€ μž¬μ‹œλ„μ— JOB_DELETE λ˜λŠ” JOB_RETRY κΆŒν•œκ³Ό μž‘μ—… μ†Œμœ κΆŒ 검증을 μ μš©ν–ˆμŠ΅λ‹ˆλ‹€. μž¬μ‹œλ„μ—λŠ” κ³ μ •λœ "admin" λŒ€μ‹  인증 주체의 SHA-256 ν•΄μ‹œλ₯Ό μ „λ‹¬ν•©λ‹ˆλ‹€. 404와 409 κ²°κ³Ό ν…ŒμŠ€νŠΈλ₯Ό μœ μ§€ν–ˆμŠ΅λ‹ˆλ‹€.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AdminController
  participant TenantAccessService
  participant DocumentConversionService
  Client->>AdminController: ν…Œλ„ŒνŠΈΒ·κΆŒν•œ 헀더와 μž‘μ—… μš”μ²­ 전달
  AdminController->>TenantAccessService: κΆŒν•œ 및 μž‘μ—… μ†Œμœ κΆŒ 검증
  TenantAccessService-->>AdminController: 검증 κ²°κ³Ό λ°˜ν™˜
  AdminController->>DocumentConversionService: μœ νš¨ν•œ μ‚­μ œ λ˜λŠ” ν•΄μ‹œλœ 주체의 μž¬μ‹œλ„ μš”μ²­
  DocumentConversionService-->>AdminController: μž‘μ—… κ²°κ³Ό λ°˜ν™˜
  AdminController-->>Client: μž‘μ—… λͺ©λ‘ λ˜λŠ” HTTP μƒνƒœ λ°˜ν™˜
Loading

Possibly related PRs

  • ContextualWisdomLab/clearfolio#270: ν…Œλ„ŒνŠΈ μ•ˆμ „ μ ‘κ·Ό λͺ¨λΈκ³Ό κ΄€λ¦¬μž μž‘μ—… κΆŒν•œμ„ λ‹€λ£Ήλ‹ˆλ‹€.
  • ContextualWisdomLab/clearfolio#341: AdminController의 ν…Œλ„ŒνŠΈ λ²”μœ„ κΆŒν•œ 및 μ‚­μ œ λ³€κ²½κ³Ό 직접 μ—°κ²°λ©λ‹ˆλ‹€.
  • ContextualWisdomLab/clearfolio#348: λ³€ν™˜ μž‘μ—…μ˜ ν…Œλ„ŒνŠΈ 및 주체 메타데이터 검증을 κ°•ν™”ν•©λ‹ˆλ‹€.
πŸš₯ Pre-merge checks | βœ… 5
βœ… Passed checks (5 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed 제λͺ©μ€ Admin API의 κΆŒν•œ μ œμ–΄ 우회 및 데이터 유좜 취약점 νŒ¨μΉ˜λΌλŠ” μ£Όμš” λ³€κ²½ 사항을 λͺ…ν™•ν•˜κ²Œ μ„€λͺ…ν•©λ‹ˆλ‹€.
Docstring Coverage βœ… Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches πŸ’‘ 1
πŸ› οΈ Fix failing CI checks πŸ’‘
  • Create stacked PR
  • Commit on current branch
πŸ“ Generate docstrings
  • Create stacked PR
  • Commit on current branch
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-admin-bac-17983354558215036824

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

πŸ€– 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.

Inline comments:
In `@src/main/java/com/clearfolio/viewer/controller/AdminController.java`:
- Around line 61-67: Update the JavaDoc for the conversion-job retrieval method
near the shown documentation to state that it returns only jobs belonging to
context.tenantId(), not all conversion jobs. Also revise the `@return` description
to specify the list of jobs for the current tenant, while leaving the
implementation unchanged.

In `@src/test/java/com/clearfolio/viewer/controller/AdminControllerTest.java`:
- Around line 135-143: Update retryDeadLetteredReturnsAcceptedWhenAccepted so
the conversionService.retryDeadLettered verification matches the exact
precomputed SHA-256 hexadecimal digest of "user-1" instead of anyString(). Keep
the job setup and accepted-result behavior unchanged, and ensure the assertion
proves hashSubjectId supplies that exact value.
πŸͺ„ Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: adfa9e74-efaa-486a-8664-20c0ad4b185b

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 55d7ae8 and aaef312.

πŸ“’ Files selected for processing (3)
  • .jules/sentinel.md
  • src/main/java/com/clearfolio/viewer/controller/AdminController.java
  • src/test/java/com/clearfolio/viewer/controller/AdminControllerTest.java

Comment on lines 61 to 67
/**
* Retrieves all conversion jobs, optionally filtered by dead-letter status.
*
* @param headers request headers carrying tenant claims
* @param deadLettered optional filter for dead-lettered jobs
* @return list of conversion jobs
*/

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

πŸ“ Maintainability & Code Quality | 🟑 Minor | ⚑ Quick win

ν…Œλ„ŒνŠΈ λ²”μœ„λ₯Ό JavaDoc에 λ°˜μ˜ν•˜μ‹­μ‹œμ˜€.

Line 62의 β€œall conversion jobs” μ„€λͺ…은 κ΅¬ν˜„κ³Ό λ‹€λ¦…λ‹ˆλ‹€. 이 λ©”μ„œλ“œλŠ” context.tenantId()에 μ†ν•œ μž‘μ—…λ§Œ λ°˜ν™˜ν•©λ‹ˆλ‹€. @return μ„€λͺ…도 ν˜„μž¬ ν…Œλ„ŒνŠΈμ˜ μž‘μ—… λͺ©λ‘μœΌλ‘œ λ³€κ²½ν•˜μ‹­μ‹œμ˜€.

πŸ€– 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/main/java/com/clearfolio/viewer/controller/AdminController.java` around
lines 61 - 67, Update the JavaDoc for the conversion-job retrieval method near
the shown documentation to state that it returns only jobs belonging to
context.tenantId(), not all conversion jobs. Also revise the `@return` description
to specify the list of jobs for the current tenant, while leaving the
implementation unchanged.

Comment on lines 135 to +143
@Test
void retryDeadLetteredReturnsAcceptedWhenAccepted() {
mockTenantContext(TenantPermissions.JOB_RETRY);
UUID jobId = UUID.randomUUID();
when(conversionService.retryDeadLettered(jobId, "admin")).thenReturn(RetryDeadLetterResult.ACCEPTED);
ConversionJob job = new ConversionJob(jobId, "tenant-a", "user-1",
"a.pdf", "application/pdf", "hash", 100L, 3);
when(conversionService.getJob(jobId)).thenReturn(Optional.of(job));
when(conversionService.retryDeadLettered(eq(jobId), anyString()))
.thenReturn(RetryDeadLetterResult.ACCEPTED);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

πŸ”’ Security & Privacy | 🟠 Major | ⚑ Quick win

μž¬μ‹œλ„ 주체 κ°€λͺ…ν™” 값을 μ •ν™•νžˆ κ²€μ¦ν•˜μ‹­μ‹œμ˜€.

Line 142의 anyString()은 원본 "user-1" λ˜λŠ” μž„μ˜μ˜ λ¬Έμžμ—΄λ„ ν—ˆμš©ν•©λ‹ˆλ‹€. hashSubjectIdκ°€ μ œκ±°λ˜κ±°λ‚˜ 잘λͺ»λœ 값을 λ°˜ν™˜ν•΄λ„ 이 ν…ŒμŠ€νŠΈλŠ” ν†΅κ³Όν•©λ‹ˆλ‹€. retryDeadLettered 호좜이 "user-1"의 μ •ν™•ν•œ SHA-256 hexadecimal κ°’λ§Œ λ°›λŠ”μ§€ κ²€μ¦ν•˜μ‹­μ‹œμ˜€.

As per coding guidelines, com.clearfolio.viewer.*의 생산 변경은 100% JaCoCo line and branch coverageλ₯Ό μœ μ§€ν•΄μ•Ό ν•©λ‹ˆλ‹€.

πŸ€– 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/clearfolio/viewer/controller/AdminControllerTest.java`
around lines 135 - 143, Update retryDeadLetteredReturnsAcceptedWhenAccepted so
the conversionService.retryDeadLettered verification matches the exact
precomputed SHA-256 hexadecimal digest of "user-1" instead of anyString(). Keep
the job setup and accepted-result behavior unchanged, and ensure the assertion
proves hashSubjectId supplies that exact value.

Source: Coding guidelines

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