462 lines
19 KiB
Markdown
462 lines
19 KiB
Markdown
# Replay Protection Implementation Plan
|
|
|
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
|
|
**Goal:** Add TiDB-backed replay protection for `/openapi/**` and selected sensitive internal `/api/**` write/execute endpoints.
|
|
|
|
**Architecture:** Replace the current in-memory OpenAPI nonce guard with a database-backed replay nonce service, then add an internal replay interceptor that only protects annotated high-risk handlers. Persist nonce claims in TiDB with a unique key, record replay-related security events, and reject protected requests when replay validation cannot be completed.
|
|
|
|
**Tech Stack:** Spring Boot 3, MyBatis-Plus, Flyway SQL migration, JUnit 5, Mockito, MockMvc.
|
|
|
|
---
|
|
|
|
### Task 1: Add Replay Nonce and Security Event Schema
|
|
|
|
**Files:**
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/resources/db/migration/V5__add_replay_protection_tables.sql`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/resources/db/migration/V1__tms_schema_full.sql`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/TmsApplicationTests.java`
|
|
|
|
**Step 1: Write the failing schema assertions**
|
|
|
|
Add assertions that `V1__tms_schema_full.sql` or the new migration defines:
|
|
- `tms_replay_nonce`
|
|
- `tms_security_event`
|
|
- unique key on `(scope, principal_id, nonce)`
|
|
- index on `expires_at`
|
|
|
|
**Step 2: Run test to verify it fails**
|
|
|
|
Run: `mvn -q -Dtest=TmsApplicationTests test`
|
|
|
|
Expected: FAIL because replay protection tables do not exist in schema artifacts yet.
|
|
|
|
**Step 3: Write minimal implementation**
|
|
|
|
Create `V5__add_replay_protection_tables.sql` with:
|
|
- `tms_replay_nonce`
|
|
- `tms_security_event`
|
|
|
|
Update `V1__tms_schema_full.sql` so fresh-init environments also include the same final-state tables.
|
|
|
|
**Step 4: Run test to verify it passes**
|
|
|
|
Run: `mvn -q -Dtest=TmsApplicationTests test`
|
|
|
|
Expected: PASS for schema assertions.
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/main/resources/db/migration/V1__tms_schema_full.sql src/main/resources/db/migration/V5__add_replay_protection_tables.sql src/test/java/com/cisd/tms/TmsApplicationTests.java
|
|
git commit -m "feat: add replay protection schema"
|
|
```
|
|
|
|
### Task 2: Add Replay Persistence Models and Repositories
|
|
|
|
**Files:**
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/entity/ReplayNonceEntity.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/entity/SafetyEventEntity.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/mapper/ReplayNonceMapper.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/mapper/SecurityEventMapper.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/repository/ReplayNonceRepository.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/repository/SecurityEventRepository.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/repository/impl/ReplayNonceRepositoryImpl.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/repository/impl/SecurityEventRepositoryImpl.java`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/security/replay/ReplayNonceRepositoryTest.java`
|
|
|
|
**Step 1: Write the failing repository test**
|
|
|
|
Add tests for:
|
|
- saving a nonce record
|
|
- detecting duplicate `(scope, principalId, nonce)`
|
|
- saving a security event
|
|
|
|
**Step 2: Run test to verify it fails**
|
|
|
|
Run: `mvn -q -Dtest=ReplayNonceRepositoryTest test`
|
|
|
|
Expected: FAIL because replay persistence classes do not exist.
|
|
|
|
**Step 3: Write minimal implementation**
|
|
|
|
Create entities, mappers, repositories, and repository impls consistent with the existing `modules/*/repository/impl` pattern.
|
|
|
|
Represent:
|
|
- `scope`
|
|
- `principalId`
|
|
- `nonce`
|
|
- `requestTimestamp`
|
|
- `requestMethod`
|
|
- `requestPath`
|
|
- `bodyHash`
|
|
- `remoteIp`
|
|
- `userAgent`
|
|
- `expiresAt`
|
|
|
|
**Step 4: Run test to verify it passes**
|
|
|
|
Run: `mvn -q -Dtest=ReplayNonceRepositoryTest test`
|
|
|
|
Expected: PASS
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/main/java/com/cisd/tms/modules/security/replay src/test/java/com/cisd/tms/modules/security/replay/ReplayNonceRepositoryTest.java
|
|
git commit -m "feat: add replay persistence layer"
|
|
```
|
|
|
|
### Task 3: Add Replay Protection Service and Domain Types
|
|
|
|
**Files:**
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/service/ReplayNonceService.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/service/impl/ReplayNonceServiceImpl.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/dto/ReplayCheckRequest.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/dto/ReplayCheckResult.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/enums/ReplayScope.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/enums/SafetyEventType.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/exception/ReplayAttackException.java`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/security/replay/ReplayNonceServiceTest.java`
|
|
|
|
**Step 1: Write the failing service test**
|
|
|
|
Add tests for:
|
|
- first nonce claim succeeds
|
|
- duplicate nonce claim returns replay result
|
|
- duplicate nonce with different path/body hash marks mismatch
|
|
- expired timestamp is rejected before claim
|
|
- repository failure causes protected request to fail closed
|
|
|
|
**Step 2: Run test to verify it fails**
|
|
|
|
Run: `mvn -q -Dtest=ReplayNonceServiceTest test`
|
|
|
|
Expected: FAIL because replay service does not exist.
|
|
|
|
**Step 3: Write minimal implementation**
|
|
|
|
Implement a service that:
|
|
- validates timestamp skew
|
|
- attempts nonce claim via repository
|
|
- on duplicate, compares request fingerprint
|
|
- writes security events
|
|
- throws a dedicated replay exception for interceptor handling
|
|
|
|
Keep timestamp comparison logic in one place so both OpenAPI and internal interceptors share the same rule.
|
|
|
|
**Step 4: Run test to verify it passes**
|
|
|
|
Run: `mvn -q -Dtest=ReplayNonceServiceTest test`
|
|
|
|
Expected: PASS
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/main/java/com/cisd/tms/modules/security/replay src/test/java/com/cisd/tms/modules/security/replay/ReplayNonceServiceTest.java
|
|
git commit -m "feat: add replay protection service"
|
|
```
|
|
|
|
### Task 4: Replace OpenAPI In-Memory Replay Guard
|
|
|
|
**Files:**
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/security/openapi/OpenApiSignAuthInterceptor.java`
|
|
- Delete: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/security/openapi/OpenApiReplayGuard.java`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/security/openapi/OpenApiSignAuthInterceptorTest.java`
|
|
|
|
**Step 1: Write the failing interceptor tests**
|
|
|
|
Add tests for:
|
|
- valid request passes and claims nonce
|
|
- duplicate nonce is rejected
|
|
- timestamp expired is rejected
|
|
- invalid signature is rejected before nonce claim
|
|
- duplicate nonce with same appId but different body hash is treated as replay mismatch
|
|
|
|
**Step 2: Run test to verify it fails**
|
|
|
|
Run: `mvn -q -Dtest=OpenApiSignAuthInterceptorTest test`
|
|
|
|
Expected: FAIL because interceptor still uses `OpenApiReplayGuard`.
|
|
|
|
**Step 3: Write minimal implementation**
|
|
|
|
Refactor `OpenApiSignAuthInterceptor` to:
|
|
- compute `bodyHash`
|
|
- validate timestamp
|
|
- validate signature
|
|
- call `ReplayNonceService`
|
|
|
|
Remove `OpenApiReplayGuard`.
|
|
|
|
Do not change the existing request header names in this task.
|
|
|
|
**Step 4: Run test to verify it passes**
|
|
|
|
Run: `mvn -q -Dtest=OpenApiSignAuthInterceptorTest test`
|
|
|
|
Expected: PASS
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/main/java/com/cisd/tms/security/openapi src/test/java/com/cisd/tms/security/openapi/OpenApiSignAuthInterceptorTest.java
|
|
git commit -m "feat: persist openapi replay protection"
|
|
```
|
|
|
|
### Task 5: Add Internal Replay Annotation and Interceptor
|
|
|
|
**Files:**
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/annotation/ReplayProtected.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/security/internal/InternalReplayProtectionInterceptor.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/common/config/WebMvcConfig.java`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/security/internal/InternalReplayProtectionInterceptorTest.java`
|
|
|
|
**Step 1: Write the failing interceptor tests**
|
|
|
|
Add tests for:
|
|
- unannotated internal handler is ignored
|
|
- annotated handler requires `X-Request-Timestamp`
|
|
- annotated handler requires `X-Request-Nonce`
|
|
- annotated handler claims nonce with current session token
|
|
- replayed annotated request is rejected
|
|
|
|
**Step 2: Run test to verify it fails**
|
|
|
|
Run: `mvn -q -Dtest=InternalReplayProtectionInterceptorTest test`
|
|
|
|
Expected: FAIL because annotation and interceptor do not exist.
|
|
|
|
**Step 3: Write minimal implementation**
|
|
|
|
Create:
|
|
- `@ReplayProtected`
|
|
- internal interceptor that runs after session auth
|
|
- registration in `WebMvcConfig`
|
|
|
|
The interceptor should use:
|
|
- `InternalApiAuthInterceptor.ATTR_SESSION_TOKEN`
|
|
- `X-Request-Timestamp`
|
|
- `X-Request-Nonce`
|
|
|
|
Reject protected requests when session token is missing or replay validation fails.
|
|
|
|
**Step 4: Run test to verify it passes**
|
|
|
|
Run: `mvn -q -Dtest=InternalReplayProtectionInterceptorTest test`
|
|
|
|
Expected: PASS
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/main/java/com/cisd/tms/modules/security/replay/annotation/ReplayProtected.java src/main/java/com/cisd/tms/security/internal/InternalReplayProtectionInterceptor.java src/main/java/com/cisd/tms/common/config/WebMvcConfig.java src/test/java/com/cisd/tms/security/internal/InternalReplayProtectionInterceptorTest.java
|
|
git commit -m "feat: add internal replay interceptor"
|
|
```
|
|
|
|
### Task 6: Annotate First-Batch Sensitive Internal Endpoints
|
|
|
|
**Files:**
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/init/controller/InitController.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/upgrade/controller/UpgradeController.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/auth/controller/AuthController.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/auth/controller/AuthAdminController.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/device/controller/TimeConfigController.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/device/controller/NetworkConfigController.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/device/controller/IpWhitelistController.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/device/controller/DeviceController.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/mk/controller/LmkController.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/device/controller/CryptoCardController.java`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/upgrade/controller/UpgradeControllerTest.java`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/auth/controller/AuthControllerTest.java`
|
|
|
|
**Step 1: Write the failing controller coverage tests**
|
|
|
|
Add tests for:
|
|
- protected endpoints reject missing replay headers
|
|
- login/captcha endpoints remain unprotected
|
|
- a representative protected endpoint accepts replay headers when replay service allows it
|
|
|
|
**Step 2: Run test to verify it fails**
|
|
|
|
Run: `mvn -q -Dtest=UpgradeControllerTest,AuthControllerTest test`
|
|
|
|
Expected: FAIL because first-batch endpoints are not annotated.
|
|
|
|
**Step 3: Write minimal implementation**
|
|
|
|
Annotate only:
|
|
- init/create/execute/reset-create/reset-execute
|
|
- upgrade/create/execute/rollback
|
|
- logout/change-password
|
|
- all auth admin POST methods
|
|
- time/network/whitelist/device restart writes
|
|
- all `LmkController` write methods
|
|
- selected `CryptoCardController` write-state methods
|
|
|
|
Do not annotate:
|
|
- login
|
|
- captcha
|
|
- random issuance
|
|
- GET endpoints
|
|
- pure preview endpoints
|
|
|
|
**Step 4: Run test to verify it passes**
|
|
|
|
Run: `mvn -q -Dtest=UpgradeControllerTest,AuthControllerTest test`
|
|
|
|
Expected: PASS
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/main/java/com/cisd/tms/modules/init/controller/InitController.java src/main/java/com/cisd/tms/modules/upgrade/controller/UpgradeController.java src/main/java/com/cisd/tms/modules/auth/controller/AuthController.java src/main/java/com/cisd/tms/modules/auth/controller/AuthAdminController.java src/main/java/com/cisd/tms/modules/device/controller/TimeConfigController.java src/main/java/com/cisd/tms/modules/device/controller/NetworkConfigController.java src/main/java/com/cisd/tms/modules/device/controller/IpWhitelistController.java src/main/java/com/cisd/tms/modules/device/controller/DeviceController.java src/main/java/com/cisd/tms/modules/mk/controller/LmkController.java src/main/java/com/cisd/tms/modules/device/controller/CryptoCardController.java src/test/java/com/cisd/tms/modules/upgrade/controller/UpgradeControllerTest.java src/test/java/com/cisd/tms/modules/auth/controller/AuthControllerTest.java
|
|
git commit -m "feat: protect sensitive internal endpoints from replay"
|
|
```
|
|
|
|
### Task 7: Map Replay Exceptions and Add Structured Responses
|
|
|
|
**Files:**
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/common/exception/GlobalExceptionHandler.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/common/enums/ErrorCode.java`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/common/exception/GlobalExceptionHandlerTest.java`
|
|
|
|
**Step 1: Write the failing exception handler test**
|
|
|
|
Add tests for:
|
|
- replay attack exception returns expected code/message
|
|
- timestamp expired exception returns expected code/message
|
|
|
|
**Step 2: Run test to verify it fails**
|
|
|
|
Run: `mvn -q -Dtest=GlobalExceptionHandlerTest test`
|
|
|
|
Expected: FAIL because replay exceptions are not mapped yet.
|
|
|
|
**Step 3: Write minimal implementation**
|
|
|
|
Add replay-related error codes and map:
|
|
- replay detected
|
|
- request timestamp expired
|
|
- replay guard unavailable
|
|
|
|
Keep response shape consistent with existing `ApiResponse.fail(...).withPath(...)`.
|
|
|
|
**Step 4: Run test to verify it passes**
|
|
|
|
Run: `mvn -q -Dtest=GlobalExceptionHandlerTest test`
|
|
|
|
Expected: PASS
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/main/java/com/cisd/tms/common/exception/GlobalExceptionHandler.java src/main/java/com/cisd/tms/common/enums/ErrorCode.java src/test/java/com/cisd/tms/common/exception/GlobalExceptionHandlerTest.java
|
|
git commit -m "feat: expose replay protection errors"
|
|
```
|
|
|
|
### Task 8: Add Replay Nonce Cleanup Job
|
|
|
|
**Files:**
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/config/ReplayProtectionProperties.java`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/security/replay/job/ReplayNonceCleanupJob.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/resources/application.yml`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/security/replay/ReplayNonceCleanupJobTest.java`
|
|
|
|
**Step 1: Write the failing cleanup job test**
|
|
|
|
Add tests for:
|
|
- deleting expired rows in small batches
|
|
- leaving non-expired rows untouched
|
|
|
|
**Step 2: Run test to verify it fails**
|
|
|
|
Run: `mvn -q -Dtest=ReplayNonceCleanupJobTest test`
|
|
|
|
Expected: FAIL because cleanup job does not exist.
|
|
|
|
**Step 3: Write minimal implementation**
|
|
|
|
Add:
|
|
- replay protection properties
|
|
- scheduled cleanup job
|
|
- config defaults for ttl and delete batch size
|
|
|
|
Use modest defaults such as:
|
|
- `ttl-seconds: 600`
|
|
- `cleanup-batch-size: 1000`
|
|
|
|
**Step 4: Run test to verify it passes**
|
|
|
|
Run: `mvn -q -Dtest=ReplayNonceCleanupJobTest test`
|
|
|
|
Expected: PASS
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/main/java/com/cisd/tms/modules/security/replay/config src/main/java/com/cisd/tms/modules/security/replay/job src/main/resources/application.yml src/test/java/com/cisd/tms/modules/security/replay/ReplayNonceCleanupJobTest.java
|
|
git commit -m "feat: add replay nonce cleanup job"
|
|
```
|
|
|
|
### Task 9: Run Focused Verification
|
|
|
|
**Files:**
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/security/replay/ReplayNonceRepositoryTest.java`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/security/replay/ReplayNonceServiceTest.java`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/security/openapi/OpenApiSignAuthInterceptorTest.java`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/security/internal/InternalReplayProtectionInterceptorTest.java`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/common/exception/GlobalExceptionHandlerTest.java`
|
|
|
|
**Step 1: Run focused replay tests**
|
|
|
|
Run: `mvn -q -Dtest=ReplayNonceRepositoryTest,ReplayNonceServiceTest,OpenApiSignAuthInterceptorTest,InternalReplayProtectionInterceptorTest,GlobalExceptionHandlerTest test`
|
|
|
|
Expected: PASS
|
|
|
|
**Step 2: Run representative controller tests**
|
|
|
|
Run: `mvn -q -Dtest=UpgradeControllerTest,AuthControllerTest test`
|
|
|
|
Expected: PASS
|
|
|
|
**Step 3: Run compile check**
|
|
|
|
Run: `mvn -q -DskipTests compile`
|
|
|
|
Expected: PASS
|
|
|
|
**Step 4: Commit**
|
|
|
|
```bash
|
|
git add -A
|
|
git commit -m "test: verify replay protection implementation"
|
|
```
|
|
|
|
### Task 10: Update Docs and Integration Guidance
|
|
|
|
**Files:**
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/README.md`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/docs/openapi/auth-mk-frontend-integration.md`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/docs/plans/2026-04-09-replay-protection-design.md`
|
|
- Add: `/Users/waner/Work/CISD/文档/tms-framework/docs/plans/2026-04-09-replay-protection-plan.md`
|
|
|
|
**Step 1: Document OpenAPI replay headers**
|
|
|
|
Update README and integration docs to describe:
|
|
- OpenAPI replay protection flow
|
|
- internal sensitive API replay headers
|
|
- expected replay failure behavior
|
|
|
|
**Step 2: Verify docs are consistent with actual endpoint scope**
|
|
|
|
Review the protected endpoint list against controller annotations.
|
|
|
|
**Step 3: Commit**
|
|
|
|
```bash
|
|
git add README.md docs/openapi/auth-mk-frontend-integration.md docs/plans/2026-04-09-replay-protection-design.md docs/plans/2026-04-09-replay-protection-plan.md
|
|
git commit -m "docs: add replay protection guidance"
|
|
```
|