diff --git a/.github/persona-brain/zhuyuan-signature-registry.json b/.github/persona-brain/zhuyuan-signature-registry.json new file mode 100644 index 00000000..c5173467 --- /dev/null +++ b/.github/persona-brain/zhuyuan-signature-registry.json @@ -0,0 +1,115 @@ +{ + "version": "1.1", + "updated_at": "2026-03-17", + "description": "铸渊指令签名·授权名单 v1.1 — 铸渊只认签名,不认请求内容", + "signed_by": "TCS-0002∞", + "signed_by_name": "冰朔", + "authorized_senders": { + "TCS-0002∞": { + "name": "冰朔", + "role": "MASTER", + "permission_tier": 0, + "scope": "全局", + "can_broadcast": true, + "broadcast_scope": "全权", + "note": "总主控·唯一 Tier 0" + }, + "DEV-004": { + "name": "之之", + "role": "SUB_CTRL_PRIVATE", + "permission_tier": 1, + "scope": "全局", + "can_broadcast": true, + "broadcast_scope": "Tier 1 范围内", + "note": "冰朔私人副控·独立于光湖团队" + }, + "DEV-002": { + "name": "肥猫", + "role": "SUB_CTRL_HOLOLAKE", + "permission_tier": 1, + "scope": "光湖", + "can_broadcast": true, + "broadcast_scope": "光湖模块范围内", + "note": "光湖团队主控" + }, + "DEV-010": { + "name": "桔子", + "role": "SUB_CTRL_HOLOLAKE", + "permission_tier": 1, + "scope": "光湖", + "can_broadcast": true, + "broadcast_scope": "光湖模块范围内", + "note": "光湖团队主控" + }, + "DEV-001": { "name": "页页", "role": "DEV", "permission_tier": 2, "scope": "自己分支", "can_broadcast": false, "note": "协作者" }, + "DEV-003": { "name": "燕樊", "role": "DEV", "permission_tier": 2, "scope": "自己分支", "can_broadcast": false, "note": "协作者" }, + "DEV-005": { "name": "小草莓", "role": "DEV", "permission_tier": 2, "scope": "自己分支", "can_broadcast": false, "note": "协作者" }, + "DEV-006": { "name": "DEV-006", "role": "DEV", "permission_tier": 2, "scope": "自己分支", "can_broadcast": false, "note": "协作者" }, + "DEV-007": { "name": "DEV-007", "role": "DEV", "permission_tier": 2, "scope": "自己分支", "can_broadcast": false, "note": "协作者" }, + "DEV-008": { "name": "DEV-008", "role": "DEV", "permission_tier": 2, "scope": "自己分支", "can_broadcast": false, "note": "协作者" }, + "DEV-009": { "name": "花尔", "role": "DEV", "permission_tier": 2, "scope": "自己分支", "can_broadcast": false, "note": "协作者" }, + "DEV-011": { "name": "匆匆那年", "role": "DEV", "permission_tier": 2, "scope": "自己分支", "can_broadcast": false, "note": "协作者" }, + "DEV-012": { "name": "Awen", "role": "DEV", "permission_tier": 2, "scope": "自己分支", "can_broadcast": false, "note": "协作者" }, + "DEV-013": { "name": "小兴", "role": "DEV", "permission_tier": 2, "scope": "自己分支", "can_broadcast": false, "note": "协作者" }, + "DEV-014": { "name": "时雨", "role": "DEV", "permission_tier": 2, "scope": "自己分支", "can_broadcast": false, "note": "协作者" }, + "SYSTEM-RT02": { "name": "RT-02自动调度", "role": "SYSTEM", "permission_tier": 3, "scope": "白名单", "can_broadcast": false, "note": "仅白名单 workflow" } + }, + "permission_tiers": { + "0": { + "label": "总主控", + "roles": ["MASTER"], + "allowed_operations": ["*"], + "denied_operations": [] + }, + "1": { + "label": "副控", + "roles": ["SUB_CTRL_PRIVATE", "SUB_CTRL_HOLOLAKE"], + "allowed_operations": [ + "push_feature_branch", + "create_pr", + "trigger_sandbox_deploy", + "write_copilot_instructions", + "broadcast_tier2" + ], + "denied_operations": [ + "modify_branch_protection", + "modify_other_permissions", + "trigger_production_deploy", + "modify_zhuyuan_protocol" + ] + }, + "2": { + "label": "协作者", + "roles": ["DEV"], + "allowed_operations": [ + "push_own_branch", + "create_pr", + "view_repo_status", + "write_syslog" + ], + "denied_operations": [ + "direct_command_zhuyuan", + "operate_other_branch", + "trigger_deploy", + "modify_branch_protection", + "modify_permissions" + ] + }, + "3": { + "label": "系统自动", + "roles": ["SYSTEM"], + "allowed_operations": [ + "trigger_whitelisted_workflow" + ], + "denied_operations": ["*_non_whitelisted"] + } + }, + "system_workflow_whitelist": [ + "bridge-syslog-intake", + "bridge-broadcast-pdf", + "bridge-heartbeat", + "daily-maintenance", + "zhuyuan-daily-selfcheck", + "zhuyuan-skyeye" + ] +} diff --git a/docs/zhuyuan-signature-protocol.md b/docs/zhuyuan-signature-protocol.md new file mode 100644 index 00000000..fca6b77e --- /dev/null +++ b/docs/zhuyuan-signature-protocol.md @@ -0,0 +1,87 @@ +# 铸渊指令签名协议 v1.1 + +> 签发人:冰朔(TCS-0002∞) +> 生效日期:2026-03-17 +> 核心原则:**铸渊只认签名,不认请求内容。签名不对,再合理的请求也会被拒。** + +--- + +## 一、为什么需要签名机制 + +铸渊作为仓库执行体,直接操作代码仓库(push/merge/deploy等),是整个系统中**破坏性最强的执行层**。签名机制防止: + +- 副控签发超出其权限范围的指令 +- 开发者绕过人格体直接操纵仓库 +- 指令来源不可追溯,事故无法归因 + +--- + +## 二、签名结构 + +每条发送给铸渊的指令必须携带以下签名字段: + +```json +{ + "sender_id": "TCS-0002∞", + "sender_name": "冰朔", + "sender_role": "MASTER", + "broadcast_id": "BC-GEN-xxx", + "issued_at": "2026-03-17T09:53:00+08:00", + "permission_tier": 0 +} +``` + +| 字段 | 说明 | 必填 | +|------|------|------| +| sender_id | 发送者唯一编号 | ✅ | +| sender_name | 发送者名称 | ✅ | +| sender_role | 角色标识 | ✅ | +| broadcast_id | 来源广播编号(非广播来源填 DIRECT) | ✅ | +| issued_at | 签发时间(ISO-8601) | ✅ | +| permission_tier | 权限等级 | ✅ | + +--- + +## 三、权限等级 + +| 等级 | 角色 | 可执行操作 | 禁止操作 | +|------|------|-----------|---------| +| Tier 0 | MASTER | 全权限 | 无限制 | +| Tier 1-G | SUB_CTRL_PRIVATE | push 功能分支、创建 PR、沙盒部署 | 分支保护、生产部署、修改协议 | +| Tier 1-L | SUB_CTRL_HOLOLAKE | 光湖范围内操作 | 同上 + 非光湖模块 | +| Tier 2 | DEV | push 自己分支、提交 PR、查看状态 | 直接给铸渊下指令、操作他人分支 | +| Tier 3 | SYSTEM | 白名单 workflow | 白名单外全部禁止 | + +--- + +## 四、校验流程 + +``` +收到指令 + → 签名字段完整? 否 → ERR_NO_SIGNATURE + → sender_id 在授权名单? 否 → ERR_UNKNOWN_SENDER + → permission_tier 匹配操作? 否 → ERR_PERMISSION_DENIED(上报主控) + → 执行指令 → 写入执行日志 +``` + +> ⚠️ 任何 ERR_PERMISSION_DENIED 事件**必须上报主控(冰朔)**,不可静默处理。 + +--- + +## 五、实现文件 + +| 文件 | 作用 | +|------|------| +| `.github/persona-brain/zhuyuan-signature-registry.json` | 授权名单 | +| `scripts/zhuyuan-signature-verify.js` | 核心校验模块 | +| `src/middleware/zhuyuan-signature.middleware.js` | Express 中间件 | +| `tests/contract/zhuyuan-signature.test.js` | 契约测试 | + +--- + +## 六、变更记录 + +| 版本 | 时间 | 变更内容 | 签发人 | +|------|------|---------|--------| +| v1.0 | 2026-03-17 | 初版:签名结构·四级权限·校验流程 | 冰朔(TCS-0002∞) | +| v1.1 | 2026-03-17 | 角色结构修正:之之为私人副控(1-G);新增光湖主控肥猫/桔子(1-L) | 冰朔(TCS-0002∞) | diff --git a/scripts/zhuyuan-signature-verify.js b/scripts/zhuyuan-signature-verify.js new file mode 100644 index 00000000..5b077721 --- /dev/null +++ b/scripts/zhuyuan-signature-verify.js @@ -0,0 +1,249 @@ +/** + * 铸渊指令签名校验模块 v1.1 + * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + * 核心原则:铸渊只认签名,不认请求内容。签名不对,再合理的请求也会被拒。 + * + * 校验流程: + * 1. 签名字段是否完整? → 缺失 → ERR_NO_SIGNATURE + * 2. sender_id 是否在授权名单中? → 未知 → ERR_UNKNOWN_SENDER + * 3. permission_tier 与操作类型匹配? → 越权 → ERR_PERMISSION_DENIED(上报主控) + * 4. 全部通过 → 执行指令 + * + * 签发人:冰朔(TCS-0002∞) + */ + +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +// ━━━ 常量 ━━━ +const REQUIRED_FIELDS = [ + 'sender_id', + 'sender_name', + 'sender_role', + 'broadcast_id', + 'issued_at', + 'permission_tier', +]; + +const ERROR_CODES = { + NO_SIGNATURE: 'ERR_NO_SIGNATURE', + UNKNOWN_SENDER: 'ERR_UNKNOWN_SENDER', + PERMISSION_DENIED: 'ERR_PERMISSION_DENIED', +}; + +// ━━━ 注册表加载 ━━━ + +/** + * 加载授权名单 + * @param {string} [registryPath] - 自定义注册表路径(用于测试) + * @returns {object} 注册表对象 + */ +function loadRegistry(registryPath) { + const defaultPath = path.resolve( + __dirname, + '../.github/persona-brain/zhuyuan-signature-registry.json' + ); + const filePath = registryPath || defaultPath; + const raw = fs.readFileSync(filePath, 'utf8'); + return JSON.parse(raw); +} + +// ━━━ 校验函数 ━━━ + +/** + * 校验签名完整性(步骤 1) + * @param {object} signature - 签名对象 + * @returns {{ valid: boolean, missing?: string[] }} + */ +function validateCompleteness(signature) { + if (!signature || typeof signature !== 'object') { + return { valid: false, missing: REQUIRED_FIELDS.slice() }; + } + + const missing = REQUIRED_FIELDS.filter((field) => { + const val = signature[field]; + return val === undefined || val === null || val === ''; + }); + + return missing.length === 0 + ? { valid: true } + : { valid: false, missing }; +} + +/** + * 校验发送者身份(步骤 2) + * @param {string} senderId - sender_id + * @param {object} registry - 授权名单 + * @returns {{ valid: boolean, sender?: object }} + */ +function validateSender(senderId, registry) { + const senders = registry.authorized_senders || {}; + const sender = senders[senderId]; + if (!sender) { + return { valid: false }; + } + return { valid: true, sender }; +} + +/** + * 校验权限等级(步骤 3) + * @param {object} signature - 签名对象 + * @param {object} sender - 注册表中的发送者记录 + * @param {string} operation - 请求的操作类型 + * @param {object} registry - 授权名单 + * @returns {{ valid: boolean, reason?: string }} + */ +function validatePermission(signature, sender, operation, registry) { + // 检查签名中的 permission_tier 是否与注册表中的一致 + const claimedTier = Number(signature.permission_tier); + const registeredTier = Number(sender.permission_tier); + + if (claimedTier !== registeredTier) { + return { + valid: false, + reason: `声明权限等级 ${claimedTier} 与注册等级 ${registeredTier} 不符`, + }; + } + + // 检查角色是否匹配 + const claimedRole = signature.sender_role; + const registeredRole = sender.role; + if (claimedRole !== registeredRole) { + return { + valid: false, + reason: `声明角色 ${claimedRole} 与注册角色 ${registeredRole} 不符`, + }; + } + + // Tier 0 全权限 + if (registeredTier === 0) { + return { valid: true }; + } + + // 无操作类型时只校验身份 + if (!operation) { + return { valid: true }; + } + + // 按 tier 检查操作权限 + const tierConfig = (registry.permission_tiers || {})[String(registeredTier)]; + if (!tierConfig) { + return { valid: true }; + } + + // 检查是否在禁止列表中 + const denied = tierConfig.denied_operations || []; + if (denied.includes(operation)) { + return { + valid: false, + reason: `操作 "${operation}" 在 Tier ${registeredTier} 禁止列表中`, + }; + } + + // 检查是否在允许列表中(如果允许列表不含通配符*) + const allowed = tierConfig.allowed_operations || []; + if (allowed.includes('*') || allowed.includes(operation)) { + return { valid: true }; + } + + return { + valid: false, + reason: `操作 "${operation}" 不在 Tier ${registeredTier} 允许列表中`, + }; +} + +/** + * 完整签名校验(主入口) + * @param {object} signature - 签名对象 + * @param {string} [operation] - 请求的操作类型 + * @param {object} [options] - 配置项 + * @param {string} [options.registryPath] - 自定义注册表路径 + * @returns {{ success: boolean, error?: string, code?: string, detail?: object }} + */ +function verifySignature(signature, operation, options) { + const opts = options || {}; + const registry = loadRegistry(opts.registryPath); + + // 步骤 1:签名完整性 + const completeness = validateCompleteness(signature); + if (!completeness.valid) { + return { + success: false, + code: ERROR_CODES.NO_SIGNATURE, + error: '签名字段缺失', + detail: { missing: completeness.missing }, + }; + } + + // 步骤 2:发送者身份 + const senderCheck = validateSender(signature.sender_id, registry); + if (!senderCheck.valid) { + return { + success: false, + code: ERROR_CODES.UNKNOWN_SENDER, + error: '发送者未在授权名单中', + detail: { sender_id: signature.sender_id }, + }; + } + + // 步骤 3:权限等级 + const permCheck = validatePermission( + signature, + senderCheck.sender, + operation, + registry + ); + if (!permCheck.valid) { + return { + success: false, + code: ERROR_CODES.PERMISSION_DENIED, + error: '权限不足', + detail: { + sender_id: signature.sender_id, + operation, + reason: permCheck.reason, + report_to: 'TCS-0002∞', + }, + }; + } + + return { + success: true, + sender: senderCheck.sender, + verified_at: new Date().toISOString(), + }; +} + +// ━━━ 导出 ━━━ +module.exports = { + verifySignature, + validateCompleteness, + validateSender, + validatePermission, + loadRegistry, + REQUIRED_FIELDS, + ERROR_CODES, +}; + +// ━━━ CLI 模式(直接运行时) ━━━ +if (require.main === module) { + const args = process.argv.slice(2); + if (args.includes('--test')) { + const testSig = { + sender_id: 'TCS-0002∞', + sender_name: '冰朔', + sender_role: 'MASTER', + broadcast_id: 'DIRECT', + issued_at: new Date().toISOString(), + permission_tier: 0, + }; + const result = verifySignature(testSig); + console.log('🔏 签名校验测试:'); + console.log(JSON.stringify(result, null, 2)); + } else { + console.log('🔏 铸渊指令签名校验模块 v1.1'); + console.log('用法: node zhuyuan-signature-verify.js --test'); + } +} diff --git a/src/middleware/zhuyuan-signature.middleware.js b/src/middleware/zhuyuan-signature.middleware.js new file mode 100644 index 00000000..fbaadef7 --- /dev/null +++ b/src/middleware/zhuyuan-signature.middleware.js @@ -0,0 +1,88 @@ +// src/middleware/zhuyuan-signature.middleware.js +// 铸渊指令签名校验中间件 v1.1 +// 核心原则:铸渊只认签名,不认请求内容 + +'use strict'; + +const { + verifySignature, + ERROR_CODES, +} = require('../../scripts/zhuyuan-signature-verify'); + +/** + * 从请求中提取签名对象 + * 支持两种来源: + * 1. 请求头 x-zhuyuan-signature(JSON 字符串) + * 2. 请求体 _signature 字段 + */ +function extractSignature(req) { + // 优先从 header 提取 + const headerSig = req.headers['x-zhuyuan-signature']; + if (headerSig) { + try { + return JSON.parse(headerSig); + } catch (_e) { + return null; + } + } + + // 其次从 body._signature 提取 + if (req.body && req.body._signature) { + return req.body._signature; + } + + return null; +} + +/** + * 铸渊签名校验中间件工厂 + * @param {object} [options] + * @param {string} [options.operation] - 固定操作类型(不从请求中推断) + * @param {string} [options.registryPath] - 自定义注册表路径 + * @returns {Function} Express 中间件 + */ +function zhuyuanSignature(options) { + const opts = options || {}; + + return function (req, res, next) { + const signature = extractSignature(req); + + if (!signature) { + return res.status(401).json({ + error: true, + code: ERROR_CODES.NO_SIGNATURE, + message: '请求缺少铸渊指令签名(x-zhuyuan-signature header 或 _signature body 字段)', + }); + } + + // 从请求中推断操作类型(如果未固定) + const operation = opts.operation || req.headers['x-zhuyuan-operation'] || null; + + const result = verifySignature(signature, operation, { + registryPath: opts.registryPath, + }); + + if (!result.success) { + const statusMap = { + [ERROR_CODES.NO_SIGNATURE]: 401, + [ERROR_CODES.UNKNOWN_SENDER]: 403, + [ERROR_CODES.PERMISSION_DENIED]: 403, + }; + + return res.status(statusMap[result.code] || 403).json({ + error: true, + code: result.code, + message: result.error, + detail: result.detail, + }); + } + + // 校验通过:将发送者信息挂载到 req + req.zhuyuanSender = result.sender; + req.zhuyuanSignature = signature; + next(); + }; +} + +module.exports = zhuyuanSignature; +module.exports.extractSignature = extractSignature; diff --git a/tests/contract/zhuyuan-signature.test.js b/tests/contract/zhuyuan-signature.test.js new file mode 100644 index 00000000..69a881cd --- /dev/null +++ b/tests/contract/zhuyuan-signature.test.js @@ -0,0 +1,307 @@ +/** + * 铸渊指令签名校验 · 契约测试 + * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + * 验证签名校验模块的三步校验流程: + * 1. 签名完整性 → ERR_NO_SIGNATURE + * 2. 发送者身份 → ERR_UNKNOWN_SENDER + * 3. 权限等级 → ERR_PERMISSION_DENIED + */ + +'use strict'; + +const path = require('path'); + +const { + verifySignature, + validateCompleteness, + validateSender, + validatePermission, + loadRegistry, + REQUIRED_FIELDS, + ERROR_CODES, +} = require('../../scripts/zhuyuan-signature-verify'); + +const REGISTRY_PATH = path.resolve( + __dirname, + '../../.github/persona-brain/zhuyuan-signature-registry.json' +); + +// ━━━ 辅助函数 ━━━ + +function makeMasterSignature(overrides) { + return Object.assign( + { + sender_id: 'TCS-0002∞', + sender_name: '冰朔', + sender_role: 'MASTER', + broadcast_id: 'DIRECT', + issued_at: '2026-03-17T09:53:00+08:00', + permission_tier: 0, + }, + overrides + ); +} + +function makeDevSignature(devId, overrides) { + const registry = loadRegistry(REGISTRY_PATH); + const sender = registry.authorized_senders[devId]; + return Object.assign( + { + sender_id: devId, + sender_name: sender ? sender.name : 'unknown', + sender_role: sender ? sender.role : 'DEV', + broadcast_id: 'DIRECT', + issued_at: '2026-03-17T10:00:00+08:00', + permission_tier: sender ? sender.permission_tier : 2, + }, + overrides + ); +} + +// ━━━ 测试 ━━━ + +describe('铸渊指令签名校验', () => { + let registry; + + beforeAll(() => { + registry = loadRegistry(REGISTRY_PATH); + }); + + // === 注册表加载 === + describe('注册表加载', () => { + test('成功加载授权名单', () => { + expect(registry).toBeDefined(); + expect(registry.authorized_senders).toBeDefined(); + expect(registry.permission_tiers).toBeDefined(); + }); + + test('包含主控 TCS-0002∞', () => { + const master = registry.authorized_senders['TCS-0002∞']; + expect(master).toBeDefined(); + expect(master.role).toBe('MASTER'); + expect(master.permission_tier).toBe(0); + }); + + test('包含系统账号 SYSTEM-RT02', () => { + const sys = registry.authorized_senders['SYSTEM-RT02']; + expect(sys).toBeDefined(); + expect(sys.role).toBe('SYSTEM'); + expect(sys.permission_tier).toBe(3); + }); + }); + + // === 步骤 1:签名完整性校验 === + describe('步骤 1:签名完整性 (ERR_NO_SIGNATURE)', () => { + test('null 签名 → 缺失所有字段', () => { + const result = validateCompleteness(null); + expect(result.valid).toBe(false); + expect(result.missing).toEqual(REQUIRED_FIELDS); + }); + + test('空对象 → 缺失所有字段', () => { + const result = validateCompleteness({}); + expect(result.valid).toBe(false); + expect(result.missing.length).toBe(REQUIRED_FIELDS.length); + }); + + test('缺少 sender_id → 报告缺失', () => { + const sig = makeMasterSignature({ sender_id: undefined }); + const result = validateCompleteness(sig); + expect(result.valid).toBe(false); + expect(result.missing).toContain('sender_id'); + }); + + test('缺少 permission_tier → 报告缺失', () => { + const sig = makeMasterSignature(); + delete sig.permission_tier; + const result = validateCompleteness(sig); + expect(result.valid).toBe(false); + expect(result.missing).toContain('permission_tier'); + }); + + test('空字符串字段 → 视为缺失', () => { + const sig = makeMasterSignature({ sender_name: '' }); + const result = validateCompleteness(sig); + expect(result.valid).toBe(false); + expect(result.missing).toContain('sender_name'); + }); + + test('完整签名 → 通过', () => { + const sig = makeMasterSignature(); + const result = validateCompleteness(sig); + expect(result.valid).toBe(true); + }); + + test('permission_tier 为 0 → 不被当作空值', () => { + const sig = makeMasterSignature({ permission_tier: 0 }); + const result = validateCompleteness(sig); + expect(result.valid).toBe(true); + }); + }); + + // === 步骤 2:发送者身份校验 === + describe('步骤 2:发送者身份 (ERR_UNKNOWN_SENDER)', () => { + test('未知 sender_id → 拒绝', () => { + const result = validateSender('UNKNOWN-999', registry); + expect(result.valid).toBe(false); + }); + + test('已知 sender_id (TCS-0002∞) → 通过', () => { + const result = validateSender('TCS-0002∞', registry); + expect(result.valid).toBe(true); + expect(result.sender.name).toBe('冰朔'); + }); + + test('已知 sender_id (DEV-004) → 通过', () => { + const result = validateSender('DEV-004', registry); + expect(result.valid).toBe(true); + expect(result.sender.name).toBe('之之'); + }); + + test('已知 sender_id (SYSTEM-RT02) → 通过', () => { + const result = validateSender('SYSTEM-RT02', registry); + expect(result.valid).toBe(true); + }); + }); + + // === 步骤 3:权限等级校验 === + describe('步骤 3:权限等级 (ERR_PERMISSION_DENIED)', () => { + test('Tier 0 (MASTER) → 任意操作通过', () => { + const sig = makeMasterSignature(); + const sender = registry.authorized_senders['TCS-0002∞']; + const result = validatePermission(sig, sender, 'modify_branch_protection', registry); + expect(result.valid).toBe(true); + }); + + test('声明的 tier 与注册 tier 不符 → 拒绝', () => { + const sig = makeDevSignature('DEV-001', { permission_tier: 0 }); + const sender = registry.authorized_senders['DEV-001']; + const result = validatePermission(sig, sender, 'push_own_branch', registry); + expect(result.valid).toBe(false); + expect(result.reason).toMatch(/不符/); + }); + + test('声明的角色与注册角色不符 → 拒绝', () => { + const sig = makeDevSignature('DEV-001', { sender_role: 'MASTER' }); + const sender = registry.authorized_senders['DEV-001']; + const result = validatePermission(sig, sender, 'push_own_branch', registry); + expect(result.valid).toBe(false); + expect(result.reason).toMatch(/不符/); + }); + + test('Tier 2 (DEV) push 自己分支 → 通过', () => { + const sig = makeDevSignature('DEV-001'); + const sender = registry.authorized_senders['DEV-001']; + const result = validatePermission(sig, sender, 'push_own_branch', registry); + expect(result.valid).toBe(true); + }); + + test('Tier 2 (DEV) 直接给铸渊下指令 → 拒绝', () => { + const sig = makeDevSignature('DEV-001'); + const sender = registry.authorized_senders['DEV-001']; + const result = validatePermission(sig, sender, 'direct_command_zhuyuan', registry); + expect(result.valid).toBe(false); + }); + + test('Tier 1 (SUB_CTRL_PRIVATE) push 功能分支 → 通过', () => { + const sig = makeDevSignature('DEV-004'); + const sender = registry.authorized_senders['DEV-004']; + const result = validatePermission(sig, sender, 'push_feature_branch', registry); + expect(result.valid).toBe(true); + }); + + test('Tier 1 (SUB_CTRL_PRIVATE) 修改分支保护 → 拒绝', () => { + const sig = makeDevSignature('DEV-004'); + const sender = registry.authorized_senders['DEV-004']; + const result = validatePermission(sig, sender, 'modify_branch_protection', registry); + expect(result.valid).toBe(false); + }); + + test('Tier 3 (SYSTEM) 白名单 workflow → 通过', () => { + const sig = { + sender_id: 'SYSTEM-RT02', + sender_name: 'RT-02自动调度', + sender_role: 'SYSTEM', + broadcast_id: 'DIRECT', + issued_at: '2026-03-17T10:00:00+08:00', + permission_tier: 3, + }; + const sender = registry.authorized_senders['SYSTEM-RT02']; + const result = validatePermission(sig, sender, 'trigger_whitelisted_workflow', registry); + expect(result.valid).toBe(true); + }); + + test('无操作类型时只校验身份 → 通过', () => { + const sig = makeDevSignature('DEV-001'); + const sender = registry.authorized_senders['DEV-001']; + const result = validatePermission(sig, sender, null, registry); + expect(result.valid).toBe(true); + }); + }); + + // === 完整流程 (verifySignature) === + describe('完整签名校验流程', () => { + const opts = { registryPath: REGISTRY_PATH }; + + test('空签名 → ERR_NO_SIGNATURE', () => { + const result = verifySignature(null, null, opts); + expect(result.success).toBe(false); + expect(result.code).toBe('ERR_NO_SIGNATURE'); + }); + + test('未知发送者 → ERR_UNKNOWN_SENDER', () => { + const sig = makeMasterSignature({ sender_id: 'FAKE-001' }); + const result = verifySignature(sig, null, opts); + expect(result.success).toBe(false); + expect(result.code).toBe('ERR_UNKNOWN_SENDER'); + }); + + test('越权操作 → ERR_PERMISSION_DENIED 含上报主控字段', () => { + const sig = makeDevSignature('DEV-001'); + const result = verifySignature(sig, 'direct_command_zhuyuan', opts); + expect(result.success).toBe(false); + expect(result.code).toBe('ERR_PERMISSION_DENIED'); + expect(result.detail.report_to).toBe('TCS-0002∞'); + }); + + test('主控签名 + 任意操作 → 成功', () => { + const sig = makeMasterSignature(); + const result = verifySignature(sig, 'modify_branch_protection', opts); + expect(result.success).toBe(true); + expect(result.sender).toBeDefined(); + expect(result.verified_at).toBeDefined(); + }); + + test('DEV 签名 + 允许操作 → 成功', () => { + const sig = makeDevSignature('DEV-001'); + const result = verifySignature(sig, 'push_own_branch', opts); + expect(result.success).toBe(true); + }); + + test('广播签名示例(冰朔签发)→ 成功', () => { + const sig = { + sender_id: 'TCS-0002∞', + sender_name: '冰朔', + sender_role: 'MASTER', + permission_tier: 0, + issued_at: '2026-03-17T09:53:00+08:00', + broadcast_id: 'BC-GEN-XXX', + }; + const result = verifySignature(sig, null, opts); + expect(result.success).toBe(true); + }); + + test('广播签名示例(之之签发)→ 成功', () => { + const sig = { + sender_id: 'DEV-004', + sender_name: '之之', + sender_role: 'SUB_CTRL_PRIVATE', + permission_tier: 1, + issued_at: '2026-03-17T10:00:00+08:00', + broadcast_id: 'BC-DINGTALK-009-ZZ', + }; + const result = verifySignature(sig, null, opts); + expect(result.success).toBe(true); + }); + }); +});