Skip to content

🛡️ Sentinel: [CRITICAL] Fix authorization bypass in admin API - #582

Draft
seonghobae wants to merge 24 commits into
mainfrom
fix-admin-auth-13895687966802332716
Draft

seonghobae wants to merge 24 commits into
mainfrom
fix-admin-auth-13895687966802332716

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 12, 2026 •

Copy link
Copy Markdown
Collaborator

요약

  • 🚨 Severity: CRITICAL
  • 취약점: AdminController가 원래 인증·권한 검사를 하지 않았고, 단순 ADMIN_READ/ADMIN_WRITE 추가만으로는 검증된 TenantContext를 버려 cross-tenant listing/delete/retry가 가능했습니다.
  • 영향: 다른 tenant의 conversion job 메타데이터 노출 및 UUID를 아는 경우 삭제·retry state transition 시도가 가능했습니다.
  • 수정: admin read/write permission을 도입하고, TenantAccessService.require(...)가 반환한 tenant context를 aggregate ownership 판단까지 유지합니다. 목록은 ConversionJob.belongsToTenant(...)로 tenant 범위를 제한하고, 삭제는 기존 tenant-aware deleteJob(jobId, tenantContext)를 사용하며, retry는 getJob → requireSameTenant → retryDeadLettered 순서로 수행합니다. foreign/missing aggregate는 404로 숨깁니다.
  • 정리: task 수행 중 생긴 one-shot source patch/scratch artifact(patch_tenant_permissions.sh, search.txt, replace.txt)는 제거했습니다.

검증 계약

  • 권한 없는 admin read/write 요청은 403이며 application service를 호출하지 않습니다.
  • admin list는 같은 tenant의 job만 반환합니다.
  • foreign tenant delete/retry는 404이고 state-changing service를 실행하지 않습니다.
  • owned retry가 ownership check 뒤 사라지는 race에서도 404를 보존합니다.
  • authoritative gate는 repository AGENTS.md에 따라 mvn -B --no-transfer-progress verify, zero skipped tests, JaCoCo 100% line/branch, public Javadoc, security/fuzz/SAST/CodeQL/current-head review입니다.

현재 PR은 Draft입니다. exact-head hosted acceptance가 모두 GREEN이 되기 전에는 완료로 보지 않습니다.


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

Summary by CodeRabbit

  • 보안 개선

    • 관리자 작업 조회, 삭제, 재시도 기능에 인증 및 권한 검사를 적용했습니다.
    • 관리자 작업은 인증된 테넌트의 리소스로 제한됩니다.
    • 권한이 없거나 다른 테넌트의 작업에 접근하면 요청이 거부됩니다.
    • 존재하지 않는 작업에 대한 삭제·재시도 요청은 404 Not Found로 처리됩니다.
  • 테스트

    • 권한 부족, 테넌트 간 접근 차단, 작업 삭제와 재시도 중 경쟁 상황을 검증하는 테스트를 추가했습니다.

🚨 Severity: CRITICAL
💡 Vulnerability: Admin API endpoints in `AdminController` did not require any authentication or authorization checks.
🎯 Impact: Any user could access administrative resources, leading to potential data exposure or loss (e.g., deleting conversion jobs, viewing all tenant jobs).
🔧 Fix: Added `ADMIN_READ` and `ADMIN_WRITE` permissions to `TenantPermissions.java`. Injected `TenantAccessService` into `AdminController` and added `tenantAccessService.require(headers, ...)` to all endpoints to enforce proper authorization.
✅ Verification: `mvn clean test` and `mvn checkstyle:check` executed. Verified via test cases `AdminControllerTest.java` returning `403 FORBIDDEN` on unauthorized access.
@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 Sep 12, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: c029a5b3-c065-468b-be44-842cb7dcf037

📥 Commits

Reviewing files that changed from the base of the PR and between 06633a2 and 16fb346.

📒 Files selected for processing (5)
  • .jules/sentinel.md
  • src/main/java/com/clearfolio/viewer/auth/TenantPermissions.java
  • src/main/java/com/clearfolio/viewer/controller/AdminController.java
  • src/test/java/com/clearfolio/viewer/controller/AdminControllerRetryRaceTest.java
  • src/test/java/com/clearfolio/viewer/controller/AdminControllerTest.java

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

