Răsfoiți Sursa

chore: classify legacy safety administration

彭峰 1 lună în urmă
părinte
comite
5088c5ca6c

+ 1 - 0
docs/migration/admin-migration-status.md

@@ -45,6 +45,7 @@
 - `Extend/QuartzTask` 管理的是 `QuartzNetTask` 任务记录、任务内日志及常驻进程中的 `IScheduler/ScheduleTaskHandler`,创建、更新、立即执行、暂停、恢复和删除都必须同步调度器。目标只存在语义不同且为空的 `SenTask`,云函数没有 Quartz 控制面;含 GET/POST 两个同名 `TaskAdd` 在内的 11 个动作由 `legacyQuartzTaskBlocker` 分别返回数据或基础设施阻塞原因。
 - 旧 `Design/Resource` 的历史资源已从线上 `DesignRes` 恢复到 Angular“可视化设计资源”页:`designResources` 提供帐套隔离、分页、搜索和 `ztype` 精确筛选,`saveDesignResource` 只允许更新名称、类型、用途、状态及 `style/use/fun` 元数据,`designResourceBatch` 覆盖单条和批量删除。`id/vpath/previewimg/cdate/userid/sourceKey` 均锁为只读;线上 Parse 文件适配器仍错误要求 MongoDB 连接串,而当前项目使用 PostgreSQL,因此新增及替换原文件/预览图继续以 `migration_blocked` 明确拒绝,不会伪造可用资源路径。
 - 旧 `User/Role` 的 9 个管理员角色、3 个用户角色与 `ARoleAuth` 权限记录已迁到 Angular“系统角色”页及 `roles/saveRole/roleAuthorization/saveRoleAuthorization/roleBatch`:支持角色类型切换、搜索、分页、名称防重、原子 `roleId`、12 个旧权限域完整编辑及权限码格式校验。角色类型创建后不得在 `admin/user` 之间迁移;删除前同时检查 `Manager.adminRole` 和 `_User.legacyUserData.UserRole`,无账号引用时才级联删除 `ARoleAuth`。Parse 原生 `_Role` 为空,`Permission` 是另一套网页设计师模型,二者都没有被误当作旧 `ZL_Role`。交付程序中 `B_ARoleAuth.Check/CheckEx/AuthCheckEx` 均固定返回 `true`,因此保留的旧权限码不用来绕过当前云函数的独立管理员会话与帐套校验。
+- `Extend/Safe` 的 15 个入口已完成数据与运行时审计:线上全部 Schema 仅命中不相关的 `SafeMobile`,没有旧 `Safe_IP`、非法词库、扫描结果或站点保护配置。旧聚合 `API` 还依赖服务器站点文件、五秒后返回的 Razor 局部页、`ContentWordHelper` 常驻线程与进度缓存;`SiteProtect_Submit` 只更新旧 `SiteConfig.Safe_ContentCheck_Models`,`ContentCheckConfig_Submit` 直接写 `IllegalWordsVerify.keywordsVPath` 服务器文件。当前内容写入链不消费这些配置,单纯存入 Parse Config 会伪造安全能力;因此 IP 三项按数据缺失、其余按基础设施缺失由 `legacySafeBlocker` 返回逐项 `migration_blocked`。
 - 旧 `NodeAdd_Submit`、`Node_API` 与 `UnionNode_Move` 的核心栏目生命周期已迁为专用 `saveNode/nodeBatch`:新增栏目以临时来源键完成 Parse 初始写入,再使用 PostgreSQL 事务锁原子分配 `nodeId/orderId` 并替换为旧 `sourceKey`;保存会校验同级名称/目录重复、父栏目帐套和回收状态,移动会阻断自身/下级循环并递归修正所有后代深度。回收、恢复和每批 1–100 个栏目迁移已接入 Angular;永久删除仅在没有内容、下级栏目、节点权限和模型模板引用时开放,通用保存与删除不能绕过。
 - 旧 `SpecialAdd_Save` 与 `UnionSpecial_Move` 已迁为专用 `saveSpecial/specialBatch`:新增专题使用临时来源键和 PostgreSQL 事务锁原子分配 `specId/orderId`,保存时校验全帐套名称/目录唯一、父专题归属与层级循环;删除会拒绝仍有下级专题的记录。旧 `UnionSpecial_Merge` 被明确列为数据阻塞:`CommonModel.specialId` 只存在于 Parse Schema、PostgreSQL 物理列缺失,商品主表也未迁移,无法安全改写内容/商品专题关系。
 - 旧 `ModelManage/Field/Order_Submit` 的安全部分已迁为 `saveModelMetadata/saveModelFieldMetadata/modelFieldOrder`:现有模型可编辑名称、条目文案、图标、说明和内容模板元数据,现有字段可编辑别名、提示、显示/搜索配置,并可在同模型内批量排序;`modelId/modelType/tableName/fieldId/modelId/fieldName/fieldType/sourceKey` 等结构字段通用只读。新增、复制、删除模型或字段会显式返回 501,因为旧实现同时执行 PostgreSQL 建表/改列、标签生成和模板文件写入,目标托管环境没有该 DDL/旧模板运行时。

+ 19 - 0
scripts/deploy-admin-functions.mjs

@@ -1563,6 +1563,25 @@ async function handler(request, response) {
         TaskList: '目标 Schema 缺少 QuartzNetTask 任务定义、类型、状态、次数和历史记录;空 SenTask 语义不等价', ViewLog: '目标没有 QuartzNetTask.Log 执行日志可按任务查看', TaskAdd: '目标没有 Quartz IScheduler、ScheduleTaskHandler 与任务持久层,无法创建一次或循环任务', TaskEdit: '目标没有可编辑的 Quartz 任务定义', TaskUpdate: '目标没有可同步更新持久层与 Quartz 调度器的控制面', ExecuteNow: '云函数环境没有已注册的 Quartz 作业可立即触发', TaskDelete: '目标没有可同时删除 Quartz 作业与任务记录的调度控制面', TaskDeleteRange: '目标没有可批量注销 Quartz 作业与删除任务记录的控制面', TaskPause: '云函数环境没有常驻 Quartz 调度器或可暂停作业', TaskResume: '云函数环境没有常驻 Quartz 调度器或可恢复作业'
       }; if (!Object.prototype.hasOwnProperty.call(blockers, action)) fail(400, '不支持的旧 Quartz 任务动作'); fail(501, 'migration_blocked: ' + blockers[action]);
     }
+    if (operation === 'legacySafeBlocker') {
+      const action = String(input.action || ''); const blockers = {
+        Default: '旧默认入口只跳转到 SiteSwitch;目标没有可切换已部署 Angular 站点的安全控制面',
+        API: '旧聚合 API 同时依赖缺失 Safe_IP 记录、五秒服务器站点检查局部页与常驻 ContentWordHelper 扫描线程',
+        SiteSwitch: '目标没有旧站点开关状态、下线页文件或能控制静态前端发布的运行时',
+        SiteIP: '目标 Schema 缺少旧 Safe_IP 白/黑名单、到期时间与管理员历史记录',
+        SiteIPAdd: '目标没有可编辑或新建的 Safe_IP IP 规则记录',
+        SiteIPAdd_Submit: '目标缺少 Safe_IP 持久层及在请求入口实际消费 IP 规则的中间件',
+        ContentProtect: '旧内容保护页仅剩已丢失的 Razor 视图,目标没有签名、防复制或防篡改配置与消费链',
+        SiteAssess: '旧站点安全评估页仅剩已丢失视图,交付包没有评分规则、基线或历史结果',
+        SiteProtect: '目标没有 SiteOption.Safe_ContentCheck_Models 站点配置模型及内容写入时的扫描消费层',
+        SiteProtect_Submit: '仅保存模型 ID 字符串不会产生内容保护;目标缺少旧 SiteConfig.Update 及扫描运行时',
+        ContentCheck: '目标没有 ContentWordHelper 常驻扫描任务、进度结果缓存和待检内容模型范围',
+        ContentCheckConfig: '云函数运行时没有旧 IllegalWordsVerify.keywordsVPath 非法词文件与编辑视图',
+        ContentCheckConfig_Submit: '云函数不能向旧服务器非法词文件执行 SafeC.WriteUnSafeFile,且当前内容接口没有该文件消费链',
+        ContentFilter: '旧内容过滤页仅剩已丢失视图,目标没有可证明等价的替换/阻断规则及执行层',
+        SiteCheck: '旧站点检查依赖服务器站点文件、五秒后局部视图与丢失的检查规则,云函数无法还原'
+      }; if (!Object.prototype.hasOwnProperty.call(blockers, action)) fail(400, '不支持的旧站点安全动作'); fail(501, 'migration_blocked: ' + blockers[action]);
+    }
     if (operation === 'legacyLogBlocker') {
       const action = String(input.action || ''); const blockers = {
         TxtLog: '云函数运行时没有旧 /Log/{LogType}/ 本地日志目录', TxtLogContent: '云函数不能按文件名读取旧服务器文本日志', TxtLog_Down: '云函数没有旧服务器日志文件可下载',

+ 15 - 0
scripts/generate-admin-coverage.mjs

@@ -324,6 +324,21 @@ const explicit = new Map([
   ['User / Role/ARoleAdd_Submit', ['implemented', 'saveRole 已覆盖管理员角色名称、说明、类型固化和原子 roleId']],
   ['User / Role/ARoleAuth', ['implemented', '已在角色详情中完整展示 model/content/shop/page/exam/user/system/office/portable/sites/other/extend']],
   ['User / Role/ARoleAuth_Submit', ['implemented', 'saveRoleAuthorization 已覆盖 12 域权限码去重校验、新建/更新和原子权限编号']],
+  ['Extend / Safe/Default', ['blocked-infrastructure', '旧默认入口只跳转 SiteSwitch;目标没有静态 Angular 站点的安全开关控制面']],
+  ['Extend / Safe/API', ['blocked-infrastructure', '旧聚合 API 依赖缺失 Safe_IP、服务器站点检查局部页和 ContentWordHelper 常驻线程']],
+  ['Extend / Safe/SiteSwitch', ['blocked-infrastructure', '目标没有站点上下线状态、下线页文件或前端发布开关']],
+  ['Extend / Safe/SiteIP', ['blocked-data', '目标 Schema 缺少旧 Safe_IP 白/黑名单、到期时间与管理员历史']],
+  ['Extend / Safe/SiteIPAdd', ['blocked-data', '目标没有可编辑或新建的 Safe_IP IP 规则记录']],
+  ['Extend / Safe/SiteIPAdd_Submit', ['blocked-data', '目标缺少 Safe_IP 持久层和实际消费 IP 规则的请求中间件']],
+  ['Extend / Safe/ContentProtect', ['blocked-infrastructure', '旧内容保护页视图已丢失,目标无签名、防复制或防篡改配置消费链']],
+  ['Extend / Safe/SiteAssess', ['blocked-infrastructure', '旧站点安全评估视图、评分规则、基线和历史结果均未交付']],
+  ['Extend / Safe/SiteProtect', ['blocked-infrastructure', '目标无 Safe_ContentCheck_Models 配置模型及内容写入扫描消费层']],
+  ['Extend / Safe/SiteProtect_Submit', ['blocked-infrastructure', '仅保存模型 ID 字符串不会产生保护;目标缺少 SiteConfig.Update 和扫描运行时']],
+  ['Extend / Safe/ContentCheck', ['blocked-infrastructure', '目标无 ContentWordHelper 扫描任务、进度结果缓存和待检模型范围']],
+  ['Extend / Safe/ContentCheckConfig', ['blocked-infrastructure', '云函数运行时没有 IllegalWordsVerify.keywordsVPath 非法词文件与旧编辑视图']],
+  ['Extend / Safe/ContentCheckConfig_Submit', ['blocked-infrastructure', '云函数不能写旧非法词文件,当前内容接口也没有该文件消费链']],
+  ['Extend / Safe/ContentFilter', ['blocked-infrastructure', '旧内容过滤视图已丢失,目标无等价替换/阻断规则与执行层']],
+  ['Extend / Safe/SiteCheck', ['blocked-infrastructure', '旧站点检查依赖服务器文件、局部 Razor 页和丢失的检查规则']],
   ['Design / Interactive/InteractiveList', ['blocked-data', '目标缺少旧 Design_Pub 交互提交表及 sceneid、uid、fname 数据;DesignAnswer/Pub 不等价']],
   ['Design / Interactive/Interactive_API', ['blocked-data', '目标没有可按旧 Design_Pub ID 删除的交互提交记录']],
   ['Office / iServer/Default', ['blocked-data', '目标 Schema 缺少旧 IServer 服务工单主表与回复表']],

+ 1 - 0
scripts/smoke-admin-functions.mjs

@@ -733,6 +733,7 @@ try {
   for (const action of ['Index','APIList','API_del','AddAPI','AddAPI_Submit','PushMsg','PushMsg_Submit','PushTlp','AddPushTlp','AddPushTlp_Submit','PushTlp_API','MsgList']) { const blocked = await callFunctionError('xiaoshu/admin/gateway', login.sessionToken, { operation: 'legacyPushBlocker', action }, 501); if (!String(blocked.message).includes('migration_blocked')) throw new Error(`旧推送动作 ${action} 未返回明确迁移阻塞`); }
   for (const action of ['Default','Default_Submit','Mobile','Mobile_Submit','Offline','Offline_Submit']) { const blocked = await callFunctionError('xiaoshu/admin/gateway', login.sessionToken, { operation: 'legacyPWABlocker', action }, 501); if (!String(blocked.message).includes('migration_blocked')) throw new Error(`旧 PWA 动作 ${action} 未返回明确迁移阻塞`); }
   for (const action of ['TaskList','ViewLog','TaskAdd','TaskEdit','TaskUpdate','ExecuteNow','TaskDelete','TaskDeleteRange','TaskPause','TaskResume']) { const blocked = await callFunctionError('xiaoshu/admin/gateway', login.sessionToken, { operation: 'legacyQuartzTaskBlocker', action }, 501); if (!String(blocked.message).includes('migration_blocked')) throw new Error(`旧 Quartz 任务动作 ${action} 未返回明确迁移阻塞`); }
+  for (const action of ['Default','API','SiteSwitch','SiteIP','SiteIPAdd','SiteIPAdd_Submit','ContentProtect','SiteAssess','SiteProtect','SiteProtect_Submit','ContentCheck','ContentCheckConfig','ContentCheckConfig_Submit','ContentFilter','SiteCheck']) { const blocked = await callFunctionError('xiaoshu/admin/gateway', login.sessionToken, { operation: 'legacySafeBlocker', action }, 501); if (!String(blocked.message).includes('migration_blocked')) throw new Error(`旧站点安全动作 ${action} 未返回明确迁移阻塞`); }
   for (const action of ['TxtLog','TxtLogContent','TxtLog_Down','TaskList','TaskAdd','TaskAdd_API','TaskAdd_Submit','Task_API','TaskCenter']) {
     const blocked = await callFunctionError('xiaoshu/admin/gateway', login.sessionToken, { operation: 'legacyLogBlocker', action }, 501); if (!String(blocked.message).includes('migration_blocked')) throw new Error(`旧日志任务动作 ${action} 未返回明确迁移阻塞`);
   }

+ 30 - 30
src/app/admin/legacy-admin-coverage.generated.ts

@@ -5340,106 +5340,106 @@ export const LEGACY_ADMIN_COVERAGE: readonly LegacyAdminCoverageEntry[] = [
     "module": "Extend / Safe",
     "action": "Default",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-infrastructure",
+    "reason": "旧默认入口只跳转 SiteSwitch;目标没有静态 Angular 站点的安全开关控制面"
   },
   {
     "module": "Extend / Safe",
     "action": "API",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-infrastructure",
+    "reason": "旧聚合 API 依赖缺失 Safe_IP、服务器站点检查局部页和 ContentWordHelper 常驻线程"
   },
   {
     "module": "Extend / Safe",
     "action": "SiteSwitch",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-infrastructure",
+    "reason": "目标没有站点上下线状态、下线页文件或前端发布开关"
   },
   {
     "module": "Extend / Safe",
     "action": "SiteIP",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-data",
+    "reason": "目标 Schema 缺少旧 Safe_IP 白/黑名单、到期时间与管理员历史"
   },
   {
     "module": "Extend / Safe",
     "action": "SiteIPAdd",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-data",
+    "reason": "目标没有可编辑或新建的 Safe_IP IP 规则记录"
   },
   {
     "module": "Extend / Safe",
     "action": "SiteIPAdd_Submit",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-data",
+    "reason": "目标缺少 Safe_IP 持久层和实际消费 IP 规则的请求中间件"
   },
   {
     "module": "Extend / Safe",
     "action": "ContentProtect",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-infrastructure",
+    "reason": "旧内容保护页视图已丢失,目标无签名、防复制或防篡改配置消费链"
   },
   {
     "module": "Extend / Safe",
     "action": "SiteAssess",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-infrastructure",
+    "reason": "旧站点安全评估视图、评分规则、基线和历史结果均未交付"
   },
   {
     "module": "Extend / Safe",
     "action": "SiteProtect",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-infrastructure",
+    "reason": "目标无 Safe_ContentCheck_Models 配置模型及内容写入扫描消费层"
   },
   {
     "module": "Extend / Safe",
     "action": "SiteProtect_Submit",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-infrastructure",
+    "reason": "仅保存模型 ID 字符串不会产生保护;目标缺少 SiteConfig.Update 和扫描运行时"
   },
   {
     "module": "Extend / Safe",
     "action": "ContentCheck",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-infrastructure",
+    "reason": "目标无 ContentWordHelper 扫描任务、进度结果缓存和待检模型范围"
   },
   {
     "module": "Extend / Safe",
     "action": "ContentCheckConfig",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-infrastructure",
+    "reason": "云函数运行时没有 IllegalWordsVerify.keywordsVPath 非法词文件与旧编辑视图"
   },
   {
     "module": "Extend / Safe",
     "action": "ContentCheckConfig_Submit",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-infrastructure",
+    "reason": "云函数不能写旧非法词文件,当前内容接口也没有该文件消费链"
   },
   {
     "module": "Extend / Safe",
     "action": "ContentFilter",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-infrastructure",
+    "reason": "旧内容过滤视图已丢失,目标无等价替换/阻断规则与执行层"
   },
   {
     "module": "Extend / Safe",
     "action": "SiteCheck",
     "occurrence": 1,
-    "status": "pending",
-    "reason": "尚未逐项迁移或完成等价性核验"
+    "status": "blocked-infrastructure",
+    "reason": "旧站点检查依赖服务器文件、局部 Razor 页和丢失的检查规则"
   },
   {
     "module": "Sale",