fix(security): add rate limiting, default-closed mode, extract isAllowedUser

- Rate limit: max 5 failed pairing attempts per user per 5 minutes
- Default closed: reject all users when no allowedUsers/pairedUsers configured
- Extract isAllowedUser() to common/pairing.ts to eliminate duplication

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
程序员阿江(Relakkes)
2026-04-08 20:51:30 +08:00
parent bf5cd6e3b8
commit 158f2de13e
3 changed files with 49 additions and 41 deletions
+2 -19
View File
@@ -16,7 +16,7 @@ import { loadConfig } from '../common/config.js'
import { splitMessage, formatToolUse, formatPermissionRequest, truncateInput } from '../common/format.js'
import { SessionStore } from '../common/session-store.js'
import { AdapterHttpClient } from '../common/http-client.js'
import { isPaired, tryPair } from '../common/pairing.js'
import { isAllowedUser, tryPair } from '../common/pairing.js'
// ---------- init ----------
@@ -56,23 +56,6 @@ let wsClient: InstanceType<typeof Lark.WSClient> | null = null
// ---------- helpers ----------
function isAllowedUser(openId: string): boolean {
try {
const cfgFile = JSON.parse(
require('node:fs').readFileSync(
require('node:path').join(
process.env.CLAUDE_CONFIG_DIR || require('node:path').join(require('node:os').homedir(), '.claude'),
'adapters.json'
),
'utf-8'
)
)
return isPaired('feishu', openId, cfgFile)
} catch {
return false
}
}
function getChatState(chatId: string): ChatState {
let state = chatStates.get(chatId)
if (!state) {
@@ -400,7 +383,7 @@ async function handleMessage(data: any): Promise<void> {
// 只处理私聊
if (chatType === 'p2p') {
if (!isAllowedUser(senderOpenId)) {
if (!isAllowedUser('feishu', senderOpenId)) {
// 尝试配对
const pairText = extractText(content, msgType)
if (pairText) {