🔐 修复SFP nonce生成偏差 + 提取magic numbers为命名常量
Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
b50e4555e5
commit
3edd723faf
|
|
@ -2,8 +2,8 @@ name: "🎖️ 铸渊·将军唤醒 · Commander Wake-up"
|
|||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # UTC 00:00 = 北京 08:00
|
||||
- cron: '0 12 * * *' # UTC 12:00 = 北京 20:00
|
||||
- cron: '0 0 * * *' # UTC 00:00 = 北京 08:00(将军早班唤醒)
|
||||
- cron: '0 12 * * *' # UTC 12:00 = 北京 20:00(将军晚班唤醒)
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
|
|
|||
|
|
@ -157,6 +157,150 @@
|
|||
"agent_id": "AG-TY",
|
||||
"timestamp": "2026-03-19T00:22:24+08:00",
|
||||
"content_hash": "7f4b1a99e8fc"
|
||||
},
|
||||
{
|
||||
"nonce": "olnmim",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:23:59+08:00",
|
||||
"content_hash": "05a17114410b"
|
||||
},
|
||||
{
|
||||
"nonce": "r42f7r",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:23:59+08:00",
|
||||
"content_hash": "d6cd1c87b887"
|
||||
},
|
||||
{
|
||||
"nonce": "2pi1t3",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:23:59+08:00",
|
||||
"content_hash": "0a5df5415ade"
|
||||
},
|
||||
{
|
||||
"nonce": "sswkv7",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:23:59+08:00",
|
||||
"content_hash": "c3b54c6c243e"
|
||||
},
|
||||
{
|
||||
"nonce": "pu7e0o",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:23:59+08:00",
|
||||
"content_hash": "c3b54c6c243e"
|
||||
},
|
||||
{
|
||||
"nonce": "cbz6t0",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:23:59+08:00",
|
||||
"content_hash": "b95bfcf4a347"
|
||||
},
|
||||
{
|
||||
"nonce": "g5dfi3",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:23:59+08:00",
|
||||
"content_hash": "10c4ec3aeafb"
|
||||
},
|
||||
{
|
||||
"nonce": "cxu97j",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:23:59+08:00",
|
||||
"content_hash": "902e455fe109"
|
||||
},
|
||||
{
|
||||
"nonce": "w2dw4b",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:23:59+08:00",
|
||||
"content_hash": "929460febd09"
|
||||
},
|
||||
{
|
||||
"nonce": "aqyuid",
|
||||
"agent_id": "AG-SY",
|
||||
"timestamp": "2026-03-19T00:23:59+08:00",
|
||||
"content_hash": "f85462954167"
|
||||
},
|
||||
{
|
||||
"nonce": "oycw4f",
|
||||
"agent_id": "AG-QQ",
|
||||
"timestamp": "2026-03-19T00:23:59+08:00",
|
||||
"content_hash": "c10beff06c0b"
|
||||
},
|
||||
{
|
||||
"nonce": "69xg61",
|
||||
"agent_id": "AG-TY",
|
||||
"timestamp": "2026-03-19T00:23:59+08:00",
|
||||
"content_hash": "7f4b1a99e8fc"
|
||||
},
|
||||
{
|
||||
"nonce": "3783f4",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:25:55+08:00",
|
||||
"content_hash": "05a17114410b"
|
||||
},
|
||||
{
|
||||
"nonce": "153ac0",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:25:55+08:00",
|
||||
"content_hash": "d6cd1c87b887"
|
||||
},
|
||||
{
|
||||
"nonce": "b1b39e",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:25:55+08:00",
|
||||
"content_hash": "0a5df5415ade"
|
||||
},
|
||||
{
|
||||
"nonce": "32af7a",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:25:55+08:00",
|
||||
"content_hash": "c3b54c6c243e"
|
||||
},
|
||||
{
|
||||
"nonce": "bda41d",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:25:55+08:00",
|
||||
"content_hash": "c3b54c6c243e"
|
||||
},
|
||||
{
|
||||
"nonce": "2c6a56",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:25:55+08:00",
|
||||
"content_hash": "b95bfcf4a347"
|
||||
},
|
||||
{
|
||||
"nonce": "250ec9",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:25:55+08:00",
|
||||
"content_hash": "10c4ec3aeafb"
|
||||
},
|
||||
{
|
||||
"nonce": "eb8195",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:25:55+08:00",
|
||||
"content_hash": "902e455fe109"
|
||||
},
|
||||
{
|
||||
"nonce": "d20686",
|
||||
"agent_id": "AG-ZY",
|
||||
"timestamp": "2026-03-19T00:25:55+08:00",
|
||||
"content_hash": "929460febd09"
|
||||
},
|
||||
{
|
||||
"nonce": "18fa39",
|
||||
"agent_id": "AG-SY",
|
||||
"timestamp": "2026-03-19T00:25:55+08:00",
|
||||
"content_hash": "f85462954167"
|
||||
},
|
||||
{
|
||||
"nonce": "8d0d13",
|
||||
"agent_id": "AG-QQ",
|
||||
"timestamp": "2026-03-19T00:25:55+08:00",
|
||||
"content_hash": "c10beff06c0b"
|
||||
},
|
||||
{
|
||||
"nonce": "7de110",
|
||||
"agent_id": "AG-TY",
|
||||
"timestamp": "2026-03-19T00:25:55+08:00",
|
||||
"content_hash": "7f4b1a99e8fc"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -20,6 +20,8 @@ const SFP_NONCE_PATH = path.join(ROOT, 'data/security/sfp-nonce-registry.json')
|
|||
const SFP_ALERT_PATH = path.join(ROOT, 'data/security/sfp-alert-log.json');
|
||||
|
||||
const BEIJING_OFFSET_MS = 8 * 3600 * 1000;
|
||||
const MAX_NONCE_REGISTRY_SIZE = 1000;
|
||||
const MAX_ALERT_LOG_SIZE = 500;
|
||||
|
||||
// ━━━ SFP 指纹正则 ━━━
|
||||
const SFP_REGEX = /⌜SFP::([^:]+)::([^:]+)::([0-9T+:.-]+)::([a-f0-9]{12})::([a-zA-Z0-9]{6})⌝/;
|
||||
|
|
@ -62,13 +64,8 @@ function computeContentHash(content) {
|
|||
|
||||
// ━━━ 生成6位随机nonce ━━━
|
||||
function generateNonce() {
|
||||
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let nonce = '';
|
||||
const bytes = crypto.randomBytes(6);
|
||||
for (let i = 0; i < 6; i++) {
|
||||
nonce += chars[bytes[i] % chars.length];
|
||||
}
|
||||
return nonce;
|
||||
// Use hex encoding of random bytes for unbiased distribution
|
||||
return crypto.randomBytes(3).toString('hex');
|
||||
}
|
||||
|
||||
// ━━━ 获取北京时间ISO字符串 ━━━
|
||||
|
|
@ -107,8 +104,8 @@ function generateSFP(agentId, content) {
|
|||
content_hash: contentHash
|
||||
});
|
||||
// 保留最近 1000 条 nonce 记录
|
||||
if (nonceRegistry.used_nonces.length > 1000) {
|
||||
nonceRegistry.used_nonces = nonceRegistry.used_nonces.slice(-1000);
|
||||
if (nonceRegistry.used_nonces.length > MAX_NONCE_REGISTRY_SIZE) {
|
||||
nonceRegistry.used_nonces = nonceRegistry.used_nonces.slice(-MAX_NONCE_REGISTRY_SIZE);
|
||||
}
|
||||
writeJSON(SFP_NONCE_PATH, nonceRegistry);
|
||||
} catch (e) {
|
||||
|
|
@ -236,8 +233,8 @@ function logAlert(alertEntry) {
|
|||
logged_at: getBeijingTimestamp()
|
||||
});
|
||||
// 保留最近 500 条
|
||||
if (alertLog.alerts.length > 500) {
|
||||
alertLog.alerts = alertLog.alerts.slice(-500);
|
||||
if (alertLog.alerts.length > MAX_ALERT_LOG_SIZE) {
|
||||
alertLog.alerts = alertLog.alerts.slice(-MAX_ALERT_LOG_SIZE);
|
||||
}
|
||||
writeJSON(SFP_ALERT_PATH, alertLog);
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ const ROOT = path.resolve(__dirname, '../..');
|
|||
const DASHBOARD_PATH = path.join(ROOT, 'data/bulletin-board/dashboard.json');
|
||||
const SKYEYE_REPORTS_DIR = path.join(ROOT, 'data/skyeye-reports');
|
||||
|
||||
const FAILURE_RATE_THRESHOLD = 20;
|
||||
const CONSECUTIVE_FAILURE_THRESHOLD_DAYS = 7;
|
||||
|
||||
// ━━━ 安全读取 JSON ━━━
|
||||
function readJSON(filePath) {
|
||||
try {
|
||||
|
|
@ -96,18 +99,18 @@ function scanSoldierHealth() {
|
|||
}
|
||||
|
||||
// 故障率 > 20% → P0
|
||||
if (result.failure_rate > 20) {
|
||||
if (result.failure_rate > FAILURE_RATE_THRESHOLD) {
|
||||
result.status = '❌';
|
||||
result.issues.push({
|
||||
type: 'high_failure_rate',
|
||||
message: `小兵故障率 ${result.failure_rate}% > 20%`,
|
||||
message: `小兵故障率 ${result.failure_rate}% > ${FAILURE_RATE_THRESHOLD}%`,
|
||||
action: 'P0工单 · 全局小兵修复'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 检查连续7天同一错误
|
||||
const recentReports = getRecentReports(7);
|
||||
const recentReports = getRecentReports(CONSECUTIVE_FAILURE_THRESHOLD_DAYS);
|
||||
if (recentReports.length >= 2) {
|
||||
// 收集各报告中的失败 workflow
|
||||
const errorMap = {};
|
||||
|
|
@ -124,7 +127,7 @@ function scanSoldierHealth() {
|
|||
|
||||
// 连续7天同一错误
|
||||
for (const [name, count] of Object.entries(errorMap)) {
|
||||
if (count >= 7) {
|
||||
if (count >= CONSECUTIVE_FAILURE_THRESHOLD_DAYS) {
|
||||
result.recurring_errors.push({
|
||||
soldier: name,
|
||||
consecutive_failures: count,
|
||||
|
|
|
|||
|
|
@ -137,10 +137,10 @@ describe('SFP v1.0 · 系统指纹安全协议', () => {
|
|||
expect(hash).toMatch(/^[a-f0-9]{12}$/);
|
||||
});
|
||||
|
||||
test('generateNonce 返回6位字母数字', () => {
|
||||
test('generateNonce 返回6位hex字符', () => {
|
||||
const nonce = sfpCore.generateNonce();
|
||||
expect(nonce).toHaveLength(6);
|
||||
expect(nonce).toMatch(/^[a-z0-9]{6}$/);
|
||||
expect(nonce).toMatch(/^[a-f0-9]{6}$/);
|
||||
});
|
||||
|
||||
test('SFP_REGEX 能正确匹配指纹格式', () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue