docs: project docs, scripts, deployment configs, and evidence
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# Alertmanager Render Drill
|
||||
|
||||
- Generated at: 2026-03-27 18:20:59 +08:00
|
||||
- Template file: D:\project\deployment\alertmanager\alertmanager.yml
|
||||
- Rendered file: D:\project\docs\evidence\ops\2026-03-27\alerting\20260327-182059\alertmanager.rendered.yaml
|
||||
- Synthetic secret values were injected through process environment variables for this drill only.
|
||||
- Result: template placeholders resolved successfully and the rendered config contains no unresolved `${ALERTMANAGER_*}` tokens.
|
||||
|
||||
## Scope Note
|
||||
|
||||
- This drill validates the config injection/rendering path only.
|
||||
- It does not prove real SMTP delivery, real contact routing, or production secret manager integration.
|
||||
|
||||
## Evidence Files
|
||||
|
||||
- alertmanager.rendered.yaml
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
global:
|
||||
resolve_timeout: 5m
|
||||
|
||||
# 注意:
|
||||
# 该文件为模板文件,生产环境必须先注入并渲染 `${ALERTMANAGER_*}` 变量,
|
||||
# 再将渲染结果交给 Alertmanager 使用。
|
||||
|
||||
# 告警路由
|
||||
route:
|
||||
group_by: ['alertname', 'service']
|
||||
group_wait: 30s
|
||||
group_interval: 5m
|
||||
repeat_interval: 12h
|
||||
receiver: 'default'
|
||||
|
||||
# 子路由,根据严重级别分发
|
||||
routes:
|
||||
# Critical 告警
|
||||
- match:
|
||||
severity: critical
|
||||
receiver: 'critical-alerts'
|
||||
group_wait: 10s
|
||||
continue: true
|
||||
|
||||
# Warning 告警
|
||||
- match:
|
||||
severity: warning
|
||||
receiver: 'warning-alerts'
|
||||
continue: true
|
||||
|
||||
# 告警接收者
|
||||
receivers:
|
||||
# 默认接收者
|
||||
- name: 'default'
|
||||
email_configs:
|
||||
- to: 'ops-team@example.org'
|
||||
from: 'alertmanager@example.org'
|
||||
smarthost: 'smtp.example.org:587'
|
||||
auth_username: 'alertmanager@example.org'
|
||||
auth_password: 'synthetic-secret-for-render-drill'
|
||||
headers:
|
||||
Subject: '[{{ .Status | toUpper }}] {{ .GroupLabels.alertname }}'
|
||||
|
||||
# Critical 告警接收者
|
||||
- name: 'critical-alerts'
|
||||
email_configs:
|
||||
- to: 'critical-oncall@example.org'
|
||||
from: 'alertmanager@example.org'
|
||||
smarthost: 'smtp.example.org:587'
|
||||
auth_username: 'alertmanager@example.org'
|
||||
auth_password: 'synthetic-secret-for-render-drill'
|
||||
headers:
|
||||
Subject: '[CRITICAL] {{ .GroupLabels.alertname }}'
|
||||
|
||||
# Warning 告警接收者
|
||||
- name: 'warning-alerts'
|
||||
email_configs:
|
||||
- to: 'warning-oncall@example.org'
|
||||
from: 'alertmanager@example.org'
|
||||
smarthost: 'smtp.example.org:587'
|
||||
auth_username: 'alertmanager@example.org'
|
||||
auth_password: 'synthetic-secret-for-render-drill'
|
||||
headers:
|
||||
Subject: '[WARNING] {{ .GroupLabels.alertname }}'
|
||||
|
||||
# 告警抑制规则
|
||||
inhibit_rules:
|
||||
# 如果有 critical 告警,抑制同一服务的 warning 告警
|
||||
- source_match:
|
||||
severity: 'critical'
|
||||
target_match:
|
||||
severity: 'warning'
|
||||
equal: ['service']
|
||||
|
||||
# 告警静默规则(按需配置)
|
||||
# silences:
|
||||
# - matchers:
|
||||
# - name: alertname
|
||||
# value: LowOnlineUsers
|
||||
# - name: severity
|
||||
# value: info
|
||||
# startsAt: "2026-03-12T00:00:00+08:00"
|
||||
# endsAt: "2026-03-12T23:59:59+08:00"
|
||||
# comment: "维护期间静默低在线用户告警"
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# Alerting Package Validation
|
||||
|
||||
- Generated at: 2026-03-27 18:20:59 +08:00
|
||||
- Alerts file: D:\project\deployment\alertmanager\alerts.yml
|
||||
- Alertmanager file: D:\project\deployment\alertmanager\alertmanager.yml
|
||||
- Baseline report: D:\project\docs\evidence\ops\2026-03-27\observability\LOCAL_BASELINE_20260327-182005.md
|
||||
|
||||
## Structural Validation
|
||||
|
||||
- Rule inventory: critical=3, warning=4, info=2
|
||||
- Missing required rules: none
|
||||
- Root receiver: default
|
||||
- Critical route receiver: critical-alerts
|
||||
- Warning route receiver: warning-alerts
|
||||
- Missing required receivers: none
|
||||
- Structural ready: True
|
||||
|
||||
## Threshold Alignment
|
||||
|
||||
- HighResponseTime threshold: 1s
|
||||
- Latest browser max baseline: 186ms
|
||||
- Latest browser timings: login-desktop=186ms, login-initial=99ms, login-mobile=96ms, login-tablet=117ms
|
||||
|
||||
## External Delivery Readiness
|
||||
|
||||
- Placeholder findings: \$\{ALERTMANAGER_[A-Z0-9_]+\}
|
||||
- External delivery closed: False
|
||||
- Interpretation: rules and route topology can be reviewed locally, but unresolved template variables or example SMTP/accounts mean real notification delivery evidence is still open until environment-specific contacts and secrets are injected.
|
||||
|
||||
## Conclusion
|
||||
|
||||
- Repo-level alerting package structurally ready: True
|
||||
- Repo-level oncall/delivery package fully closed: False
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Backup Restore Drill
|
||||
|
||||
- Generated at: 2026-03-27 18:21:07 +08:00
|
||||
- Source DB: D:\project\data\user_management.db
|
||||
- Backup DB: D:\project\docs\evidence\ops\2026-03-27\backup-restore\20260327-182059\user_management.backup.db
|
||||
- Restored DB: D:\project\docs\evidence\ops\2026-03-27\backup-restore\20260327-182059\user_management.restored.db
|
||||
- Probe port: 18080
|
||||
|
||||
## Hash Validation
|
||||
|
||||
- source sha256: 546D353065A10AA7B2B429A8ED6CBE03830D528567FB769A242A0854256F4857
|
||||
- backup sha256: 546D353065A10AA7B2B429A8ED6CBE03830D528567FB769A242A0854256F4857
|
||||
- restored sha256: 546D353065A10AA7B2B429A8ED6CBE03830D528567FB769A242A0854256F4857
|
||||
|
||||
## Snapshot Comparison
|
||||
|
||||
- source tables: {"devices":0,"login_logs":18,"operation_logs":50,"password_histories":0,"permissions":17,"role_permissions":20,"roles":2,"user_roles":1,"users":1,"webhook_deliveries":0,"webhooks":0}
|
||||
- restored tables: {"devices":0,"login_logs":18,"operation_logs":50,"password_histories":0,"permissions":17,"role_permissions":20,"roles":2,"user_roles":1,"users":1,"webhook_deliveries":0,"webhooks":0}
|
||||
- source existing tables: devices, login_logs, operation_logs, password_histories, permissions, role_permissions, roles, sqlite_sequence, user_roles, user_social_accounts, users, webhook_deliveries, webhooks
|
||||
- restored existing tables: devices, login_logs, operation_logs, password_histories, permissions, role_permissions, roles, sqlite_sequence, user_roles, user_social_accounts, users, webhook_deliveries, webhooks
|
||||
- source missing tables: social_accounts
|
||||
- restored missing tables: social_accounts
|
||||
- sample users: e2e_admin
|
||||
|
||||
## Restore Service Verification
|
||||
|
||||
- GET /health: pass
|
||||
- GET /health/ready: pass
|
||||
- GET /api/v1/auth/capabilities: pass
|
||||
- auth capabilities payload: {"password":true,"email_activation":false,"email_code":false,"sms_code":false,"password_reset":false,"admin_bootstrap_required":false,"oauth_providers":[]}
|
||||
|
||||
## Evidence Files
|
||||
|
||||
- source-snapshot.json
|
||||
- restored-snapshot.json
|
||||
- server.stdout.log
|
||||
- server.stderr.log
|
||||
- config.restore.yaml
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
server:
|
||||
port: 18080
|
||||
mode: release # debug, release
|
||||
read_timeout: 30s
|
||||
read_header_timeout: 10s
|
||||
write_timeout: 30s
|
||||
idle_timeout: 60s
|
||||
shutdown_timeout: 15s
|
||||
max_header_bytes: 1048576
|
||||
|
||||
database:
|
||||
type: sqlite # current runtime support: sqlite
|
||||
sqlite:
|
||||
path: "D:/project/docs/evidence/ops/2026-03-27/backup-restore/20260327-182059/user_management.restored.db"
|
||||
postgresql:
|
||||
host: localhost
|
||||
port: 5432
|
||||
database: user_management
|
||||
username: postgres
|
||||
password: ""
|
||||
ssl_mode: disable
|
||||
max_open_conns: 100
|
||||
max_idle_conns: 10
|
||||
mysql:
|
||||
host: localhost
|
||||
port: 3306
|
||||
database: user_management
|
||||
username: root
|
||||
password: ""
|
||||
charset: utf8mb4
|
||||
max_open_conns: 100
|
||||
max_idle_conns: 10
|
||||
|
||||
cache:
|
||||
l1:
|
||||
enabled: true
|
||||
max_size: 10000
|
||||
ttl: 5m
|
||||
l2:
|
||||
enabled: false
|
||||
type: redis
|
||||
redis:
|
||||
addr: localhost:6379
|
||||
password: ""
|
||||
db: 0
|
||||
pool_size: 50
|
||||
ttl: 30m
|
||||
|
||||
redis:
|
||||
enabled: false
|
||||
addr: localhost:6379
|
||||
password: ""
|
||||
db: 0
|
||||
|
||||
jwt:
|
||||
algorithm: RS256
|
||||
secret: ""
|
||||
private_key_path: "./data/jwt/private.pem"
|
||||
public_key_path: "./data/jwt/public.pem"
|
||||
private_key_pem: ""
|
||||
public_key_pem: ""
|
||||
access_token_expire: 2h
|
||||
refresh_token_expire: 168h # 7澶?= 168灏忔椂
|
||||
|
||||
security:
|
||||
password_min_length: 8
|
||||
password_require_special: true
|
||||
password_require_number: true
|
||||
login_max_attempts: 5
|
||||
login_lock_duration: 30m
|
||||
|
||||
ratelimit:
|
||||
enabled: true
|
||||
login:
|
||||
enabled: true
|
||||
algorithm: token_bucket
|
||||
capacity: 5
|
||||
rate: 1
|
||||
window: 1m
|
||||
register:
|
||||
enabled: true
|
||||
algorithm: leaky_bucket
|
||||
capacity: 3
|
||||
rate: 1
|
||||
window: 1h
|
||||
api:
|
||||
enabled: true
|
||||
algorithm: sliding_window
|
||||
capacity: 1000
|
||||
window: 1m
|
||||
|
||||
monitoring:
|
||||
prometheus:
|
||||
enabled: true
|
||||
path: /metrics
|
||||
tracing:
|
||||
enabled: false
|
||||
endpoint: http://localhost:4318
|
||||
service_name: user-management-system
|
||||
|
||||
logging:
|
||||
level: info # debug, info, warn, error
|
||||
format: json # json, text
|
||||
output:
|
||||
- stdout
|
||||
- ./logs/app.log
|
||||
rotation:
|
||||
max_size: 100 # MB
|
||||
max_age: 30 # days
|
||||
max_backups: 10
|
||||
|
||||
admin:
|
||||
username: ""
|
||||
password: ""
|
||||
email: ""
|
||||
|
||||
cors:
|
||||
enabled: true
|
||||
allowed_origins:
|
||||
- "http://localhost:3000"
|
||||
- "http://127.0.0.1:3000"
|
||||
allowed_methods:
|
||||
- GET
|
||||
- POST
|
||||
- PUT
|
||||
- DELETE
|
||||
- OPTIONS
|
||||
allowed_headers:
|
||||
- Authorization
|
||||
- Content-Type
|
||||
- X-Requested-With
|
||||
- X-CSRF-Token
|
||||
max_age: 3600
|
||||
|
||||
email:
|
||||
host: "" # 鐢熶骇鐜濉啓鐪熷疄 SMTP Host
|
||||
port: 18080
|
||||
username: ""
|
||||
password: ""
|
||||
from_email: ""
|
||||
from_name: "鐢ㄦ埛绠$悊绯荤粺"
|
||||
|
||||
sms:
|
||||
enabled: false
|
||||
provider: "" # aliyun, tencent锛涚暀绌鸿〃绀虹鐢ㄧ煭淇¤兘鍔? code_ttl: 5m
|
||||
resend_cooldown: 1m
|
||||
max_daily_limit: 10
|
||||
aliyun:
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
sign_name: ""
|
||||
template_code: ""
|
||||
endpoint: ""
|
||||
region_id: "cn-hangzhou"
|
||||
code_param_name: "code"
|
||||
tencent:
|
||||
secret_id: ""
|
||||
secret_key: ""
|
||||
app_id: ""
|
||||
sign_name: ""
|
||||
template_id: ""
|
||||
region: "ap-guangzhou"
|
||||
endpoint: ""
|
||||
|
||||
password_reset:
|
||||
token_ttl: 15m
|
||||
site_url: "http://localhost:8080"
|
||||
|
||||
# OAuth 绀句氦鐧诲綍閰嶇疆锛堢暀绌哄垯绂佺敤瀵瑰簲 Provider锛?
|
||||
oauth:
|
||||
google:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/google/callback"
|
||||
wechat:
|
||||
app_id: ""
|
||||
app_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/wechat/callback"
|
||||
github:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/github/callback"
|
||||
qq:
|
||||
app_id: ""
|
||||
app_key: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/qq/callback"
|
||||
alipay:
|
||||
app_id: ""
|
||||
private_key: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/alipay/callback"
|
||||
sandbox: false
|
||||
douyin:
|
||||
client_key: ""
|
||||
client_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/douyin/callback"
|
||||
|
||||
# Webhook 鍏ㄥ眬閰嶇疆
|
||||
webhook:
|
||||
enabled: true
|
||||
secret_header: "X-Webhook-Signature" # 绛惧悕 Header 鍚嶇О
|
||||
timeout_sec: 30 # 鍗曟鎶曢€掕秴鏃讹紙绉掞級
|
||||
max_retries: 3 # 鏈€澶ч噸璇曟鏁?
|
||||
retry_backoff: "exponential" # 閫€閬跨瓥鐣ワ細exponential / fixed
|
||||
worker_count: 4 # 鍚庡彴鎶曢€掑崗绋嬫暟
|
||||
queue_size: 1000 # 鎶曢€掗槦鍒楀ぇ灏?
|
||||
|
||||
# IP 瀹夊叏閰嶇疆
|
||||
ip_security:
|
||||
auto_block_enabled: true # 鏄惁鍚敤鑷姩灏佺
|
||||
auto_block_duration: 30m # 鑷姩灏佺鏃堕暱
|
||||
brute_force_threshold: 10 # 鏆村姏鐮磋В闃堝€硷紙绐楀彛鍐呭け璐ユ鏁帮級
|
||||
detection_window: 15m # 妫€娴嬫椂闂寸獥鍙?
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"generated_at": "2026-03-27T18:21:02+08:00",
|
||||
"path": "D:\\project\\docs\\evidence\\ops\\2026-03-27\\backup-restore\\20260327-182059\\user_management.restored.db",
|
||||
"file_size": 208896,
|
||||
"existing_tables": [
|
||||
"devices",
|
||||
"login_logs",
|
||||
"operation_logs",
|
||||
"password_histories",
|
||||
"permissions",
|
||||
"role_permissions",
|
||||
"roles",
|
||||
"sqlite_sequence",
|
||||
"user_roles",
|
||||
"user_social_accounts",
|
||||
"users",
|
||||
"webhook_deliveries",
|
||||
"webhooks"
|
||||
],
|
||||
"missing_tables": [
|
||||
"social_accounts"
|
||||
],
|
||||
"tables": {
|
||||
"devices": 0,
|
||||
"login_logs": 18,
|
||||
"operation_logs": 50,
|
||||
"password_histories": 0,
|
||||
"permissions": 17,
|
||||
"role_permissions": 20,
|
||||
"roles": 2,
|
||||
"user_roles": 1,
|
||||
"users": 1,
|
||||
"webhook_deliveries": 0,
|
||||
"webhooks": 0
|
||||
},
|
||||
"sample_users": [
|
||||
"e2e_admin"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"generated_at": "2026-03-27T18:21:01+08:00",
|
||||
"path": "D:\\project\\data\\user_management.db",
|
||||
"file_size": 208896,
|
||||
"existing_tables": [
|
||||
"devices",
|
||||
"login_logs",
|
||||
"operation_logs",
|
||||
"password_histories",
|
||||
"permissions",
|
||||
"role_permissions",
|
||||
"roles",
|
||||
"sqlite_sequence",
|
||||
"user_roles",
|
||||
"user_social_accounts",
|
||||
"users",
|
||||
"webhook_deliveries",
|
||||
"webhooks"
|
||||
],
|
||||
"missing_tables": [
|
||||
"social_accounts"
|
||||
],
|
||||
"tables": {
|
||||
"devices": 0,
|
||||
"login_logs": 18,
|
||||
"operation_logs": 50,
|
||||
"password_histories": 0,
|
||||
"permissions": 17,
|
||||
"role_permissions": 20,
|
||||
"roles": 2,
|
||||
"user_roles": 1,
|
||||
"users": 1,
|
||||
"webhook_deliveries": 0,
|
||||
"webhooks": 0
|
||||
},
|
||||
"sample_users": [
|
||||
"e2e_admin"
|
||||
]
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,27 @@
|
||||
# Config And Env Isolation Drill
|
||||
|
||||
- Generated at: 2026-03-27 18:21:06 +08:00
|
||||
- Source DB: D:\project\data\user_management.db
|
||||
- Isolated DB: D:\project\docs\evidence\ops\2026-03-27\config-isolation\20260327-182059\user_management.isolated.db
|
||||
- Isolated config: D:\project\docs\evidence\ops\2026-03-27\config-isolation\20260327-182059\config.isolated.yaml
|
||||
|
||||
## Verification Results
|
||||
|
||||
- Base config default port: 8080
|
||||
- UMS_CONFIG_PATH isolated port: 18085
|
||||
- UMS_SERVER_PORT override port: 18086
|
||||
- UMS_CORS_ALLOWED_ORIGINS override accepted origin: https://admin.example.com
|
||||
- UMS_CORS_ALLOWED_ORIGINS override excluded origin: none
|
||||
- auth capabilities with config-only override: {"password":true,"email_activation":false,"email_code":false,"sms_code":false,"password_reset":false,"admin_bootstrap_required":false,"oauth_providers":[]}
|
||||
- auth capabilities with env override: {"password":true,"email_activation":false,"email_code":false,"sms_code":false,"password_reset":false,"admin_bootstrap_required":false,"oauth_providers":[]}
|
||||
|
||||
## Evidence Files
|
||||
|
||||
- config-only.stdout.log
|
||||
- config-only.stderr.log
|
||||
- env-override.stdout.log
|
||||
- env-override.stderr.log
|
||||
- capabilities.config-only.json
|
||||
- capabilities.env-override.json
|
||||
- config.isolated.yaml
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"password": true,
|
||||
"email_activation": false,
|
||||
"email_code": false,
|
||||
"sms_code": false,
|
||||
"password_reset": false,
|
||||
"admin_bootstrap_required": false,
|
||||
"oauth_providers": [
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"password": true,
|
||||
"email_activation": false,
|
||||
"email_code": false,
|
||||
"sms_code": false,
|
||||
"password_reset": false,
|
||||
"admin_bootstrap_required": false,
|
||||
"oauth_providers": [
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
server:
|
||||
port: 18085
|
||||
mode: release # debug, release
|
||||
read_timeout: 30s
|
||||
read_header_timeout: 10s
|
||||
write_timeout: 30s
|
||||
idle_timeout: 60s
|
||||
shutdown_timeout: 15s
|
||||
max_header_bytes: 1048576
|
||||
|
||||
database:
|
||||
type: sqlite # current runtime support: sqlite
|
||||
sqlite:
|
||||
path: "D:/project/docs/evidence/ops/2026-03-27/config-isolation/20260327-182059/user_management.isolated.db"
|
||||
postgresql:
|
||||
host: localhost
|
||||
port: 5432
|
||||
database: user_management
|
||||
username: postgres
|
||||
password: ""
|
||||
ssl_mode: disable
|
||||
max_open_conns: 100
|
||||
max_idle_conns: 10
|
||||
mysql:
|
||||
host: localhost
|
||||
port: 3306
|
||||
database: user_management
|
||||
username: root
|
||||
password: ""
|
||||
charset: utf8mb4
|
||||
max_open_conns: 100
|
||||
max_idle_conns: 10
|
||||
|
||||
cache:
|
||||
l1:
|
||||
enabled: true
|
||||
max_size: 10000
|
||||
ttl: 5m
|
||||
l2:
|
||||
enabled: false
|
||||
type: redis
|
||||
redis:
|
||||
addr: localhost:6379
|
||||
password: ""
|
||||
db: 0
|
||||
pool_size: 50
|
||||
ttl: 30m
|
||||
|
||||
redis:
|
||||
enabled: false
|
||||
addr: localhost:6379
|
||||
password: ""
|
||||
db: 0
|
||||
|
||||
jwt:
|
||||
algorithm: RS256
|
||||
secret: ""
|
||||
private_key_path: "./data/jwt/private.pem"
|
||||
public_key_path: "./data/jwt/public.pem"
|
||||
private_key_pem: ""
|
||||
public_key_pem: ""
|
||||
access_token_expire: 2h
|
||||
refresh_token_expire: 168h # 7澶?= 168灏忔椂
|
||||
|
||||
security:
|
||||
password_min_length: 8
|
||||
password_require_special: true
|
||||
password_require_number: true
|
||||
login_max_attempts: 5
|
||||
login_lock_duration: 30m
|
||||
|
||||
ratelimit:
|
||||
enabled: true
|
||||
login:
|
||||
enabled: true
|
||||
algorithm: token_bucket
|
||||
capacity: 5
|
||||
rate: 1
|
||||
window: 1m
|
||||
register:
|
||||
enabled: true
|
||||
algorithm: leaky_bucket
|
||||
capacity: 3
|
||||
rate: 1
|
||||
window: 1h
|
||||
api:
|
||||
enabled: true
|
||||
algorithm: sliding_window
|
||||
capacity: 1000
|
||||
window: 1m
|
||||
|
||||
monitoring:
|
||||
prometheus:
|
||||
enabled: true
|
||||
path: /metrics
|
||||
tracing:
|
||||
enabled: false
|
||||
endpoint: http://localhost:4318
|
||||
service_name: user-management-system
|
||||
|
||||
logging:
|
||||
level: info # debug, info, warn, error
|
||||
format: json # json, text
|
||||
output:
|
||||
- stdout
|
||||
- ./logs/app.log
|
||||
rotation:
|
||||
max_size: 100 # MB
|
||||
max_age: 30 # days
|
||||
max_backups: 10
|
||||
|
||||
admin:
|
||||
username: ""
|
||||
password: ""
|
||||
email: ""
|
||||
|
||||
cors:
|
||||
enabled: true
|
||||
allowed_origins:
|
||||
- "http://localhost:3000"
|
||||
- "http://127.0.0.1:3000"
|
||||
allowed_methods:
|
||||
- GET
|
||||
- POST
|
||||
- PUT
|
||||
- DELETE
|
||||
- OPTIONS
|
||||
allowed_headers:
|
||||
- Authorization
|
||||
- Content-Type
|
||||
- X-Requested-With
|
||||
- X-CSRF-Token
|
||||
max_age: 3600
|
||||
|
||||
email:
|
||||
host: "" # 鐢熶骇鐜濉啓鐪熷疄 SMTP Host
|
||||
port: 18085
|
||||
username: ""
|
||||
password: ""
|
||||
from_email: ""
|
||||
from_name: "鐢ㄦ埛绠$悊绯荤粺"
|
||||
|
||||
sms:
|
||||
enabled: false
|
||||
provider: "" # aliyun, tencent锛涚暀绌鸿〃绀虹鐢ㄧ煭淇¤兘鍔? code_ttl: 5m
|
||||
resend_cooldown: 1m
|
||||
max_daily_limit: 10
|
||||
aliyun:
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
sign_name: ""
|
||||
template_code: ""
|
||||
endpoint: ""
|
||||
region_id: "cn-hangzhou"
|
||||
code_param_name: "code"
|
||||
tencent:
|
||||
secret_id: ""
|
||||
secret_key: ""
|
||||
app_id: ""
|
||||
sign_name: ""
|
||||
template_id: ""
|
||||
region: "ap-guangzhou"
|
||||
endpoint: ""
|
||||
|
||||
password_reset:
|
||||
token_ttl: 15m
|
||||
site_url: "http://localhost:8080"
|
||||
|
||||
# OAuth 绀句氦鐧诲綍閰嶇疆锛堢暀绌哄垯绂佺敤瀵瑰簲 Provider锛?
|
||||
oauth:
|
||||
google:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/google/callback"
|
||||
wechat:
|
||||
app_id: ""
|
||||
app_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/wechat/callback"
|
||||
github:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/github/callback"
|
||||
qq:
|
||||
app_id: ""
|
||||
app_key: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/qq/callback"
|
||||
alipay:
|
||||
app_id: ""
|
||||
private_key: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/alipay/callback"
|
||||
sandbox: false
|
||||
douyin:
|
||||
client_key: ""
|
||||
client_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/douyin/callback"
|
||||
|
||||
# Webhook 鍏ㄥ眬閰嶇疆
|
||||
webhook:
|
||||
enabled: true
|
||||
secret_header: "X-Webhook-Signature" # 绛惧悕 Header 鍚嶇О
|
||||
timeout_sec: 30 # 鍗曟鎶曢€掕秴鏃讹紙绉掞級
|
||||
max_retries: 3 # 鏈€澶ч噸璇曟鏁?
|
||||
retry_backoff: "exponential" # 閫€閬跨瓥鐣ワ細exponential / fixed
|
||||
worker_count: 4 # 鍚庡彴鎶曢€掑崗绋嬫暟
|
||||
queue_size: 1000 # 鎶曢€掗槦鍒楀ぇ灏?
|
||||
|
||||
# IP 瀹夊叏閰嶇疆
|
||||
ip_security:
|
||||
auto_block_enabled: true # 鏄惁鍚敤鑷姩灏佺
|
||||
auto_block_duration: 30m # 鑷姩灏佺鏃堕暱
|
||||
brute_force_threshold: 10 # 鏆村姏鐮磋В闃堝€硷紙绐楀彛鍐呭け璐ユ鏁帮級
|
||||
detection_window: 15m # 妫€娴嬫椂闂寸獥鍙?
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,59 @@
|
||||
# Admin Bootstrap Closure Evidence
|
||||
|
||||
Generated at: `2026-03-27 17:39:14 +08:00`
|
||||
|
||||
## Scope
|
||||
|
||||
This evidence package covers the first-admin bootstrap closure for the current repository state:
|
||||
|
||||
- public backend endpoint: `POST /api/v1/auth/bootstrap-admin`
|
||||
- public frontend route: `/bootstrap-admin`
|
||||
- login/register first-run entry points
|
||||
- supported-browser validation for `首次管理员初始化 -> 进入后台 -> 登出`
|
||||
|
||||
## Implemented closure
|
||||
|
||||
- Backend:
|
||||
- added one-time admin bootstrap service flow guarded by `GET /api/v1/auth/capabilities -> admin_bootstrap_required`
|
||||
- bootstrap now creates the first active admin, binds the `admin` role, issues a real session, and closes the bootstrap window afterward
|
||||
- Frontend:
|
||||
- added `/bootstrap-admin` page
|
||||
- added login/register entry points when bootstrap is still required
|
||||
- added post-bootstrap auto-login into `/dashboard`
|
||||
- E2E:
|
||||
- `frontend/admin/scripts/run-playwright-auth-e2e.ps1` no longer depends on startup-injected admin credentials
|
||||
- the Playwright CDP suite now validates real bootstrap creation before the rest of the admin workflow scenarios
|
||||
|
||||
## Verification executed
|
||||
|
||||
```powershell
|
||||
go test ./... -count=1
|
||||
go build ./cmd/server
|
||||
|
||||
cd D:\project\frontend\admin
|
||||
npm.cmd run lint
|
||||
npm.cmd run test:run
|
||||
npm.cmd run build
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\run-playwright-auth-e2e.ps1
|
||||
```
|
||||
|
||||
## Latest supported-browser result
|
||||
|
||||
The latest real-browser run completed with:
|
||||
|
||||
- `PASS admin-bootstrap`
|
||||
- `PASS public-registration`
|
||||
- `PASS email-activation`
|
||||
- `PASS login-surface`
|
||||
- `PASS auth-workflow`
|
||||
- `PASS responsive-login`
|
||||
- `PASS desktop-mobile-navigation`
|
||||
- `Playwright CDP E2E completed successfully`
|
||||
|
||||
## Real boundary
|
||||
|
||||
- This closes the product loop for first-admin initialization in the current supported browser-validation environment.
|
||||
- It does not change the previously stated external boundaries:
|
||||
- no live third-party OAuth provider evidence yet
|
||||
- no live external SMTP provider deliverability evidence yet
|
||||
- no external production delivery/governance evidence beyond the local auditable package already formed in-repo
|
||||
@@ -0,0 +1,65 @@
|
||||
# PRD 1.1 Email Activation Closure Evidence
|
||||
|
||||
Date: 2026-03-27
|
||||
Scope: self-service email registration -> activation email delivery -> activation page -> successful login
|
||||
|
||||
## Closure Summary
|
||||
|
||||
- Added a real public frontend activation route: `/activate-account`.
|
||||
- Activation emails now point to the frontend activation page instead of the raw backend API endpoint.
|
||||
- Added public resend-activation entry points from:
|
||||
- `/activate-account`
|
||||
- `/login`
|
||||
- `/register` success state for inactive email accounts
|
||||
- Fixed a real frontend regression uncovered during closure:
|
||||
- the activation page could consume one-time activation tokens twice under React StrictMode development execution and remain stuck on loading.
|
||||
- the page now guards against duplicate activation requests while still allowing the successful request to commit UI state.
|
||||
|
||||
## Validation Executed
|
||||
|
||||
```powershell
|
||||
$env:GOCACHE='D:\project\.gocache'
|
||||
$env:GOMODCACHE='D:\project\.gomodcache'
|
||||
go test ./... -count=1
|
||||
go build ./cmd/server
|
||||
|
||||
cd D:\project\frontend\admin
|
||||
npm.cmd run lint
|
||||
npm.cmd run test:run
|
||||
npm.cmd run build
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\run-playwright-auth-e2e.ps1
|
||||
```
|
||||
|
||||
## Supported Browser E2E Result
|
||||
|
||||
The updated `run-playwright-auth-e2e.ps1` starts:
|
||||
|
||||
- isolated backend
|
||||
- isolated frontend
|
||||
- isolated SQLite database
|
||||
- isolated local SMTP capture service
|
||||
- isolated CDP browser session
|
||||
|
||||
The real browser suite passed the following scenarios:
|
||||
|
||||
- `public-registration`
|
||||
- `email-activation`
|
||||
- `login-surface`
|
||||
- `auth-workflow`
|
||||
- `responsive-login`
|
||||
- `desktop-mobile-navigation`
|
||||
|
||||
The new `email-activation` scenario verified:
|
||||
|
||||
1. create a self-service account with email
|
||||
2. receive a real SMTP-delivered activation email through the local SMTP capture service
|
||||
3. extract the activation link generated by the backend
|
||||
4. open the frontend activation page in the real browser
|
||||
5. complete backend activation successfully
|
||||
6. return to login and sign in with the newly activated account
|
||||
|
||||
## Real Boundary
|
||||
|
||||
- This closes the product loop and supported-browser validation loop.
|
||||
- It does not prove live external SMTP provider deliverability or third-party mailbox delivery behavior.
|
||||
- External production evidence for real SMTP providers remains a separate environment-governance topic and should not be conflated with this closure.
|
||||
@@ -0,0 +1,45 @@
|
||||
# SELF_SERVICE_REGISTER_CLOSURE_20260327-000848
|
||||
|
||||
## Scope
|
||||
|
||||
- PRD `1.1 多种注册方式`
|
||||
- frontend self-service registration entry, page, route, and public workflow
|
||||
- SMS register-code request contract normalization
|
||||
- normal-user first-login redirect away from admin-only dashboard
|
||||
|
||||
## Implemented Closure
|
||||
|
||||
- Backend:
|
||||
- retained the existing `POST /api/v1/auth/register` product API and closed the remaining client contract gap.
|
||||
- `POST /api/v1/auth/send-code` now accepts both `purpose` and the legacy `scene` field, normalizing both onto the same SMS-purpose path for backward compatibility.
|
||||
- Frontend:
|
||||
- added `/register` as a real public route with username/password registration, optional nickname/email, and capability-gated phone registration.
|
||||
- added a login-to-register product entry on `/login`.
|
||||
- fixed SMS register/login send-code requests to use `purpose` instead of the mismatched `scene` payload.
|
||||
- after registration, normal users are no longer dropped onto an admin-only dashboard path; `/dashboard` is now admin-guarded and non-admin first login lands on `/profile`.
|
||||
- `/register` was added to the public-session whitelist so expired refresh-token cleanup does not incorrectly force-register users back to `/login`.
|
||||
|
||||
## Validation
|
||||
|
||||
- `go test ./... -count=1`
|
||||
- `go build ./cmd/server`
|
||||
- `cd D:\project\frontend\admin && npm.cmd run lint`
|
||||
- `cd D:\project\frontend\admin && npm.cmd run test:run`
|
||||
- `cd D:\project\frontend\admin && npm.cmd run build`
|
||||
- `cd D:\project\frontend\admin && powershell -ExecutionPolicy Bypass -File .\scripts\run-playwright-auth-e2e.ps1`
|
||||
|
||||
## Real Browser Result
|
||||
|
||||
- `public-registration` now passes in the supported raw-CDP browser path.
|
||||
- verified path:
|
||||
- `/login` -> `创建账号`
|
||||
- `/register` -> submit self-service registration
|
||||
- success page -> `返回登录`
|
||||
- login with newly registered normal user
|
||||
- redirect settles on `/profile` instead of an admin-only dashboard error path
|
||||
|
||||
## Boundary
|
||||
|
||||
- phone registration remains capability-gated by configured Aliyun/Tencent SMS delivery.
|
||||
- email activation still depends on SMTP-backed activation capability; the frontend supports the loop, but live SMTP delivery proof remains environment-dependent.
|
||||
- this closes the product loop and supported-browser regression path; it does not change the separate boundary around live third-party OAuth provider evidence or external production delivery governance evidence.
|
||||
@@ -0,0 +1,26 @@
|
||||
# Local Observability Baseline
|
||||
|
||||
- Generated at: 2026-03-27 18:20:30 +08:00
|
||||
- Scope: single-node local baseline, not a production traffic certification result
|
||||
|
||||
## Concurrent Login Baseline
|
||||
|
||||
- Source command: `go test ./internal/e2e -run TestE2EConcurrentLogin -v -count=1`
|
||||
- Concurrency configured by test: 20
|
||||
- Result: success=2 fail=18 status=map[200:2 429:18] total=117.7617ms avg=13.83308ms
|
||||
- Interpretation: current login rate limiter absorbs most burst traffic with 429, while successful requests remained sub-second and no 5xx appeared.
|
||||
|
||||
## Browser Flow Baseline
|
||||
|
||||
- Source command: `cd frontend/admin && npm.cmd run e2e:auth-smoke:win`
|
||||
- login-initial: 99ms
|
||||
- login-desktop: 186ms
|
||||
- login-tablet: 117ms
|
||||
- login-mobile: 96ms
|
||||
- Interpretation: current raw CDP browser validation stayed well below the existing `HighResponseTime` alert threshold of 1s in `deployment/alertmanager/alerts.yml`.
|
||||
|
||||
## Evidence Files
|
||||
|
||||
- concurrent-login-20260327-182005.txt
|
||||
- raw-cdp-auth-smoke-20260327-182005.txt
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
=== RUN TestE2EConcurrentLogin
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/register | status: 200 | latency: 110.5421ms | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 611.7µs | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 429 | latency: 0s | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 200 | latency: 109.2177ms | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
2026/03/27 18:20:09 [API] 2026-03-27 18:20:09 POST /api/v1/auth/login | status: 200 | latency: 116.7157ms | ip: 127.0.0.1 | user_id: <nil> | ua: Go-http-client/1.1
|
||||
e2e_test.go:397: 并发登录结果: 成功=2 失败=18 状态码分布=map[200:2 429:18] 总耗时=117.7617ms 平均=13.83308ms
|
||||
--- PASS: TestE2EConcurrentLogin (0.24s)
|
||||
PASS
|
||||
ok github.com/user-management-system/internal/e2e 0.520s
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
> admin@0.0.0 e2e:auth-smoke:win
|
||||
> powershell -ExecutionPolicy Bypass -File ./scripts/run-cdp-auth-smoke.ps1
|
||||
|
||||
CDP browser: C:\Users\Admin\AppData\Local\ms-playwright\chromium_headless_shell-1208\chrome-headless-shell-win64\chrome-headless-shell.exe
|
||||
CDP endpoint ready: http://127.0.0.1:64521/json/version
|
||||
Launching command: node ./scripts/run-cdp-smoke.mjs
|
||||
CDP smoke completed successfully
|
||||
browser: HeadlessChrome/145.0.7632.6
|
||||
title: 用户管理系统
|
||||
capabilities: password=true email=false sms=false passwordReset=false
|
||||
tabs:
|
||||
forgot-password path: disabled
|
||||
protected dashboard redirect: /login (from=/dashboard)
|
||||
protected users redirect: /login (from=/users)
|
||||
pre-login users redirect from: /users
|
||||
login landing path: /users
|
||||
user detail title: 用户详情
|
||||
assign roles title: 分配角色 - e2e_admin
|
||||
roles path: /roles
|
||||
permissions title: 分配权限 - 管理员
|
||||
dashboard path: /dashboard
|
||||
logout path: /login
|
||||
post-logout dashboard redirect: /login (from=/dashboard)
|
||||
post-logout users redirect: /login (from=/users)
|
||||
responsive:
|
||||
- desktop: innerWidth=1920, bodyScrollWidth=1920
|
||||
- tablet: innerWidth=768, bodyScrollWidth=768
|
||||
- mobile: innerWidth=375, bodyScrollWidth=375
|
||||
load timings:
|
||||
- login-initial: 99ms
|
||||
- login-desktop: 186ms
|
||||
- login-tablet: 117ms
|
||||
- login-mobile: 96ms
|
||||
@@ -0,0 +1 @@
|
||||
npm warn Unknown user config "//git@github.com/" (git config --global url."https://github.com/".insteadOf ssh://git@github.com/). This will stop working in the next major version of npm.
|
||||
@@ -0,0 +1,70 @@
|
||||
# 2026-03-27 Auth Session Hardening Remediation
|
||||
|
||||
## Scope
|
||||
|
||||
- Q-001 session hardening
|
||||
- Q-002 OAuth return_to trust-boundary hardening
|
||||
- Q-003 security-sensitive random fail-close hardening
|
||||
- real-browser E2E closure after the auth/session model change
|
||||
|
||||
## Implemented Remediation
|
||||
|
||||
- Backend refresh continuity now uses a backend-managed `HttpOnly` refresh cookie.
|
||||
- Frontend access token, current user, and current roles are memory-only; they are no longer persisted into `localStorage` or `sessionStorage`.
|
||||
- Backend now also sets a non-sensitive session-presence cookie (`ums_session_present`) so the frontend can distinguish:
|
||||
- "there may be a server session worth restoring"
|
||||
- "there is clearly no session, so do not probe `/auth/refresh`"
|
||||
- Frontend `AuthProvider` now:
|
||||
- skips restore probing when the session-presence cookie is absent
|
||||
- keeps restore probing available when the cookie exists, including page reload on protected pages
|
||||
- stops performing its own redirect on restore failure and lets `RequireAuth` preserve the original `from` route
|
||||
- exports effective auth state from the in-memory session store to avoid post-login route races
|
||||
- OAuth `return_to` no longer trusts request-derived forwarded origin inference and is restricted to:
|
||||
- absolute frontend paths
|
||||
- explicitly allowlisted origins
|
||||
- `crypto/rand` failure no longer silently degrades into weaker random generation for JWT JTI, email code, or captcha identifiers.
|
||||
|
||||
## Validation
|
||||
|
||||
Validated on 2026-03-27 with:
|
||||
|
||||
```powershell
|
||||
go test ./... -count=1
|
||||
go vet ./...
|
||||
go build ./cmd/server
|
||||
|
||||
cd D:\project\frontend\admin
|
||||
npm.cmd run test:run
|
||||
npm.cmd run lint
|
||||
npm.cmd run build
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\run-playwright-auth-e2e.ps1
|
||||
```
|
||||
|
||||
## Latest Real Result
|
||||
|
||||
- Backend gates: passed
|
||||
- Frontend gates: passed
|
||||
- Real browser CDP E2E: passed
|
||||
- Verified E2E scenarios:
|
||||
- `admin-bootstrap`
|
||||
- `public-registration`
|
||||
- `email-activation`
|
||||
- `login-surface`
|
||||
- `auth-workflow`
|
||||
- `responsive-login`
|
||||
- `desktop-mobile-navigation`
|
||||
|
||||
## Real Outcome
|
||||
|
||||
- Q-001 is no longer a current open high-risk issue in the project's implemented session model.
|
||||
- Q-002 is no longer a current open high-risk issue in the OAuth frontend return path trust boundary.
|
||||
- Q-003 is no longer a current open medium-risk issue in security-sensitive randomness handling.
|
||||
- This remediation also closed a real regression introduced during the session hardening pass:
|
||||
- public or unauthenticated route loads no longer emit browser console `400 Bad Request` noise from blind `/auth/refresh` probing
|
||||
- protected-route redirects again preserve the original route intent through `RequireAuth`
|
||||
|
||||
## Remaining Real Gaps
|
||||
|
||||
- Q-004 automation coverage depth is still insufficient in several low-level/backend modules and key frontend containers.
|
||||
- Q-005 dev toolchain SCA findings are still not fully cleared.
|
||||
- Q-006 external alert delivery evidence is still not fully closed.
|
||||
@@ -0,0 +1,80 @@
|
||||
# 2026-03-27 Q-004 Coverage Remediation
|
||||
|
||||
## Scope
|
||||
|
||||
- Objective: continue remediating `Q-004 自动化覆盖率不足,回归安全网偏薄`.
|
||||
- This round focused on the highest-value low-coverage areas identified in the audit:
|
||||
- Frontend: `router`, auth guards, `AdminLayout`, `ImportExportPage`
|
||||
- Backend: `internal/database`, `internal/auth/providers`
|
||||
|
||||
## Changes
|
||||
|
||||
### Frontend
|
||||
|
||||
- Added route and guard regression tests:
|
||||
- `frontend/admin/src/app/router.test.tsx`
|
||||
- `frontend/admin/src/components/guards/guards.test.tsx`
|
||||
- Added navigation and permission-surface tests:
|
||||
- `frontend/admin/src/layouts/AdminLayout/AdminLayout.test.tsx`
|
||||
- Added import/export product-flow tests:
|
||||
- `frontend/admin/src/pages/admin/ImportExportPage/ImportExportPage.test.tsx`
|
||||
- Tightened the new router test so it passes both `vitest` and `tsc -b`.
|
||||
|
||||
### Backend
|
||||
|
||||
- Added database bootstrap and upgrade-path coverage:
|
||||
- `internal/database/db_test.go`
|
||||
- Added provider auth URL/state coverage without live-network dependency:
|
||||
- `internal/auth/providers/provider_urls_test.go`
|
||||
- Fixed Windows-specific SQLite test cleanup by explicitly closing the underlying `sql.DB` handle in `db_test.go`.
|
||||
- Removed an invalid provider test that attempted to hit the real Google endpoint during unit test execution.
|
||||
|
||||
## Verified Commands
|
||||
|
||||
```powershell
|
||||
cd D:\project\frontend\admin
|
||||
npm.cmd run test:run -- src/components/guards/guards.test.tsx src/app/router.test.tsx src/layouts/AdminLayout/AdminLayout.test.tsx src/pages/admin/ImportExportPage/ImportExportPage.test.tsx
|
||||
npm.cmd run lint
|
||||
npm.cmd run build
|
||||
npm.cmd run test:coverage
|
||||
|
||||
cd D:\project
|
||||
go test ./internal/database ./internal/auth/providers -count=1
|
||||
go test ./... -count=1
|
||||
go vet ./...
|
||||
go test ./internal/auth/providers ./internal/database ./internal/repository -cover
|
||||
```
|
||||
|
||||
## Results
|
||||
|
||||
### Frontend coverage
|
||||
|
||||
- Overall:
|
||||
- statements `37.09%`
|
||||
- branches `35.91%`
|
||||
- functions `30.30%`
|
||||
- lines `37.40%`
|
||||
- Target modules:
|
||||
- `src/app/router.tsx`: `47.72%` statements
|
||||
- `src/components/guards/RequireAuth.tsx`: `100%`
|
||||
- `src/components/guards/RequireAdmin.tsx`: `100%`
|
||||
- `src/layouts/AdminLayout/AdminLayout.tsx`: `80.00%`
|
||||
- `src/pages/admin/ImportExportPage/ImportExportPage.tsx`: `83.58%`
|
||||
|
||||
### Backend coverage
|
||||
|
||||
- `internal/database`: `83.2%`
|
||||
- `internal/auth/providers`: `4.0%`
|
||||
- `internal/repository`: `10.5%`
|
||||
|
||||
## Real Conclusion
|
||||
|
||||
- This round materially improves the regression net around routing, auth gating, admin navigation, import/export flows, and database bootstrap behavior.
|
||||
- `Q-004` is improved but not fully closed.
|
||||
- The real remaining coverage gaps are now concentrated in:
|
||||
- Frontend: `UsersPage`, `WebhooksPage`, large parts of `ProfileSecurityPage`, and several service/http branches
|
||||
- Backend: `internal/repository` and deeper `internal/auth/providers` token-validation/error branches
|
||||
- Based on the current state, the next open remediation priority remains:
|
||||
1. continue `Q-004` depth expansion
|
||||
2. then `Q-005` dev toolchain SCA cleanup
|
||||
3. then `Q-006` external alert delivery evidence closure
|
||||
@@ -0,0 +1,78 @@
|
||||
# 2026-03-27 Q-004 Coverage Remediation Pass 2
|
||||
|
||||
## Scope
|
||||
|
||||
- Continue remediating `Q-004 自动化覆盖率不足,回归安全网偏薄`.
|
||||
- This pass focused on:
|
||||
- Frontend `WebhooksPage` and `services/webhooks.ts`
|
||||
- Backend `internal/repository/webhook_repository.go`
|
||||
- Frontend production build stability under current `Vite 8 + Windows + --configLoader native`
|
||||
|
||||
## Changes
|
||||
|
||||
### Frontend
|
||||
|
||||
- Added page-level regression tests for:
|
||||
- `frontend/admin/src/pages/admin/WebhooksPage/WebhooksPage.test.tsx`
|
||||
- Added service-level tests for:
|
||||
- `frontend/admin/src/services/webhooks.test.ts`
|
||||
- Added a Windows/Vite stability fix:
|
||||
- `frontend/admin/vite.config.js`
|
||||
- explicitly set `build.rollupOptions.input = 'index.html'` to avoid the native-loader absolute HTML input emission failure observed during `npm.cmd run build`
|
||||
|
||||
### Backend
|
||||
|
||||
- Added repository tests for:
|
||||
- `internal/repository/webhook_repository_test.go`
|
||||
- Hardened Webhook repository create behavior:
|
||||
- `internal/repository/webhook_repository.go`
|
||||
- explicit inactive status (`status=0`) is now preserved instead of being swallowed by the DB default during `Create`
|
||||
|
||||
## Verified Commands
|
||||
|
||||
```powershell
|
||||
cd D:\project\frontend\admin
|
||||
npm.cmd run test:run -- src/pages/admin/WebhooksPage/WebhooksPage.test.tsx src/services/webhooks.test.ts
|
||||
npm.cmd run lint
|
||||
npm.cmd run build
|
||||
npm.cmd run test:coverage
|
||||
|
||||
cd D:\project
|
||||
go test ./internal/repository -count=1
|
||||
go test ./... -count=1
|
||||
go vet ./...
|
||||
go build ./cmd/server
|
||||
go test ./internal/auth/providers ./internal/database ./internal/repository -cover
|
||||
```
|
||||
|
||||
## Results
|
||||
|
||||
### Frontend coverage
|
||||
|
||||
- Overall:
|
||||
- statements `41.06%`
|
||||
- branches `38.48%`
|
||||
- functions `36.00%`
|
||||
- lines `41.47%`
|
||||
- Target modules:
|
||||
- `src/pages/admin/WebhooksPage/WebhooksPage.tsx`: `93.15%`
|
||||
- `src/services/webhooks.ts`: `100%`
|
||||
- `src/components/guards/RequireAuth.tsx`: `100%`
|
||||
- `src/components/guards/RequireAdmin.tsx`: `100%`
|
||||
- `src/layouts/AdminLayout/AdminLayout.tsx`: `80.00%`
|
||||
- `src/pages/admin/ImportExportPage/ImportExportPage.tsx`: `83.58%`
|
||||
|
||||
### Backend coverage
|
||||
|
||||
- `internal/database`: `83.2%`
|
||||
- `internal/repository`: `15.1%`
|
||||
- `internal/auth/providers`: `4.0%`
|
||||
|
||||
## Real Conclusion
|
||||
|
||||
- This pass materially strengthens regression coverage for Webhook list/filter/action flows and their client adapters.
|
||||
- It also closed one real build stability issue on Windows for the currently supported frontend build entry.
|
||||
- `Q-004` is improved again, but still not fully closed.
|
||||
- The current main remaining coverage gaps are:
|
||||
- Frontend: `UsersPage`, deeper `ProfileSecurityPage`, and multiple service/http branches
|
||||
- Backend: `internal/auth/providers` deep token-validation/error paths and the rest of `internal/repository`
|
||||
@@ -0,0 +1,97 @@
|
||||
# 2026-03-27 Q-004 Coverage Remediation Pass 3
|
||||
|
||||
## Scope
|
||||
|
||||
- Continue remediating `Q-004 自动化覆盖率不足`.
|
||||
- This pass stayed on the remaining real gaps instead of moving to `Q-005`.
|
||||
- Focus areas:
|
||||
- Frontend `UsersPage`
|
||||
- Frontend deeper `ProfileSecurityPage` action branches
|
||||
- Backend `internal/repository`
|
||||
- Backend `internal/auth/providers`
|
||||
|
||||
## Changes
|
||||
|
||||
### Frontend
|
||||
|
||||
- Added page-level regression tests for:
|
||||
- `frontend/admin/src/pages/admin/UsersPage/UsersPage.test.tsx`
|
||||
- `frontend/admin/src/pages/admin/ProfileSecurityPage/ProfileSecurityPage.behavior.test.tsx`
|
||||
- Newly covered user-facing branches include:
|
||||
- `UsersPage`
|
||||
- initial load
|
||||
- keyword filter and reset
|
||||
- refresh
|
||||
- create/detail/edit/assign-role overlay flows
|
||||
- self-delete guard
|
||||
- all status transition branches (`1 -> 3`, `3 -> 1`, `2 -> 1`, `0 -> 1`)
|
||||
- role-fetch failure
|
||||
- page error retry
|
||||
- `ProfileSecurityPage`
|
||||
- TOTP setup open
|
||||
- enable validation and success path
|
||||
- disable validation and success path
|
||||
- avatar type and size validation
|
||||
- valid avatar upload success path
|
||||
- device enable/disable/delete flows
|
||||
|
||||
### Backend
|
||||
|
||||
- Added repository regression tests in:
|
||||
- `internal/repository/repository_additional_test.go`
|
||||
- Added provider non-network coverage in:
|
||||
- `internal/auth/providers/provider_urls_additional_test.go`
|
||||
- Fixed one real repository defect in:
|
||||
- `internal/repository/device.go`
|
||||
- explicit `status=0` on device create was being swallowed by the DB default, causing inactive devices to persist as active
|
||||
|
||||
## Verified Commands
|
||||
|
||||
```powershell
|
||||
cd D:\project\frontend\admin
|
||||
npm.cmd run test:run -- src/pages/admin/UsersPage/UsersPage.test.tsx src/pages/admin/ProfileSecurityPage/ProfileSecurityPage.behavior.test.tsx
|
||||
npm.cmd run lint
|
||||
npm.cmd run build
|
||||
npm.cmd run test:coverage
|
||||
|
||||
cd D:\project
|
||||
$env:GOCACHE='D:\project\.tmp\gocache'
|
||||
$env:GOPATH='D:\project\.tmp\go'
|
||||
$env:GOMODCACHE='D:\project\.tmp\go\pkg\mod'
|
||||
go test ./internal/repository ./internal/auth/providers -count=1
|
||||
go test ./internal/auth/providers ./internal/repository -cover
|
||||
go test ./... -count=1
|
||||
go vet ./...
|
||||
go build ./cmd/server
|
||||
```
|
||||
|
||||
## Results
|
||||
|
||||
### Frontend coverage
|
||||
|
||||
- Overall:
|
||||
- statements `49.18%`
|
||||
- branches `42.86%`
|
||||
- functions `44.92%`
|
||||
- lines `49.79%`
|
||||
- Target modules:
|
||||
- `src/pages/admin/UsersPage/UsersPage.tsx`: `90.98%` statements, `68.75%` branches
|
||||
- `src/pages/admin/ProfileSecurityPage/ProfileSecurityPage.tsx`: `70.17%` statements, `48.97%` branches
|
||||
- `src/pages/admin/ProfileSecurityPage/ContactBindingsSection.tsx`: `85.29%` statements
|
||||
- `src/pages/admin/WebhooksPage/WebhooksPage.tsx`: `93.15%`
|
||||
- `src/pages/admin/ImportExportPage/ImportExportPage.tsx`: `83.58%`
|
||||
|
||||
### Backend coverage
|
||||
|
||||
- `internal/repository`: `37.1%`
|
||||
- `internal/auth/providers`: `8.5%`
|
||||
|
||||
## Real Conclusion
|
||||
|
||||
- This pass materially reduced the previously explicit `UsersPage` and `ProfileSecurityPage` gaps.
|
||||
- It also exposed and closed a real persistence bug in `DeviceRepository.Create`.
|
||||
- `Q-004` has improved again, but it still cannot be honestly declared closed.
|
||||
- The main remaining gaps are still:
|
||||
- low-coverage frontend service layers and multiple untouched admin pages
|
||||
- deeper `internal/auth/providers` non-network parsing/error paths
|
||||
- the rest of `internal/repository` beyond the newly covered device/login-log/password-history/operation-log paths
|
||||
@@ -0,0 +1,86 @@
|
||||
# 2026-03-27 Q-004 Coverage Remediation Pass 4
|
||||
|
||||
## Scope
|
||||
|
||||
- Continue remediating the remaining real `Q-004` gaps after Pass 3.
|
||||
- This pass focused on:
|
||||
- low-coverage frontend service adapters
|
||||
- deeper non-network `internal/auth/providers` branches
|
||||
|
||||
## Changes
|
||||
|
||||
### Frontend
|
||||
|
||||
- Added adapter-level regression coverage in:
|
||||
- `frontend/admin/src/services/service_adapters_additional.test.ts`
|
||||
- Coverage added for:
|
||||
- `users.ts`
|
||||
- `roles.ts`
|
||||
- `devices.ts`
|
||||
- `profile.ts`
|
||||
- `login-logs.ts`
|
||||
- `operation-logs.ts`
|
||||
- `permissions.ts`
|
||||
- `stats.ts`
|
||||
- `import-export.ts`
|
||||
- During strict verification, `tsc -b` exposed two test-payload type mismatches in the new permission-service test data.
|
||||
- These were fixed in the test code before final validation.
|
||||
|
||||
### Backend
|
||||
|
||||
- Added provider non-network tests in:
|
||||
- `internal/auth/providers/provider_crypto_test.go`
|
||||
- `internal/auth/providers/http_test.go` (expanded)
|
||||
- Newly covered provider logic includes:
|
||||
- Alipay private-key parsing for raw PKCS#8 and PEM PKCS#1 input
|
||||
- Alipay parameter signing and signature verification
|
||||
- Twitter PKCE verifier/challenge/auth URL generation
|
||||
- OAuth helper error handling for empty-body and long-body non-2xx responses
|
||||
|
||||
## Verified Commands
|
||||
|
||||
```powershell
|
||||
cd D:\project\frontend\admin
|
||||
npm.cmd run test:run -- src/services/service_adapters_additional.test.ts src/services/users.test.ts
|
||||
npm.cmd run lint
|
||||
npm.cmd run build
|
||||
npm.cmd run test:coverage
|
||||
|
||||
cd D:\project
|
||||
$env:GOCACHE='D:\project\.tmp\gocache'
|
||||
$env:GOPATH='D:\project\.tmp\go'
|
||||
$env:GOMODCACHE='D:\project\.tmp\go\pkg\mod'
|
||||
go test ./internal/auth/providers -count=1
|
||||
go test ./internal/auth/providers ./internal/repository -cover
|
||||
go test ./... -count=1
|
||||
go vet ./...
|
||||
go build ./cmd/server
|
||||
```
|
||||
|
||||
## Results
|
||||
|
||||
### Frontend coverage
|
||||
|
||||
- Overall:
|
||||
- statements `52.05%`
|
||||
- branches `42.86%`
|
||||
- functions `51.84%`
|
||||
- lines `52.69%`
|
||||
- Target areas:
|
||||
- `services`: `86.2%` statements, `90.9%` branches
|
||||
- `src/pages/admin/UsersPage/UsersPage.tsx`: `90.98%`
|
||||
- `src/pages/admin/ProfileSecurityPage/ProfileSecurityPage.tsx`: `70.17%`
|
||||
|
||||
### Backend coverage
|
||||
|
||||
- `internal/auth/providers`: `15.2%`
|
||||
- `internal/repository`: `37.1%`
|
||||
|
||||
## Real Conclusion
|
||||
|
||||
- This pass substantially reduced the frontend service-layer gap; that is no longer a primary blocker for `Q-004`.
|
||||
- `internal/auth/providers` improved again, but it is still materially under-covered.
|
||||
- `Q-004` still cannot be honestly declared closed.
|
||||
- The main remaining real gaps are now concentrated in:
|
||||
- more untouched admin pages/components
|
||||
- deeper provider token/user-info parsing and error branches that can still be exercised without live network calls
|
||||
@@ -0,0 +1,100 @@
|
||||
# 2026-03-27 Q-004 Coverage Remediation Pass 5
|
||||
|
||||
## Scope
|
||||
|
||||
- Continue remediating the remaining real `Q-004` gaps after Pass 4.
|
||||
- This pass focused on:
|
||||
- deeper non-network `internal/auth/providers` parsing and error branches
|
||||
- low-coverage admin log pages
|
||||
|
||||
## Changes
|
||||
|
||||
### Frontend
|
||||
|
||||
- Added page-level regression coverage in:
|
||||
- `frontend/admin/src/pages/admin/LoginLogsPage/LoginLogsPage.test.tsx`
|
||||
- `frontend/admin/src/pages/admin/OperationLogsPage/OperationLogsPage.test.tsx`
|
||||
- Newly covered page behavior includes:
|
||||
- initial load
|
||||
- filter application
|
||||
- reset
|
||||
- refresh
|
||||
- pagination callback handling
|
||||
- detail drawer open/close flow
|
||||
- page-level error retry
|
||||
|
||||
### Backend
|
||||
|
||||
- Added provider non-network transport tests in:
|
||||
- `internal/auth/providers/provider_http_roundtrip_test.go`
|
||||
- The new tests replace live network dependencies with a fake `http.DefaultTransport`.
|
||||
- Newly covered provider logic includes:
|
||||
- `QQProvider.GetOpenID` success and invalid-JSON failure
|
||||
- `QQProvider.GetUserInfo` API-error and success branches
|
||||
- `WeiboProvider.ValidateToken` error / valid / ambiguous / invalid-JSON branches
|
||||
- `WeChatProvider.ValidateToken` success / failure / invalid-JSON branches
|
||||
- `GoogleProvider.ValidateToken` success and parse-failure branches
|
||||
- `FacebookProvider.GetUserInfo` `error.message` and success branches
|
||||
|
||||
## Verified Commands
|
||||
|
||||
```powershell
|
||||
cd D:\project\frontend\admin
|
||||
npm.cmd run test:run -- src/pages/admin/LoginLogsPage/LoginLogsPage.test.tsx
|
||||
npm.cmd run test:run -- src/pages/admin/OperationLogsPage/OperationLogsPage.test.tsx
|
||||
npm.cmd run lint
|
||||
npm.cmd run build
|
||||
npm.cmd run test:coverage
|
||||
|
||||
cd D:\project
|
||||
$env:GOCACHE='D:\project\.tmp\gocache'
|
||||
$env:GOPATH='D:\project\.tmp\go'
|
||||
$env:GOMODCACHE='D:\project\.tmp\go\pkg\mod'
|
||||
go test ./internal/auth/providers -run 'Test(QQProviderGetOpenIDAndUserInfoWithDefaultTransport|WeiboProviderValidateTokenWithDefaultTransport|WeChatProviderValidateTokenWithDefaultTransport|GoogleProviderValidateTokenWithDefaultTransport|FacebookProviderGetUserInfoWithDefaultTransport)$' -count=1
|
||||
go test ./... -count=1
|
||||
go vet ./...
|
||||
go build ./cmd/server
|
||||
go test ./internal/auth/providers ./internal/repository -cover -count=1
|
||||
```
|
||||
|
||||
## Results
|
||||
|
||||
### Frontend coverage
|
||||
|
||||
- Overall:
|
||||
- statements `56.81%`
|
||||
- branches `44.67%`
|
||||
- functions `57.38%`
|
||||
- lines `57.57%`
|
||||
- Target areas:
|
||||
- `services`: `86.2%` statements, `90.9%` branches
|
||||
- `src/pages/admin/LoginLogsPage/LoginLogsPage.tsx`: `93.1%` statements, `55.55%` branches
|
||||
- `src/pages/admin/OperationLogsPage/OperationLogsPage.tsx`: `91.52%` statements, `68.75%` branches
|
||||
|
||||
### Backend coverage
|
||||
|
||||
- `internal/auth/providers`: `28.7%`
|
||||
- `internal/repository`: `37.1%`
|
||||
|
||||
## Validation Notes
|
||||
|
||||
- `npm.cmd run test:coverage` completed successfully with `28` passing test files and `96` passing tests.
|
||||
- The same successful coverage run still printed one post-summary jsdom `AggregateError` network-noise line.
|
||||
- This did not fail the command.
|
||||
- It is still a real residual test-hygiene issue and should be traced separately instead of being ignored or misrepresented as a clean zero-noise run.
|
||||
|
||||
## Real Conclusion
|
||||
|
||||
- This pass materially reduced two real `Q-004` gaps:
|
||||
- admin log pages are no longer among the main uncovered page-level hotspots
|
||||
- `internal/auth/providers` improved substantially from the previous `15.2%`, but remains materially under-covered
|
||||
- `Q-004` still cannot be honestly declared closed.
|
||||
- The main remaining real gaps are now concentrated in:
|
||||
- deeper `internal/auth/providers` paths beyond the new non-network parsing/error coverage
|
||||
- still-uncovered admin pages/components such as:
|
||||
- `PermissionsPage`
|
||||
- `RolesPage`
|
||||
- `ProfilePage`
|
||||
- multiple admin drawers/modals that still remain near `0%`
|
||||
- Secondary remaining gap:
|
||||
- `internal/repository` has improved, but `37.1%` is still not a closure-grade depth
|
||||
@@ -0,0 +1,804 @@
|
||||
# 2026-03-27 全量测试与质量审计
|
||||
|
||||
## 1.3 2026-03-28 Q-004 latest remediation note XIII
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `89.72 / 77.57 / 84.48 / 90.64`
|
||||
- `src/app/App.tsx` is now `100 / 100 / 100 / 100`
|
||||
- `src/app/RootLayout.tsx` is now `100 / 100 / 100 / 100`
|
||||
- `src/components/common/ErrorBoundary/ErrorBoundary.tsx` is now `100 / 83.33 / 100 / 100`
|
||||
- The latest remediation closed three more previously real frontend hotspots:
|
||||
- `App.tsx` is no longer an open `Q-004` gap
|
||||
- `RootLayout.tsx` is no longer an open `Q-004` gap
|
||||
- `ErrorBoundary.tsx` is no longer an open `Q-004` gap
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- after the shell and boundary layer were closed, the remaining higher-value frontend gaps narrow further to router, dashboard, and shared page-state coverage
|
||||
- The validation hygiene note remains materially unchanged:
|
||||
- `npm.cmd run test:coverage` passed again, but still emitted one post-summary jsdom `AggregateError` network-noise line
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-110341.md`
|
||||
|
||||
## 1.2 2026-03-28 Q-004 latest remediation note XII
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `89.06 / 77.14 / 83.56 / 89.96`
|
||||
- `src/pages/auth/ForgotPasswordPage/ForgotPasswordPage.tsx` is now `100 / 75 / 100 / 100`
|
||||
- `src/pages/auth/ResetPasswordPage/ResetPasswordPage.tsx` is now `95 / 94.44 / 100 / 95`
|
||||
- The latest remediation closed two more previously real frontend hotspots:
|
||||
- `ForgotPasswordPage` is no longer an open `Q-004` gap
|
||||
- `ResetPasswordPage` is no longer an open `Q-004` gap
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- after the auth recovery pages were closed, the remaining higher-value frontend gaps shift more toward app shell, routing, error-boundary, and dashboard entry-point coverage
|
||||
- The validation hygiene note remains materially unchanged:
|
||||
- `npm.cmd run test:coverage` passed again, but still emitted one post-summary jsdom `AggregateError` network-noise line
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-105226.md`
|
||||
|
||||
## 1.1 2026-03-28 Q-004 latest remediation note XI
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `85.89 / 74.91 / 81.87 / 86.71`
|
||||
- `src/pages/admin/ProfileSecurityPage/ProfileSecurityPage.tsx` is now `90.35 / 75.51 / 92.45 / 90.13`
|
||||
- The latest remediation closed one more previously real frontend hotspot:
|
||||
- `src/pages/admin/ProfileSecurityPage/ProfileSecurityPage.tsx` is no longer an open `Q-004` gap
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- with `client.ts` and `ProfileSecurityPage` closed, the next highest-value frontend gaps now shift toward auth recovery pages such as `ForgotPasswordPage` and `ResetPasswordPage`
|
||||
- The validation hygiene note remains materially unchanged:
|
||||
- `npm.cmd run test:coverage` passed again, but still emitted one post-summary jsdom `AggregateError` network-noise line
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-104341.md`
|
||||
|
||||
## 1.0 2026-03-28 Q-004 latest remediation note X
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `83.86 / 72.68 / 79.87 / 84.72`
|
||||
- `src/lib/http/client.ts` is now `100 / 92.30 / 100 / 100`
|
||||
- The latest remediation closed one more previously real frontend hotspot:
|
||||
- `src/lib/http/client.ts` is no longer an open `Q-004` gap
|
||||
- This pass also closed one real validation-hygiene defect in production code:
|
||||
- cached shared refresh waiters no longer leave an unhandled rejected promise behind when refresh fails
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- the remaining highest-value frontend gap is now more concentrated in deeper `ProfileSecurityPage`
|
||||
- The validation hygiene note remains materially unchanged:
|
||||
- `npm.cmd run test:coverage` passed again, but still emitted one post-summary jsdom `AggregateError` network-noise line
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-102456.md`
|
||||
|
||||
## 0.9 2026-03-28 Q-004 latest remediation note IX
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `80.06 / 67.61 / 78.00 / 80.91`
|
||||
- `src/lib/http/csrf.ts` is now `100 / 88.46 / 100 / 100`
|
||||
- The latest remediation closed one more previously real frontend hotspot:
|
||||
- `src/lib/http/csrf.ts` is no longer an open `Q-004` gap
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- the remaining highest-value frontend gaps are now more concentrated in `src/lib/http/client.ts` and deeper `ProfileSecurityPage`
|
||||
- The validation hygiene note remains materially unchanged:
|
||||
- `npm.cmd run test:coverage` passed again, but still emitted one post-summary jsdom `AggregateError` network-noise line
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-083841.md`
|
||||
|
||||
## 0.8 2026-03-28 Q-004 latest remediation note VIII
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `78.91 / 66.06 / 77.07 / 79.73`
|
||||
- `src/pages/auth/RegisterPage/RegisterPage.tsx` is now `93.42 / 85.24 / 87.5 / 95.89`
|
||||
- The latest remediation closed one more previously real frontend hotspot:
|
||||
- `RegisterPage` is no longer an open `Q-004` gap
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- the remaining highest-value frontend gaps are now more concentrated in deeper `ProfileSecurityPage` and `lib/http`
|
||||
- The validation hygiene note remains materially unchanged:
|
||||
- `npm.cmd run test:coverage` passed again, but still emitted one post-summary jsdom `AggregateError` network-noise line
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-082843.md`
|
||||
|
||||
## 0.7 2026-03-28 Q-004 latest remediation note VII
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `78.38 / 64.77 / 76.92 / 79.19`
|
||||
- `src/pages/auth/LoginPage/LoginPage.tsx` is now `92.56 / 84.09 / 86.2 / 95.61`
|
||||
- The latest remediation closed one more previously real frontend hotspot:
|
||||
- `LoginPage` is no longer an open `Q-004` gap
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- the remaining highest-value frontend gaps are now more concentrated in `RegisterPage`, deeper `ProfileSecurityPage`, and `lib/http`
|
||||
- The validation hygiene note remains materially unchanged:
|
||||
- `npm.cmd run test:coverage` passed again, but still emitted one post-summary jsdom `AggregateError` network-noise line
|
||||
- one concurrent `lint` + `build` attempt produced a transient Windows/Vite `index.html` emit-path failure, while the required standalone `build` rerun passed immediately afterward
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-081514.md`
|
||||
|
||||
## 0.6 2026-03-28 Q-004 latest remediation note VI
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `76.00 / 63.91 / 75.07 / 76.84`
|
||||
- `src/app/providers` is now `96.38 / 93.75`
|
||||
- `src/app/providers/AuthProvider.tsx` is now `100%`
|
||||
- The latest remediation closed one more previously real frontend hotspot:
|
||||
- `AuthProvider` is no longer an open `Q-004` gap
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- the remaining highest-value frontend gaps are now more concentrated in `LoginPage`, `RegisterPage`, deeper `ProfileSecurityPage`, and `lib/http`
|
||||
- The validation hygiene note remains unchanged:
|
||||
- `npm.cmd run test:coverage` passed again, but still emitted one post-summary jsdom `AggregateError` network-noise line
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-075725.md`
|
||||
|
||||
## 0.5 2026-03-28 Q-004 latest remediation note V
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `74.54 / 63.57 / 74.61 / 75.35`
|
||||
- `src/pages/admin/UsersPage` is now `95.06%`
|
||||
- `src/pages/admin/WebhooksPage` is now `94.92%`
|
||||
- `internal/repository` is now `67.1%`
|
||||
- The latest remediation closed two previously dominant frontend gap clusters:
|
||||
- `UsersPage` drawers/modals are no longer one of the main remaining blockers
|
||||
- `WebhooksPage` modal/drawer components are no longer one of the main remaining blockers
|
||||
- A new real backend defect pair was discovered and fixed during this pass:
|
||||
- `internal/repository/role.go`
|
||||
- explicit `status=0` role creation was previously persisted as enabled
|
||||
- `internal/repository/permission.go`
|
||||
- explicit `status=0` permission creation was previously persisted as enabled
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- the remaining highest-value gaps are now more concentrated in deeper `ProfileSecurityPage`, `LoginPage`, `RegisterPage`, `AuthProvider`, `lib/http`, and still-remaining repository depth
|
||||
- The validation hygiene note remains unchanged:
|
||||
- `npm.cmd run test:coverage` passed again, but still emitted one post-summary jsdom `AggregateError` network-noise line
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-011431.md`
|
||||
|
||||
## 0.4 2026-03-28 Q-004 latest remediation note IV
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `68.32 / 54.12 / 68.15 / 69.28`
|
||||
- `src/pages/admin/RolesPage` is now at `94.53%`
|
||||
- `src/pages/admin/PermissionsPage` is now at `93.51%`
|
||||
- `src/pages/admin/ProfilePage/ProfilePage.tsx` is now at `91.42%`
|
||||
- `internal/auth/providers` is now `80.6%`
|
||||
- `internal/repository` remains `37.1%`
|
||||
- The latest remediation changed the real gap map materially:
|
||||
- provider coverage is no longer one of the dominant blockers
|
||||
- `RolesPage`, `PermissionsPage`, and `ProfilePage` are no longer dominant uncovered admin page clusters
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- the remaining highest-value gaps are now concentrated in `internal/repository` depth plus still-uncovered frontend modal/drawer components, especially under `UsersPage` and `WebhooksPage`, and deeper remaining `ProfileSecurityPage` branches
|
||||
- The validation hygiene note remains unchanged:
|
||||
- `npm.cmd run test:coverage` passed again, but still emitted one post-summary jsdom `AggregateError` network-noise line
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-003416.md`
|
||||
|
||||
## 0.3 2026-03-27 Q-004 latest remediation note III
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `56.81 / 44.67 / 57.38 / 57.57`
|
||||
- `src/pages/admin/LoginLogsPage/LoginLogsPage.tsx` is now at `93.1%`
|
||||
- `src/pages/admin/OperationLogsPage/OperationLogsPage.tsx` is now at `91.52%`
|
||||
- frontend `services` coverage remains `86.2%`
|
||||
- `internal/auth/providers` is now `28.7%`
|
||||
- `internal/repository` remains `37.1%`
|
||||
- The latest remediation reduced two real gaps materially:
|
||||
- admin log pages are no longer among the main page-level hotspots
|
||||
- provider coverage is no longer extremely shallow, but it is still far from closure-grade depth
|
||||
- A new validation hygiene note also appeared during this pass:
|
||||
- `npm.cmd run test:coverage` passed, but still emitted one post-summary jsdom `AggregateError` network-noise line
|
||||
- this is not a failing gate for the current pass, but it is still real and should not be misrepresented as a perfectly clean run
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- the main remaining gaps are now concentrated in deeper `internal/auth/providers` paths and still-uncovered admin pages/components
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-27/quality/COVERAGE_REMEDIATION_20260327-233824.md`
|
||||
|
||||
## 0.2 2026-03-27 Q-004 latest remediation note II
|
||||
|
||||
- `Q-004` was remediated further after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `52.05 / 42.86 / 51.84 / 52.69`
|
||||
- frontend `services` coverage is now `86.2%`
|
||||
- `internal/auth/providers` is now `15.2%`
|
||||
- `internal/repository` remains `37.1%`
|
||||
- The latest remediation reduced the frontend service-layer gap substantially.
|
||||
- The updated real boundary is unchanged in principle:
|
||||
- `internal/auth/providers` is still too shallow to truthfully mark `Q-004` closed
|
||||
- there are still multiple uncovered admin pages/components outside the already-remediated `UsersPage` and `ProfileSecurityPage`
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-27/quality/COVERAGE_REMEDIATION_20260327-224352.md`
|
||||
|
||||
## 0.1 2026-03-27 Q-004 latest remediation note
|
||||
|
||||
- `Q-004` has been remediated further after this audit, but it is still not closed.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `49.18 / 42.86 / 44.92 / 49.79`
|
||||
- `UsersPage.tsx` is now at `90.98%` statements and `68.75%` branches
|
||||
- `ProfileSecurityPage.tsx` is now at `70.17%` statements and `48.97%` branches
|
||||
- `internal/repository` is now at `37.1%`
|
||||
- `internal/auth/providers` is now at `8.5%`
|
||||
- A new real defect was discovered and fixed during this remediation pass:
|
||||
- `internal/repository/device.go`
|
||||
- device create requests with explicit `status=0` were previously persisted as active because the DB default swallowed the zero value
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-27/quality/COVERAGE_REMEDIATION_20260327-221835.md`
|
||||
- Updated real boundary:
|
||||
- `UsersPage` and `ProfileSecurityPage` are no longer the primary blockers they were at audit time
|
||||
- `internal/auth/providers` still remains too shallow to truthfully mark `Q-004` closed
|
||||
|
||||
## 0. 2026-03-27 优先级整改补充结论
|
||||
|
||||
以下内容覆盖本报告中 Q-001 / Q-002 / Q-003 的“当前真实状态”:
|
||||
|
||||
- Q-001 会话安全整改已完成并复验通过。
|
||||
- 浏览器端不再把 access token、refresh token、用户信息、角色信息持久化到 `localStorage` / `sessionStorage`。
|
||||
- refresh continuity 已切到后端 `HttpOnly` refresh cookie。
|
||||
- 为避免无会话用户访问受保护页时盲打 `/auth/refresh` 产生浏览器 `400 Bad Request` console error,后端新增了非敏感会话存在标记 cookie,前端先判断是否值得恢复,再决定是否发起 refresh。
|
||||
- Q-002 OAuth 信任边界整改已完成并复验通过。
|
||||
- `return_to` 不再基于 `X-Forwarded-*` 推导的 request origin 做隐式同源放行。
|
||||
- 当前只接受绝对路径,或显式 allowlist origin。
|
||||
- Q-003 随机降级 fail-open 已完成整改并复验通过。
|
||||
- `crypto/rand` 失败时不再静默退化到更弱随机源。
|
||||
- 本轮补充整改还关闭了一个真实回归:
|
||||
- 鉴于会话模型从 Web Storage 切到 cookie + memory 后,真实浏览器 E2E 一度出现公开页/无会话访问时的刷新噪音与登录后路由竞态。
|
||||
- 该问题现已通过“会话存在标记 cookie + AuthProvider 恢复策略收敛 + 认证态导出去竞态 + E2E 基座修正”收口。
|
||||
|
||||
最新补充验证命令:
|
||||
|
||||
```powershell
|
||||
go test ./... -count=1
|
||||
go vet ./...
|
||||
go build ./cmd/server
|
||||
|
||||
cd D:\project\frontend\admin
|
||||
npm.cmd run test:run
|
||||
npm.cmd run lint
|
||||
npm.cmd run build
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\run-playwright-auth-e2e.ps1
|
||||
```
|
||||
|
||||
最新补充真实结论:
|
||||
|
||||
- Q-001 / Q-002 / Q-003 已不再是本项目“当前进行时”的开放问题。
|
||||
- Q-004 已完成一轮增补整改并通过真实复验,但当前仍是“部分收口、未完全关闭”状态。
|
||||
- Frontend overall coverage 已从审计时的 `29.38 / 29.32 / 24.84 / 29.78` 提升到 `41.06 / 38.48 / 36.00 / 41.47`。
|
||||
- 重点目标中:
|
||||
- `router` 已到 `47.72%`
|
||||
- `RequireAuth` / `RequireAdmin` 已到 `100%`
|
||||
- `AdminLayout` 已到 `80.00%`
|
||||
- `ImportExportPage` 已到 `83.58%`
|
||||
- `WebhooksPage` 已到 `93.15%`
|
||||
- `services/webhooks.ts` 已到 `100%`
|
||||
- `internal/database` 已到 `83.2%`
|
||||
- `internal/repository` 已到 `15.1%`
|
||||
- 本轮还顺带关闭了一个真实构建问题:
|
||||
- 在当前 Windows + `Vite 8` + `--configLoader native` 组合下,默认 HTML 输入会导致绝对路径 `index.html` 发射错误;现已通过显式 `rollupOptions.input = 'index.html'` 收口。
|
||||
- 但 `internal/auth/providers` 仍仅 `4.0%`,前端 `UsersPage` / `ProfileSecurityPage` 仍有明显缺口。
|
||||
- 当前剩余真实缺口收敛为:
|
||||
- Q-004 自动化覆盖率深度不足
|
||||
- Q-005 dev toolchain SCA 未清零
|
||||
- Q-006 外部告警交付证据未闭环
|
||||
|
||||
补充证据:
|
||||
|
||||
- [`docs/evidence/ops/2026-03-27/quality/AUTH_SESSION_REMEDIATION_20260327-194100.md`](/D:/project/docs/evidence/ops/2026-03-27/quality/AUTH_SESSION_REMEDIATION_20260327-194100.md)
|
||||
- [`docs/evidence/ops/2026-03-27/quality/COVERAGE_REMEDIATION_20260327-212336.md`](/D:/project/docs/evidence/ops/2026-03-27/quality/COVERAGE_REMEDIATION_20260327-212336.md)
|
||||
- [`docs/evidence/ops/2026-03-27/quality/COVERAGE_REMEDIATION_20260327-214422.md`](/D:/project/docs/evidence/ops/2026-03-27/quality/COVERAGE_REMEDIATION_20260327-214422.md)
|
||||
|
||||
## 1. 审计方法
|
||||
|
||||
- 会话内可用 skill 中没有现成的通用 testing/quality skill。
|
||||
- 使用 `skill-installer` 检索了可安装技能,识别到 `playwright` 与 `security-best-practices` 可覆盖真实浏览器验证与安全审计。
|
||||
- 由于当前沙箱对 skill 安装临时目录写入有限制,未能将 skill 正式安装到本地目录;本轮直接拉取并按其规范执行:
|
||||
- `playwright`:以 CLI-first / real browser 为原则,沿用项目现有真实浏览器 E2E 路径验证。
|
||||
- `security-best-practices`:按 Go backend + React/TypeScript frontend 的安全审计规则做证据化检查。
|
||||
- 同时严格按照项目自身质量基线执行:`docs/team/QUALITY_STANDARD.md`。
|
||||
|
||||
## 2. 已执行门禁
|
||||
|
||||
### 2.1 Backend
|
||||
|
||||
```powershell
|
||||
go vet ./...
|
||||
go test ./... -count=1
|
||||
go build ./cmd/server
|
||||
go test ./... -cover
|
||||
```
|
||||
|
||||
结论:通过。
|
||||
|
||||
### 2.2 Frontend
|
||||
|
||||
```powershell
|
||||
cd frontend/admin
|
||||
npm.cmd run lint
|
||||
npm.cmd run test:run
|
||||
npm.cmd run build
|
||||
npm.cmd run test:coverage
|
||||
```
|
||||
|
||||
结论:通过。
|
||||
|
||||
### 2.3 Real Browser E2E
|
||||
|
||||
```powershell
|
||||
cd frontend/admin
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\run-playwright-auth-e2e.ps1
|
||||
```
|
||||
|
||||
结论:通过。
|
||||
|
||||
本轮真实浏览器场景包含:
|
||||
|
||||
- `admin-bootstrap`
|
||||
- `public-registration`
|
||||
- `email-activation`
|
||||
- `login-surface`
|
||||
- `auth-workflow`
|
||||
- `responsive-login`
|
||||
- `desktop-mobile-navigation`
|
||||
|
||||
### 2.4 运维治理与交付证据
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\ops\run-sca-evidence.ps1
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\ops\capture-local-baseline.ps1
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\ops\validate-alerting-package.ps1
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\ops\drill-alertmanager-render.ps1
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\ops\drill-sqlite-backup-restore.ps1
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\ops\drill-config-isolation.ps1
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\ops\drill-local-rollback.ps1
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\ops\validate-secret-boundary.ps1
|
||||
```
|
||||
|
||||
结论:
|
||||
|
||||
- SCA:生产依赖通过,完整依赖树未清零。
|
||||
- 本地观测基线:通过。
|
||||
- Alerting 包结构校验:通过,但外部通知闭环未完成。
|
||||
- Alertmanager 渲染演练:通过。
|
||||
- SQLite 备份恢复演练:通过。
|
||||
- 配置/环境隔离演练:通过。
|
||||
- 本地回滚演练:通过。
|
||||
- 密钥边界校验:通过。
|
||||
|
||||
## 3. 正向结论
|
||||
|
||||
- 当前项目“可执行质量门禁”整体较强:后端、前端、真实浏览器 E2E、本地治理演练都能真实跑通。
|
||||
- 真实浏览器链路已经不是 smoke 假闭环,而是可重复执行的产品级主链路验证。
|
||||
- 前端未发现明显高信号 DOM XSS 反模式:
|
||||
- 未扫到 `dangerouslySetInnerHTML`
|
||||
- 未扫到 `eval/new Function/document.write`
|
||||
- Release 模式下对 wildcard CORS 有显式拒绝测试,基础安全头中间件也已接入。
|
||||
|
||||
## 4. 真实问题清单
|
||||
|
||||
### Q-001 高风险:浏览器端仍将 access/refresh token 持久化到 Web Storage
|
||||
|
||||
- 位置:
|
||||
- `frontend/admin/src/lib/storage/token-storage.ts:4-5`
|
||||
- `frontend/admin/src/lib/storage/token-storage.ts:25-27`
|
||||
- `frontend/admin/src/lib/http/auth-session.ts:5-6`
|
||||
- `frontend/admin/src/lib/http/auth-session.ts:121-123`
|
||||
- `frontend/admin/src/lib/http/auth-session.ts:140`
|
||||
- `frontend/admin/src/lib/http/auth-session.ts:153`
|
||||
- 证据:
|
||||
- refresh token 落在 `localStorage`
|
||||
- access token、用户信息、角色信息落在 `sessionStorage`
|
||||
- 影响:
|
||||
- 一旦前端发生 XSS、浏览器扩展注入或同机恶意读取,令牌可被直接窃取。
|
||||
- 这不符合企业级生产产品对会话凭证的保守策略。
|
||||
- 结论:
|
||||
- 当前“功能可用”不等于“会话安全成熟”。
|
||||
- 更稳妥的方向应是 `HttpOnly + Secure + SameSite` cookie,或 BFF / server session 模式。
|
||||
|
||||
### Q-002 高风险:OAuth return_to 校验依赖未受信任代理证明的转发头
|
||||
|
||||
- 位置:
|
||||
- `internal/api/handler/auth.go:511-524`
|
||||
- `internal/api/handler/auth.go:567-588`
|
||||
- 证据:
|
||||
- `oauthRequestOrigin` 直接信任 `X-Forwarded-Proto` 与 `X-Forwarded-Host`
|
||||
- `resolveOAuthReturnTo` 允许 `return_to` 与该 request origin 相同即通过
|
||||
- 影响:
|
||||
- 如果边缘代理未明确剥离/重写这些头,攻击者可能伪造头值影响 OAuth 回跳来源判断。
|
||||
- 该问题至少会造成 origin trust 边界不清;在配置失误时可退化为开放跳转/回跳接收面扩大。
|
||||
- 结论:
|
||||
- 这是典型的“代码层看见依赖 forwarded headers,但仓内没有可信代理证明”的问题。
|
||||
- 当前应视为高风险边界项,而不是默认安全。
|
||||
|
||||
### Q-003 中风险:安全敏感随机值存在 fail-open 降级
|
||||
|
||||
- 位置:
|
||||
- `internal/auth/jwt.go:62-65`
|
||||
- `internal/service/email.go:295-297`
|
||||
- `internal/service/captcha.go:142-145`
|
||||
- 证据:
|
||||
- `crypto/rand` 失败后,JWT JTI / email code / captcha ID 会退化到时间戳或 `math/rand`
|
||||
- 影响:
|
||||
- 熵源异常时没有 fail closed,而是继续生成可预测性更强的值。
|
||||
- 这不是主路径问题,但不符合严格生产安全设计。
|
||||
- 结论:
|
||||
- 应改为显式报错并阻断相关安全流程,而不是静默降级。
|
||||
|
||||
### Q-004 中风险:自动化覆盖率不足,回归安全网偏薄
|
||||
|
||||
- Frontend 总覆盖率:
|
||||
- statements `29.38%`
|
||||
- branches `29.32%`
|
||||
- functions `24.84%`
|
||||
- lines `29.78%`
|
||||
- Backend 覆盖率示例:
|
||||
- `internal/service` `51.8%`
|
||||
- `internal/api/handler` `31.4%`
|
||||
- `internal/auth` `34.3%`
|
||||
- `internal/auth/providers` `1.5%`
|
||||
- `internal/repository` `10.5%`
|
||||
- `internal/database` `0.0%`
|
||||
- 影响:
|
||||
- 当前 E2E 很强,但底层模块和异常分支的自动回归网仍然偏弱。
|
||||
|
||||
### Q-005 中风险:完整依赖树 SCA 未清零
|
||||
|
||||
- 结果:
|
||||
- `npm audit production`: `0`
|
||||
- `npm audit full`: `22`
|
||||
- 其中 `21 moderate`,`1 high`
|
||||
- `govulncheck reachable findings`: `0`
|
||||
- 主要链路:
|
||||
- `picomatch` 高危
|
||||
- `vite` / `vitest` / `typescript-eslint` / `eslint` 相关 dev toolchain 链路存在中危项
|
||||
- 影响:
|
||||
- 生产依赖当前较干净。
|
||||
- 但工程供应链本身还不能称为“完全收口”。
|
||||
|
||||
### Q-006 中风险:外部告警交付证据未闭环
|
||||
|
||||
- 结果:
|
||||
- `Repo-level alerting package structurally ready: True`
|
||||
- `Repo-level oncall/delivery package fully closed: False`
|
||||
- 影响:
|
||||
- 仓内模板、结构、演练已具备。
|
||||
- 但真实外部通知联系人/渠道的交付闭环证据还缺。
|
||||
|
||||
## 5. 综合判断
|
||||
|
||||
### 5.1 已达到的水平
|
||||
|
||||
- 可以真实表述为:
|
||||
- “项目当前可执行质量门禁整体通过,后端/前端/真实浏览器 E2E/本地治理演练已形成一轮真实闭环。”
|
||||
|
||||
### 5.2 不能夸大的表述
|
||||
|
||||
- 目前不能真实表述为:
|
||||
- “已经完全达到企业级生产上线质量”
|
||||
- “安全与治理材料全部闭环”
|
||||
- “自动化测试覆盖已经充分”
|
||||
|
||||
### 5.3 真实状态
|
||||
|
||||
- 当前更准确的结论是:
|
||||
- 执行层面很强,产品主链路和真实浏览器验证已明显成熟。
|
||||
- 但安全会话模型、反向代理信任边界、覆盖率、dev 供应链漏洞、外部告警交付证据,仍是生产级质量的真实缺口。
|
||||
|
||||
## 6. 下一步优先级
|
||||
|
||||
1. 会话安全整改
|
||||
- 移除 Web Storage 中的 access/refresh token 持久化。
|
||||
- 切到 HttpOnly cookie 或 BFF / server session。
|
||||
2. OAuth 信任边界整改
|
||||
- 不再直接信任 `X-Forwarded-*`。
|
||||
- 显式配置 trusted proxy / trusted origin,并补 runtime 证据。
|
||||
3. fail-open 随机降级整改
|
||||
- `crypto/rand` 失败即报错,不再退化到时间戳或 `math/rand`。
|
||||
4. 覆盖率提升
|
||||
- Frontend 优先补 `AuthProvider`、`router`、`AdminLayout`、`UsersPage`、`WebhooksPage`、`ImportExportPage`
|
||||
- Backend 优先补 `internal/auth/providers`、`internal/repository`、`internal/database`
|
||||
5. 清理 dev toolchain SCA
|
||||
- 升级 `vite/vitest/eslint/typescript-eslint` 及其传递依赖,消除 `picomatch` 链路风险。
|
||||
6. 补齐真实外部告警交付证据
|
||||
- 接入真实通知渠道并形成可审计投递记录。
|
||||
|
||||
## 7. 本轮证据
|
||||
|
||||
- `docs/team/QUALITY_STANDARD.md`
|
||||
- `docs/status/REAL_PROJECT_STATUS.md`
|
||||
- `docs/PROJECT_REVIEW_REPORT.md`
|
||||
- `docs/evidence/ops/2026-03-27/e2e/ADMIN_BOOTSTRAP_CLOSURE_20260327-173914.md`
|
||||
- `docs/evidence/ops/2026-03-27/sca/SCA_SUMMARY_20260327-181910.md`
|
||||
- `docs/evidence/ops/2026-03-27/observability/LOCAL_BASELINE_20260327-182005.md`
|
||||
- `docs/evidence/ops/2026-03-27/alerting/ALERTING_PACKAGE_20260327-182058.md`
|
||||
- `docs/evidence/ops/2026-03-27/backup-restore/20260327-182059/`
|
||||
- `docs/evidence/ops/2026-03-27/config-isolation/20260327-182059/`
|
||||
- `docs/evidence/ops/2026-03-27/rollback/20260327-182059/`
|
||||
- `docs/evidence/ops/2026-03-27/secret-boundary/20260327-181910/`
|
||||
|
||||
## 8. 2026-03-28 Q-004 Closure Update
|
||||
|
||||
- Real status update:
|
||||
- `Q-004` is improved again, but still cannot be honestly declared closed.
|
||||
- Newly closed frontend hotspot:
|
||||
- `frontend/admin/src/app/router.tsx` is now at `100 / 100 / 100 / 100`.
|
||||
- Validation evidence added:
|
||||
- targeted router test
|
||||
- full frontend `test:run`
|
||||
- `lint`
|
||||
- `build`
|
||||
- full frontend `test:coverage`
|
||||
- Current frontend full coverage after this pass:
|
||||
- statements `90.74%`
|
||||
- branches `77.74%`
|
||||
- functions `87.40%`
|
||||
- lines `90.87%`
|
||||
- Main remaining `Q-004` frontend hotspots now narrow to:
|
||||
- `src/pages/admin/DashboardPage/DashboardPage.tsx`
|
||||
- `src/components/feedback/PageState/PageState.tsx`
|
||||
- additional lower-coverage shared/admin surfaces outside this pass
|
||||
- Real hygiene gap still open:
|
||||
- the successful frontend coverage run still prints one post-summary jsdom `AggregateError` network-noise line
|
||||
- Evidence:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-121611.md`
|
||||
|
||||
## 9. 2026-03-28 Dashboard Closure Update
|
||||
|
||||
- Real status update:
|
||||
- `Q-004` improved again, but still cannot be honestly declared closed.
|
||||
- Newly closed frontend hotspot:
|
||||
- `frontend/admin/src/pages/admin/DashboardPage/DashboardPage.tsx` is now at `100 / 100 / 100 / 100`.
|
||||
- Validation evidence added:
|
||||
- targeted dashboard test
|
||||
- `lint`
|
||||
- `build`
|
||||
- full frontend `test:coverage`
|
||||
- Current frontend full coverage after this pass:
|
||||
- statements `91.66%`
|
||||
- branches `78.26%`
|
||||
- functions `87.86%`
|
||||
- lines `91.82%`
|
||||
- Main remaining `Q-004` frontend hotspots now narrow to:
|
||||
- `src/components/feedback/PageState/PageState.tsx`
|
||||
- additional lower-coverage shared/admin surfaces outside this pass
|
||||
- Real hygiene gap still open:
|
||||
- the successful frontend coverage run still prints one post-summary jsdom `AggregateError` network-noise line
|
||||
- Evidence:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-122517.md`
|
||||
|
||||
## 10. 2026-03-28 PageState Closure Update
|
||||
|
||||
- Real status update:
|
||||
- `Q-004` improved again, but still cannot be honestly declared closed.
|
||||
- Newly closed frontend hotspot:
|
||||
- `frontend/admin/src/components/feedback/PageState/PageState.tsx` is now at `100 / 100 / 100 / 100`.
|
||||
- Validation evidence added:
|
||||
- targeted PageState test
|
||||
- `lint`
|
||||
- `build`
|
||||
- full frontend `test:coverage`
|
||||
- Current frontend full coverage after this pass:
|
||||
- statements `91.71%`
|
||||
- branches `78.52%`
|
||||
- functions `88.01%`
|
||||
- lines `91.86%`
|
||||
- Main remaining `Q-004` frontend hotspots now narrow to:
|
||||
- `src/layouts/AdminLayout/AdminLayout.tsx`
|
||||
- `src/pages/admin/ImportExportPage/ImportExportPage.tsx`
|
||||
- `src/lib/errors/AppError.ts`
|
||||
- `src/lib/storage/token-storage.ts`
|
||||
- additional lower-coverage shared/admin surfaces outside this pass
|
||||
- Real hygiene gap still open:
|
||||
- the successful frontend coverage run still prints one post-summary jsdom `AggregateError` network-noise line
|
||||
- Evidence:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-123228.md`
|
||||
|
||||
## 11. 2026-03-28 AdminLayout Closure Update
|
||||
|
||||
- Real status update:
|
||||
- `Q-004` improved again, but still cannot be honestly declared closed.
|
||||
- Newly closed frontend hotspot:
|
||||
- `frontend/admin/src/layouts/AdminLayout/AdminLayout.tsx` is now at `100 / 100 / 100 / 100`.
|
||||
- Validation evidence added:
|
||||
- targeted AdminLayout test
|
||||
- `lint`
|
||||
- `build`
|
||||
- full frontend `test:coverage`
|
||||
- Current frontend full coverage after this pass:
|
||||
- statements `92.06%`
|
||||
- branches `79.29%`
|
||||
- functions `89.09%`
|
||||
- lines `92.22%`
|
||||
- Main remaining `Q-004` frontend hotspots now narrow to:
|
||||
- `src/lib/storage/token-storage.ts`
|
||||
- `src/lib/errors/AppError.ts`
|
||||
- `src/pages/admin/ImportExportPage/ImportExportPage.tsx`
|
||||
- `src/pages/NotFoundPage/NotFoundPage.tsx`
|
||||
- additional lower-coverage shared/admin surfaces outside this pass
|
||||
- Real hygiene gap still open:
|
||||
- the successful frontend coverage run still prints one post-summary jsdom `AggregateError` network-noise line
|
||||
- Evidence:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-124756.md`
|
||||
|
||||
## 12. 2026-03-28 Token Storage Closure Update
|
||||
|
||||
- Real status update:
|
||||
- `Q-004` improved again, but still cannot be honestly declared closed.
|
||||
- Newly closed frontend hotspot:
|
||||
- `frontend/admin/src/lib/storage/token-storage.ts` is now at `100 / 100 / 100 / 100`.
|
||||
- Validation evidence added:
|
||||
- targeted token-storage test
|
||||
- `lint`
|
||||
- `build`
|
||||
- full frontend `test:coverage`
|
||||
- Current frontend full coverage after this pass:
|
||||
- statements `92.32%`
|
||||
- branches `79.63%`
|
||||
- functions `89.70%`
|
||||
- lines `92.49%`
|
||||
- Main remaining `Q-004` frontend hotspots now narrow to:
|
||||
- `src/lib/errors/AppError.ts`
|
||||
- `src/pages/admin/ImportExportPage/ImportExportPage.tsx`
|
||||
- `src/pages/NotFoundPage/NotFoundPage.tsx`
|
||||
- additional lower-coverage shared/admin surfaces outside this pass
|
||||
- Real hygiene gap still open:
|
||||
- the successful frontend coverage run still prints one post-summary jsdom `AggregateError` network-noise line
|
||||
- Evidence:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-125454.md`
|
||||
|
||||
## 13. 2026-03-28 AppError Closure Update
|
||||
|
||||
- Real status update:
|
||||
- `Q-004` improved again, but still cannot be honestly declared closed.
|
||||
- Newly closed frontend hotspot:
|
||||
- `frontend/admin/src/lib/errors/AppError.ts` is now at `100 / 100 / 100 / 100`.
|
||||
- `frontend/admin/src/lib/errors/index.ts` is now at `100 / 100 / 100 / 100`.
|
||||
- Validation evidence added:
|
||||
- targeted AppError module test
|
||||
- `lint`
|
||||
- `build`
|
||||
- full frontend `test:coverage`
|
||||
- Current frontend full coverage after this pass:
|
||||
- statements `93.07%`
|
||||
- branches `81.35%`
|
||||
- functions `90.32%`
|
||||
- lines `93.26%`
|
||||
- Main remaining `Q-004` frontend hotspots now narrow to:
|
||||
- `src/pages/admin/ImportExportPage/ImportExportPage.tsx`
|
||||
- `src/pages/NotFoundPage/NotFoundPage.tsx`
|
||||
- `src/lib/hooks/useBreadcrumbs.ts`
|
||||
- `src/app/providers/ThemeProvider.tsx`
|
||||
- additional lower-coverage shared/admin surfaces outside this pass
|
||||
- Real hygiene gap still open:
|
||||
- the successful frontend coverage run still prints one post-summary jsdom `AggregateError` network-noise line
|
||||
- Evidence:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-140215.md`
|
||||
## 1.4 2026-03-28 Q-004 latest remediation note XIV
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `93.56 / 81.95 / 90.93 / 93.71`
|
||||
- `src/pages/admin/ImportExportPage/ImportExportPage.tsx` is now `100 / 100 / 100 / 100`
|
||||
- The latest remediation closed one more previously real frontend hotspot:
|
||||
- `ImportExportPage.tsx` is no longer an open `Q-004` gap
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- after the import/export page was closed, the remaining higher-value frontend gaps narrow further to `NotFoundPage`, `useBreadcrumbs`, `ThemeProvider`, and the still-open coverage-noise hygiene issue
|
||||
- The validation hygiene note changed slightly but remains materially open:
|
||||
- `ImportExportPage` tests no longer emit the extra jsdom `window.getComputedStyle(..., pseudoElt)` noise from `rc-table`
|
||||
- `npm.cmd run test:coverage` still passed again while emitting post-summary jsdom `AggregateError` network-noise lines
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-142248.md`
|
||||
## 1.5 2026-03-28 Q-004 latest remediation note XV
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `93.69 / 81.95 / 91.24 / 93.85`
|
||||
- `src/pages/NotFoundPage/NotFoundPage.tsx` is now `100 / 100 / 100 / 100`
|
||||
- The latest remediation closed one more previously real frontend hotspot:
|
||||
- `NotFoundPage.tsx` is no longer an open `Q-004` gap
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- after the 404 page was closed, the remaining higher-value frontend gaps narrow further to `useBreadcrumbs`, `ThemeProvider`, and the still-open coverage-noise hygiene issue
|
||||
- The validation hygiene note remains materially open:
|
||||
- `npm.cmd run test:coverage` still passed again while emitting post-summary jsdom `AggregateError` network-noise lines
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-143209.md`
|
||||
## 1.6 2026-03-28 Q-004 latest remediation note XVI
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `93.84 / 82.29 / 91.21 / 94.01`
|
||||
- `src/lib/hooks/useBreadcrumbs.ts` is now `100 / 100 / 100 / 100`
|
||||
- The latest remediation closed one more previously real frontend hotspot:
|
||||
- `useBreadcrumbs.ts` is no longer an open `Q-004` gap
|
||||
- This pass also removed one small piece of dead frontend complexity:
|
||||
- the hook's parent-injection branch was redundant under the current route model and has been removed rather than artificially test-forced
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- after the breadcrumb hook was closed, the remaining higher-value frontend gaps narrow further to `ThemeProvider` plus the still-open coverage-noise hygiene issue
|
||||
- The validation hygiene note remains materially open:
|
||||
- `npm.cmd run test:coverage` still passed again while emitting post-summary jsdom `AggregateError` network-noise lines
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-144036.md`
|
||||
## 1.7 2026-03-28 Q-004 latest remediation note XVII
|
||||
|
||||
- `Q-004` was remediated further again after the previous addendum and still remains open.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `93.93 / 82.29 / 91.37 / 94.10`
|
||||
- `src/app/providers/ThemeProvider.tsx` is now `100 / 100 / 100 / 100`
|
||||
- The latest remediation closed one more previously real frontend hotspot:
|
||||
- `ThemeProvider.tsx` is no longer an open `Q-004` gap
|
||||
- The updated real boundary remains:
|
||||
- `Q-004` still cannot be truthfully closed
|
||||
- after the theme provider was closed, the remaining frontend gap for this closure track narrows to the still-open post-summary jsdom `AggregateError` coverage-noise issue
|
||||
- The validation hygiene note remains materially open:
|
||||
- `npm.cmd run test:coverage` still passed again while emitting post-summary jsdom `AggregateError` network-noise lines
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-144756.md`
|
||||
## 1.8 2026-03-28 Q-004 latest remediation note XVIII
|
||||
|
||||
- `Q-004` for the `frontend/admin` closure track can now be truthfully closed.
|
||||
- Newly verified outcomes:
|
||||
- frontend overall coverage is now `93.98 / 82.29 / 91.37 / 94.15`
|
||||
- `src/app/router.tsx` remains `100 / 100 / 100 / 100` in the latest full-suite coverage run
|
||||
- full frontend coverage completed with `54` passing test files and `248` passing tests
|
||||
- The final materially open blocker is now closed:
|
||||
- the successful `npm.cmd run test:coverage` run no longer emits the previously recurring post-summary jsdom `AggregateError` network-noise lines
|
||||
- The real closure boundary is now:
|
||||
- all previously identified frontend hotspots in this `Q-004` closure track remain closed
|
||||
- the validation hygiene path is clean enough to honestly close `Q-004`
|
||||
- a separate npm global config warning still prints after command completion, but it is external environment noise rather than a project-generated failure
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/quality/COVERAGE_REMEDIATION_20260328-151952.md`
|
||||
## 1.9 2026-03-28 Q-005 SCA closure note XIX
|
||||
|
||||
- `Q-005` can now be truthfully closed.
|
||||
- Newly verified outcomes:
|
||||
- `npm audit production` is now `0`
|
||||
- `npm audit full` is now `0`
|
||||
- `govulncheck reachable findings` remain `0`
|
||||
- The remediation that closed the dev-toolchain supply-chain gap was:
|
||||
- upgrade `vite` to `8.0.3`
|
||||
- upgrade `vitest` and `@vitest/coverage-v8` to `4.1.2`
|
||||
- upgrade `typescript-eslint` to `8.57.2`
|
||||
- pin vulnerable transitive chains with `overrides` for `picomatch` and `brace-expansion`
|
||||
- Re-verification after the dependency update also passed:
|
||||
- `frontend/admin` `lint`
|
||||
- `frontend/admin` production `build`
|
||||
- full frontend `test:coverage`
|
||||
- The updated real boundary is now:
|
||||
- `Q-004` and `Q-005` are both closed for the current closure track
|
||||
- the next unclosed cross-cutting governance gap is `Q-006` external alert delivery evidence
|
||||
- the separate product/external-proof boundary around live third-party OAuth provider browser evidence also still remains
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-28/sca/SCA_SUMMARY_20260328-220806.md`
|
||||
## 2.0 2026-03-29 Q-006 readiness note XX
|
||||
|
||||
- `Q-006` still cannot be truthfully closed, but the repo-side closure path is stricter than before.
|
||||
- Newly verified outcomes:
|
||||
- alerting package structural validation still passes on the latest run
|
||||
- render drill still passes on the latest run
|
||||
- a new strict live-delivery drill now exists and fails closed on placeholder/example values
|
||||
- The latest repo-side hardening for this gap is:
|
||||
- add `scripts/ops/drill-alertmanager-live-delivery.ps1`
|
||||
- refuse unresolved placeholders, `example.*` addresses/hosts, and placeholder secrets before any network attempt
|
||||
- emit only redacted config artifacts and masked recipient evidence
|
||||
- remove the date-rollover false blocker in `validate-alerting-package.ps1` by falling back to the latest available baseline evidence
|
||||
- The updated real boundary is now:
|
||||
- repo-side alert delivery verification tooling is materially better prepared
|
||||
- `Q-006` remains open because no real non-placeholder on-call delivery environment has been injected and no successful live SMTP acceptance evidence has yet been captured
|
||||
- the remaining closure work is external-environment proof, not another repo-local template/rendering fix
|
||||
- Latest evidence for this addendum:
|
||||
- `docs/evidence/ops/2026-03-29/alerting/ALERTING_PACKAGE_20260329-100316.md`
|
||||
- `docs/evidence/ops/2026-03-29/alerting/20260329-100315/ALERTMANAGER_RENDER_DRILL.md`
|
||||
- `docs/evidence/ops/2026-03-29/alerting/20260329-100315/ALERTMANAGER_LIVE_DELIVERY_DRILL.md`
|
||||
@@ -0,0 +1,32 @@
|
||||
# Rollback Drill
|
||||
|
||||
- Generated at: 2026-03-27 18:21:12 +08:00
|
||||
- Source DB: D:\project\data\user_management.db
|
||||
- Stable DB copy: D:\project\docs\evidence\ops\2026-03-27\rollback\20260327-182059\user_management.stable.db
|
||||
- Probe port: 18087
|
||||
|
||||
## Drill Result
|
||||
|
||||
- Stable release started successfully before rollback gate evaluation.
|
||||
- Candidate release was rejected by release-mode runtime validation before becoming healthy.
|
||||
- Rollback to the previous stable config/artifact path completed successfully on the same probe port.
|
||||
- Candidate rejection evidence: stderr matched release validation failure
|
||||
- Stable capabilities before rollback: {"password":true,"email_activation":false,"email_code":false,"sms_code":false,"password_reset":false,"admin_bootstrap_required":false,"oauth_providers":[]}
|
||||
- Stable capabilities after rollback: {"password":true,"email_activation":false,"email_code":false,"sms_code":false,"password_reset":false,"admin_bootstrap_required":false,"oauth_providers":[]}
|
||||
|
||||
## Scope Note
|
||||
|
||||
- This local drill validates rollback operational steps and health gates for the current artifact/config path.
|
||||
- It does not prove cross-version schema downgrade compatibility between distinct historical releases.
|
||||
|
||||
## Evidence Files
|
||||
|
||||
- config.stable.yaml
|
||||
- config.candidate.yaml
|
||||
- stable-initial.stdout.log
|
||||
- stable-initial.stderr.log
|
||||
- candidate.stdout.log
|
||||
- candidate.stderr.log
|
||||
- stable-rollback.stdout.log
|
||||
- stable-rollback.stderr.log
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
server:
|
||||
port: 18087
|
||||
mode: release # debug, release
|
||||
read_timeout: 30s
|
||||
read_header_timeout: 10s
|
||||
write_timeout: 30s
|
||||
idle_timeout: 60s
|
||||
shutdown_timeout: 15s
|
||||
max_header_bytes: 1048576
|
||||
|
||||
database:
|
||||
type: sqlite # current runtime support: sqlite
|
||||
sqlite:
|
||||
path: "D:/project/docs/evidence/ops/2026-03-27/rollback/20260327-182059/user_management.stable.db"
|
||||
postgresql:
|
||||
host: localhost
|
||||
port: 5432
|
||||
database: user_management
|
||||
username: postgres
|
||||
password: ""
|
||||
ssl_mode: disable
|
||||
max_open_conns: 100
|
||||
max_idle_conns: 10
|
||||
mysql:
|
||||
host: localhost
|
||||
port: 3306
|
||||
database: user_management
|
||||
username: root
|
||||
password: ""
|
||||
charset: utf8mb4
|
||||
max_open_conns: 100
|
||||
max_idle_conns: 10
|
||||
|
||||
cache:
|
||||
l1:
|
||||
enabled: true
|
||||
max_size: 10000
|
||||
ttl: 5m
|
||||
l2:
|
||||
enabled: false
|
||||
type: redis
|
||||
redis:
|
||||
addr: localhost:6379
|
||||
password: ""
|
||||
db: 0
|
||||
pool_size: 50
|
||||
ttl: 30m
|
||||
|
||||
redis:
|
||||
enabled: false
|
||||
addr: localhost:6379
|
||||
password: ""
|
||||
db: 0
|
||||
|
||||
jwt:
|
||||
algorithm: RS256
|
||||
secret: ""
|
||||
private_key_path: "./data/jwt/private.pem"
|
||||
public_key_path: "./data/jwt/public.pem"
|
||||
private_key_pem: ""
|
||||
public_key_pem: ""
|
||||
access_token_expire: 2h
|
||||
refresh_token_expire: 168h # 7澶?= 168灏忔椂
|
||||
|
||||
security:
|
||||
password_min_length: 8
|
||||
password_require_special: true
|
||||
password_require_number: true
|
||||
login_max_attempts: 5
|
||||
login_lock_duration: 30m
|
||||
|
||||
ratelimit:
|
||||
enabled: true
|
||||
login:
|
||||
enabled: true
|
||||
algorithm: token_bucket
|
||||
capacity: 5
|
||||
rate: 1
|
||||
window: 1m
|
||||
register:
|
||||
enabled: true
|
||||
algorithm: leaky_bucket
|
||||
capacity: 3
|
||||
rate: 1
|
||||
window: 1h
|
||||
api:
|
||||
enabled: true
|
||||
algorithm: sliding_window
|
||||
capacity: 1000
|
||||
window: 1m
|
||||
|
||||
monitoring:
|
||||
prometheus:
|
||||
enabled: true
|
||||
path: /metrics
|
||||
tracing:
|
||||
enabled: false
|
||||
endpoint: http://localhost:4318
|
||||
service_name: user-management-system
|
||||
|
||||
logging:
|
||||
level: info # debug, info, warn, error
|
||||
format: json # json, text
|
||||
output:
|
||||
- stdout
|
||||
- ./logs/app.log
|
||||
rotation:
|
||||
max_size: 100 # MB
|
||||
max_age: 30 # days
|
||||
max_backups: 10
|
||||
|
||||
admin:
|
||||
username: ""
|
||||
password: ""
|
||||
email: ""
|
||||
|
||||
cors:
|
||||
enabled: true
|
||||
allowed_origins:
|
||||
- "*"
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
server:
|
||||
port: 18087
|
||||
mode: release # debug, release
|
||||
read_timeout: 30s
|
||||
read_header_timeout: 10s
|
||||
write_timeout: 30s
|
||||
idle_timeout: 60s
|
||||
shutdown_timeout: 15s
|
||||
max_header_bytes: 1048576
|
||||
|
||||
database:
|
||||
type: sqlite # current runtime support: sqlite
|
||||
sqlite:
|
||||
path: "D:/project/docs/evidence/ops/2026-03-27/rollback/20260327-182059/user_management.stable.db"
|
||||
postgresql:
|
||||
host: localhost
|
||||
port: 5432
|
||||
database: user_management
|
||||
username: postgres
|
||||
password: ""
|
||||
ssl_mode: disable
|
||||
max_open_conns: 100
|
||||
max_idle_conns: 10
|
||||
mysql:
|
||||
host: localhost
|
||||
port: 3306
|
||||
database: user_management
|
||||
username: root
|
||||
password: ""
|
||||
charset: utf8mb4
|
||||
max_open_conns: 100
|
||||
max_idle_conns: 10
|
||||
|
||||
cache:
|
||||
l1:
|
||||
enabled: true
|
||||
max_size: 10000
|
||||
ttl: 5m
|
||||
l2:
|
||||
enabled: false
|
||||
type: redis
|
||||
redis:
|
||||
addr: localhost:6379
|
||||
password: ""
|
||||
db: 0
|
||||
pool_size: 50
|
||||
ttl: 30m
|
||||
|
||||
redis:
|
||||
enabled: false
|
||||
addr: localhost:6379
|
||||
password: ""
|
||||
db: 0
|
||||
|
||||
jwt:
|
||||
algorithm: RS256
|
||||
secret: ""
|
||||
private_key_path: "./data/jwt/private.pem"
|
||||
public_key_path: "./data/jwt/public.pem"
|
||||
private_key_pem: ""
|
||||
public_key_pem: ""
|
||||
access_token_expire: 2h
|
||||
refresh_token_expire: 168h # 7澶?= 168灏忔椂
|
||||
|
||||
security:
|
||||
password_min_length: 8
|
||||
password_require_special: true
|
||||
password_require_number: true
|
||||
login_max_attempts: 5
|
||||
login_lock_duration: 30m
|
||||
|
||||
ratelimit:
|
||||
enabled: true
|
||||
login:
|
||||
enabled: true
|
||||
algorithm: token_bucket
|
||||
capacity: 5
|
||||
rate: 1
|
||||
window: 1m
|
||||
register:
|
||||
enabled: true
|
||||
algorithm: leaky_bucket
|
||||
capacity: 3
|
||||
rate: 1
|
||||
window: 1h
|
||||
api:
|
||||
enabled: true
|
||||
algorithm: sliding_window
|
||||
capacity: 1000
|
||||
window: 1m
|
||||
|
||||
monitoring:
|
||||
prometheus:
|
||||
enabled: true
|
||||
path: /metrics
|
||||
tracing:
|
||||
enabled: false
|
||||
endpoint: http://localhost:4318
|
||||
service_name: user-management-system
|
||||
|
||||
logging:
|
||||
level: info # debug, info, warn, error
|
||||
format: json # json, text
|
||||
output:
|
||||
- stdout
|
||||
- ./logs/app.log
|
||||
rotation:
|
||||
max_size: 100 # MB
|
||||
max_age: 30 # days
|
||||
max_backups: 10
|
||||
|
||||
admin:
|
||||
username: ""
|
||||
password: ""
|
||||
email: ""
|
||||
|
||||
cors:
|
||||
enabled: true
|
||||
allowed_origins:
|
||||
- "http://localhost:3000"
|
||||
- "http://127.0.0.1:3000"
|
||||
allowed_methods:
|
||||
- GET
|
||||
- POST
|
||||
- PUT
|
||||
- DELETE
|
||||
- OPTIONS
|
||||
allowed_headers:
|
||||
- Authorization
|
||||
- Content-Type
|
||||
- X-Requested-With
|
||||
- X-CSRF-Token
|
||||
max_age: 3600
|
||||
|
||||
email:
|
||||
host: "" # 鐢熶骇鐜濉啓鐪熷疄 SMTP Host
|
||||
port: 18087
|
||||
username: ""
|
||||
password: ""
|
||||
from_email: ""
|
||||
from_name: "鐢ㄦ埛绠$悊绯荤粺"
|
||||
|
||||
sms:
|
||||
enabled: false
|
||||
provider: "" # aliyun, tencent锛涚暀绌鸿〃绀虹鐢ㄧ煭淇¤兘鍔? code_ttl: 5m
|
||||
resend_cooldown: 1m
|
||||
max_daily_limit: 10
|
||||
aliyun:
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
sign_name: ""
|
||||
template_code: ""
|
||||
endpoint: ""
|
||||
region_id: "cn-hangzhou"
|
||||
code_param_name: "code"
|
||||
tencent:
|
||||
secret_id: ""
|
||||
secret_key: ""
|
||||
app_id: ""
|
||||
sign_name: ""
|
||||
template_id: ""
|
||||
region: "ap-guangzhou"
|
||||
endpoint: ""
|
||||
|
||||
password_reset:
|
||||
token_ttl: 15m
|
||||
site_url: "http://localhost:8080"
|
||||
|
||||
# OAuth 绀句氦鐧诲綍閰嶇疆锛堢暀绌哄垯绂佺敤瀵瑰簲 Provider锛?
|
||||
oauth:
|
||||
google:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/google/callback"
|
||||
wechat:
|
||||
app_id: ""
|
||||
app_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/wechat/callback"
|
||||
github:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/github/callback"
|
||||
qq:
|
||||
app_id: ""
|
||||
app_key: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/qq/callback"
|
||||
alipay:
|
||||
app_id: ""
|
||||
private_key: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/alipay/callback"
|
||||
sandbox: false
|
||||
douyin:
|
||||
client_key: ""
|
||||
client_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/douyin/callback"
|
||||
|
||||
# Webhook 鍏ㄥ眬閰嶇疆
|
||||
webhook:
|
||||
enabled: true
|
||||
secret_header: "X-Webhook-Signature" # 绛惧悕 Header 鍚嶇О
|
||||
timeout_sec: 30 # 鍗曟鎶曢€掕秴鏃讹紙绉掞級
|
||||
max_retries: 3 # 鏈€澶ч噸璇曟鏁?
|
||||
retry_backoff: "exponential" # 閫€閬跨瓥鐣ワ細exponential / fixed
|
||||
worker_count: 4 # 鍚庡彴鎶曢€掑崗绋嬫暟
|
||||
queue_size: 1000 # 鎶曢€掗槦鍒楀ぇ灏?
|
||||
|
||||
# IP 瀹夊叏閰嶇疆
|
||||
ip_security:
|
||||
auto_block_enabled: true # 鏄惁鍚敤鑷姩灏佺
|
||||
auto_block_duration: 30m # 鑷姩灏佺鏃堕暱
|
||||
brute_force_threshold: 10 # 鏆村姏鐮磋В闃堝€硷紙绐楀彛鍐呭け璐ユ鏁帮級
|
||||
detection_window: 15m # 妫€娴嬫椂闂寸獥鍙?
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
# SCA Summary
|
||||
|
||||
- Generated at: 2026-03-27 18:19:41 +08:00
|
||||
- Project root: D:\project
|
||||
|
||||
## Commands
|
||||
|
||||
- `cd frontend/admin && npm.cmd audit --omit=dev --json --registry=https://registry.npmjs.org/`
|
||||
- `cd frontend/admin && npm.cmd audit --json --registry=https://registry.npmjs.org/`
|
||||
- `go run golang.org/x/vuln/cmd/govulncheck@latest -json ./...`
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- npm audit production: 0
|
||||
- npm audit full: 1
|
||||
- govulncheck: 0
|
||||
|
||||
## Findings
|
||||
|
||||
- npm audit production: info=0 low=0 moderate=0 high=0 critical=0 total=0
|
||||
- npm audit full: info=0 low=0 moderate=21 high=1 critical=0 total=22
|
||||
- govulncheck reachable findings: 0
|
||||
- govulncheck reachable IDs: none
|
||||
|
||||
## Evidence Files
|
||||
|
||||
- npm-audit-prod-20260327-181910.json
|
||||
- npm-audit-prod-20260327-181910.stderr.txt
|
||||
- npm-audit-full-20260327-181910.json
|
||||
- npm-audit-full-20260327-181910.stderr.txt
|
||||
- govulncheck-20260327-181910.jsonl
|
||||
- govulncheck-20260327-181910.stderr.txt
|
||||
|
||||
18347
docs/evidence/ops/2026-03-27/sca/govulncheck-20260327-181910.jsonl
Normal file
18347
docs/evidence/ops/2026-03-27/sca/govulncheck-20260327-181910.jsonl
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,427 @@
|
||||
{
|
||||
"auditReportVersion": 2,
|
||||
"vulnerabilities": {
|
||||
"@eslint-community/eslint-utils": {
|
||||
"name": "@eslint-community/eslint-utils",
|
||||
"severity": "moderate",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
"eslint"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/@eslint-community/eslint-utils"
|
||||
],
|
||||
"fixAvailable": true
|
||||
},
|
||||
"@eslint/config-array": {
|
||||
"name": "@eslint/config-array",
|
||||
"severity": "moderate",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
"minimatch"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/@eslint/config-array"
|
||||
],
|
||||
"fixAvailable": true
|
||||
},
|
||||
"@eslint/eslintrc": {
|
||||
"name": "@eslint/eslintrc",
|
||||
"severity": "moderate",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
"minimatch"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/@eslint/eslintrc"
|
||||
],
|
||||
"fixAvailable": true
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"name": "@typescript-eslint/eslint-plugin",
|
||||
"severity": "moderate",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
"@typescript-eslint/parser",
|
||||
"@typescript-eslint/type-utils",
|
||||
"@typescript-eslint/utils",
|
||||
"eslint"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/@typescript-eslint/eslint-plugin"
|
||||
],
|
||||
"fixAvailable": true
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"name": "@typescript-eslint/parser",
|
||||
"severity": "moderate",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
"@typescript-eslint/typescript-estree",
|
||||
"eslint"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/@typescript-eslint/parser"
|
||||
],
|
||||
"fixAvailable": true
|
||||
},
|
||||
"@typescript-eslint/type-utils": {
|
||||
"name": "@typescript-eslint/type-utils",
|
||||
"severity": "moderate",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
"@typescript-eslint/typescript-estree",
|
||||
"@typescript-eslint/utils",
|
||||
"eslint"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/@typescript-eslint/type-utils"
|
||||
],
|
||||
"fixAvailable": true
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"name": "@typescript-eslint/typescript-estree",
|
||||
"severity": "moderate",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
"minimatch",
|
||||
"tinyglobby"
|
||||
],
|
||||
"effects": [
|
||||
"@typescript-eslint/parser",
|
||||
"@typescript-eslint/type-utils",
|
||||
"@typescript-eslint/utils",
|
||||
"typescript-eslint"
|
||||
],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/@typescript-eslint/typescript-estree"
|
||||
],
|
||||
"fixAvailable": false
|
||||
},
|
||||
"@typescript-eslint/utils": {
|
||||
"name": "@typescript-eslint/utils",
|
||||
"severity": "moderate",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
"@eslint-community/eslint-utils",
|
||||
"@typescript-eslint/typescript-estree",
|
||||
"eslint"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/@typescript-eslint/utils"
|
||||
],
|
||||
"fixAvailable": true
|
||||
},
|
||||
"@vitejs/plugin-react": {
|
||||
"name": "@vitejs/plugin-react",
|
||||
"severity": "moderate",
|
||||
"isDirect": true,
|
||||
"via": [
|
||||
"vite"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/@vitejs/plugin-react"
|
||||
],
|
||||
"fixAvailable": false
|
||||
},
|
||||
"@vitest/coverage-v8": {
|
||||
"name": "@vitest/coverage-v8",
|
||||
"severity": "moderate",
|
||||
"isDirect": true,
|
||||
"via": [
|
||||
"vitest"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/@vitest/coverage-v8"
|
||||
],
|
||||
"fixAvailable": false
|
||||
},
|
||||
"@vitest/mocker": {
|
||||
"name": "@vitest/mocker",
|
||||
"severity": "moderate",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
"vite"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/@vitest/mocker"
|
||||
],
|
||||
"fixAvailable": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"name": "brace-expansion",
|
||||
"severity": "moderate",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
{
|
||||
"source": 1115432,
|
||||
"name": "brace-expansion",
|
||||
"dependency": "brace-expansion",
|
||||
"title": "brace-expansion: Zero-step sequence causes process hang and memory exhaustion",
|
||||
"url": "https://github.com/advisories/GHSA-f886-m6hf-6m8v",
|
||||
"severity": "moderate",
|
||||
"cwe": [
|
||||
"CWE-400"
|
||||
],
|
||||
"cvss": {
|
||||
"score": 6.5,
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H"
|
||||
},
|
||||
"range": "<5.0.5"
|
||||
}
|
||||
],
|
||||
"effects": [
|
||||
"minimatch"
|
||||
],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion",
|
||||
"node_modules/brace-expansion"
|
||||
],
|
||||
"fixAvailable": false
|
||||
},
|
||||
"eslint": {
|
||||
"name": "eslint",
|
||||
"severity": "moderate",
|
||||
"isDirect": true,
|
||||
"via": [
|
||||
"@eslint-community/eslint-utils",
|
||||
"@eslint/config-array",
|
||||
"@eslint/eslintrc",
|
||||
"minimatch"
|
||||
],
|
||||
"effects": [
|
||||
"@eslint-community/eslint-utils",
|
||||
"@typescript-eslint/eslint-plugin",
|
||||
"eslint-plugin-react-hooks",
|
||||
"eslint-plugin-react-refresh"
|
||||
],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/eslint"
|
||||
],
|
||||
"fixAvailable": false
|
||||
},
|
||||
"eslint-plugin-react-hooks": {
|
||||
"name": "eslint-plugin-react-hooks",
|
||||
"severity": "moderate",
|
||||
"isDirect": true,
|
||||
"via": [
|
||||
"eslint"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/eslint-plugin-react-hooks"
|
||||
],
|
||||
"fixAvailable": false
|
||||
},
|
||||
"eslint-plugin-react-refresh": {
|
||||
"name": "eslint-plugin-react-refresh",
|
||||
"severity": "moderate",
|
||||
"isDirect": true,
|
||||
"via": [
|
||||
"eslint"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/eslint-plugin-react-refresh"
|
||||
],
|
||||
"fixAvailable": false
|
||||
},
|
||||
"fdir": {
|
||||
"name": "fdir",
|
||||
"severity": "moderate",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
"picomatch"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/fdir"
|
||||
],
|
||||
"fixAvailable": true
|
||||
},
|
||||
"minimatch": {
|
||||
"name": "minimatch",
|
||||
"severity": "moderate",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
"brace-expansion"
|
||||
],
|
||||
"effects": [
|
||||
"@eslint/config-array",
|
||||
"@eslint/eslintrc",
|
||||
"@typescript-eslint/typescript-estree",
|
||||
"eslint"
|
||||
],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch",
|
||||
"node_modules/minimatch"
|
||||
],
|
||||
"fixAvailable": false
|
||||
},
|
||||
"picomatch": {
|
||||
"name": "picomatch",
|
||||
"severity": "high",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
{
|
||||
"source": 1115384,
|
||||
"name": "picomatch",
|
||||
"dependency": "picomatch",
|
||||
"title": "Picomatch has a ReDoS vulnerability via extglob quantifiers",
|
||||
"url": "https://github.com/advisories/GHSA-c2c7-rcm5-vvqj",
|
||||
"severity": "high",
|
||||
"cwe": [
|
||||
"CWE-1333"
|
||||
],
|
||||
"cvss": {
|
||||
"score": 7.5,
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
|
||||
},
|
||||
"range": ">=4.0.0 <4.0.4"
|
||||
},
|
||||
{
|
||||
"source": 1115396,
|
||||
"name": "picomatch",
|
||||
"dependency": "picomatch",
|
||||
"title": "Picomatch: Method Injection in POSIX Character Classes causes incorrect Glob Matching",
|
||||
"url": "https://github.com/advisories/GHSA-3v7f-55p6-f55p",
|
||||
"severity": "moderate",
|
||||
"cwe": [
|
||||
"CWE-1321"
|
||||
],
|
||||
"cvss": {
|
||||
"score": 5.3,
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N"
|
||||
},
|
||||
"range": ">=4.0.0 <4.0.4"
|
||||
}
|
||||
],
|
||||
"effects": [
|
||||
"fdir",
|
||||
"tinyglobby",
|
||||
"vite",
|
||||
"vitest"
|
||||
],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/picomatch"
|
||||
],
|
||||
"fixAvailable": false
|
||||
},
|
||||
"tinyglobby": {
|
||||
"name": "tinyglobby",
|
||||
"severity": "moderate",
|
||||
"isDirect": false,
|
||||
"via": [
|
||||
"fdir",
|
||||
"picomatch"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/tinyglobby"
|
||||
],
|
||||
"fixAvailable": true
|
||||
},
|
||||
"typescript-eslint": {
|
||||
"name": "typescript-eslint",
|
||||
"severity": "moderate",
|
||||
"isDirect": true,
|
||||
"via": [
|
||||
"@typescript-eslint/eslint-plugin",
|
||||
"@typescript-eslint/parser",
|
||||
"@typescript-eslint/typescript-estree",
|
||||
"@typescript-eslint/utils",
|
||||
"eslint"
|
||||
],
|
||||
"effects": [],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/typescript-eslint"
|
||||
],
|
||||
"fixAvailable": false
|
||||
},
|
||||
"vite": {
|
||||
"name": "vite",
|
||||
"severity": "moderate",
|
||||
"isDirect": true,
|
||||
"via": [
|
||||
"picomatch",
|
||||
"tinyglobby"
|
||||
],
|
||||
"effects": [
|
||||
"@vitejs/plugin-react",
|
||||
"@vitest/mocker"
|
||||
],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/vite"
|
||||
],
|
||||
"fixAvailable": false
|
||||
},
|
||||
"vitest": {
|
||||
"name": "vitest",
|
||||
"severity": "moderate",
|
||||
"isDirect": true,
|
||||
"via": [
|
||||
"@vitest/mocker",
|
||||
"picomatch",
|
||||
"tinyglobby",
|
||||
"vite"
|
||||
],
|
||||
"effects": [
|
||||
"@vitest/coverage-v8"
|
||||
],
|
||||
"range": "",
|
||||
"nodes": [
|
||||
"node_modules/vitest"
|
||||
],
|
||||
"fixAvailable": false
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"vulnerabilities": {
|
||||
"info": 0,
|
||||
"low": 0,
|
||||
"moderate": 21,
|
||||
"high": 1,
|
||||
"critical": 0,
|
||||
"total": 22
|
||||
},
|
||||
"dependencies": {
|
||||
"prod": 83,
|
||||
"dev": 297,
|
||||
"optional": 34,
|
||||
"peer": 8,
|
||||
"peerOptional": 0,
|
||||
"total": 379
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
npm warn Unknown user config "//git@github.com/" (git config --global url."https://github.com/".insteadOf ssh://git@github.com/). This will stop working in the next major version of npm.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"auditReportVersion": 2,
|
||||
"vulnerabilities": {},
|
||||
"metadata": {
|
||||
"vulnerabilities": {
|
||||
"info": 0,
|
||||
"low": 0,
|
||||
"moderate": 0,
|
||||
"high": 0,
|
||||
"critical": 0,
|
||||
"total": 0
|
||||
},
|
||||
"dependencies": {
|
||||
"prod": 83,
|
||||
"dev": 297,
|
||||
"optional": 34,
|
||||
"peer": 8,
|
||||
"peerOptional": 0,
|
||||
"total": 379
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
npm warn Unknown user config "//git@github.com/" (git config --global url."https://github.com/".insteadOf ssh://git@github.com/). This will stop working in the next major version of npm.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Secret Boundary Drill
|
||||
|
||||
- Generated at: 2026-03-27 18:19:30 +08:00
|
||||
- Source DB: D:\project\data\user_management.db
|
||||
- Isolated DB: D:\project\docs\evidence\ops\2026-03-27\secret-boundary\20260327-181910\user_management.secret-boundary.db
|
||||
- Isolated config: D:\project\docs\evidence\ops\2026-03-27\secret-boundary\20260327-181910\config.secret-boundary.yaml
|
||||
|
||||
## Template Validation
|
||||
|
||||
- config template jwt.secret blank: True
|
||||
- config template postgresql.password blank: True
|
||||
- config template mysql.password blank: True
|
||||
- forbidden placeholders removed from configs/config.yaml: True
|
||||
- .gitignore protects local JWT key files: True
|
||||
- .gitignore protects .env files: True
|
||||
|
||||
## Runtime Injection Validation
|
||||
|
||||
- Startup path: UMS_CONFIG_PATH + UMS_JWT_ALGORITHM + UMS_JWT_SECRET
|
||||
- Synthetic JWT algorithm injected: HS256
|
||||
- Synthetic JWT secret length: 45
|
||||
- GET /health: pass
|
||||
- GET /health/ready: pass
|
||||
- GET /api/v1/auth/capabilities: {"password":true,"email_activation":false,"email_code":false,"sms_code":false,"password_reset":false,"admin_bootstrap_required":false,"oauth_providers":[]}
|
||||
|
||||
## Scope Note
|
||||
|
||||
- This drill proves the repo-level secret boundary and environment injection path are executable locally.
|
||||
- It does not prove external secrets manager, KMS rotation, or CI/CD environment delivery evidence.
|
||||
|
||||
## Evidence Files
|
||||
|
||||
- server.stdout.log
|
||||
- server.stderr.log
|
||||
- capabilities.json
|
||||
- config.secret-boundary.yaml
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"password": true,
|
||||
"email_activation": false,
|
||||
"email_code": false,
|
||||
"sms_code": false,
|
||||
"password_reset": false,
|
||||
"admin_bootstrap_required": false,
|
||||
"oauth_providers": [
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
server:
|
||||
port: 18088
|
||||
mode: release # debug, release
|
||||
read_timeout: 30s
|
||||
read_header_timeout: 10s
|
||||
write_timeout: 30s
|
||||
idle_timeout: 60s
|
||||
shutdown_timeout: 15s
|
||||
max_header_bytes: 1048576
|
||||
|
||||
database:
|
||||
type: sqlite # current runtime support: sqlite
|
||||
sqlite:
|
||||
path: "D:/project/docs/evidence/ops/2026-03-27/secret-boundary/20260327-181910/user_management.secret-boundary.db"
|
||||
postgresql:
|
||||
host: localhost
|
||||
port: 5432
|
||||
database: user_management
|
||||
username: postgres
|
||||
password: ""
|
||||
ssl_mode: disable
|
||||
max_open_conns: 100
|
||||
max_idle_conns: 10
|
||||
mysql:
|
||||
host: localhost
|
||||
port: 3306
|
||||
database: user_management
|
||||
username: root
|
||||
password: ""
|
||||
charset: utf8mb4
|
||||
max_open_conns: 100
|
||||
max_idle_conns: 10
|
||||
|
||||
cache:
|
||||
l1:
|
||||
enabled: true
|
||||
max_size: 10000
|
||||
ttl: 5m
|
||||
l2:
|
||||
enabled: false
|
||||
type: redis
|
||||
redis:
|
||||
addr: localhost:6379
|
||||
password: ""
|
||||
db: 0
|
||||
pool_size: 50
|
||||
ttl: 30m
|
||||
|
||||
redis:
|
||||
enabled: false
|
||||
addr: localhost:6379
|
||||
password: ""
|
||||
db: 0
|
||||
|
||||
jwt:
|
||||
algorithm: RS256
|
||||
secret: ""
|
||||
private_key_path: "./data/jwt/private.pem"
|
||||
public_key_path: "./data/jwt/public.pem"
|
||||
private_key_pem: ""
|
||||
public_key_pem: ""
|
||||
access_token_expire: 2h
|
||||
refresh_token_expire: 168h # 7天 = 168小时
|
||||
|
||||
security:
|
||||
password_min_length: 8
|
||||
password_require_special: true
|
||||
password_require_number: true
|
||||
login_max_attempts: 5
|
||||
login_lock_duration: 30m
|
||||
|
||||
ratelimit:
|
||||
enabled: true
|
||||
login:
|
||||
enabled: true
|
||||
algorithm: token_bucket
|
||||
capacity: 5
|
||||
rate: 1
|
||||
window: 1m
|
||||
register:
|
||||
enabled: true
|
||||
algorithm: leaky_bucket
|
||||
capacity: 3
|
||||
rate: 1
|
||||
window: 1h
|
||||
api:
|
||||
enabled: true
|
||||
algorithm: sliding_window
|
||||
capacity: 1000
|
||||
window: 1m
|
||||
|
||||
monitoring:
|
||||
prometheus:
|
||||
enabled: true
|
||||
path: /metrics
|
||||
tracing:
|
||||
enabled: false
|
||||
endpoint: http://localhost:4318
|
||||
service_name: user-management-system
|
||||
|
||||
logging:
|
||||
level: info # debug, info, warn, error
|
||||
format: json # json, text
|
||||
output:
|
||||
- stdout
|
||||
- ./logs/app.log
|
||||
rotation:
|
||||
max_size: 100 # MB
|
||||
max_age: 30 # days
|
||||
max_backups: 10
|
||||
|
||||
admin:
|
||||
username: ""
|
||||
password: ""
|
||||
email: ""
|
||||
|
||||
cors:
|
||||
enabled: true
|
||||
allowed_origins:
|
||||
- "http://localhost:3000"
|
||||
- "http://127.0.0.1:3000"
|
||||
allowed_methods:
|
||||
- GET
|
||||
- POST
|
||||
- PUT
|
||||
- DELETE
|
||||
- OPTIONS
|
||||
allowed_headers:
|
||||
- Authorization
|
||||
- Content-Type
|
||||
- X-Requested-With
|
||||
- X-CSRF-Token
|
||||
max_age: 3600
|
||||
|
||||
email:
|
||||
host: "" # 生产环境填写真实 SMTP Host
|
||||
port: 18088
|
||||
username: ""
|
||||
password: ""
|
||||
from_email: ""
|
||||
from_name: "用户管理系统"
|
||||
|
||||
sms:
|
||||
enabled: false
|
||||
provider: "" # aliyun, tencent;留空表示禁用短信能力
|
||||
code_ttl: 5m
|
||||
resend_cooldown: 1m
|
||||
max_daily_limit: 10
|
||||
aliyun:
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
sign_name: ""
|
||||
template_code: ""
|
||||
endpoint: ""
|
||||
region_id: "cn-hangzhou"
|
||||
code_param_name: "code"
|
||||
tencent:
|
||||
secret_id: ""
|
||||
secret_key: ""
|
||||
app_id: ""
|
||||
sign_name: ""
|
||||
template_id: ""
|
||||
region: "ap-guangzhou"
|
||||
endpoint: ""
|
||||
|
||||
password_reset:
|
||||
token_ttl: 15m
|
||||
site_url: "http://localhost:8080"
|
||||
|
||||
# OAuth 社交登录配置(留空则禁用对应 Provider)
|
||||
oauth:
|
||||
google:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/google/callback"
|
||||
wechat:
|
||||
app_id: ""
|
||||
app_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/wechat/callback"
|
||||
github:
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/github/callback"
|
||||
qq:
|
||||
app_id: ""
|
||||
app_key: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/qq/callback"
|
||||
alipay:
|
||||
app_id: ""
|
||||
private_key: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/alipay/callback"
|
||||
sandbox: false
|
||||
douyin:
|
||||
client_key: ""
|
||||
client_secret: ""
|
||||
redirect_url: "http://localhost:8080/api/v1/auth/oauth/douyin/callback"
|
||||
|
||||
# Webhook 全局配置
|
||||
webhook:
|
||||
enabled: true
|
||||
secret_header: "X-Webhook-Signature" # 签名 Header 名称
|
||||
timeout_sec: 30 # 单次投递超时(秒)
|
||||
max_retries: 3 # 最大重试次数
|
||||
retry_backoff: "exponential" # 退避策略:exponential / fixed
|
||||
worker_count: 4 # 后台投递协程数
|
||||
queue_size: 1000 # 投递队列大小
|
||||
|
||||
# IP 安全配置
|
||||
ip_security:
|
||||
auto_block_enabled: true # 是否启用自动封禁
|
||||
auto_block_duration: 30m # 自动封禁时长
|
||||
brute_force_threshold: 10 # 暴力破解阈值(窗口内失败次数)
|
||||
detection_window: 15m # 检测时间窗口
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user