Files
Gnuboard7/tests/Feature/Middleware/SetLocaleExpiredTokenTest.php
T
HeuJung cf1d8ab52c fix(security): 보안 제보 14건 + 유사 결함 전수조사 수정
sir.kr 커뮤니티 제보(Xbuilder 님 외)와 전수조사 발굴분을 코어·게시판·
이커머스·페이지·ckeditor5 전반에 반영.

- 통합 검색 하이라이트: 코어 SearchHighlighter 로 원문 이스케이프 후 강조
 (저장형 XSS 차단, board/ecommerce/page 3중복제 통합)
- 공개 최신글·인기글·관리자 대시보드: 읽기권한 없는 게시판·블라인드·비활성
 게시판 글 필터. 비밀글은 기존 공개 정책대로 제목 노출 유지(본문만 보호)
- 게시판 관리 대시보드 권한 게이트, 이커머스 쿠폰 다운로드·리뷰 대상·주문옵션
 확정 스코프 강화, 취소/환불 항목 검증 회원/비회원 대칭
- 만료 토큰 거부(SetLocale), 회원 탈퇴·정보관리 실패 시 내부 오류 원문 마스킹
- 예외→응답 매핑 정비(generic catch 상태코드), ckeditor 업로드 권한 게이트 정리

정책 정합 정정: 착수 계획이 "비밀글 제목 노출=취약점"으로 전제해 공개 최신글·
대시보드에 넣었던 is_secret 필터는, 저장소가 확정한 "제목 공개, 본문만 보호"
정책(2026-01-02/02-04/06-18/06-19)과 배치되어 되돌림. 읽기권한·블라인드 필터는 유지.
Chrome MCP 실측(T1~T10) + 회귀 테스트로 확인.
2026-08-17 15:46:38 +09:00

90 lines
3.2 KiB
PHP

<?php
namespace Tests\Feature\Middleware;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Route;
use Laravel\Sanctum\PersonalAccessToken;
use Tests\TestCase;
/**
* SetLocale 미들웨어가 만료된 Sanctum 토큰을 인정하지 않는지 검증합니다.
*
* 결함(㉗): resolveUser() 가 토큰의 만료(expires_at) 여부를 검사하지 않아
* 만료된 토큰으로도 그 회원의 로케일이 해석되었다. OptionalSanctumMiddleware 와
* 동일한 만료 검사를 이식하여 만료 토큰은 guest(null) 로 취급한다.
*/
class SetLocaleExpiredTokenTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
// 인증 불필요 공개 라우트 — auth:sanctum 이 만료 토큰을 먼저 차단하지 않도록
// SetLocale(api 그룹) 단독 동작을 관찰한다.
Route::middleware(['api'])->get('/api/test-expired-locale', function () {
return response()->json([
'locale' => App::getLocale(),
]);
});
}
/**
* 만료된 토큰으로 요청하면 그 회원의 언어 설정을 따르지 않고
* guest 로케일(Accept-Language)로 폴백해야 합니다.
*
* @scenario case=expired_token_locale
*
* @effects expired_token_treated_as_guest_for_locale
*/
public function test_expired_token_does_not_resolve_member_locale(): void
{
$user = User::factory()->create(['language' => 'ko']);
$newToken = $user->createToken('test-token');
$plainTextToken = $newToken->plainTextToken;
// 토큰 만료 시각을 과거로 직접 세팅 (Sanctum 기본 expiration 은 null 이라 미설정됨)
PersonalAccessToken::findToken($plainTextToken)->update([
'expires_at' => now()->subDay(),
]);
$response = $this->withHeaders([
'Authorization' => 'Bearer '.$plainTextToken,
'Accept-Language' => 'en-US,en;q=0.9',
])->getJson('/api/test-expired-locale');
$response->assertStatus(200);
// 회원 언어(ko)를 따르지 않고 Accept-Language(en) 로 폴백해야 한다.
$this->assertNotSame('ko', $response->json('locale'));
$this->assertSame('en', $response->json('locale'));
}
/**
* 유효한 토큰은 그 회원의 언어 설정을 따라야 합니다 (기존 동작 유지).
*
* @scenario case=valid_token_locale
*
* @effects valid_token_applies_member_locale
*/
public function test_valid_token_resolves_member_locale(): void
{
$user = User::factory()->create(['language' => 'ko']);
$plainTextToken = $user->createToken('test-token')->plainTextToken;
$response = $this->withHeaders([
'Authorization' => 'Bearer '.$plainTextToken,
'Accept-Language' => 'en-US,en;q=0.9',
])->getJson('/api/test-expired-locale');
$response->assertStatus(200);
// 회원 언어(ko)가 Accept-Language(en)보다 우선한다.
$this->assertSame('ko', $response->json('locale'));
}
}