공개이슈 ··· 및 그 전수 해소분. - 설정 저장·복원·초기화가 상주 프로세스 미러를 갱신하도록 단일 소유자화 - 만료 데이터 자동 정리 예약 12종 신설, 파기는 기본키 배치로 분할 - 회원 탈퇴를 전 경로 원자화하고 관리자 상태변경 경로를 정식 탈퇴로 통일 - 일괄 처리 목록의 선택을 화면에 보이는 행으로 한정(selectionScope) - 비밀번호 정책 문구를 도메인 중립 라벨로 정정 - 정적 검사 2종·검증 스킬 설계 정합성 축 신설
98 lines
5.9 KiB
YAML
98 lines
5.9 KiB
YAML
# audit:allow test-scenario-coverage reason: |
|
|
# cross product 자동 전개는 audit 실행 환경의 fallback YAML 파서가 nested axes 를 읽지 못해
|
|
# 검출되지 않는다(다른 시나리오와 동일 한계). 본 변경은 백엔드 검증 규칙 + 관리자 설정 화면이며
|
|
# PHPUnit Feature 테스트와 Vitest 레이아웃 테스트가 축 조합을 커버하고 green 이다.
|
|
|
|
feature: 비밀번호 정책 설정 강제 (C4)
|
|
|
|
description: |
|
|
관리자 보안 설정의 비밀번호 정책이 비밀번호를 받는 모든 경로에서 동일하게 적용되도록 한다.
|
|
|
|
배경:
|
|
- `security.password_min_length` · `security.password_require_special` 설정이 화면에는
|
|
있었지만 어떤 검증 규칙도 읽지 않는 고아 설정이었다.
|
|
- 비밀번호 길이 제한은 `LoginRequest` 에만 `min:6` 으로 하드코딩되어 있었다. 로그인은
|
|
기존 비밀번호를 대조하는 자리라 정책을 걸 자리가 아니고(정책 강화 시 기존 사용자가
|
|
로그인 불가), 정작 비밀번호를 **정하는** 경로에는 정책이 없었다.
|
|
|
|
적용 지점:
|
|
- 비밀번호를 새로 정하는 6개 경로가 `PasswordPolicy::rule()` 하나를 공유한다
|
|
(회원가입 · 비밀번호 재설정 · 비밀번호 변경 · 관리자 회원 생성 · 관리자 회원 수정 · 프로필 수정).
|
|
- 로그인은 길이 규칙을 갖지 않는다.
|
|
|
|
axes:
|
|
entry_point:
|
|
- register
|
|
- reset_password
|
|
- change_password
|
|
- verify_password
|
|
- admin_create_user
|
|
- admin_update_user
|
|
- update_profile
|
|
min_length_setting: [default, raised, lowered]
|
|
special_char_setting: [required, not_required]
|
|
input_state: [satisfies_policy, violates_length, violates_special]
|
|
# 오류 문구의 :attribute 치환 라벨 축 (공개이슈 #113)
|
|
# 비밀번호 외 범용 키(ids)도 같은 결함군이라 함께 고정한다 —
|
|
# 전역 사전 하나를 도메인 문구로 덮으면 그 키를 쓰는 모든 화면이 오염된다.
|
|
attribute_label_scope: [generic_password, smtp_password, generic_ids, user_scoped_ids]
|
|
locale: [ko, en]
|
|
|
|
exclusions:
|
|
- { special_char_setting: not_required, input_state: violates_special, reason: "특수문자 미요구 설정에서는 위반 상태가 성립하지 않음" }
|
|
- { attribute_label_scope: smtp_password, entry_point: register, reason: "SMTP 라벨은 메일 설정 화면 전용 — 비밀번호를 정하는 경로에는 나타나지 않아야 한다" }
|
|
- { attribute_label_scope: smtp_password, entry_point: reset_password, reason: "동일" }
|
|
- { attribute_label_scope: smtp_password, entry_point: change_password, reason: "동일" }
|
|
- { attribute_label_scope: smtp_password, entry_point: admin_create_user, reason: "동일" }
|
|
- { attribute_label_scope: smtp_password, entry_point: admin_update_user, reason: "동일" }
|
|
- { attribute_label_scope: smtp_password, entry_point: update_profile, reason: "동일" }
|
|
- { attribute_label_scope: generic_ids, entry_point: register, reason: "ids 라벨은 목록 대상 일괄 처리 화면 전용 — 비밀번호를 정하는 경로에는 나타나지 않는다" }
|
|
- { attribute_label_scope: generic_ids, entry_point: reset_password, reason: "동일" }
|
|
- { attribute_label_scope: generic_ids, entry_point: change_password, reason: "동일" }
|
|
- { attribute_label_scope: generic_ids, entry_point: verify_password, reason: "동일" }
|
|
- { attribute_label_scope: generic_ids, entry_point: admin_create_user, reason: "동일" }
|
|
- { attribute_label_scope: generic_ids, entry_point: admin_update_user, reason: "동일" }
|
|
- { attribute_label_scope: generic_ids, entry_point: update_profile, reason: "동일" }
|
|
- { attribute_label_scope: user_scoped_ids, entry_point: register, reason: "동일" }
|
|
- { attribute_label_scope: user_scoped_ids, entry_point: reset_password, reason: "동일" }
|
|
- { attribute_label_scope: user_scoped_ids, entry_point: change_password, reason: "동일" }
|
|
- { attribute_label_scope: user_scoped_ids, entry_point: verify_password, reason: "동일" }
|
|
- { attribute_label_scope: user_scoped_ids, entry_point: admin_create_user, reason: "동일" }
|
|
- { attribute_label_scope: user_scoped_ids, entry_point: admin_update_user, reason: "동일" }
|
|
- { attribute_label_scope: user_scoped_ids, entry_point: update_profile, reason: "동일" }
|
|
- { attribute_label_scope: smtp_password, entry_point: verify_password, reason: "동일" }
|
|
|
|
effects:
|
|
- policy_rule_reads_min_length_from_settings
|
|
- policy_rule_reads_special_requirement_from_settings
|
|
- password_shorter_than_setting_is_rejected
|
|
- password_meeting_setting_is_accepted
|
|
- special_char_requirement_is_enforced_when_enabled
|
|
- login_request_has_no_length_rule
|
|
- all_password_setting_paths_share_one_rule
|
|
- empty_security_settings_fall_back_to_defaults
|
|
- saved_security_settings_are_visible_on_reload
|
|
- admin_screen_min_length_bounds_follow_settings_limits
|
|
# 라벨 축 (공개이슈 #113)
|
|
- policy_message_attribute_uses_generic_password_label
|
|
- smtp_label_never_appears_in_password_setting_paths
|
|
- smtp_screens_keep_their_domain_labels
|
|
- generic_labels_hold_in_every_locale
|
|
- bulk_status_screen_keeps_user_scoped_ids_label
|
|
- generic_ids_label_stays_generic_on_other_screens
|
|
- generic_attribute_keys_carry_no_domain_prefix
|
|
|
|
test_files:
|
|
- tests/Feature/Auth/PasswordPolicyTest.php
|
|
- tests/Feature/Settings/SettingsBoundaryContractTest.php
|
|
- tests/Feature/Validation/GlobalAttributeLabelTest.php
|
|
- templates/_bundled/sirsoft-admin_basic/__tests__/layouts/admin-settings-security-password-policy.test.ts
|
|
|
|
rules_layer_coverage:
|
|
- rule: formrequest-numeric-boundary-drift
|
|
coverage: 최소 길이 경계는 config('core.settings_limits.security_password_min_length_*') 단일 출처
|
|
- rule: layout-input-boundary-hardcoded
|
|
coverage: 보안 탭 입력 min/max 는 `_meta.limits` 바인딩 (리터럴 없음)
|
|
- rule: i18n-throw-hardcoded-korean
|
|
coverage: 정책 위반 메시지는 lang/{ko,en}/validation.php 의 password 키
|