212dbc4e1d
这是一次大型重构和功能新增: 1. 将原 zen_box 工具重命名为 web_zen_box,调整所有相关路径与配置 2. 新增 web_order_box 原生Web应用的完整资源与部署脚本 3. 新增私人剪贴板后端技能与配套测试脚本 4. 修复订单盒子APP的API地址配置,避免路径重复拼接 5. 新增本地开发跨域白名单支持localhost 6. 清理旧版zen_box的冗余文件
109 lines
3.1 KiB
JavaScript
109 lines
3.1 KiB
JavaScript
// 私人剪贴板 API 验证测试
|
|
const http = require('http')
|
|
|
|
const BASE = 'http://localhost:8976'
|
|
const API = `${BASE}/api/v1/ingest/private_clipboard`
|
|
const HEADERS = {
|
|
'X-API-Id': 'private_clipboard',
|
|
'X-API-Key': 'pcb_nuS0qf-W63qcvAPsrG5ePA',
|
|
'Content-Type': 'application/json; charset=utf-8'
|
|
}
|
|
|
|
function post(path, body) {
|
|
return new Promise((resolve, reject) => {
|
|
const data = JSON.stringify(body)
|
|
const req = http.request(`${API}${path}`, {
|
|
method: 'POST',
|
|
headers: { ...HEADERS, 'Content-Length': Buffer.byteLength(data) }
|
|
}, (res) => {
|
|
let body = ''
|
|
res.on('data', d => body += d)
|
|
res.on('end', () => {
|
|
try { resolve({ status: res.statusCode, body: JSON.parse(body) }) }
|
|
catch { resolve({ status: res.statusCode, body }) }
|
|
})
|
|
})
|
|
req.on('error', reject)
|
|
req.write(data)
|
|
req.end()
|
|
})
|
|
}
|
|
|
|
function get(path) {
|
|
return new Promise((resolve, reject) => {
|
|
http.get(`${API}${path}`, { headers: HEADERS }, (res) => {
|
|
let body = ''
|
|
res.on('data', d => body += d)
|
|
res.on('end', () => {
|
|
try { resolve({ status: res.statusCode, body: JSON.parse(body) }) }
|
|
catch { resolve({ status: res.statusCode, body }) }
|
|
})
|
|
}).on('error', reject)
|
|
})
|
|
}
|
|
|
|
async function sleep(ms) { return new Promise(r => setTimeout(r, ms)) }
|
|
|
|
async function runTests() {
|
|
let pass = 0, fail = 0
|
|
|
|
console.log('=== 1. 中文编码测试 ===')
|
|
const r1 = await post('/text', { text: '你好世界' })
|
|
if (r1.status === 200 && r1.body.ok) {
|
|
// 查询最新一条
|
|
const r1q = await get('/latest?limit=1')
|
|
const text = r1q.body.items[0].text_content
|
|
if (text === '你好世界') {
|
|
console.log(' ✅ 中文存储和查询正常')
|
|
pass++
|
|
} else {
|
|
console.log(` ❌ 查询返回: "${text}" (期望: "你好世界")`)
|
|
fail++
|
|
}
|
|
} else {
|
|
console.log(` ❌ 写入失败: ${JSON.stringify(r1)}`)
|
|
fail++
|
|
}
|
|
|
|
console.log('\n=== 2. XSS 转义测试 ===')
|
|
const xssPayload = '<script>alert(1)</script>'
|
|
const r2 = await post('/text', { text: xssPayload })
|
|
if (r2.status === 200 && r2.body.ok) {
|
|
const r2q = await get('/latest?limit=1')
|
|
const text = r2q.body.items[0].text_content
|
|
if (text.includes('<') && text.includes('>') && !text.includes('<script>')) {
|
|
console.log(` ✅ XSS 已转义: "${text}"`)
|
|
pass++
|
|
} else {
|
|
console.log(` ❌ XSS 未转义: "${text}"`)
|
|
fail++
|
|
}
|
|
} else {
|
|
console.log(` ❌ 写入失败: ${JSON.stringify(r2)}`)
|
|
fail++
|
|
}
|
|
|
|
console.log('\n=== 3. 速率限制测试 ===')
|
|
let rateLimited = false
|
|
for (let i = 0; i < 62; i++) {
|
|
const r = await post('/text', { text: `ratelimit-test-${i}` })
|
|
if (r.status === 429) {
|
|
console.log(` ✅ 第 ${i + 1} 次请求被限制 (429)`)
|
|
rateLimited = true
|
|
break
|
|
}
|
|
// 快速请求
|
|
}
|
|
if (!rateLimited) {
|
|
console.log(' ❌ 62 次请求全部通过,未触发速率限制')
|
|
fail++
|
|
} else {
|
|
pass++
|
|
}
|
|
|
|
console.log(`\n=== 结果: ${pass} 通过, ${fail} 失败 ===`)
|
|
process.exit(fail > 0 ? 1 : 0)
|
|
}
|
|
|
|
runTests().catch(e => { console.error('Error:', e); process.exit(1) })
|