fix(core,board,ecommerce,page,kginicis): 정책값 하드코딩·계약 분리 결함 일괄 수정
관리자 환경설정이 값을 저장은 하지만 실제 동작에는 닿지 않던 결함군을 정리했다. 설정 화면(min/max)·저장 검증(FormRequest)·런타임 적용(Service) 세 계층이 각자 리터럴을 들고 있어 서로 조용히 갈라지던 것이 공통 원인이다. 경계값 SSoT 도입: - config/core.php `settings_limits` 를 단일 출처로 두고, FormRequest 는 이 값으로 규칙을 만들고 화면은 설정 응답의 `_meta.limits` 로 같은 값을 받아 바인딩한다. - 게시판·이커머스도 각 모듈 config 의 limits 를 동일 구조로 노출한다. 적용되지 않던 정책을 실제로 적용: - 비밀번호 최소 길이·특수문자 필수(신규 지정 7개 경로. 로그인은 제외 — 정책을 올렸을 때 기존 회원이 자기 계정에 접근조차 못 하는 데드락을 막기 위함) - 계정 잠금 시간 0 = 무한대(전용 컬럼 분리. timestamp 상한과 NULL 규약 때문에 기존 컬럼 재사용이 불가능하다) + 관리자 수동 해제 경로 - 업로드 최대 크기·허용 확장자, 게시판 첨부 개수·댓글 깊이, 페이지 첨부 정책, 장바구니 수량·구매 한도, 마일리지 사용 한도, 주문 부가세 파생 결함: - 큐 Job(sync 드라이버)이 진행 중인 요청의 Request 바인딩을 덮어써, 뒤이어 실행되는 훅 리스너가 헤더를 잃고 조용히 건너뛰었다. Job 이 원 바인딩을 복원하도록 고치고, 전송 헤더가 필요한 리스너는 동기 실행으로 선언한다. - 확장 업데이트 실패 시 상태만 되돌리고 상태 캐시를 비우지 않아, 그 확장의 관리자 화면만 캐시 TTL(기본 하루) 동안 404 로 남았다. 실패 경로에도 무효화를 대칭으로 두고, 빈 목록 자체를 캐시하지 않는다. - 글 작성 시 첨부 파일이 Service 로 전달되지 않아 검증·권한은 통과하고 첨부만 사라졌다. - 환경설정 저장 실패 안내가 내부 설정 키를 그대로 노출했다(105개 항목에 라벨 부여). 재발 방지: - audit 룰 3종(레이아웃 경계 리터럴 / FormRequest 경계 불일치 / Service 재클램프) - 테스트 실행 범위 강제 훅(check-test-scope) - 트러블슈팅 사례 2건(큐 Job 요청 오염 / 확장 상태 캐시 고착) 공개 이슈: gnuboard/g7
This commit is contained in:
@@ -38,6 +38,7 @@ return [
|
||||
'logout' => 'Logout',
|
||||
'login_failed' => 'Login failed',
|
||||
'account_locked' => 'Account locked',
|
||||
'account_unlocked' => 'Account unlocked',
|
||||
'request' => 'Request',
|
||||
'expired' => 'Expired',
|
||||
'verify' => 'Identity verified',
|
||||
@@ -148,6 +149,8 @@ return [
|
||||
'auth_record_consents' => 'Consent recorded',
|
||||
'auth_login_failed' => 'Login failed (:email)',
|
||||
'auth_account_locked' => 'Account locked (:attempts attempts, :minutes min lockout)',
|
||||
'auth_account_locked_permanently' => 'Account locked permanently (:attempts attempts, administrator unlock required)',
|
||||
'auth_account_unlocked' => 'Account unlocked (:email)',
|
||||
|
||||
// Schedule management
|
||||
'schedule_index' => 'Schedule list viewed',
|
||||
|
||||
@@ -27,6 +27,7 @@ return [
|
||||
'file_required' => 'Please select a file.',
|
||||
'file_invalid' => 'The file is not valid.',
|
||||
'file_max' => 'The file size cannot exceed :max MB.',
|
||||
'file_extension_invalid' => 'This file type is not allowed. Allowed types: :extensions',
|
||||
'files_required' => 'Please select files.',
|
||||
'files_array' => 'The files format is invalid.',
|
||||
'files_min' => 'Please select at least one file.',
|
||||
|
||||
@@ -33,6 +33,8 @@ return [
|
||||
'account_withdrawn' => 'This account has been withdrawn.',
|
||||
'account_pending_verification' => 'Identity verification is not complete. Please complete the email verification.',
|
||||
'account_locked' => 'Too many failed login attempts. Your account is locked for :minutes minute(s).',
|
||||
'account_locked_permanently' => 'Too many failed login attempts. Your account has been locked. Please contact an administrator.',
|
||||
'account_unlocked' => 'The account lock has been released.',
|
||||
|
||||
// Password reset
|
||||
'reset_token_invalid' => 'Invalid password reset token.',
|
||||
|
||||
@@ -1200,5 +1200,70 @@ return [
|
||||
'ses_key' => 'SES access key',
|
||||
'ses_secret' => 'SES secret key',
|
||||
'ses_region' => 'SES region',
|
||||
'mailgun_endpoint' => 'Mailgun endpoint',
|
||||
// General settings (additional)
|
||||
'channels' => 'notification channels',
|
||||
'currency' => 'default currency',
|
||||
'maintenance_mode' => 'maintenance mode',
|
||||
'asset_url_mode' => 'asset URL mode',
|
||||
'site_logo' => 'site logo',
|
||||
// SEO settings (additional)
|
||||
'bot_user_agents' => 'bot user agents',
|
||||
'bot_detection_enabled' => 'bot detection',
|
||||
'bot_detection_library_enabled' => 'bot detection library',
|
||||
'og_default_site_name' => 'OG default site name',
|
||||
'og_image_default_width' => 'OG image default width',
|
||||
'og_image_default_height' => 'OG image default height',
|
||||
'twitter_default_card' => 'Twitter default card type',
|
||||
'twitter_default_site' => 'Twitter default account',
|
||||
'seo_page_cache_enabled' => 'SEO page cache',
|
||||
'cache_ttl' => 'SEO page cache lifetime',
|
||||
'sitemap_enabled' => 'sitemap',
|
||||
'sitemap_cache_ttl' => 'sitemap cache lifetime',
|
||||
'sitemap_urls_per_file' => 'sitemap URLs per file',
|
||||
'sitemap_gzip' => 'sitemap compression',
|
||||
'sitemap_serve_stale_on_miss' => 'serve stale sitemap on miss',
|
||||
'sitemap_max_urls_per_contributor' => 'sitemap max URLs per contributor',
|
||||
'sitemap_hreflang_enabled' => 'sitemap hreflang links',
|
||||
'sitemap_schedule' => 'sitemap schedule',
|
||||
'sitemap_schedule_time' => 'sitemap schedule time',
|
||||
'generator_enabled' => 'SEO page generator',
|
||||
'generator_content' => 'SEO page generator content',
|
||||
// Security settings (additional)
|
||||
'force_https' => 'force HTTPS',
|
||||
'login_attempt_enabled' => 'login attempt limit',
|
||||
'auth_token_lifetime' => 'auth token lifetime',
|
||||
'max_login_attempts' => 'max login attempts',
|
||||
'login_lockout_time' => 'login lockout time',
|
||||
'password_min_length' => 'minimum password length',
|
||||
'require_password_special_char' => 'require special character in password',
|
||||
'allow_internal_outbound_urls' => 'allow internal outbound URLs',
|
||||
// Advanced settings (additional)
|
||||
'advanced_cache_enabled' => 'cache',
|
||||
'layout_cache_enabled' => 'layout cache',
|
||||
'layout_cache_ttl' => 'layout cache lifetime',
|
||||
'stats_cache_enabled' => 'statistics cache',
|
||||
'stats_cache_ttl' => 'statistics cache lifetime',
|
||||
'seo_cache_enabled' => 'SEO cache',
|
||||
'seo_cache_ttl' => 'SEO cache lifetime',
|
||||
'seo_sitemap_cache_ttl' => 'SEO sitemap cache lifetime',
|
||||
'debug_mode' => 'debug mode',
|
||||
'sql_query_log' => 'SQL query log',
|
||||
'core_update_github_url' => 'core update GitHub URL',
|
||||
'core_update_github_token' => 'core update GitHub token',
|
||||
'geoip_enabled' => 'GeoIP',
|
||||
'geoip_license_key' => 'GeoIP license key',
|
||||
'geoip_auto_update_enabled' => 'GeoIP auto update',
|
||||
// Driver settings (additional)
|
||||
'websocket_app_id' => 'WebSocket app ID',
|
||||
'websocket_app_secret' => 'WebSocket app secret',
|
||||
'websocket_verify_ssl' => 'WebSocket SSL verification',
|
||||
'websocket_server_host' => 'WebSocket server host',
|
||||
'websocket_server_port' => 'WebSocket server port',
|
||||
'websocket_server_scheme' => 'WebSocket server scheme',
|
||||
'search_engine_driver' => 'search engine driver',
|
||||
'log_driver' => 'log driver',
|
||||
'log_level' => 'log level',
|
||||
'log_days' => 'log retention days',
|
||||
],
|
||||
];
|
||||
|
||||
@@ -38,6 +38,7 @@ return [
|
||||
'logout' => '로그아웃',
|
||||
'login_failed' => '로그인 실패',
|
||||
'account_locked' => '계정 잠금',
|
||||
'account_unlocked' => '계정 잠금 해제',
|
||||
'request' => '요청',
|
||||
'expired' => '만료',
|
||||
'verify' => '본인인증',
|
||||
@@ -148,6 +149,8 @@ return [
|
||||
'auth_record_consents' => '이용약관 동의 기록',
|
||||
'auth_login_failed' => '로그인 실패 (:email)',
|
||||
'auth_account_locked' => '계정 잠금 (:attempts회 실패, :minutes분 차단)',
|
||||
'auth_account_locked_permanently' => '계정 영구 잠금 (:attempts회 실패, 관리자 해제 필요)',
|
||||
'auth_account_unlocked' => '계정 잠금 해제 (:email)',
|
||||
|
||||
// 스케줄 관리
|
||||
'schedule_index' => '스케줄 목록 조회',
|
||||
|
||||
@@ -27,6 +27,7 @@ return [
|
||||
'file_required' => '파일을 선택해주세요.',
|
||||
'file_invalid' => '유효한 파일이 아닙니다.',
|
||||
'file_max' => '파일 크기는 :maxMB를 초과할 수 없습니다.',
|
||||
'file_extension_invalid' => '허용되지 않는 파일 형식입니다. 허용 형식: :extensions',
|
||||
'files_required' => '파일을 선택해주세요.',
|
||||
'files_array' => '파일 형식이 올바르지 않습니다.',
|
||||
'files_min' => '최소 1개 이상의 파일을 선택해주세요.',
|
||||
|
||||
@@ -33,6 +33,8 @@ return [
|
||||
'account_withdrawn' => '탈퇴한 계정입니다.',
|
||||
'account_pending_verification' => '본인인증이 완료되지 않은 계정입니다. 이메일 인증을 완료해주세요.',
|
||||
'account_locked' => '로그인 시도 횟수 초과로 계정이 잠겼습니다. :minutes분 후 다시 시도해주세요.',
|
||||
'account_locked_permanently' => '로그인 시도 횟수 초과로 계정이 잠겼습니다. 관리자에게 문의해주세요.',
|
||||
'account_unlocked' => '계정 잠금이 해제되었습니다.',
|
||||
|
||||
// 비밀번호 재설정
|
||||
'reset_token_invalid' => '유효하지 않은 비밀번호 재설정 토큰입니다.',
|
||||
|
||||
@@ -1193,5 +1193,70 @@ return [
|
||||
'ses_key' => 'SES 액세스 키',
|
||||
'ses_secret' => 'SES 시크릿 키',
|
||||
'ses_region' => 'SES 리전',
|
||||
'mailgun_endpoint' => 'Mailgun 엔드포인트',
|
||||
// 일반 설정 (추가)
|
||||
'channels' => '알림 채널',
|
||||
'currency' => '기본 통화',
|
||||
'maintenance_mode' => '점검 모드',
|
||||
'asset_url_mode' => '자산 주소 방식',
|
||||
'site_logo' => '사이트 로고',
|
||||
// SEO 설정 (추가)
|
||||
'bot_user_agents' => '봇 User-Agent 목록',
|
||||
'bot_detection_enabled' => '봇 감지 사용',
|
||||
'bot_detection_library_enabled' => '봇 감지 라이브러리 사용',
|
||||
'og_default_site_name' => 'OG 기본 사이트 이름',
|
||||
'og_image_default_width' => 'OG 이미지 기본 너비',
|
||||
'og_image_default_height' => 'OG 이미지 기본 높이',
|
||||
'twitter_default_card' => '트위터 기본 카드 유형',
|
||||
'twitter_default_site' => '트위터 기본 계정',
|
||||
'seo_page_cache_enabled' => 'SEO 페이지 캐시 사용',
|
||||
'cache_ttl' => 'SEO 페이지 캐시 유지시간',
|
||||
'sitemap_enabled' => '사이트맵 사용',
|
||||
'sitemap_cache_ttl' => '사이트맵 캐시 유지시간',
|
||||
'sitemap_urls_per_file' => '사이트맵 파일당 URL 수',
|
||||
'sitemap_gzip' => '사이트맵 압축',
|
||||
'sitemap_serve_stale_on_miss' => '사이트맵 미생성 시 이전 파일 제공',
|
||||
'sitemap_max_urls_per_contributor' => '사이트맵 항목별 최대 URL 수',
|
||||
'sitemap_hreflang_enabled' => '사이트맵 대체 언어 링크 사용',
|
||||
'sitemap_schedule' => '사이트맵 자동 생성 주기',
|
||||
'sitemap_schedule_time' => '사이트맵 자동 생성 시각',
|
||||
'generator_enabled' => 'SEO 페이지 생성기 사용',
|
||||
'generator_content' => 'SEO 페이지 생성기 콘텐츠',
|
||||
// 보안 설정 (추가)
|
||||
'force_https' => 'HTTPS 강제 적용',
|
||||
'login_attempt_enabled' => '로그인 시도 제한',
|
||||
'auth_token_lifetime' => '인증 토큰 유지시간',
|
||||
'max_login_attempts' => '최대 로그인 시도 횟수',
|
||||
'login_lockout_time' => '로그인 차단 시간',
|
||||
'password_min_length' => '비밀번호 최소 길이',
|
||||
'require_password_special_char' => '비밀번호 특수문자 필수',
|
||||
'allow_internal_outbound_urls' => '내부 네트워크 주소 호출 허용',
|
||||
// 고급 설정 (추가)
|
||||
'advanced_cache_enabled' => '캐시 사용',
|
||||
'layout_cache_enabled' => '레이아웃 캐시 사용',
|
||||
'layout_cache_ttl' => '레이아웃 캐시 유지시간',
|
||||
'stats_cache_enabled' => '통계 캐시 사용',
|
||||
'stats_cache_ttl' => '통계 캐시 유지시간',
|
||||
'seo_cache_enabled' => 'SEO 캐시 사용',
|
||||
'seo_cache_ttl' => 'SEO 캐시 유지시간',
|
||||
'seo_sitemap_cache_ttl' => 'SEO 사이트맵 캐시 유지시간',
|
||||
'debug_mode' => '디버그 모드',
|
||||
'sql_query_log' => 'SQL 쿼리 로그',
|
||||
'core_update_github_url' => '코어 업데이트 GitHub 주소',
|
||||
'core_update_github_token' => '코어 업데이트 GitHub 토큰',
|
||||
'geoip_enabled' => 'GeoIP 사용',
|
||||
'geoip_license_key' => 'GeoIP 라이선스 키',
|
||||
'geoip_auto_update_enabled' => 'GeoIP 자동 업데이트',
|
||||
// 드라이버 설정 (추가)
|
||||
'websocket_app_id' => '웹소켓 앱 ID',
|
||||
'websocket_app_secret' => '웹소켓 앱 시크릿',
|
||||
'websocket_verify_ssl' => '웹소켓 SSL 인증서 검증',
|
||||
'websocket_server_host' => '웹소켓 서버 호스트',
|
||||
'websocket_server_port' => '웹소켓 서버 포트',
|
||||
'websocket_server_scheme' => '웹소켓 서버 프로토콜',
|
||||
'search_engine_driver' => '검색 엔진 드라이버',
|
||||
'log_driver' => '로그 드라이버',
|
||||
'log_level' => '로그 레벨',
|
||||
'log_days' => '로그 보관 일수',
|
||||
],
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user