@sovovs/bycli 2.1.51 → 2.1.52
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.
- package/clis/weixin/_wechat/user-info.js +112 -18
- package/package.json +1 -1
|
@@ -53,26 +53,50 @@ export function buildSelectTabScript(label) {
|
|
|
53
53
|
const compact = value => String(value || '').trim().replace(/\\s+/g, ' ');
|
|
54
54
|
const visible = node => {
|
|
55
55
|
if (!node || node.hidden || node.closest('[hidden], [aria-hidden="true"]')) return false;
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
for (let current = node; current && current.nodeType === 1; current = current.parentElement) {
|
|
57
|
+
const style = window.getComputedStyle(current);
|
|
58
|
+
if (style.display === 'none' || style.visibility === 'hidden' || Number(style.opacity) === 0) return false;
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
58
61
|
};
|
|
59
|
-
const
|
|
62
|
+
const selectors = [
|
|
63
|
+
'a[href]',
|
|
64
|
+
'button',
|
|
60
65
|
'[role="tab"]',
|
|
61
66
|
'.weui-desktop-tab__nav',
|
|
62
67
|
'.tab_nav',
|
|
63
|
-
'a',
|
|
64
|
-
'button',
|
|
65
68
|
'li',
|
|
66
|
-
]
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
];
|
|
70
|
+
let node = null;
|
|
71
|
+
for (const selector of selectors) {
|
|
72
|
+
node = Array.from(document.querySelectorAll(selector)).find(candidate => visible(candidate)
|
|
73
|
+
&& compact(candidate.textContent) === ${JSON.stringify(label)});
|
|
74
|
+
if (node) break;
|
|
75
|
+
}
|
|
76
|
+
if (!node) return { selected: false, disabled: false, href: null };
|
|
70
77
|
const disabled = node.disabled === true
|
|
71
78
|
|| node.getAttribute('aria-disabled') === 'true'
|
|
72
79
|
|| /(^|\\s)(disabled|is-disabled)(\\s|$)/.test(node.className || '');
|
|
73
|
-
if (disabled) return { selected: false, disabled: true };
|
|
80
|
+
if (disabled) return { selected: false, disabled: true, href: null };
|
|
81
|
+
const rawHref = node.tagName === 'A' ? node.getAttribute('href') : null;
|
|
82
|
+
if (rawHref) {
|
|
83
|
+
const href = node.href;
|
|
84
|
+
let destination = null;
|
|
85
|
+
try { destination = new URL(href); } catch { /* handled as a click-only placeholder */ }
|
|
86
|
+
const trustedNavigation = destination
|
|
87
|
+
&& destination.origin === window.location.origin
|
|
88
|
+
&& destination.pathname === '/cgi-bin/settingpage'
|
|
89
|
+
&& Boolean(destination.searchParams.get('token'));
|
|
90
|
+
if (trustedNavigation) return { selected: true, disabled: false, href };
|
|
91
|
+
const clickOnly = /^javascript:/i.test(rawHref)
|
|
92
|
+
|| rawHref.charAt(0) === '#'
|
|
93
|
+
|| (destination
|
|
94
|
+
&& destination.origin === window.location.origin
|
|
95
|
+
&& destination.pathname === '/cgi-bin/settingpage');
|
|
96
|
+
if (!clickOnly) return { selected: true, disabled: false, href };
|
|
97
|
+
}
|
|
74
98
|
node.click();
|
|
75
|
-
return { selected: true, disabled: false };
|
|
99
|
+
return { selected: true, disabled: false, href: null };
|
|
76
100
|
})()`;
|
|
77
101
|
}
|
|
78
102
|
|
|
@@ -84,8 +108,11 @@ export const SETTINGS_SESSION_SCRIPT = `(() => {
|
|
|
84
108
|
];
|
|
85
109
|
const visible = node => {
|
|
86
110
|
if (!node || node.hidden || node.closest('[hidden], [aria-hidden="true"]')) return false;
|
|
87
|
-
|
|
88
|
-
|
|
111
|
+
for (let current = node; current && current.nodeType === 1; current = current.parentElement) {
|
|
112
|
+
const style = window.getComputedStyle(current);
|
|
113
|
+
if (style.display === 'none' || style.visibility === 'hidden' || Number(style.opacity) === 0) return false;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
89
116
|
};
|
|
90
117
|
return {
|
|
91
118
|
href: window.location.href,
|
|
@@ -121,8 +148,11 @@ export const USER_INFO_EXTRACT_SCRIPT = `(() => {
|
|
|
121
148
|
};
|
|
122
149
|
const visible = node => {
|
|
123
150
|
if (!node || node.hidden || node.closest('[hidden], [aria-hidden="true"]')) return false;
|
|
124
|
-
|
|
125
|
-
|
|
151
|
+
for (let current = node; current && current.nodeType === 1; current = current.parentElement) {
|
|
152
|
+
const style = window.getComputedStyle(current);
|
|
153
|
+
if (style.display === 'none' || style.visibility === 'hidden' || Number(style.opacity) === 0) return false;
|
|
154
|
+
}
|
|
155
|
+
return true;
|
|
126
156
|
};
|
|
127
157
|
const firstVisible = (root, selectors) => {
|
|
128
158
|
for (const selector of selectors) {
|
|
@@ -186,8 +216,22 @@ export const USER_INFO_EXTRACT_SCRIPT = `(() => {
|
|
|
186
216
|
const actions = [];
|
|
187
217
|
const seenFields = new Set();
|
|
188
218
|
const seenActions = new Set();
|
|
219
|
+
const fieldRows = new Set();
|
|
220
|
+
const genericParts = node => Array.from(node.children).flatMap(child => {
|
|
221
|
+
if (!visible(child)
|
|
222
|
+
|| /^(H1|H2|H3|H4|H5|H6|A|BUTTON)$/.test(child.tagName)
|
|
223
|
+
|| child.getAttribute('role') === 'button') return [];
|
|
224
|
+
const copy = child.cloneNode(true);
|
|
225
|
+
copy.querySelectorAll('a, button, [role="button"], svg, .setting_opr, .weui-desktop-setting__status, .setting_status, .status')
|
|
226
|
+
.forEach(item => item.remove());
|
|
227
|
+
const value = compact(copy.textContent);
|
|
228
|
+
return value ? [value] : [];
|
|
229
|
+
});
|
|
189
230
|
|
|
190
231
|
for (const row of rows) {
|
|
232
|
+
const headerCells = row.tagName === 'TR' ? row.querySelectorAll(':scope > th') : [];
|
|
233
|
+
const dataCells = row.tagName === 'TR' ? row.querySelectorAll(':scope > td') : [];
|
|
234
|
+
if (headerCells.length > 0 && dataCells.length === 0) continue;
|
|
191
235
|
const labelNode = firstVisible(row, [
|
|
192
236
|
'.frm_label',
|
|
193
237
|
'.weui-desktop-setting__label',
|
|
@@ -220,16 +264,48 @@ export const USER_INFO_EXTRACT_SCRIPT = `(() => {
|
|
|
220
264
|
'.status',
|
|
221
265
|
]);
|
|
222
266
|
let fieldValue = null;
|
|
267
|
+
let fieldStatus = compact(statusNode && statusNode.textContent);
|
|
223
268
|
if (valueNode) {
|
|
224
269
|
const copy = valueNode.cloneNode(true);
|
|
225
270
|
copy.querySelectorAll('a, button, [role="button"], .setting_opr, .weui-desktop-setting__status, .setting_status, .status')
|
|
226
271
|
.forEach(node => node.remove());
|
|
227
272
|
fieldValue = compact(copy.textContent);
|
|
228
273
|
}
|
|
274
|
+
if (fieldValue === null) {
|
|
275
|
+
const parts = genericParts(row);
|
|
276
|
+
const labelIndex = parts.indexOf(fieldLabel);
|
|
277
|
+
if (labelIndex !== -1) {
|
|
278
|
+
fieldValue = parts[labelIndex + 1] || null;
|
|
279
|
+
if (fieldStatus === null && parts.length > labelIndex + 2) {
|
|
280
|
+
fieldStatus = parts.slice(labelIndex + 2).join(' ');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
229
284
|
const item = {
|
|
230
285
|
label: fieldLabel,
|
|
231
286
|
value: fieldValue,
|
|
232
|
-
status:
|
|
287
|
+
status: fieldStatus,
|
|
288
|
+
};
|
|
289
|
+
const key = JSON.stringify(item);
|
|
290
|
+
if (!seenFields.has(key)) {
|
|
291
|
+
seenFields.add(key);
|
|
292
|
+
fields.push(item);
|
|
293
|
+
}
|
|
294
|
+
fieldRows.add(row);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
let genericRows = Array.from(sectionNode.querySelectorAll('div, li'))
|
|
298
|
+
.filter(node => visible(node)
|
|
299
|
+
&& !fieldRows.has(node)
|
|
300
|
+
&& !rows.some(row => row !== node && row.contains(node))
|
|
301
|
+
&& genericParts(node).length >= 2);
|
|
302
|
+
genericRows = genericRows.filter(node => !genericRows.some(other => other !== node && node.contains(other)));
|
|
303
|
+
for (const row of genericRows) {
|
|
304
|
+
const parts = genericParts(row);
|
|
305
|
+
const item = {
|
|
306
|
+
label: parts[0],
|
|
307
|
+
value: parts[1] || null,
|
|
308
|
+
status: parts.length > 2 ? parts.slice(2).join(' ') : null,
|
|
233
309
|
};
|
|
234
310
|
const key = JSON.stringify(item);
|
|
235
311
|
if (!seenFields.has(key)) {
|
|
@@ -248,7 +324,9 @@ export const USER_INFO_EXTRACT_SCRIPT = `(() => {
|
|
|
248
324
|
actions.push(item);
|
|
249
325
|
}
|
|
250
326
|
}
|
|
251
|
-
|
|
327
|
+
if (fields.length > 0 || actions.length > 0) return [{ label, fields, actions }];
|
|
328
|
+
const empty = Boolean(sectionNode.querySelector('table thead th, table tr > th'));
|
|
329
|
+
return empty ? [{ label, fields, actions, empty: true }] : [];
|
|
252
330
|
});
|
|
253
331
|
|
|
254
332
|
if (sections.length === 0 && /(无权限|暂无权限|暂不支持|不可用)/.test(document.body.textContent || '')) {
|
|
@@ -305,7 +383,9 @@ export function normalizeUserInfoTab(tabId, payload) {
|
|
|
305
383
|
const actions = rawActions.map((action, actionIndex) => (
|
|
306
384
|
normalizeAction(action, definition.label, sectionIndex, actionIndex)
|
|
307
385
|
));
|
|
308
|
-
return fields.length > 0 || actions.length > 0
|
|
386
|
+
return fields.length > 0 || actions.length > 0 || section.empty === true
|
|
387
|
+
? [{ label, fields, actions }]
|
|
388
|
+
: [];
|
|
309
389
|
});
|
|
310
390
|
|
|
311
391
|
execution(sections.length > 0, `WeChat ${definition.label} exposed no recognizable settings`);
|
|
@@ -332,6 +412,20 @@ export async function collectUserInfoTabs(page, { settle = 2 } = {}) {
|
|
|
332
412
|
});
|
|
333
413
|
continue;
|
|
334
414
|
}
|
|
415
|
+
if (selection.href !== null && selection.href !== undefined) {
|
|
416
|
+
let navigationUrl;
|
|
417
|
+
try {
|
|
418
|
+
navigationUrl = new URL(selection.href);
|
|
419
|
+
} catch {
|
|
420
|
+
throw new CommandExecutionError('WeChat account settings exposed an invalid tab destination');
|
|
421
|
+
}
|
|
422
|
+
execution(
|
|
423
|
+
navigationUrl.origin === ORIGIN
|
|
424
|
+
&& Boolean(navigationUrl.searchParams.get('token')?.trim()),
|
|
425
|
+
'WeChat account settings exposed an untrusted tab destination',
|
|
426
|
+
);
|
|
427
|
+
await page.goto(navigationUrl.toString());
|
|
428
|
+
}
|
|
335
429
|
await page.wait(settle);
|
|
336
430
|
const payload = await page.evaluate(USER_INFO_EXTRACT_SCRIPT);
|
|
337
431
|
result.push({ id: definition.id, data: normalizeUserInfoTab(definition.id, payload) });
|