@seasonkoh/webaz 0.1.10 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -4006,7 +4006,7 @@ export async function startMCPServer() {
|
|
|
4006
4006
|
type: 'text',
|
|
4007
4007
|
text: `用户需求:${args.user_intent || '(未提供)'}\n\n` +
|
|
4008
4008
|
`请使用 webaz_search 工具(可粘贴外链精准匹配,详见工具描述)找到最匹配的商品,` +
|
|
4009
|
-
`然后用 webaz_verify_price 锁价(返回 session_token,
|
|
4009
|
+
`然后用 webaz_verify_price 锁价(返回 session_token,10 分钟有效),` +
|
|
4010
4010
|
`最后用 webaz_place_order(session_token=...) 下单。` +
|
|
4011
4011
|
`下单时记得跟用户确认收货地址 + 是否需要使用推荐人 promoter_api_key(可选)。\n\n` +
|
|
4012
4012
|
`重要:不要跳过 verify_price 这一步 — 它是防价格篡改的关键。`,
|
package/dist/pwa/public/app.js
CHANGED
|
@@ -10687,7 +10687,18 @@ window.doRegister = async () => {
|
|
|
10687
10687
|
// #1049 Turnstile token(若启用)
|
|
10688
10688
|
if (window._turnstileToken) body.turnstile_token = window._turnstileToken
|
|
10689
10689
|
const res = await POST('/register', body)
|
|
10690
|
-
if (res.error)
|
|
10690
|
+
if (res.error) {
|
|
10691
|
+
// P0-3: token 单次消耗,任何注册失败(name 重 / invite 缺 / captcha 假)后都要 reset widget 拿新 challenge,
|
|
10692
|
+
// 否则用户 retry 时旧 token 已失效 → 后端 timeout-or-duplicate → 死循环
|
|
10693
|
+
try {
|
|
10694
|
+
window._turnstileToken = ''
|
|
10695
|
+
if (window.turnstile) {
|
|
10696
|
+
const slot = document.getElementById('reg-turnstile-slot')
|
|
10697
|
+
if (slot) window.turnstile.reset(slot)
|
|
10698
|
+
}
|
|
10699
|
+
} catch {}
|
|
10700
|
+
return showMsg('error', res.error)
|
|
10701
|
+
}
|
|
10691
10702
|
// 注册成功后清掉 hint(已绑定)
|
|
10692
10703
|
localStorage.removeItem('webaz_share_hint')
|
|
10693
10704
|
localStorage.removeItem('webaz_ref')
|
|
@@ -11631,7 +11642,6 @@ async function renderDiscover(app) {
|
|
|
11631
11642
|
position: idx + 1,
|
|
11632
11643
|
item: {
|
|
11633
11644
|
'@type': 'Product',
|
|
11634
|
-
inLanguage: lang,
|
|
11635
11645
|
name: nameField,
|
|
11636
11646
|
url: location.origin + '/#order-product/' + pp.id,
|
|
11637
11647
|
...(img ? { image: img } : {}),
|
|
@@ -15723,7 +15733,6 @@ async function renderBuyPage(app, productId) {
|
|
|
15723
15733
|
setJsonLd({
|
|
15724
15734
|
'@context': 'https://schema.org',
|
|
15725
15735
|
'@type': 'Product',
|
|
15726
|
-
inLanguage: curLang,
|
|
15727
15736
|
name: nameField,
|
|
15728
15737
|
description: descField,
|
|
15729
15738
|
sku: p.id,
|
package/dist/pwa/public/sw.js
CHANGED
|
@@ -113,9 +113,11 @@ export function buildPaymentMandate(input) {
|
|
|
113
113
|
forSign.proof.signature = '';
|
|
114
114
|
return { canonical: canonical(forSign), mandate };
|
|
115
115
|
}
|
|
116
|
-
/** 把 build*Mandate 输出的 canonical 经 signFn
|
|
116
|
+
/** 把 build*Mandate 输出的 canonical 经 signFn 签名后,返回带 signature 的深 clone(不 mutate 入参) */
|
|
117
117
|
export async function signMandate(out, signFn) {
|
|
118
118
|
const sig = await signFn(out.canonical);
|
|
119
|
-
out.mandate
|
|
120
|
-
|
|
119
|
+
// P2-3:深 clone 后再塞 signature;入参 out.mandate 保持不变,防调用方持引用被旁路修改
|
|
120
|
+
const signed = JSON.parse(JSON.stringify(out.mandate));
|
|
121
|
+
signed.proof.signature = sig;
|
|
122
|
+
return signed;
|
|
121
123
|
}
|
|
@@ -15,6 +15,10 @@ export function registerAuthRegisterRoutes(app, deps) {
|
|
|
15
15
|
if (!turnstile_token || typeof turnstile_token !== 'string') {
|
|
16
16
|
return void errorRes(res, 400, 'CAPTCHA_REQUIRED', '请完成人机校验');
|
|
17
17
|
}
|
|
18
|
+
// P1-3:Cloudflare Turnstile token 通常 <2KB,卡 4KB 防大体积注入 siteverify 浪费带宽
|
|
19
|
+
if (turnstile_token.length > 4096) {
|
|
20
|
+
return void errorRes(res, 400, 'CAPTCHA_INVALID', '人机校验未通过,请刷新后重试');
|
|
21
|
+
}
|
|
18
22
|
try {
|
|
19
23
|
const remoteIp = req.ip || req.socket?.remoteAddress || '';
|
|
20
24
|
const verifyRes = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
|
package/dist/pwa/server.js
CHANGED
|
@@ -4995,8 +4995,9 @@ const CROSS_USER_READ_DAILY_CAP = {
|
|
|
4995
4995
|
};
|
|
4996
4996
|
const crossUserReadBuckets = new Map();
|
|
4997
4997
|
function extractCrossUserTarget(path, currentUserId) {
|
|
4998
|
-
// /api/users/:id/anything → :id;同 user 不算跨;sys_* 协议账号不算
|
|
4999
|
-
|
|
4998
|
+
// /api/users/:id 或 /api/users/:id/anything → :id;同 user 不算跨;sys_* 协议账号不算
|
|
4999
|
+
// P1-4 修:允许 trailing path 缺失(/api/users/:user_id 是合法 endpoint,返回用户档案)
|
|
5000
|
+
const m = path.match(/^\/api\/users\/([^/?#]+)(?:[/?#]|$)/);
|
|
5000
5001
|
if (!m)
|
|
5001
5002
|
return null;
|
|
5002
5003
|
const target = m[1];
|
|
@@ -5193,7 +5194,9 @@ app.use((req, res, next) => {
|
|
|
5193
5194
|
});
|
|
5194
5195
|
}
|
|
5195
5196
|
// #1043(补 A) 跨用户读日 cap — 真人也罩;只对路径里显式带 other user id 的 GET 计数
|
|
5196
|
-
|
|
5197
|
+
// P0-2 优化:先 regex 快判 path 命中(零开销),命中才查 owner.id + level — 避免每 GET 跑 SQLite
|
|
5198
|
+
// P1-4 修:正则允许 /api/users/:id 无 trailing slash(GET /api/users/:user_id 是返回用户档案的合法端点)
|
|
5199
|
+
if (req.method === 'GET' && /^\/api\/users\/[^/?#]+/.test(req.path)) {
|
|
5197
5200
|
const owner = db.prepare(`SELECT id FROM users WHERE api_key = ?`).get(apiKey);
|
|
5198
5201
|
if (owner) {
|
|
5199
5202
|
const target = extractCrossUserTarget(req.path, owner.id);
|
package/package.json
CHANGED