Consolidate Persimmons source
This commit is contained in:
1 parent
f8f7d286f8
commit
60616291f4
1 file changed
+59
@@ -0,0 +1,59 @@
|
||||
export class VaultUnavailableError extends Error {}
|
||||
const BASE = 'https://teahub.xyz/tea/site/chat/persimmons';
|
||||
export async function vaultRequest(method = 'GET', owner, id, body, fetcher = fetch) {
|
||||
if (!['GET', 'POST', 'PUT', 'DELETE'].includes(method) || (id !== undefined && !/^[0-9a-f]{32}$/.test(id))) throw new Error('Invalid vault operation.');
|
||||
// Chromium omits Origin on permitted extension GETs. POST reads carry its
|
||||
// genuine Origin so the server retains the pinned-caller check.
|
||||
try {
|
||||
const response = await fetcher(`${BASE}/human-vault/items${id ? '/' + id : ''}${method === 'GET' ? '/read' : ''}`, {
|
||||
method: method === 'GET' ? 'POST' : method, credentials: 'include', cache: 'no-store', redirect: 'error', signal: AbortSignal.timeout(15000),
|
||||
headers: {Accept: 'application/json', ...(owner ? {'X-Vault-Owner': owner} : {}), ...(body ? {'Content-Type': 'application/json'} : {})},
|
||||
...(body ? {body: JSON.stringify(body)} : {})});
|
||||
if (!response.ok || !(response.headers.get('content-type') || '').includes('application/json')) throw new Error();
|
||||
const data = await response.json();
|
||||
if (!/^[a-z0-9][a-z0-9_.-]{0,99}$/.test(data.owner) || (owner && data.owner !== owner) || data.error) throw new Error();
|
||||
return data;
|
||||
} catch { throw new VaultUnavailableError('Vault unavailable or session changed. Sign in to Persimmons and reconnect.'); }
|
||||
}
|
||||
|
||||
// Serialized into the isolated main-frame world only after an explicit Fill click.
|
||||
// Check origin again inside the actual target document to close navigation races.
|
||||
export function fillAccount(expectedOrigin, account) {
|
||||
try {
|
||||
if (location.protocol !== 'https:' || location.origin !== expectedOrigin || window.top !== window) return false;
|
||||
const visible = input => !input.disabled && !input.readOnly && input.getClientRects().length > 0;
|
||||
const passwords = [...document.querySelectorAll('input[type="password"]')].filter(visible);
|
||||
if (passwords.length !== 1) return false;
|
||||
const password = passwords[0];
|
||||
const form = password.form;
|
||||
if (!form || new URL(form.action || location.href, location.href).origin !== expectedOrigin) return false;
|
||||
const users = [...form.querySelectorAll('input')].filter(input =>
|
||||
['text', 'email'].includes(input.type) && visible(input));
|
||||
if (users.length !== 1) return false;
|
||||
const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
|
||||
setter.call(users[0], account.username); setter.call(password, account.password);
|
||||
for (const input of [users[0], password]) {
|
||||
input.dispatchEvent(new Event('input', {bubbles: true}));
|
||||
input.dispatchEvent(new Event('change', {bubbles: true}));
|
||||
}
|
||||
return true;
|
||||
} finally { account.username = ''; account.password = ''; account = null; }
|
||||
}
|
||||
|
||||
|
||||
// Serialized into the isolated main-frame world only after an explicit Capture click.
|
||||
export function captureAccount(expectedOrigin) {
|
||||
try {
|
||||
if (location.protocol !== 'https:' || location.origin !== expectedOrigin) return null;
|
||||
const visible = input => !input.disabled && !input.readOnly && input.getClientRects().length > 0;
|
||||
const passwords = [...document.querySelectorAll('input[type="password"]')].filter(input => visible(input) && input.value);
|
||||
if (passwords.length !== 1) return null;
|
||||
const password = passwords[0];
|
||||
const form = password.form;
|
||||
if (!form || new URL(form.action || location.href, location.href).origin !== expectedOrigin) return null;
|
||||
const users = [...form.querySelectorAll('input')].filter(input =>
|
||||
['text', 'email'].includes(input.type) && visible(input) && input.value);
|
||||
if (users.length !== 1) return null;
|
||||
return {username: users[0].value, password: password.value};
|
||||
} catch { return null; }
|
||||
}
|
||||
Reference in new issue
Block a user