Something went wrong. Try again.
makes it not torturous to use. saves in sessionstorage so i guess the rest of the site can read it. only worry is that it writes to sessionstorage so bewareee idk
Something went wrong. Try again.
9.1 kB · 246 lines · Created 3w ago JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246// ==UserScript==// @name gov.br autofill fix thank you claude web chat// @namespace local.govbr.onescreen// @version 1.4// @description Corrige autofill do CPF, adiciona campo de senha na 1ª tela, envia sozinho e preenche a 2ª etapa (handoff da senha só em memória)// @match https://sso.acesso.gov.br/*// @run-at document-end// @grant unsafeWindow// ==/UserScript==
(function () { 'use strict';
const AUTOSUBMIT = true; const LAST_AUTO = 'last_autosubmit'; const MAX_AGE_MS = 30 * 1000; const AUTO_COOLDOWN_MS = 30 * 1000;
const pageWin = unsafeWindow;
// --------------------------------------------------------------------- // sessionStorage no Chromium é gravado em disco (LevelDB do perfil, em // "Session Storage/") para permitir restauração de sessão. Por isso a // senha NUNCA passa por aqui — só o timestamp de cooldown, que não é // sensível. // --------------------------------------------------------------------- const PREFIX = '__govbr_internal_'; const store = { get(k, def) { try { const v = sessionStorage.getItem(PREFIX + k); return v === null ? def : v; } catch (_) { return def; } }, set(k, v) { try { sessionStorage.setItem(PREFIX + k, String(v)); } catch (_) {} }, del(k) { try { sessionStorage.removeItem(PREFIX + k); } catch (_) {} }, };
// --------------------------------------------------------------------- // Handoff da senha: SOMENTE MEMÓRIA. // window.name sobrevive à navegação dentro da mesma aba (é estado do // contexto de navegação, não do documento) e vive apenas na memória do // processo — não é gravado no DOM Storage em disco. É lido e apagado no // primeiro instante da 2ª tela. // --------------------------------------------------------------------- const VAULT_MARK = '__govbr_pw__:';
const vault = { put(password) { let prev = ''; try { prev = pageWin.name || ''; } catch (_) {} if (prev.indexOf(VAULT_MARK) === 0) prev = ''; // não empilha lixo try { pageWin.name = VAULT_MARK + JSON.stringify({ p: password, // senha t: Date.now(), // validade n: prev, // window.name original, para restaurar depois }); } catch (_) {} },
// apaga sem devolver (e restaura o window.name original, se houver) clear() { let raw = ''; try { raw = pageWin.name || ''; } catch (_) { return; } if (raw.indexOf(VAULT_MARK) !== 0) return; let data = null; try { data = JSON.parse(raw.slice(VAULT_MARK.length)); } catch (_) {} try { pageWin.name = (data && data.n) || ''; } catch (_) {} },
// lê e apaga no mesmo instante take() { let raw = ''; try { raw = pageWin.name || ''; } catch (_) { return null; } if (raw.indexOf(VAULT_MARK) !== 0) return null; let data = null; try { data = JSON.parse(raw.slice(VAULT_MARK.length)); } catch (_) {} try { pageWin.name = (data && data.n) || ''; } catch (_) {} return data; }, };
const log = (...a) => console.log('[govbr-login]', ...a); log('carregado em', location.pathname);
// rede de segurança: handoff velho esquecido de uma tentativa anterior (function expireVault() { let raw = ''; try { raw = pageWin.name || ''; } catch (_) { return; } if (raw.indexOf(VAULT_MARK) !== 0) return; let d = null; try { d = JSON.parse(raw.slice(VAULT_MARK.length)); } catch (_) {} if (!d || typeof d.t !== 'number' || Date.now() - d.t > MAX_AGE_MS) vault.clear(); })();
const cpfButton = document.getElementById('enter-account-id'); let cpfInput = document.getElementById('accountId');
if (cpfButton && cpfInput) { stepCpf(); } else { const pwInput = document.querySelector('input[type="password"]'); if (pwInput) stepPassword(pwInput); }
function whenCaptchaReady(cb) { const started = Date.now(); const timer = setInterval(() => { const hasCaptcha = !!document.querySelector('script[src*="hcaptcha"]'); const ready = !hasCaptcha || typeof pageWin.hcaptchaWidgetId !== 'undefined'; if (ready || Date.now() - started > 6000) { clearInterval(timer); setTimeout(cb, 150); } }, 100); }
// ---------- Etapa 1: CPF (+ campo de senha injetado) ---------- function stepCpf() { vault.clear();
const form = document.getElementById('loginData'); if (form) form.setAttribute('autocomplete', 'on'); const hadFocus = document.activeElement === cpfInput; const fresh = cpfInput.cloneNode(false); fresh.type = 'text'; fresh.removeAttribute('inputmode'); fresh.setAttribute('autocomplete', 'username'); fresh.setAttribute('autocapitalize', 'off'); fresh.setAttribute('spellcheck', 'false'); fresh.value = cpfInput.value; cpfInput.replaceWith(fresh); cpfInput = fresh; if (hadFocus) fresh.focus();
const brokenLabel = document.querySelector('label[for="cpf"]'); const cpfLabel = brokenLabel ? brokenLabel.cloneNode(true) : null; if (brokenLabel) brokenLabel.htmlFor = cpfInput.id;
const realCpf = document.createElement('input'); realCpf.type = 'hidden'; realCpf.name = 'accountId'; cpfInput.name = 'username'; (form || document.body).appendChild(realCpf); const syncCpf = () => { realCpf.value = cpfInput.value; }; ['input', 'change', 'blur'].forEach((ev) => cpfInput.addEventListener(ev, syncCpf)); syncCpf();
const pw = cpfInput.cloneNode(false); pw.id = 'password'; pw.removeAttribute('name'); pw.removeAttribute('inputmode'); pw.type = 'password'; pw.value = ''; pw.placeholder = 'Digite sua senha'; pw.setAttribute('autocomplete', 'current-password');
const label = cpfLabel ? cpfLabel.cloneNode(true) : document.createElement('label'); label.htmlFor = 'password'; label.textContent = 'Senha'; cpfInput.after(label, pw);
document.addEventListener('click', (e) => { const btn = e.target.closest && e.target.closest('#enter-account-id'); if (btn) syncCpf(); if (btn && pw.value) { vault.put(pw.value); // memória apenas — nada de sessionStorage } }, true);
if (!AUTOSUBMIT) return;
let typed = false; let submitted = false; const markTyped = (e) => { if (e.type === 'paste' || (e.key && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey)) { typed = true; } }; [cpfInput, pw].forEach((el) => { el.addEventListener('keydown', markTyped); el.addEventListener('paste', markTyped); });
let debounce; const maybeSubmit = () => { if (submitted) return; if (typed) { log('sem envio automático: houve digitação/colagem nos campos'); return; } clearTimeout(debounce); debounce = setTimeout(() => { const digits = cpfInput.value.replace(/\D/g, ''); if (digits.length !== 11 || !pw.value) { log('aguardando campos', { dígitosCPF: digits.length, senhaPreenchida: !!pw.value }); return; } const since = Date.now() - Number(store.get(LAST_AUTO, 0)); if (since < AUTO_COOLDOWN_MS) { log('bloqueado pelo cooldown; tente de novo em', Math.ceil((AUTO_COOLDOWN_MS - since) / 1000), 's'); return; } log('enviando automaticamente'); submitted = true; store.set(LAST_AUTO, Date.now()); // só timestamp: sem problema ir pro disco // grava o handoff também no autosubmit — o clique programático não passa pelo // listener de clique em alguns navegadores quando o alvo é removido antes vault.put(pw.value); whenCaptchaReady(() => cpfButton.click()); }, 250); }; cpfInput.addEventListener('input', maybeSubmit); cpfInput.addEventListener('change', maybeSubmit); pw.addEventListener('input', maybeSubmit); pw.addEventListener('change', maybeSubmit); setTimeout(maybeSubmit, 100); setTimeout(maybeSubmit, 1000); }
// ---------- Etapa 2: tela de senha real ---------- function stepPassword(input) { input.setAttribute('autocomplete', 'current-password');
const data = vault.take(); // lê e apaga imediatamente if (!data || Date.now() - data.t > MAX_AGE_MS) return;
const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set; setter.call(input, data.p); input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true }));
const form = input.form || document; const buttons = [...form.querySelectorAll('button[type="submit"], input[type="submit"]')]; const target = buttons.find((b) => b.value === 'enter-password') || buttons.find((b) => /continuar|entrar|login|acessar/i.test(b.textContent || b.value)) || buttons[0]; if (!target) return;
whenCaptchaReady(() => target.click()); }})();
lowkey if they dont want me to give me security issues by forcing me to use sessionstorage they should idk make a better login screen that doesnt literally load a new page for me to put my password !