fix: repair legacy qdreader fallback bindings
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
//[author: Hermes]
|
//[author: Hermes]
|
||||||
//[service: BOSS]
|
//[service: BOSS]
|
||||||
//[class: 工具类]
|
//[class: 工具类]
|
||||||
//[version: 2.4.1]
|
//[version: 2.4.2]
|
||||||
//[platform: web,qq,wx,tg,tb,fs,we]
|
//[platform: web,qq,wx,tg,tb,fs,we]
|
||||||
//[public: false]
|
//[public: false]
|
||||||
//[price: 0]
|
//[price: 0]
|
||||||
@@ -124,6 +124,23 @@ function currentUserId() {
|
|||||||
function currentOwnerId() {
|
function currentOwnerId() {
|
||||||
return currentUserId()
|
return currentUserId()
|
||||||
}
|
}
|
||||||
|
function currentIsAdmin() {
|
||||||
|
try { if (typeof isAdmin === "function" && isAdmin()) return true } catch (e) {}
|
||||||
|
try { if (typeof sender !== "undefined" && sender && typeof sender.isAdmin === "function" && sender.isAdmin()) return true } catch (e2) {}
|
||||||
|
try { if (typeof Sender !== "undefined" && Sender && typeof Sender.isAdmin === "function" && Sender.isAdmin()) return true } catch (e3) {}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
function looksLikeLegacyFallbackOwner(owner) {
|
||||||
|
owner = trim(owner)
|
||||||
|
if (!owner) return false
|
||||||
|
if (owner === "__qdreader_default_owner__") return true
|
||||||
|
if (/^[A-Za-z0-9._-]{1,32}$/.test(owner) && !/^\d{5,}$/.test(owner)) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
function canRepairBinding(oldOwner, me) {
|
||||||
|
if (!oldOwner || oldOwner === me) return true
|
||||||
|
return currentIsAdmin() && looksLikeLegacyFallbackOwner(oldOwner)
|
||||||
|
}
|
||||||
function currentUserDebug() {
|
function currentUserDebug() {
|
||||||
var parts = []
|
var parts = []
|
||||||
var fns = ["GetUserID", "GetChatID", "GetImType", "ImType", "GetUsername"]
|
var fns = ["GetUserID", "GetChatID", "GetImType", "ImType", "GetUsername"]
|
||||||
@@ -242,7 +259,7 @@ function contentText() {
|
|||||||
}
|
}
|
||||||
function usage() {
|
function usage() {
|
||||||
return [
|
return [
|
||||||
"启点读书签到插件 v2.4.1",
|
"启点读书签到插件 v2.4.2",
|
||||||
"【一级菜单】",
|
"【一级菜单】",
|
||||||
"账号管理:启点账号",
|
"账号管理:启点账号",
|
||||||
"执行查询:启点查询",
|
"执行查询:启点查询",
|
||||||
@@ -658,6 +675,10 @@ function handleSaveCookie(arg, store, bind) {
|
|||||||
oldOwner = ""
|
oldOwner = ""
|
||||||
lines.push("检测到 uid: " + uid + " 只有旧绑定没有CK,已自动清理残留绑定")
|
lines.push("检测到 uid: " + uid + " 只有旧绑定没有CK,已自动清理残留绑定")
|
||||||
}
|
}
|
||||||
|
if (store[uid] && oldOwner && oldOwner !== me && canRepairBinding(oldOwner, me)) {
|
||||||
|
lines.push("检测到 uid: " + uid + " 旧绑定来自历史兜底标识 " + maskUid(oldOwner) + ",已修复为当前用户")
|
||||||
|
oldOwner = ""
|
||||||
|
}
|
||||||
if (store[uid] && oldOwner && me && oldOwner !== me) {
|
if (store[uid] && oldOwner && me && oldOwner !== me) {
|
||||||
lines.push("无权更新 uid: " + uid + "(该账号已绑定其他用户)")
|
lines.push("无权更新 uid: " + uid + "(该账号已绑定其他用户)")
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
//[author: Hermes]
|
//[author: Hermes]
|
||||||
//[service: BOSS]
|
//[service: BOSS]
|
||||||
//[class: 工具类]
|
//[class: 工具类]
|
||||||
//[version: 2.4.1]
|
//[version: 2.4.2]
|
||||||
//[platform: web,qq,wx,tg,tb,fs,we]
|
//[platform: web,qq,wx,tg,tb,fs,we]
|
||||||
//[public: false]
|
//[public: false]
|
||||||
//[price: 0]
|
//[price: 0]
|
||||||
@@ -124,6 +124,23 @@ function currentUserId() {
|
|||||||
function currentOwnerId() {
|
function currentOwnerId() {
|
||||||
return currentUserId()
|
return currentUserId()
|
||||||
}
|
}
|
||||||
|
function currentIsAdmin() {
|
||||||
|
try { if (typeof isAdmin === "function" && isAdmin()) return true } catch (e) {}
|
||||||
|
try { if (typeof sender !== "undefined" && sender && typeof sender.isAdmin === "function" && sender.isAdmin()) return true } catch (e2) {}
|
||||||
|
try { if (typeof Sender !== "undefined" && Sender && typeof Sender.isAdmin === "function" && Sender.isAdmin()) return true } catch (e3) {}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
function looksLikeLegacyFallbackOwner(owner) {
|
||||||
|
owner = trim(owner)
|
||||||
|
if (!owner) return false
|
||||||
|
if (owner === "__qdreader_default_owner__") return true
|
||||||
|
if (/^[A-Za-z0-9._-]{1,32}$/.test(owner) && !/^\d{5,}$/.test(owner)) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
function canRepairBinding(oldOwner, me) {
|
||||||
|
if (!oldOwner || oldOwner === me) return true
|
||||||
|
return currentIsAdmin() && looksLikeLegacyFallbackOwner(oldOwner)
|
||||||
|
}
|
||||||
function currentUserDebug() {
|
function currentUserDebug() {
|
||||||
var parts = []
|
var parts = []
|
||||||
var fns = ["GetUserID", "GetChatID", "GetImType", "ImType", "GetUsername"]
|
var fns = ["GetUserID", "GetChatID", "GetImType", "ImType", "GetUsername"]
|
||||||
@@ -242,7 +259,7 @@ function contentText() {
|
|||||||
}
|
}
|
||||||
function usage() {
|
function usage() {
|
||||||
return [
|
return [
|
||||||
"启点读书签到插件 v2.4.1",
|
"启点读书签到插件 v2.4.2",
|
||||||
"【一级菜单】",
|
"【一级菜单】",
|
||||||
"账号管理:启点账号",
|
"账号管理:启点账号",
|
||||||
"执行查询:启点查询",
|
"执行查询:启点查询",
|
||||||
@@ -658,6 +675,10 @@ function handleSaveCookie(arg, store, bind) {
|
|||||||
oldOwner = ""
|
oldOwner = ""
|
||||||
lines.push("检测到 uid: " + uid + " 只有旧绑定没有CK,已自动清理残留绑定")
|
lines.push("检测到 uid: " + uid + " 只有旧绑定没有CK,已自动清理残留绑定")
|
||||||
}
|
}
|
||||||
|
if (store[uid] && oldOwner && oldOwner !== me && canRepairBinding(oldOwner, me)) {
|
||||||
|
lines.push("检测到 uid: " + uid + " 旧绑定来自历史兜底标识 " + maskUid(oldOwner) + ",已修复为当前用户")
|
||||||
|
oldOwner = ""
|
||||||
|
}
|
||||||
if (store[uid] && oldOwner && me && oldOwner !== me) {
|
if (store[uid] && oldOwner && me && oldOwner !== me) {
|
||||||
lines.push("无权更新 uid: " + uid + "(该账号已绑定其他用户)")
|
lines.push("无权更新 uid: " + uid + "(该账号已绑定其他用户)")
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import vm from 'node:vm';
|
|||||||
const pluginPath = new URL('../plugins/qdreader/qdreader_sign_autman.js', import.meta.url).pathname;
|
const pluginPath = new URL('../plugins/qdreader/qdreader_sign_autman.js', import.meta.url).pathname;
|
||||||
const code = fs.readFileSync(pluginPath, 'utf8');
|
const code = fs.readFileSync(pluginPath, 'utf8');
|
||||||
|
|
||||||
function runPlugin({ content = '', store = {}, bucketStore = {}, requests = [], userId = 'user-a', listens = [], senderOverrides = {}, globals = {} } = {}) {
|
function runPlugin({ content = '', store = {}, bucketStore = {}, requests = [], userId = 'user-a', listens = [], senderOverrides = {}, globals = {}, admin = true } = {}) {
|
||||||
const replies = [];
|
const replies = [];
|
||||||
const calls = [];
|
const calls = [];
|
||||||
let requestIndex = 0;
|
let requestIndex = 0;
|
||||||
@@ -14,7 +14,7 @@ function runPlugin({ content = '', store = {}, bucketStore = {}, requests = [],
|
|||||||
reply: (msg) => replies.push(String(msg)),
|
reply: (msg) => replies.push(String(msg)),
|
||||||
getMessage: () => content,
|
getMessage: () => content,
|
||||||
getContent: () => content,
|
getContent: () => content,
|
||||||
isAdmin: () => true,
|
isAdmin: () => admin,
|
||||||
getUserID: () => userId,
|
getUserID: () => userId,
|
||||||
listen: () => (listenIndex < listens.length ? listens[listenIndex++] : 'q'),
|
listen: () => (listenIndex < listens.length ? listens[listenIndex++] : 'q'),
|
||||||
};
|
};
|
||||||
@@ -178,15 +178,38 @@ test('quick submit can re-add cookie when old binding remains but cookie was man
|
|||||||
assert.equal(JSON.parse(r.store.qdreader_user_bind_json)['12345'], 'user-a');
|
assert.equal(JSON.parse(r.store.qdreader_user_bind_json)['12345'], 'user-a');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('quick submit still rejects overwrite when cookie and another owner binding both exist', () => {
|
test('quick submit still rejects overwrite when cookie and real another owner binding both exist', () => {
|
||||||
const store = {
|
const store = {
|
||||||
qdreader_cookie_json: JSON.stringify({ '12345': cookie }),
|
qdreader_cookie_json: JSON.stringify({ '12345': cookie }),
|
||||||
qdreader_user_bind_json: JSON.stringify({ '12345': 'old-user' }),
|
qdreader_user_bind_json: JSON.stringify({ '12345': '987654321' }),
|
||||||
};
|
};
|
||||||
const r = runPlugin({ content: '启点ck ' + cookie.replace('qdh=qdh', 'qdh=newqdh'), store, userId: 'user-a', listens: ['n'] });
|
const r = runPlugin({ content: '启点ck ' + cookie.replace('qdh=qdh', 'qdh=newqdh'), store, userId: 'user-a', listens: ['n'] });
|
||||||
assert.match(r.replies.join('\n'), /无权更新 uid: 12345/);
|
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_cookie_json)['12345'], cookie);
|
||||||
assert.equal(JSON.parse(r.store.qdreader_user_bind_json)['12345'], 'old-user');
|
assert.equal(JSON.parse(r.store.qdreader_user_bind_json)['12345'], '987654321');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('quick submit can repair legacy fallback owner when admin submits existing cookie', () => {
|
||||||
|
const store = {
|
||||||
|
qdreader_cookie_json: JSON.stringify({ '12345': cookie }),
|
||||||
|
qdreader_user_bind_json: JSON.stringify({ '12345': 'chickliu' }),
|
||||||
|
};
|
||||||
|
const r = runPlugin({ content: '启点ck ' + cookie.replace('qdh=qdh', 'qdh=newqdh'), store, userId: 'real-user', admin: true, listens: ['n'] });
|
||||||
|
assert.match(r.replies.join('\n'), /旧绑定来自历史兜底标识/);
|
||||||
|
assert.match(r.replies.join('\n'), /CK更新成功/);
|
||||||
|
assert.equal(JSON.parse(r.store.qdreader_user_bind_json)['12345'], 'real-user');
|
||||||
|
assert.match(JSON.parse(r.store.qdreader_cookie_json)['12345'], /qdh=newqdh/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('non-admin quick submit still rejects existing cookie bound to legacy fallback owner', () => {
|
||||||
|
const store = {
|
||||||
|
qdreader_cookie_json: JSON.stringify({ '12345': cookie }),
|
||||||
|
qdreader_user_bind_json: JSON.stringify({ '12345': 'chickliu' }),
|
||||||
|
};
|
||||||
|
const r = runPlugin({ content: '启点ck ' + cookie.replace('qdh=qdh', 'qdh=newqdh'), store, userId: 'real-user', admin: false, listens: ['n'] });
|
||||||
|
assert.match(r.replies.join('\n'), /无权更新 uid: 12345/);
|
||||||
|
assert.equal(JSON.parse(r.store.qdreader_user_bind_json)['12345'], 'chickliu');
|
||||||
|
assert.equal(JSON.parse(r.store.qdreader_cookie_json)['12345'], cookie);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('quick submit rejects save when Autman JS has no user identity', () => {
|
test('quick submit rejects save when Autman JS has no user identity', () => {
|
||||||
@@ -422,11 +445,11 @@ test('unbound global cookies are not visible or usable by normal users', () => {
|
|||||||
test('user cannot overwrite another user bound cookie', () => {
|
test('user cannot overwrite another user bound cookie', () => {
|
||||||
const store = {
|
const store = {
|
||||||
qdreader_cookie_json: JSON.stringify({ '12345': cookie }),
|
qdreader_cookie_json: JSON.stringify({ '12345': cookie }),
|
||||||
qdreader_user_bind_json: JSON.stringify({ '12345': 'user-a' }),
|
qdreader_user_bind_json: JSON.stringify({ '12345': '987654321' }),
|
||||||
};
|
};
|
||||||
const newCookie = cookie.replace('sessionyw=s', 'sessionyw=changed');
|
const newCookie = cookie.replace('sessionyw=s', 'sessionyw=changed');
|
||||||
const r = runPlugin({ content: '启点ck ' + newCookie, store, userId: 'user-b' });
|
const r = runPlugin({ content: '启点ck ' + newCookie, store, userId: 'user-b' });
|
||||||
assert.match(r.replies.join('\n'), /无权更新 uid: 12345/);
|
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_cookie_json)['12345'], cookie);
|
||||||
assert.equal(JSON.parse(r.store.qdreader_user_bind_json)['12345'], 'user-a');
|
assert.equal(JSON.parse(r.store.qdreader_user_bind_json)['12345'], '987654321');
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user