관리자 엔드포인트에 ADMIN_READ·ADMIN_WRITE 권한 검증과 테넌트 소유권 검사가 추가되었습니다. 작업 목록은 현재 테넌트로 제한됩니다. 삭제·재시도 응답과 관련 회귀 테스트가 갱신되었습니다.

Changes

관리자 테넌트 접근 제어

Layer / File(s) Summary
관리자 권한 계약과 접근 서비스 연결
src/main/java/com/clearfolio/viewer/auth/TenantPermissions.java, src/main/java/com/clearfolio/viewer/controller/AdminController.java
ADMIN_READ와 ADMIN_WRITE 권한이 추가되었습니다. AdminController가 TenantAccessService와 요청 헤더를 사용합니다.
관리자 엔드포인트 권한과 소유권 검사
src/main/java/com/clearfolio/viewer/controller/AdminController.java, .jules/sentinel.md
작업 목록은 현재 테넌트 작업만 반환합니다. 삭제와 재시도는 관리자 권한과 동일 테넌트 여부를 확인합니다. 삭제 실패와 재시도 중 작업 소실은 404로 처리됩니다. 보안 저널에 인증 및 테넌트 격리 항목이 기록되었습니다.
권한·소유권·경쟁 조건 회귀 검증
src/test/java/com/clearfolio/viewer/controller/AdminControllerTest.java, src/test/java/com/clearfolio/viewer/controller/AdminControllerRetryRaceTest.java
테스트가 권한 부족, 외부 테넌트 작업 차단, 작업 상태별 응답, 재시도 경쟁 조건을 검증합니다.

Priority: ⬆️ High

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

Change: Bug fix · Severity of issue fixed: High

Sequence Diagram(s)

sequenceDiagram
  participant 관리자
  participant AdminController
  participant TenantAccessService
  participant DocumentConversionService
  관리자->>AdminController: 관리자 요청과 HttpHeaders 전송
  AdminController->>TenantAccessService: 권한 및 테넌트 컨텍스트 검증
  TenantAccessService-->>AdminController: TenantContext 반환
  AdminController->>DocumentConversionService: 테넌트 작업 조회 또는 변경
  DocumentConversionService-->>AdminController: 작업 결과 반환
  AdminController-->>관리자: HTTP 응답 반환
Loading

Possibly related PRs

  • ContextualWisdomLab/clearfolio#460: AdminController의 관리자 인증·권한 검사를 다루며, 본 변경은 이를 ADMIN_READ·ADMIN_WRITE와 테넌트 소유권 검사로 확장합니다.

Merge Risk: ⚪ Minimal · up to 16fb3

Admin job listing, deletion, and retry now enforce tenant permissions and ownership, including the retry race response contract. No actionable merge risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 20 functions across 4 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 관리자 API의 인증 우회 문제를 수정하는 주요 변경 사항을 정확히 설명합니다. 짧고 구체적이며 변경 내용과 직접 관련됩니다.
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 20 functions across 4 files. (1 skipped: 1 unsupported.)

✨ 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-auth-13895687966802332716

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.

seonghobae and others added 5 commits September 12, 2026 21:07
🚨 Severity: CRITICAL
💡 Vulnerability: Admin API endpoints in `AdminController` did not require any authentication or authorization checks.
🎯 Impact: Any user could access administrative resources, leading to potential data exposure or loss (e.g., deleting conversion jobs, viewing all tenant jobs).
🔧 Fix: Added `ADMIN_READ` and `ADMIN_WRITE` permissions to `TenantPermissions.java`. Injected `TenantAccessService` into `AdminController` and added `tenantAccessService.require(headers, ...)` to all endpoints to enforce proper authorization.
✅ Verification: `mvn clean test` and `mvn checkstyle:check` executed. Verified via test cases `AdminControllerTest.java` returning `403 FORBIDDEN` on unauthorized access.
@seonghobae
seonghobae marked this pull request as draft September 12, 2026 21:09

Copy link
Copy Markdown
Collaborator Author

현재 exact head 1133ce6892f1d1d55231170b0ab340d80a7b739b는 권한 존재 여부만이 아니라 tenant isolation까지 검증하도록 test-first로 강화했습니다. 또한 one-shot source patch 산출물 patch_tenant_permissions.sh, search.txt, replace.txt는 production delta와 무관한 purpose-complete source-fix artifact라 ordinary-forward로 제거했습니다.

현재 production AdminController는 TenantAccessService.require(...)가 반환한 TenantContext를 버리고 있습니다. 이 때문에 admin:read를 가진 tenant A가 conversionService.getAllJobs()의 tenant B job까지 볼 수 있고, admin:write를 가진 tenant A가 UUID만 알면 기존 deleteJob(jobId) / retryDeadLettered(jobId, "admin")로 다른 tenant의 job을 조작할 수 있습니다. 이 저장소의 기존 계약은 ConversionController와 TenantAccessService.requireSameTenant(...), DocumentConversionService.deleteJob(jobId, tenantContext)가 이미 tenant ownership을 강제하고 있으므로 admin route도 같은 bounded-context invariant를 지켜야 합니다.

새 RED는 다음을 고정합니다.

  • admin list는 권한이 있어도 request tenant의 job만 반환합니다.
  • admin delete는 tenant-aware deleteJob(jobId, tenantContext)를 사용하고 foreign/missing job을 404로 숨깁니다.
  • admin retry는 먼저 getJob + requireSameTenant를 거친 뒤에만 state transition을 호출합니다.
  • ADMIN_WRITE가 없으면 delete/retry application service를 호출하지 않습니다.

@jules 이 exact RED를 기준으로 최소 causal fix를 ordinary-forward로 적용해 주세요. require() 반환 TenantContext를 각 endpoint에서 보존하고, list는 belongsToTenant, delete는 이미 존재하는 tenant-aware service overload, retry는 getJob→requireSameTenant→retry 순서로 맞추십시오. 별도의 global-admin 개념을 새로 만들거나 permission만 확인한 뒤 cross-tenant service를 호출하는 방식은 acceptance가 아닙니다. current-head Maven tests/checkstyle/security gates가 GREEN이 된 뒤에만 Ready로 올리십시오.

@google-labs-jules

Copy link
Copy Markdown

현재 exact head 1133ce6892f1d1d55231170b0ab340d80a7b739b는 권한 존재 여부만이 아니라 tenant isolation까지 검증하도록 test-first로 강화했습니다. 또한 one-shot source patch 산출물 patch_tenant_permissions.sh, search.txt, replace.txt는 production delta와 무관한 purpose-complete source-fix artifact라 ordinary-forward로 제거했습니다.

현재 production AdminController는 TenantAccessService.require(...)가 반환한 TenantContext를 버리고 있습니다. 이 때문에 admin:read를 가진 tenant A가 conversionService.getAllJobs()의 tenant B job까지 볼 수 있고, admin:write를 가진 tenant A가 UUID만 알면 기존 deleteJob(jobId) / retryDeadLettered(jobId, "admin")로 다른 tenant의 job을 조작할 수 있습니다. 이 저장소의 기존 계약은 ConversionController와 TenantAccessService.requireSameTenant(...), DocumentConversionService.deleteJob(jobId, tenantContext)가 이미 tenant ownership을 강제하고 있으므로 admin route도 같은 bounded-context invariant를 지켜야 합니다.

새 RED는 다음을 고정합니다.

  • admin list는 권한이 있어도 request tenant의 job만 반환합니다.
  • admin delete는 tenant-aware deleteJob(jobId, tenantContext)를 사용하고 foreign/missing job을 404로 숨깁니다.
  • admin retry는 먼저 getJob + requireSameTenant를 거친 뒤에만 state transition을 호출합니다.
  • ADMIN_WRITE가 없으면 delete/retry application service를 호출하지 않습니다.

@jules 이 exact RED를 기준으로 최소 causal fix를 ordinary-forward로 적용해 주세요. require() 반환 TenantContext를 각 endpoint에서 보존하고, list는 belongsToTenant, delete는 이미 존재하는 tenant-aware service overload, retry는 getJob→requireSameTenant→retry 순서로 맞추십시오. 별도의 global-admin 개념을 새로 만들거나 permission만 확인한 뒤 cross-tenant service를 호출하는 방식은 acceptance가 아닙니다. current-head Maven tests/checkstyle/security gates가 GREEN이 된 뒤에만 Ready로 올리십시오.

요청하신 대로 권한 확인과 동시에 Tenant Isolation을 강제하도록 모든 Admin 엔드포인트 수정 및 테스트를 강화하여 반영했습니다.

  • getAllJobs에 job.belongsToTenant() 필터 추가 완료
  • deleteJob이 tenantContext를 받아서 소유권을 확인하도록 적용 완료
  • retryDeadLettered에서 getJob() 후 requireSameTenant 검증 추가 완료
  • AdminControllerTest를 업데이트하여 필터링 로직 및 404/202 흐름이 의도대로 동작하는지 확인하였고, 100% 테스트 커버리지를 복원했습니다.
  • mvn checkstyle:check 및 mvn clean test 성공을 확인했습니다.

추가적인 수정사항이 필요하면 알려주세요.

seonghobae and others added 10 commits September 12, 2026 21:18
🚨 Severity: CRITICAL
💡 Vulnerability: Admin API endpoints retrieved and managed conversion jobs globally without isolating them to the requested tenant's context, leading to unauthorized cross-tenant data access and operations.
🎯 Impact: An authenticated admin could read metadata of or manipulate conversion jobs that belonged to other isolated tenants within the system, violating multi-tenancy guarantees.
🔧 Fix: Updated `AdminController` to restrict list operations via `belongsToTenant()` and enforce strict ownership on state changes via `tenantAccessService.requireSameTenant()` and `conversionService.deleteJob(jobId, tenantContext)`.
✅ Verification: Expanded test suite in `AdminControllerTest` to explicitly cover `belongsToTenant()` filters and 404 responses for out-of-scope actions. Successfully verified `mvn clean test`, `mvn checkstyle:check`, and 100% JaCoCo coverage.
🚨 Severity: CRITICAL
💡 Vulnerability: Admin API endpoints retrieved and managed conversion jobs globally without isolating them to the requested tenant's context, leading to unauthorized cross-tenant data access and operations.
🎯 Impact: An authenticated admin could read metadata of or manipulate conversion jobs that belonged to other isolated tenants within the system, violating multi-tenancy guarantees.
🔧 Fix: Updated `AdminController` to restrict list operations via `belongsToTenant()` and enforce strict ownership on state changes via `tenantAccessService.requireSameTenant()` and `conversionService.deleteJob(jobId, tenantContext)`.
✅ Verification: Expanded test suite in `AdminControllerTest` to explicitly cover `belongsToTenant()` filters and 404 responses for out-of-scope actions. Successfully verified `mvn clean test`, `mvn checkstyle:check`, and 100% JaCoCo coverage.
🚨 Severity: CRITICAL
💡 Vulnerability: Admin API endpoints retrieved and managed conversion jobs globally without isolating them to the requested tenant's context, leading to unauthorized cross-tenant data access and operations.
🎯 Impact: An authenticated admin could read metadata of or manipulate conversion jobs that belonged to other isolated tenants within the system, violating multi-tenancy guarantees.
🔧 Fix: Updated `AdminController` to restrict list operations via `belongsToTenant()` and enforce strict ownership on state changes via `tenantAccessService.requireSameTenant()` and `conversionService.deleteJob(jobId, tenantContext)`.
✅ Verification: Expanded test suite in `AdminControllerTest` to explicitly cover `belongsToTenant()` filters and 404 responses for out-of-scope actions. Successfully verified `mvn clean test`, `mvn checkstyle:check`, and 100% JaCoCo coverage.

Copy link
Copy Markdown
Collaborator Author

Current-head RED→GREEN evidence for the retry disappearance boundary:

  • Hosted RED at test-only c54e10424452fef4cc735ac84b37a6814e2dfe57: CI run 34724420986, Maven merge-compatibility job 103635931063 failed because retryDeadLettered() returned HTTP 202 after the owned job disappeared between ownership check and the service retry call; expected HTTP 404. Generation total: 519 tests, 1 failure.
  • Minimal causal fix: exact head 16fb34683f333fac25828ba14712324d99fcd3d6 maps the service's existing RetryDeadLetterResult.NOT_FOUND to the controller's existing 404 contract, without weakening ADMIN_WRITE or same-tenant checks.
  • Current-head CI run 34724577667 is GREEN: Maven test, Maven merge compatibility, and buyer-readiness script tests all succeeded. Maven test job 103636348824 ran 519 tests with 0 failures, 0 errors, 0 skipped; JaCoCo coverage gates and strict Javadoc also passed. Current-head Security Scan 34724577647 and fuzz 34724577644 are GREEN.

