From fd268fb8032104b8b7f79bc2f251b264435d0ab2 Mon Sep 17 00:00:00 2001 From: malei <> Date: Sun, 2 Aug 2026 03:17:18 +0800 Subject: [PATCH] docs: define sensitive outbound logging boundary --- graph/ontology.jsonl | 9 ++++---- ontology/rules.md | 9 ++++++++ ...决策-微信出站调用敏感日志边界-2026-08-02.md | 22 +++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 架构决策-微信出站调用敏感日志边界-2026-08-02.md diff --git a/graph/ontology.jsonl b/graph/ontology.jsonl index 491af0a..ded84b1 100644 --- a/graph/ontology.jsonl +++ b/graph/ontology.jsonl @@ -4,12 +4,12 @@ {"id":"entity:report","type":"Entity","name":"Report","nameZh":"服务报告","domain":"report","fields":["id","appointment_id","remark","user_id","customer_user_id","author_staff_id","store_id","report_token","pet_name","service_type","appointment_time","staff_name","send_status","sent_at","sent_by_user_id","send_channel","highlight_video_url","highlight_video_status","highlight_video_error","highlight_duration_sec","highlight_compose_mode","highlight_fail_reason","highlight_share_cover_url","create_time","update_time","deleted"],"systems":["Report Media Backend","Store Miniapp FE","Store Admin FE"],"code":"backend/src/main/java/com/petstore/entity/Report.java; backend/src/main/java/com/petstore/service/ReportService.java","doc":"docs/产品设计文档.md §三 t_report §5.3; docs/架构决策-报告确认发送状态-2026-08-02.md","test":"ReportServiceTest; ReportControllerTest; ReportSendMigrationTest","evidence":"anchored"} {"id":"entity:report_lead","type":"Entity","name":"ReportLead","nameZh":"留资线索","domain":"lead","fields":["id","report_id","store_id","phone","pet_name","service_type","wechat_openid","wechat_unionid","reminder_type","remind_date","remind_status","unsubscribe_token","client_ip","create_time","update_time","deleted"],"systems":["Report Media Backend"],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java; backend/src/main/java/com/petstore/entity/ReportLead.java","doc":"docs/本期主线-下一步产品优化点.md §C 留资","test":"ReportLeadControllerTest","evidence":"anchored"} {"id":"entity:business_event","type":"Entity","name":"BusinessEvent","nameZh":"不可变业务事件","domain":"platform","jpa":true,"fields":["id","event_id","event_type","event_version","store_id","store_customer_id","aggregate_type","aggregate_id","actor_user_id","actor_role","source","occurred_at","metadata_json","idempotency_key","create_time"],"systems":["Backend Core","Report Media Backend","Store Admin FE"],"code":"backend/src/main/java/com/petstore/entity/BusinessEvent.java; backend/src/main/java/com/petstore/service/BusinessEventService.java","doc":"docs/ontology/objects.md#entity:business_event; docs/架构决策-业务事件落库-2026-08-01.md","test":"BusinessEventServiceTest; BusinessEventMigrationTest","evidence":"anchored"} -{"id":"action:register_boss","type":"Action","name":"registerBoss","nameZh":"微信核验老板入驻","inputs":["storeName","bossName","phoneCode","loginCode"],"outputs":["entity:store","entity:user","session_token","entity:business_event","entity:audit_log"],"entrypoints":["POST /api/onboarding/register-boss","MerchantOnboardingController#registerBoss","MerchantOnboardingService#registerVerifiedBoss"],"appliesRules":["rule:BR-ONBOARD-001","rule:BR-AUDIT-001"],"code":"backend/src/main/java/com/petstore/controller/MerchantOnboardingController.java#registerBoss; backend/src/main/java/com/petstore/service/MerchantOnboardingService.java#registerVerifiedBoss","test":"MerchantOnboardingServiceTest; BusinessEventServiceTest","evidence":"anchored","domain":"user"} +{"id":"action:register_boss","type":"Action","name":"registerBoss","nameZh":"微信核验老板入驻","inputs":["storeName","bossName","phoneCode","loginCode"],"outputs":["entity:store","entity:user","session_token","entity:business_event","entity:audit_log"],"entrypoints":["POST /api/onboarding/register-boss","MerchantOnboardingController#registerBoss","MerchantOnboardingService#registerVerifiedBoss"],"appliesRules":["rule:BR-ONBOARD-001","rule:BR-AUDIT-001","rule:BR-AUTH-003"],"code":"backend/src/main/java/com/petstore/controller/MerchantOnboardingController.java#registerBoss; backend/src/main/java/com/petstore/service/MerchantOnboardingService.java#registerVerifiedBoss","test":"MerchantOnboardingServiceTest; BusinessEventServiceTest","evidence":"anchored","domain":"user"} {"id":"action:update_store","type":"Action","name":"updateStore","nameZh":"更新店铺","inputs":["entity:store"],"outputs":["entity:store"],"entrypoints":["PUT /api/store/update","StoreController#update","StoreService#update"],"appliesRules":["rule:BR-STORE-001"],"code":"backend/src/main/java/com/petstore/controller/StoreController.java#update","test":"StoreControllerTest","evidence":"anchored","domain":"store"} {"id":"action:delete_store","type":"Action","name":"deleteStore","nameZh":"删除店铺","inputs":["id"],"outputs":[],"entrypoints":["DELETE /api/store/delete","StoreController#delete"],"appliesRules":["rule:BR-STORE-001"],"code":"backend/src/main/java/com/petstore/controller/StoreController.java#delete","test":"StoreControllerTest","evidence":"anchored","domain":"store"} {"id":"action:get_store_by_invite_code","type":"Action","name":"getStoreByInviteCode","nameZh":"永久邀请码查询(停用)","inputs":["code"],"outputs":[],"entrypoints":["GET /api/store/invite-code","StoreController#getByInviteCode"],"appliesRules":["rule:BR-STORE-002"],"code":"backend/src/main/java/com/petstore/controller/StoreController.java#getByInviteCode","test":"StoreControllerTest","evidence":"anchored","domain":"store","status":"deprecated","deprecated":true,"note":"固定返回 LEGACY_INVITE_DISABLED,不查询或返回门店"} {"id":"action:login","type":"Action","name":"login","nameZh":"手机号验证码登录","inputs":["phone","code"],"outputs":["entity:user","entity:store","session_token"],"entrypoints":["POST /api/user/login","UserService#login"],"appliesRules":["rule:BR-AUTH-001","rule:BR-AUTH-002"],"code":"backend/src/main/java/com/petstore/service/UserService.java#login","test":"UserServiceLoginStrategyTest","evidence":"anchored","domain":"auth"} -{"id":"action:wx_phone_login","type":"Action","name":"wxPhoneLogin","nameZh":"微信手机号授权登录","inputs":["phoneCode","loginCode"],"outputs":["entity:user","entity:store","session_token"],"entrypoints":["POST /api/user/wx-phone-login","UserController#wxPhoneLogin","UserService#loginByVerifiedPhone"],"appliesRules":["rule:BR-AUTH-001"],"code":"backend/src/main/java/com/petstore/controller/UserController.java#wxPhoneLogin","evidence":"anchored","domain":"auth"} +{"id":"action:wx_phone_login","type":"Action","name":"wxPhoneLogin","nameZh":"微信手机号授权登录","inputs":["phoneCode","loginCode"],"outputs":["entity:user","entity:store","session_token"],"entrypoints":["POST /api/user/wx-phone-login","UserController#wxPhoneLogin","UserService#loginByVerifiedPhone"],"appliesRules":["rule:BR-AUTH-001","rule:BR-AUTH-003"],"code":"backend/src/main/java/com/petstore/controller/UserController.java#wxPhoneLogin","evidence":"anchored","domain":"auth"} {"id":"action:bind_wechat","type":"Action","name":"bindWechat","nameZh":"绑定微信标识","inputs":["userId","openid","unionid"],"outputs":["entity:user"],"entrypoints":["UserService#bindWechatMiniIdentity"],"appliesRules":[],"code":"backend/src/main/java/com/petstore/service/UserService.java#bindWechatMiniIdentity","evidence":"anchored","domain":"auth"} {"id":"action:create_appointment","type":"Action","name":"createAppointment","nameZh":"创建预约","inputs":["petName","petType","serviceType","appointmentTime","storeId","remark","petId","customerUserId","customerPhone","customerName","followUpTaskId"],"outputs":["entity:appointment","entity:follow_up_task"],"entrypoints":["POST /api/appointment/create","AppointmentController#create","AppointmentService#createBooking"],"appliesRules":["rule:BR-APPT-001","rule:BR-APPT-003","rule:BR-APPT-004","rule:BR-SC-001","rule:BR-BE-001","rule:BR-FU-002"],"code":"backend/src/main/java/com/petstore/controller/AppointmentController.java#create; backend/src/main/java/com/petstore/service/AppointmentService.java#createBooking; backend/src/main/java/com/petstore/service/FollowUpTaskService.java#prepareRebook; #markRebooked; backend/src/main/java/com/petstore/service/StoreCustomerService.java#touchBooking; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordAppointmentCreated","test":"AppointmentControllerIdentityTest; AppointmentServiceTest; FollowUpTaskServiceTest; StoreCustomerServiceTest; BusinessEventServiceTest","evidence":"anchored","domain":"appointment"} {"id":"action:start_service","type":"Action","name":"startService","nameZh":"开始服务","inputs":["appointmentId"],"outputs":["entity:appointment"],"entrypoints":["POST /api/appointment/start","AppointmentController#start","AppointmentService#startService"],"appliesRules":["rule:BR-APPT-001","rule:BR-APPT-002","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/AppointmentController.java#start; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordAppointmentStatusChanged","test":"AppointmentServiceTest; BusinessEventServiceTest","evidence":"anchored","domain":"appointment"} @@ -21,7 +21,7 @@ {"id":"action:get_report_by_token","type":"Action","name":"getReportByToken","nameZh":"公开报告查询","inputs":["token"],"outputs":["entity:report"],"entrypoints":["GET /api/report/get","ReportController#getByAppointmentId"],"appliesRules":["rule:BR-RPT-006"],"code":"backend/src/main/java/com/petstore/controller/ReportController.java#getByAppointmentId","test":"ReportControllerTest#publicTokenQueryHidesInternalFields","evidence":"anchored","domain":"report"} {"id":"action:track_report_open","type":"Action","name":"trackReportOpen","nameZh":"报告打开埋点","inputs":["token","visitType"],"outputs":["entity:business_event"],"entrypoints":["POST /api/report/open-track","ReportController#trackReportOpen"],"appliesRules":["rule:BR-RPT-007","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/ReportController.java#trackReportOpen; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordReportOpenByToken","test":"ReportControllerTest; BusinessEventServiceTest","evidence":"anchored","domain":"report"} {"id":"action:delete_report","type":"Action","name":"deleteReport","nameZh":"删除报告","inputs":["id"],"outputs":[],"entrypoints":["DELETE /api/report/delete","ReportController#delete"],"appliesRules":["rule:BR-RPT-005"],"code":"backend/src/main/java/com/petstore/controller/ReportController.java#delete","evidence":"anchored","domain":"report"} -{"id":"action:submit_lead","type":"Action","name":"submitLead","nameZh":"宠主留资","inputs":["token","phone","consent","loginCode"],"outputs":["leadId","remindDate","unsubscribeToken","wechatBound","entity:follow_up_task"],"entrypoints":["POST /api/report/{token}/reminder","ReportLeadController#submitReminder","ReportLeadService#submit"],"appliesRules":["rule:BR-LEAD-001","rule:BR-SC-001","rule:BR-BE-001","rule:BR-FU-001"],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#submitReminder; backend/src/main/java/com/petstore/service/ReportLeadService.java#submit; backend/src/main/java/com/petstore/service/FollowUpTaskService.java#ensureForLead; backend/src/main/java/com/petstore/service/StoreCustomerService.java#touchLead; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordLeadSubmitted","evidence":"anchored","test":"ReportLeadControllerTest; ReportLeadServiceStoreCustomerTest; FollowUpTaskServiceTest; StoreCustomerServiceTest; BusinessEventServiceTest","domain":"lead"} +{"id":"action:submit_lead","type":"Action","name":"submitLead","nameZh":"宠主留资","inputs":["token","phone","consent","loginCode"],"outputs":["leadId","remindDate","unsubscribeToken","wechatBound","entity:follow_up_task"],"entrypoints":["POST /api/report/{token}/reminder","ReportLeadController#submitReminder","ReportLeadService#submit"],"appliesRules":["rule:BR-LEAD-001","rule:BR-SC-001","rule:BR-BE-001","rule:BR-FU-001","rule:BR-AUTH-003"],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#submitReminder; backend/src/main/java/com/petstore/service/ReportLeadService.java#submit; backend/src/main/java/com/petstore/service/FollowUpTaskService.java#ensureForLead; backend/src/main/java/com/petstore/service/StoreCustomerService.java#touchLead; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordLeadSubmitted","evidence":"anchored","test":"ReportLeadControllerTest; ReportLeadServiceStoreCustomerTest; FollowUpTaskServiceTest; StoreCustomerServiceTest; BusinessEventServiceTest","domain":"lead"} {"id":"action:unsubscribe","type":"Action","name":"unsubscribe","nameZh":"一键退订","inputs":["unsubscribeToken"],"outputs":["entity:follow_up_task"],"entrypoints":["POST /api/report/unsubscribe","ReportLeadController#unsubscribe"],"appliesRules":["rule:BR-UNSUB-001","rule:BR-FU-001"],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#unsubscribe; backend/src/main/java/com/petstore/service/FollowUpTaskService.java#cancelForUnsubscribe","evidence":"anchored","domain":"lead","test":"ReportLeadControllerTest; FollowUpTaskServiceTest"} {"id":"action:list_leads","type":"Action","name":"listLeads","nameZh":"回访池列表","inputs":["status"],"outputs":["entity:report_lead"],"entrypoints":["GET /api/report/leads","ReportLeadController#leads"],"appliesRules":["rule:BR-LEAD-002","rule:BR-LEAD-003"],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#leads","evidence":"anchored","test":"ReportLeadControllerTest","domain":"lead"} {"id":"event:store_registered","type":"Event","name":"StoreRegistered","nameZh":"门店入驻","domain":"store","payload":["storeId","bossUserId","occurredAt"],"emittedBy":["action:register_boss"],"code":"backend/src/main/java/com/petstore/service/BusinessEventService.java#recordStoreRegistered","test":"BusinessEventServiceTest","evidence":"anchored","note":"持久化 BusinessEvent 与 AuditLog,均不含手机号或 legacy inviteCode"} @@ -38,6 +38,7 @@ {"id":"event:lead_submitted","type":"Event","name":"LeadSubmitted","nameZh":"留资提交","domain":"lead","payload":["storeId","storeCustomerId","leadId","repeatSubmit","occurredAt"],"emittedBy":["action:submit_lead"],"appliesRules":["rule:BR-LEAD-001","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/service/ReportLeadService.java#submit; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordLeadSubmitted","doc":"docs/本期主线-下一步产品优化点.md 埋点事件 schema","test":"ReportLeadServiceStoreCustomerTest; BusinessEventMigrationTest","evidence":"anchored"} {"id":"rule:BR-AUTH-001","type":"Rule","name":"HMAC session token + 活跃账号范围鉴权","domain":"auth","appliesTo":["action:update_store","action:delete_store","action:get_store_by_invite_code","action:create_appointment","action:start_service","action:transition_appointment_status","action:get_appointment_detail","action:list_appointments","action:submit_report","action:delete_report","action:list_leads","action:admin_list_leads","action:start_follow_up_task","action:reschedule_follow_up_task","action:close_follow_up_task","action:get_store_onboarding","action:complete_store_onboarding","action:create_staff_invitation","action:list_staff_invitations","action:revoke_staff_invitation","action:list_staff","action:delete_staff"],"code":"backend/src/main/java/com/petstore/auth/AuthInterceptor.java; SessionTokenService.java; CurrentUserContext.java","test":"SessionTokenServiceTest; AuthInterceptorActiveUserTest; AdminFollowUpTaskControllerTest","evidence":"anchored","note":"除签名和过期外,每次受保护请求校验账号仍存在且 role/storeId 与 token 一致,门店角色还须归属有效 Store"} {"id":"rule:BR-AUTH-002","type":"Rule","name":"生产登录策略:仅微信授权","domain":"auth","appliesTo":["action:login","action:wx_phone_login"],"code":"backend/src/main/java/com/petstore/service/UserService.java#login; SmsController.java","test":"UserServiceLoginStrategyTest","evidence":"anchored"} +{"id":"rule:BR-AUTH-003","type":"Rule","name":"微信出站敏感日志边界","domain":"auth","appliesTo":["action:wx_phone_login","action:register_boss","action:accept_staff_invitation","action:submit_lead"],"code":"backend/src/main/java/com/petstore/service/WechatMiniProgramService.java; backend/src/main/java/com/petstore/auth/SessionTokenService.java; backend/src/main/java/com/petstore/controller/UserController.java; backend/src/main/java/com/petstore/controller/FileController.java","doc":"docs/架构决策-微信出站调用敏感日志边界-2026-08-02.md","test":"SensitiveLoggingContractTest","evidence":"anchored","note":"异常日志只记 HTTP 状态/微信错误码/异常类型;不记录响应体、手机号、access_token、AppSecret、一次性 code、带 query URL、外部错误文案或完整异常"} {"id":"rule:BR-STORE-001","type":"Rule","name":"店铺更新/删除仅 boss + 同店","domain":"store","appliesTo":["action:update_store","action:delete_store"],"code":"backend/src/main/java/com/petstore/controller/StoreController.java#update; #delete","test":"StoreControllerTest","evidence":"anchored"} {"id":"rule:BR-STORE-002","type":"Rule","name":"legacy 永久邀请码禁用","domain":"store","appliesTo":["action:get_store_by_invite_code","action:register_staff"],"code":"backend/src/main/java/com/petstore/controller/StoreController.java#getByInviteCode; backend/src/main/java/com/petstore/controller/UserController.java#registerStaff","test":"StoreControllerTest","evidence":"anchored","note":"legacy invite_code 保留数据库兼容,不返回客户端、不再作为注册凭证"} {"id":"rule:BR-APPT-001","type":"Rule","name":"预约状态机","domain":"appointment","appliesTo":["action:start_service","action:transition_appointment_status","action:submit_report"],"code":"backend/src/main/java/com/petstore/service/AppointmentService.java#startService; #transitionStatus","test":"AppointmentServiceTest","evidence":"anchored"} @@ -247,7 +248,7 @@ {"id":"action:list_staff_invitations","type":"Action","name":"listStaffInvitations","nameZh":"员工邀请记录","domain":"user","inputs":["entity:current_user"],"outputs":["entity:staff_invitation"],"entrypoints":["GET /api/admin/staff-invitations","StaffInvitationController#list","StaffInvitationService#list"],"appliesRules":["rule:BR-AUTH-001","rule:BR-USER-001","rule:BR-INVITE-001"],"code":"backend/src/main/java/com/petstore/controller/StaffInvitationController.java#list; backend/src/main/java/com/petstore/service/StaffInvitationService.java#list","test":"StaffInvitationServiceTest","evidence":"anchored","note":"不返回 invitedPhone、tokenHash 或原始 token"} {"id":"action:revoke_staff_invitation","type":"Action","name":"revokeStaffInvitation","nameZh":"撤销员工邀请","domain":"user","inputs":["invitationId","entity:current_user"],"outputs":["entity:staff_invitation","entity:audit_log"],"entrypoints":["DELETE /api/admin/staff-invitations","StaffInvitationController#revoke","StaffInvitationService#revoke"],"appliesRules":["rule:BR-AUTH-001","rule:BR-USER-001","rule:BR-INVITE-001","rule:BR-AUDIT-001"],"code":"backend/src/main/java/com/petstore/controller/StaffInvitationController.java#revoke; backend/src/main/java/com/petstore/service/StaffInvitationService.java#revoke","test":"StaffInvitationServiceTest","evidence":"anchored"} {"id":"action:preview_staff_invitation","type":"Action","name":"previewStaffInvitation","nameZh":"公开预览员工邀请","domain":"user","inputs":["token"],"outputs":["storeName","invitedName","maskedPhone","status","expiresAt","canAccept"],"entrypoints":["GET /api/staff-invitations/preview","StaffInvitationController#preview","StaffInvitationService#preview"],"appliesRules":["rule:BR-INVITE-001"],"code":"backend/src/main/java/com/petstore/controller/StaffInvitationController.java#preview; backend/src/main/java/com/petstore/service/StaffInvitationService.java#preview","test":"StaffInvitationServiceTest","evidence":"anchored"} -{"id":"action:accept_staff_invitation","type":"Action","name":"acceptStaffInvitation","nameZh":"微信核验并接受员工邀请","domain":"user","inputs":["token","phoneCode","loginCode"],"outputs":["entity:user","entity:store","session_token","entity:audit_log"],"entrypoints":["POST /api/staff-invitations/accept","StaffInvitationController#accept","StaffInvitationService#acceptVerified"],"appliesRules":["rule:BR-INVITE-001","rule:BR-AUDIT-001","rule:BR-USER-003"],"code":"backend/src/main/java/com/petstore/controller/StaffInvitationController.java#accept; backend/src/main/java/com/petstore/service/StaffInvitationService.java#acceptVerified; frontend/src/pages/login/Login.vue","test":"StaffInvitationServiceTest","evidence":"anchored"} +{"id":"action:accept_staff_invitation","type":"Action","name":"acceptStaffInvitation","nameZh":"微信核验并接受员工邀请","domain":"user","inputs":["token","phoneCode","loginCode"],"outputs":["entity:user","entity:store","session_token","entity:audit_log"],"entrypoints":["POST /api/staff-invitations/accept","StaffInvitationController#accept","StaffInvitationService#acceptVerified"],"appliesRules":["rule:BR-INVITE-001","rule:BR-AUDIT-001","rule:BR-USER-003","rule:BR-AUTH-003"],"code":"backend/src/main/java/com/petstore/controller/StaffInvitationController.java#accept; backend/src/main/java/com/petstore/service/StaffInvitationService.java#acceptVerified; frontend/src/pages/login/Login.vue","test":"StaffInvitationServiceTest","evidence":"anchored"} {"id":"rule:BR-ONBOARD-001","type":"Rule","name":"verifiedMerchantOnboarding","nameZh":"微信核验入驻与显式开通","domain":"store","appliesTo":["action:register_boss","action:legacy_register_boss_disabled","action:get_store_onboarding","action:complete_store_onboarding"],"code":"backend/src/main/java/com/petstore/service/MerchantOnboardingService.java; backend/src/main/java/com/petstore/service/StoreOnboardingService.java","doc":"docs/ontology/rules.md#rule:BR-ONBOARD-001","test":"StoreOnboardingServiceTest","evidence":"anchored","note":"新老板手机号由微信核验;完成开通要求门店资料、预约容量、至少一个服务项目,邀请员工为选填;历史状态 unknown 不推断"} {"id":"rule:BR-INVITE-001","type":"Rule","name":"oneTimeStaffInvitation","nameZh":"一次性员工邀请安全边界","domain":"user","appliesTo":["action:create_staff_invitation","action:list_staff_invitations","action:revoke_staff_invitation","action:preview_staff_invitation","action:accept_staff_invitation"],"code":"backend/src/main/java/com/petstore/service/StaffInvitationService.java; backend/src/main/java/com/petstore/controller/StaffInvitationController.java","doc":"docs/ontology/rules.md#rule:BR-INVITE-001","test":"StaffInvitationServiceTest; StoreOnboardingMigrationTest","evidence":"anchored","note":"256-bit token 原文只返回一次,库内 SHA-256;手机号绑定且由微信核验;1-30 天、一次使用、可撤销;账号/微信冲突拒绝,不升级 customer 角色"} {"id":"rule:BR-AUDIT-001","type":"Rule","name":"immutableLowSensitivityAudit","nameZh":"不可变低敏操作审计","domain":"platform","appliesTo":["action:register_boss","action:update_store","action:complete_store_onboarding","action:create_staff_invitation","action:revoke_staff_invitation","action:accept_staff_invitation","action:delete_staff"],"code":"backend/src/main/java/com/petstore/service/AuditLogService.java","doc":"docs/ontology/rules.md#rule:BR-AUDIT-001","test":"AuditLogServiceTest; StoreOnboardingMigrationTest","evidence":"anchored","note":"只追加;metadata 仅服务端白名单标量且禁止 PII、凭证、地址坐标和原始请求"} diff --git a/ontology/rules.md b/ontology/rules.md index df40b57..82f488b 100644 --- a/ontology/rules.md +++ b/ontology/rules.md @@ -22,6 +22,15 @@ - **测试**:`UserServiceLoginStrategyTest` - **证据**:`anchored` +### rule:BR-AUTH-003 + +**微信出站敏感日志边界**:`jscode2session`、`getuserphonenumber` 和 `access_token` 的异常日志只记录 HTTP 状态码、微信错误码和异常类型。不得记录完整响应体、手机号、access_token、AppSecret、`phoneCode/jsCode`、含 query 的请求 URL 或异常消息;返回客户端的连接异常使用固定低敏文案。 + +- **适用动作**:`action:wx_phone_login`、`action:register_boss`、`action:accept_staff_invitation`、`action:submit_lead` +- **代码**:`backend/src/main/java/com/petstore/service/WechatMiniProgramService.java` +- **测试**:`SensitiveLoggingContractTest` +- **证据**:`anchored` + ## Store ### rule:BR-STORE-001 diff --git a/架构决策-微信出站调用敏感日志边界-2026-08-02.md b/架构决策-微信出站调用敏感日志边界-2026-08-02.md new file mode 100644 index 0000000..c6b984a --- /dev/null +++ b/架构决策-微信出站调用敏感日志边界-2026-08-02.md @@ -0,0 +1,22 @@ +# 架构决策:微信出站调用敏感日志边界 + +> 日期:2026-08-02 +> 决策:微信接口异常只记录低敏诊断维度,不记录请求/响应原文 +> 本体:`rule:BR-AUTH-003` + +## 背景 + +`jscode2session`、`getuserphonenumber` 和 `access_token` 都涉及一次性 code、openid、手机号或 AppSecret。原异常分支曾把微信完整响应体或原始手机号写入日志,并把 `Exception#getMessage()` 拼入客户端错误文案。网络异常消息可能包含含 query 的请求 URI,从而间接暴露 AppSecret、access_token 或一次性 code;完整微信响应也可能包含手机号。 + +## 决策 + +1. 微信出站日志只允许 HTTP 状态码、微信数值错误码和 Java 异常类型。 +2. 禁止把完整响应体、手机号、openid/unionid、access_token、AppSecret、`phoneCode/jsCode`、含 query 的 URL 或异常消息作为日志参数。 +3. 网络或解析异常返回固定低敏文案;微信明确返回的业务错误仍可通过受控 `errcode` 映射成可理解提示。 +4. `SensitiveLoggingContractTest` 扫描微信服务及相邻的身份、session、邀请和上传边界,阻止敏感变量、外部错误文案、异常消息或完整异常重新成为日志参数,并禁止把异常消息拼入客户端响应。 + +## 边界 + +- 该决策不降低线上可观测性;HTTP 状态、微信错误码、异常类型和现有请求级 trace 足以定位网络、凭据和 code 生命周期问题。 +- 运维排障不得临时打开完整 HTTP body 或 query 日志。确需与微信支持协查时,只记录时间窗、匿名化请求关联和错误码。 +- 微信真实 AppID/AppSecret 仍须在生产前轮换并由受控环境变量提供;本变更不读取、生成或验证真实凭据。