🔏 铸渊指令签名校验协议 v1.1:授权名单 + 校验模块 + 中间件 + 契约测试
Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
8829579c09
commit
84d4d91aec
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
@ -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∞) |
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue