diff --git a/js/qdreader_sign_autman.js b/js/qdreader_sign_autman.js index 05e19a0..166c9b9 100644 --- a/js/qdreader_sign_autman.js +++ b/js/qdreader_sign_autman.js @@ -4,7 +4,7 @@ //[author: Hermes] //[service: BOSS] //[class: 工具类] -//[version: 2.3.2] +//[version: 2.3.3] //[platform: web,qq,wx,tg,tb,fs,we] //[public: false] //[price: 0] @@ -102,7 +102,7 @@ function currentUserId() { function boundToCurrent(uid, bind) { var owner = bind && bind[String(uid)] ? String(bind[String(uid)]) : "" var me = currentUserId() - return !owner || !me || owner === me + return !!owner && !!me && owner === me } function visibleUids(store, bind, allUsers) { var uids = Object.keys(store || {}).sort() @@ -183,7 +183,7 @@ function contentText() { } function usage() { return [ - "启点读书签到插件 v2.3.2", + "启点读书签到插件 v2.3.3", "【一级菜单】", "账号管理:启点账号", "执行查询:启点查询", @@ -592,9 +592,18 @@ function handleSaveCookie(arg, store, bind) { var uid = entries[i].uid || parsedUid if (!uid) return "无法从 Cookie 识别 uid。请确认 QX 抓的是启点读书福利中心 getlogininfo 请求,Cookie 内应包含 QDHeader 或 uid。" var isNew = !store[uid] + var oldOwner = bind[uid] || "" + if (store[uid] && oldOwner && me && oldOwner !== me) { + lines.push("无权更新 uid: " + uid + "(该账号已绑定其他用户)") + continue + } + if (!me) { + lines.push("无法识别当前用户,拒绝保存 uid: " + uid) + continue + } var changed = store[uid] !== cookie store[uid] = cookie - if (me) bind[uid] = me + bind[uid] = me lines.push("启点读书CK" + (isNew ? "新增" : (changed ? "更新" : "未变化")) + "成功") lines.push("uid: " + uid) if (entries.length > 1 && parsedUid && parsedUid !== uid) lines.push("提示: JSON uid 与 Cookie 解析 uid 不一致,已按 JSON uid 保存") diff --git a/plugins/qdreader/README.md b/plugins/qdreader/README.md index 0607ec8..6e4df18 100644 --- a/plugins/qdreader/README.md +++ b/plugins/qdreader/README.md @@ -69,8 +69,10 @@ qdreader sign ## 账号与备注 -- 保存 Cookie 后自动绑定当前 `sender.getUserID()`。 +- 保存 Cookie 后必须绑定当前 `sender.getUserID()`;无法识别当前用户时拒绝保存。 - 查询、删除、手动签到默认只操作当前用户绑定的账号。 +- 未绑定到当前用户的全局 CK 不会在普通用户菜单中显示,也不会被普通用户手动签到使用。 +- 已绑定其他用户的 UID 不能被当前用户覆盖更新。 - 账号详情、删除、单账号查询都使用编号选择,不要求用户复制/输入 UID。 - 添加/更新账号后会提示是否添加备注。 - 修改备注在“账号详情/修改备注”中完成。 diff --git a/plugins/qdreader/qdreader_sign_autman.js b/plugins/qdreader/qdreader_sign_autman.js index 05e19a0..166c9b9 100644 --- a/plugins/qdreader/qdreader_sign_autman.js +++ b/plugins/qdreader/qdreader_sign_autman.js @@ -4,7 +4,7 @@ //[author: Hermes] //[service: BOSS] //[class: 工具类] -//[version: 2.3.2] +//[version: 2.3.3] //[platform: web,qq,wx,tg,tb,fs,we] //[public: false] //[price: 0] @@ -102,7 +102,7 @@ function currentUserId() { function boundToCurrent(uid, bind) { var owner = bind && bind[String(uid)] ? String(bind[String(uid)]) : "" var me = currentUserId() - return !owner || !me || owner === me + return !!owner && !!me && owner === me } function visibleUids(store, bind, allUsers) { var uids = Object.keys(store || {}).sort() @@ -183,7 +183,7 @@ function contentText() { } function usage() { return [ - "启点读书签到插件 v2.3.2", + "启点读书签到插件 v2.3.3", "【一级菜单】", "账号管理:启点账号", "执行查询:启点查询", @@ -592,9 +592,18 @@ function handleSaveCookie(arg, store, bind) { var uid = entries[i].uid || parsedUid if (!uid) return "无法从 Cookie 识别 uid。请确认 QX 抓的是启点读书福利中心 getlogininfo 请求,Cookie 内应包含 QDHeader 或 uid。" var isNew = !store[uid] + var oldOwner = bind[uid] || "" + if (store[uid] && oldOwner && me && oldOwner !== me) { + lines.push("无权更新 uid: " + uid + "(该账号已绑定其他用户)") + continue + } + if (!me) { + lines.push("无法识别当前用户,拒绝保存 uid: " + uid) + continue + } var changed = store[uid] !== cookie store[uid] = cookie - if (me) bind[uid] = me + bind[uid] = me lines.push("启点读书CK" + (isNew ? "新增" : (changed ? "更新" : "未变化")) + "成功") lines.push("uid: " + uid) if (entries.length > 1 && parsedUid && parsedUid !== uid) lines.push("提示: JSON uid 与 Cookie 解析 uid 不一致,已按 JSON uid 保存") diff --git a/tests/qdreader_plugin.test.mjs b/tests/qdreader_plugin.test.mjs index ed1359e..3ba34b4 100644 --- a/tests/qdreader_plugin.test.mjs +++ b/tests/qdreader_plugin.test.mjs @@ -92,7 +92,10 @@ test('manual cookie save falls back to QDHeader embedded UserId before ywguid', }); test('manual sign and cron sign both call sign gateway then qidian checkin', () => { - const store = { qdreader_cookie_json: JSON.stringify({ '12345': cookie }) }; + const store = { + qdreader_cookie_json: JSON.stringify({ '12345': cookie }), + qdreader_user_bind_json: JSON.stringify({ '12345': 'user-a' }), + }; const manual = runPlugin({ content: '启点签到', store: { ...store } }); assert.equal(manual.calls.length, 2); assert.match(manual.calls[0].url, /api\.120399\.xyz\/qdreader\/sign$/); @@ -112,7 +115,7 @@ test('manual sign and cron sign both call sign gateway then qidian checkin', () assert.equal(JSON.parse(cron.store.qdreader_last_run_json).source, 'cron'); }); -test('bucketGet/bucketSet fallback works when get/set are unavailable in Autman-like env', () => { +test('bucketGet/bucketSet fallback requires identifiable user before saving', () => { const replies = []; const bucketStore = {}; const ctx = { @@ -125,9 +128,8 @@ test('bucketGet/bucketSet fallback works when get/set are unavailable in Autman- }; vm.createContext(ctx); vm.runInContext(code, ctx, { filename: pluginPath }); - assert.match(replies.join('\n'), /CK新增成功/); - assert.equal(JSON.parse(bucketStore['hermes_qidian.qdreader_cookie_json'])['12345'], cookie); - assert.equal(JSON.parse(bucketStore['hermes_qidian.qdreader_user_bind_json'])['12345'], undefined); + assert.match(replies.join('\n'), /无法识别当前用户,拒绝保存/); + assert.equal(bucketStore['hermes_qidian.qdreader_cookie_json'], '{}'); }); test('hermes_qidian bucket values are read and written without legacy bucket fallback', () => { @@ -141,7 +143,7 @@ test('hermes_qidian bucket values are read and written without legacy bucket fal }); test('qidian request retries transient request errors', () => { - const store = { qdreader_cookie_json: JSON.stringify({ '12345': cookie }), qdreader_retry_count: '2' }; + const store = { qdreader_cookie_json: JSON.stringify({ '12345': cookie }), qdreader_user_bind_json: JSON.stringify({ '12345': 'user-a' }), qdreader_retry_count: '2' }; const r = runPlugin({ content: '启点签到', store, @@ -290,3 +292,29 @@ test('manual sign only runs current user accounts while cron runs all accounts', assert.equal(cron.calls.length, 4); assert.match(cron.replies[0], /总计 2/); }); + +test('unbound global cookies are not visible or usable by normal users', () => { + const store = { qdreader_cookie_json: JSON.stringify({ '12345': cookie }) }; + const account = runPlugin({ content: '启点账号', store: { ...store }, userId: 'user-a', listens: ['1'] }); + assert.match(account.replies.join('\n'), /当前用户还没有保存启点读书CK/); + + const manual = runPlugin({ content: '启点签到', store: { ...store }, userId: 'user-a' }); + assert.equal(manual.calls.length, 0); + assert.match(manual.replies[0], /还没有保存启点读书CK/); + + const cron = runPlugin({ content: '', store: { ...store }, userId: 'cron' }); + assert.equal(cron.calls.length, 2); + assert.match(cron.replies[0], /总计 1/); +}); + +test('user cannot overwrite another user bound cookie', () => { + const store = { + qdreader_cookie_json: JSON.stringify({ '12345': cookie }), + qdreader_user_bind_json: JSON.stringify({ '12345': 'user-a' }), + }; + const newCookie = cookie.replace('sessionyw=s', 'sessionyw=changed'); + const r = runPlugin({ content: '启点ck ' + newCookie, store, userId: 'user-b' }); + assert.match(r.replies.join('\n'), /无权更新 uid: 12345/); + assert.equal(JSON.parse(r.store.qdreader_cookie_json)['12345'], cookie); + assert.equal(JSON.parse(r.store.qdreader_user_bind_json)['12345'], 'user-a'); +});