SAST is still running, CodeQL is still queued, and there is no qualifying current-head independent review yet. Keep this PR Draft until those exact-head gates are satisfied; do not reuse verdicts from older generations.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== HEAD ==='
git rev-parse HEAD
git status --short
git diff --stat origin/main...HEAD || true

echo '=== Candidate files ==='
fd -a 'AdminController.java|DocumentConversionService.java|TenantAccessService.java|ConversionJob.java' . || true

echo '=== Java outlines ==='
fd -a -e java . | xargs -r ast-grep outline --items all --type class,method --match 'AdminController|retryDeadLettered|deleteJob|requireSameTenant|belongsToTenant|getAllJobs' 2>/dev/null || true

echo '=== Relevant definitions and call sites ==='
rg -n -C 5 --type java \
  'class\s+AdminController\b|TenantAccessService\.require\s*\(|requireSameTenant\s*\(|getAllJobs\s*\(|deleteJob\s*\(|retryDeadLettered\s*\(|belongsToTenant\s*\(|RetryDeadLetterResult' \
  . -g '!**/target/**' || true

echo '=== Changed Java and test files ==='
git diff --name-only origin/main...HEAD | sed -n '1,200p'
git diff --check origin/main...HEAD

Length of output: 149879


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Repository instructions ==='
if [ -f AGENTS.md ]; then
  cat -n AGENTS.md
fi

echo '=== Complete PR diff ==='
git diff --find-renames --find-copies origin/main...HEAD -- \
  src/main/java/com/clearfolio/viewer/auth/TenantPermissions.java \
  src/main/java/com/clearfolio/viewer/controller/AdminController.java \
  src/test/java/com/clearfolio/viewer/controller/AdminControllerTest.java \
  src/test/java/com/clearfolio/viewer/controller/AdminControllerRetryRaceTest.java \
  .jules/sentinel.md

echo '=== AdminController with line numbers ==='
cat -n src/main/java/com/clearfolio/viewer/controller/AdminController.java

echo '=== Admin controller tests with line numbers ==='
cat -n src/test/java/com/clearfolio/viewer/controller/AdminControllerTest.java
cat -n src/test/java/com/clearfolio/viewer/controller/AdminControllerRetryRaceTest.java

echo '=== Permissions and authorization parsing ==='
cat -n src/main/java/com/clearfolio/viewer/auth/TenantPermissions.java
sed -n '1,160p' src/main/java/com/clearfolio/viewer/auth/TenantAccessService.java | cat -n

echo '=== Unrelated artifacts or patch files at HEAD ==='
fd -a 'patch_tenant_permissions.sh|search.txt|replace.txt' . || true

Length of output: 69495


@seonghobae Review completed for exact head 16fb34683f333fac25828ba14712324d99fcd3d6.

I found no code findings in the reviewed change. AdminController now preserves the verified TenantContext for all admin routes. The list route filters by tenant ownership. The delete route uses the tenant-aware delete operation. The retry route checks ownership before the state transition and maps RetryDeadLetterResult.NOT_FOUND to HTTP 404.

The supplied Maven, Security Scan, and fuzz evidence is consistent with the repository gate requirements. SAST and CodeQL remain pending. Keep PR #582 as Draft until those exact-head checks and the required independent review pass.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

🚨 Severity: CRITICAL
💡 Vulnerability: Admin API endpoints retrieved and managed conversion jobs globally without isolating them to the requested tenant's context, leading to unauthorized cross-tenant data access and operations.
🎯 Impact: An authenticated admin could read metadata of or manipulate conversion jobs that belonged to other isolated tenants within the system, violating multi-tenancy guarantees.
🔧 Fix: Updated `AdminController` to restrict list operations via `belongsToTenant()` and enforce strict ownership on state changes via `tenantAccessService.requireSameTenant()` and `conversionService.deleteJob(jobId, tenantContext)`.
✅ Verification: Expanded test suite in `AdminControllerTest` to explicitly cover `belongsToTenant()` filters and 404 responses for out-of-scope actions. Successfully verified `mvn clean test`, `mvn checkstyle:check`, and 100% JaCoCo coverage.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Please review exact head 78b5efa0fec57994eee2cee4b98ac2b6605bc534. Focus on authorization-before-service-call, tenant-context preservation through aggregate ownership checks, foreign/missing resource non-disclosure, and the owned-retry disappearance race returning 404. CI/Security/fuzz/SAST are GREEN; prior or central CodeQL failures are not a substitute for current-head source review.

