fix(钉钉): 兼容桌面端免登来源

This commit is contained in:
WuYongJie 2026-07-15 21:59:18 +08:00
parent f4812dc6a2
commit 0b83ef7f88
3 changed files with 37 additions and 1 deletions

View File

@ -79,3 +79,16 @@ test('通知中心来源只可进入一次性票据回调', () => {
path: '/api/equipment/1'
}).status, 403)
})
test('钉钉 WebView 的 null Origin 只可进入一次性票据回调', () => {
assert.equal(runGuard({
method: 'POST',
origin: 'null',
path: '/api/dingtalk/broker-login'
}).nextCalls, 1)
assert.equal(runGuard({
method: 'POST',
origin: 'null',
path: '/api/equipment'
}).status, 403)
})

View File

@ -45,6 +45,24 @@ test('跨源写操作默认拒绝,只单独放行通知中心票据回调', ()
requestOrigin: 'https://asset.example.com',
path: '/api/dingtalk/broker-login'
}, env), true)
assert.equal(isUnsafeRequestOriginAllowed({
method: 'POST',
origin: 'null',
requestOrigin: 'https://asset.example.com',
path: '/api/dingtalk/broker-login'
}, env), true)
assert.equal(isUnsafeRequestOriginAllowed({
method: 'POST',
origin: 'null',
requestOrigin: 'https://asset.example.com',
path: '/api/equipment'
}, env), false)
assert.equal(isUnsafeRequestOriginAllowed({
method: 'POST',
origin: 'https://evil.example.com',
requestOrigin: 'https://asset.example.com',
path: '/api/dingtalk/broker-login'
}, env), false)
assert.equal(isUnsafeRequestOriginAllowed({
method: 'DELETE',
origin: 'https://evil.example.com',

View File

@ -39,11 +39,16 @@ export const isUnsafeRequestOriginAllowed = (input: {
if (safeMethods.has(String(input.method || '').toUpperCase())) return true
if (!input.origin) return true
const isBrokerCallback = input.path === '/api/dingtalk/broker-login'
// 钉钉桌面 WebView 的跨域 form POST 会把 Origin 固定为 null。
// 仅一次性票据回调接受该值;票据仍需通过项目签名兑换、受众校验、60秒过期和原子单次消费。
if (isBrokerCallback && input.origin.trim().toLowerCase() === 'null') return true
const origin = parseOrigin(input.origin)
const requestOrigin = parseOrigin(input.requestOrigin)
if (!origin || !requestOrigin) return false
if (origin === requestOrigin || getAllowedCorsOrigins(env).has(origin)) return true
const notificationCenterOrigin = parseOrigin(String(env.YYD_NOTIFICATION_CENTER_URL || '').trim())
return input.path === '/api/dingtalk/broker-login' && origin === notificationCenterOrigin
return isBrokerCallback && origin === notificationCenterOrigin
}