La decima extension de atuq. Ofrece la credencial que corresponde al sitio abierto y la pone en el formulario, pero NO puede sacar una contrasena de la boveda por su cuenta: pregunta cuales corresponden —y eso contesta titulo y usuario, nada mas— y la contrasena sale por otro verbo que pregunta en el escritorio antes de contestar. Si la extension queda comprometida, o si una pagina consigue hablarle, lo que obtiene es la lista de titulos de los sitios que coinciden con su propia direccion. La direccion siempre la pone el chrome y nunca la pagina. Lo unico que el trozo que corre dentro del sitio aporta es que HAY un formulario y lo que el usuario tipeo; si pudiera decir de quien es la pagina, podria decir que es el banco. Y la extension no decide nada: quien sabe que credencial va en que sitio es la funcion del otro lado del cable. Ponerlo en JS seria reimplementar el original y, peor, poner en la pagina la decision de a quien se le entrega una contrasena. EL GESTOR DE GECKO SE APAGA, pero como valor de arranque y no como politica. Dos gestores peleando por el mismo campo es la peor experiencia que puede tener alguien que solo quiere entrar a su correo: el sitio se rellena dos veces, o ninguna, y no hay forma de saber cual tiene la buena. De fabrica manda la boveda; el que prefiera el de Gecko lo prende y listo. Prohibirselo seria decidir por el, que es lo que este fichero dice en su cabecera que atuq no quiere ser. Y un guardian nuevo, que corre en un segundo y sin construir nada. Una extension esta enchufada en CUATRO lugares distintos y ninguno da error si falta: el identificador que ella declara, la politica que la instala, el permiso para hablarle al host, y las preferencias que la acompanan. Si falta la politica no se instala. Si falta el permiso se instala y se conecta a nada, en silencio, y la insignia no aparece nunca. Si faltan las preferencias los dos gestores se pelean. Ninguno de los tres se ve como un error: se ven como "no anda". El guardian los mira todos y trae su control negativo, que le saca el permiso del host y exige que lo detecte. No reemplaza al guardian de metal —servidor, navegador real, login real, el dialogo a la vista—: lo precede, y ahorra descubrir construyendo que faltaba un renglon en un JSON. Los cinco verbos del otro lado ya estan en tawasuyu (bc6903f9e), con trece tests que leen el cable.
76 lines
2.5 KiB
JavaScript
76 lines
2.5 KiB
JavaScript
"use strict";
|
|
// bóveda · campos — lo único que corre DENTRO de la página.
|
|
//
|
|
// Su trabajo es mínimo a propósito, porque es la parte que comparte proceso con
|
|
// un sitio cualquiera: avisa que hay un formulario de acceso, pone lo que el
|
|
// fondo le manda, y avisa cuando el usuario envió algo.
|
|
//
|
|
// **No decide nada y no dice de qué sitio es.** La dirección la pone el chrome
|
|
// (`sender.tab.url`), no este archivo: si la página pudiera decir quién es,
|
|
// podría decir que es el banco.
|
|
|
|
function campoClave() {
|
|
return document.querySelector('input[type="password"]:not([disabled])');
|
|
}
|
|
|
|
function campoUsuario(clave) {
|
|
// El de usuario es el campo de texto anterior al de contraseña en el mismo
|
|
// formulario. Es una heurística, y por eso rellenarlo NO es obligatorio: si
|
|
// no se encuentra, se pone sólo la contraseña, que es lo que importa.
|
|
const f = clave && clave.form;
|
|
const candidatos = (f || document).querySelectorAll(
|
|
'input[type="text"], input[type="email"], input:not([type])'
|
|
);
|
|
let ultimo = null;
|
|
for (const c of candidatos) {
|
|
if (clave && c.compareDocumentPosition(clave) & Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
ultimo = c;
|
|
}
|
|
}
|
|
return ultimo;
|
|
}
|
|
|
|
function poner(campo, valor) {
|
|
if (!campo) { return; }
|
|
campo.focus();
|
|
campo.value = valor;
|
|
// Los dos eventos: los formularios modernos escuchan `input`, y algunos
|
|
// validadores sólo `change`. Sin esto el sitio cree que el campo está vacío.
|
|
campo.dispatchEvent(new Event("input", { bubbles: true }));
|
|
campo.dispatchEvent(new Event("change", { bubbles: true }));
|
|
}
|
|
|
|
browser.runtime.onMessage.addListener(msg => {
|
|
if (!msg || msg.tipo !== "rellenar") { return; }
|
|
const clave = campoClave();
|
|
if (!clave) { return; }
|
|
poner(campoUsuario(clave), msg.username);
|
|
poner(clave, msg.password);
|
|
});
|
|
|
|
function avisarSiHay() {
|
|
if (campoClave()) {
|
|
browser.runtime.sendMessage({ tipo: "hay-formulario" });
|
|
}
|
|
}
|
|
|
|
document.addEventListener("submit", ev => {
|
|
const f = ev.target;
|
|
if (!f || !f.querySelector) { return; }
|
|
const clave = f.querySelector('input[type="password"]');
|
|
if (!clave || !clave.value) { return; }
|
|
const usuario = campoUsuario(clave);
|
|
browser.runtime.sendMessage({
|
|
tipo: "se-envio",
|
|
username: usuario ? usuario.value : "",
|
|
password: clave.value,
|
|
});
|
|
}, true);
|
|
|
|
avisarSiHay();
|
|
// Un formulario que aparece después (una pantalla de acceso que se abre sola).
|
|
new MutationObserver(avisarSiHay).observe(document.documentElement, {
|
|
childList: true,
|
|
subtree: true,
|
|
});
|