@coderabbitai

coderabbitai Bot commented Sep 13, 2026 •

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected_head='78b5efa0fec57994eee2cee4b98ac2b6605bc534'

echo '=== Exact head ==='
actual_head="$(git rev-parse HEAD)"
printf 'expected=%s\nactual=%s\n' "$expected_head" "$actual_head"
test "$actual_head" = "$expected_head"

echo '=== Worktree and PR delta ==='
git status --short
git diff --check origin/main...HEAD
git diff --name-status origin/main...HEAD

echo '=== Repository instructions ==='
fd -a -I '^AGENTS\.md$|^CONTRIBUTING\.md$' . -x sh -c 'echo "--- $1"; cat -n "$1"' sh {} \; 2>/dev/null || true

echo '=== Relevant source and test map ==='
fd -a -e java . | xargs -r ast-grep outline --items all --type class,method \
  --match 'AdminController|TenantAccessService|DocumentConversionService|ConversionJob|AdminControllerTest|AdminControllerRetryRaceTest' \
  2>/dev/null || true

echo '=== Changed authorization-related diff ==='
git diff --find-renames origin/main...HEAD -- \
  src/main/java/com/clearfolio/viewer/auth/TenantPermissions.java \
  src/main/java/com/clearfolio/viewer/auth/TenantAccessService.java \
  src/main/java/com/clearfolio/viewer/controller/AdminController.java \
  src/main/java/com/clearfolio/viewer/service/DocumentConversionService.java \
  src/main/java/com/clearfolio/viewer/model/ConversionJob.java \
  src/test/java/com/clearfolio/viewer/controller/AdminControllerTest.java \
  src/test/java/com/clearfolio/viewer/controller/AdminControllerRetryRaceTest.java

echo '=== Admin route implementations ==='
admin_file="$(fd -a -e java '^AdminController\.java$' src | head -n1)"
test -n "$admin_file"
cat -n "$admin_file"

echo '=== Authorization and service contracts ==='
rg -n -C 8 --type java \
  'TenantContext\s+require\s*\(|void\s+requireSameTenant\s*\(|boolean\s+deleteJob\s*\(|RetryDeadLetterResult\s+retryDeadLettered\s*\(|Optional<ConversionJob>\s+getJob\s*\(|boolean\s+belongsToTenant\s*\(' \
  src/main/java -g '!**/target/**'

echo '=== Admin route test assertions and interaction order ==='
rg -n -C 6 --type java \
  'AdminController|ADMIN_READ|ADMIN_WRITE|belongsToTenant|requireSameTenant|deleteJob|retryDeadLettered|NOT_FOUND|notFound|status\(\)\.isNotFound|never\(\)|InOrder' \
  src/test/java/com/clearfolio/viewer/controller -g '!**/target/**'

echo '=== Current PR metadata and checks ==='
gh pr view 582 --json number,headRefOid,isDraft,reviewDecision,statusCheckRollup \
  --jq '{number,headRefOid,isDraft,reviewDecision,checks:[.statusCheckRollup[]|{name,conclusion,status}]}'

Length of output: 133814


@seonghobae Review completed for exact head 78b5efa0fec57994eee2cee4b98ac2b6605bc534.

I found one issue.

  • AdminController.retryDeadLettered(...) does not map RetryDeadLetterResult.NOT_FOUND to HTTP 404. After getJob(...) and requireSameTenant(...) succeed, the job can disappear before conversionService.retryDeadLettered(...). In that case, the service returns NOT_FOUND, but Lines 120-124 handle only NOT_ELIGIBLE and return HTTP 202. Add the existing NOT_FOUND mapping before the conflict check. This preserves the non-disclosure and disappearance-race contract.

The other reviewed paths preserve the verified TenantContext. They check authorization before the relevant application-service calls. Listing filters with belongsToTenant(...). Deletion uses the tenant-aware delete overload and returns 404 when it returns false.

⚠️ Action not completed

Head commit changed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@seonghobae seonghobae added bug Something isn't working priority: critical Immediate blocker, P0, urgent deadlock, or critical incident labels Sep 19, 2026 — with ChatGPT Codex Connector
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working priority: critical Immediate blocker, P0, urgent deadlock, or critical incident

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant