mirror of
https://gitee.com/samwaf/SamWaf.git
synced 2026-10-30 17:51:44 +08:00
File diff suppressed because one or more lines are too long
@@ -0,0 +1,85 @@
|
||||
/*!
|
||||
* Toastify js 1.12.0
|
||||
* https://github.com/apvarun/toastify-js
|
||||
* @license MIT licensed
|
||||
*
|
||||
* Copyright (C) 2018 Varun A P
|
||||
*/
|
||||
|
||||
.toastify {
|
||||
padding: 12px 20px;
|
||||
color: #ffffff;
|
||||
display: inline-block;
|
||||
box-shadow: 0 3px 6px -1px rgba(0, 0, 0, 0.12), 0 10px 36px -4px rgba(77, 96, 232, 0.3);
|
||||
background: -webkit-linear-gradient(315deg, #73a5ff, #5477f5);
|
||||
background: linear-gradient(135deg, #73a5ff, #5477f5);
|
||||
position: fixed;
|
||||
opacity: 0;
|
||||
transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
max-width: calc(50% - 20px);
|
||||
z-index: 2147483647;
|
||||
}
|
||||
|
||||
.toastify.on {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.toast-close {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-size: 1em;
|
||||
opacity: 0.4;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.toastify-right {
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
.toastify-left {
|
||||
left: 15px;
|
||||
}
|
||||
|
||||
.toastify-top {
|
||||
top: -150px;
|
||||
}
|
||||
|
||||
.toastify-bottom {
|
||||
bottom: -150px;
|
||||
}
|
||||
|
||||
.toastify-rounded {
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
.toastify-avatar {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
margin: -7px 5px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.toastify-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-width: fit-content;
|
||||
max-width: -moz-fit-content;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 360px) {
|
||||
.toastify-right, .toastify-left {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-width: fit-content;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,360 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<link rel="shortcut icon" type="image/ico" href="/samwaf_captcha/favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="/samwaf_captcha/css/gocaptcha.global.css">
|
||||
<link rel="stylesheet" type="text/css" href="/samwaf_captcha/css/toastify.css">
|
||||
<title>进行验证</title>
|
||||
<style>
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #3C3C3C;
|
||||
background: #EBF3FB;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.example {
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.box{
|
||||
margin: 30px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="example">
|
||||
<div class="box" id="click-wrap"></div>
|
||||
</div>
|
||||
<!-- GoCaptcha -->
|
||||
<script type="text/javascript" src="/samwaf_captcha/js/gocaptcha.global.js"></script>
|
||||
<!-- Http Request -->
|
||||
<script src="/samwaf_captcha/js/axios.js"></script>
|
||||
<!-- Request parameter serialization-->
|
||||
<script src="/samwaf_captcha/js/qs.js"></script>
|
||||
<!-- Toast Tip -->
|
||||
<script src="/samwaf_captcha/js/toastify.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
// 机器人检测模块
|
||||
(function() {
|
||||
// 初始化检测变量
|
||||
let mouseMovements = 0;
|
||||
let lastMouseX = 0;
|
||||
let lastMouseY = 0;
|
||||
let mouseTrack = [];
|
||||
let startTime = Date.now();
|
||||
let botScore = 0;
|
||||
let isBotDetected = false;
|
||||
let clickTimes = [];
|
||||
|
||||
// 检测自动化工具和模拟器
|
||||
function detectAutomation() {
|
||||
// 检测常见的自动化工具特征
|
||||
const automationFlags = [
|
||||
!!window.document.documentElement.getAttribute('selenium'),
|
||||
!!window.document.documentElement.getAttribute('webdriver'),
|
||||
!!window._phantom,
|
||||
!!window.callPhantom,
|
||||
!!window.__nightmare,
|
||||
!!window.domAutomation,
|
||||
!!window.domAutomationController,
|
||||
navigator.webdriver === true,
|
||||
navigator.userAgent.indexOf('Selenium') !== -1,
|
||||
navigator.userAgent.indexOf('PhantomJS') !== -1,
|
||||
navigator.userAgent.indexOf('Headless') !== -1
|
||||
];
|
||||
|
||||
// 检测到任何自动化工具标志
|
||||
automationFlags.forEach(flag => {
|
||||
if (flag) botScore += 20;
|
||||
});
|
||||
|
||||
// 检测异常的浏览器特征
|
||||
if (navigator.hardwareConcurrency < 2) botScore += 10;
|
||||
if (navigator.plugins.length === 0) botScore += 10;
|
||||
if (navigator.languages === undefined) botScore += 10;
|
||||
|
||||
// 检测Canvas指纹
|
||||
try {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.textBaseline = "top";
|
||||
ctx.font = "14px 'Arial'";
|
||||
ctx.fillStyle = "#f60";
|
||||
ctx.fillRect(125, 1, 62, 20);
|
||||
ctx.fillStyle = "#069";
|
||||
ctx.fillText("SamWaf防护", 2, 15);
|
||||
ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
|
||||
ctx.fillText("SamWaf防护", 4, 17);
|
||||
|
||||
const dataURL = canvas.toDataURL();
|
||||
if (dataURL === 'data:,') botScore += 20; // 空数据URL通常表示headless浏览器
|
||||
} catch(e) {
|
||||
botScore += 15; // Canvas操作异常
|
||||
}
|
||||
|
||||
return botScore >= 30;
|
||||
}
|
||||
|
||||
// 监听鼠标移动
|
||||
document.addEventListener('mousemove', function(e) {
|
||||
mouseMovements++;
|
||||
|
||||
// 记录鼠标轨迹
|
||||
if (mouseTrack.length < 100) {
|
||||
mouseTrack.push({
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
time: Date.now() - startTime
|
||||
});
|
||||
}
|
||||
|
||||
// 检测鼠标移动是否过于机械
|
||||
if (lastMouseX !== 0 && lastMouseY !== 0) {
|
||||
const dx = e.clientX - lastMouseX;
|
||||
const dy = e.clientY - lastMouseY;
|
||||
|
||||
// 检测直线移动
|
||||
if (dx === 0 || dy === 0) {
|
||||
botScore += 1;
|
||||
}
|
||||
|
||||
// 检测等距移动
|
||||
if (mouseTrack.length > 3) {
|
||||
const last = mouseTrack[mouseTrack.length - 1];
|
||||
const prev = mouseTrack[mouseTrack.length - 2];
|
||||
const prevPrev = mouseTrack[mouseTrack.length - 3];
|
||||
|
||||
const dist1 = Math.sqrt(Math.pow(last.x - prev.x, 2) + Math.pow(last.y - prev.y, 2));
|
||||
const dist2 = Math.sqrt(Math.pow(prev.x - prevPrev.x, 2) + Math.pow(prev.y - prevPrev.y, 2));
|
||||
|
||||
if (Math.abs(dist1 - dist2) < 0.5) {
|
||||
botScore += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastMouseX = e.clientX;
|
||||
lastMouseY = e.clientY;
|
||||
});
|
||||
|
||||
// 监听点击事件
|
||||
document.addEventListener('click', function(e) {
|
||||
clickTimes.push(Date.now());
|
||||
|
||||
// 检测点击间隔是否过于规律
|
||||
if (clickTimes.length >= 3) {
|
||||
const interval1 = clickTimes[clickTimes.length - 1] - clickTimes[clickTimes.length - 2];
|
||||
const interval2 = clickTimes[clickTimes.length - 2] - clickTimes[clickTimes.length - 3];
|
||||
|
||||
if (Math.abs(interval1 - interval2) < 50) {
|
||||
botScore += 5; // 点击间隔过于规律
|
||||
}
|
||||
}
|
||||
|
||||
// 检测点击位置是否过于精确
|
||||
if (e.clientX % 1 === 0 && e.clientY % 1 === 0) {
|
||||
botScore += 2; // 精确到整数像素的点击
|
||||
}
|
||||
});
|
||||
|
||||
// 定期检查机器人特征
|
||||
setInterval(function() {
|
||||
// 检测鼠标移动是否太少
|
||||
const timeElapsed = (Date.now() - startTime) / 1000;
|
||||
if (timeElapsed > 3 && mouseMovements < 5) {
|
||||
botScore += 10; // 3秒内鼠标移动次数少于5次
|
||||
}
|
||||
|
||||
// 检测自动化工具
|
||||
if (detectAutomation()) {
|
||||
isBotDetected = true;
|
||||
}
|
||||
|
||||
// 如果机器人分数过高,标记为机器人
|
||||
if (botScore >= 30) {
|
||||
isBotDetected = true;
|
||||
}
|
||||
|
||||
// 如果检测到机器人,发送到服务器
|
||||
if (isBotDetected) {
|
||||
// 向服务器报告可能的机器人行为
|
||||
axios({
|
||||
method: 'post',
|
||||
url: '/samwaf_captcha/report_bot',
|
||||
data: Qs.stringify({
|
||||
score: botScore,
|
||||
userAgent: navigator.userAgent,
|
||||
screenInfo: `${window.screen.width}x${window.screen.height}`,
|
||||
timestamp: Date.now()
|
||||
})
|
||||
}).catch(e => {
|
||||
// 静默处理错误
|
||||
});
|
||||
|
||||
// 增加验证难度或显示额外验证
|
||||
const captchaEl = document.getElementById('click-wrap');
|
||||
if (captchaEl) {
|
||||
// 可以在这里修改验证码的难度或行为
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
})();
|
||||
|
||||
axios.defaults.baseURL = '/';
|
||||
|
||||
function toastSuccess(msg) {
|
||||
Toastify({
|
||||
text: msg,
|
||||
duration: 1000,
|
||||
newWindow: true,
|
||||
gravity: "top",
|
||||
position: "center",
|
||||
style: {
|
||||
background: "#f0f9eb",
|
||||
border: "1px solid #dcf9cc",
|
||||
color: "#5eaa2f",
|
||||
borderRadius: "6px",
|
||||
boxShadow: "1px 1px 10px #e0e0e0",
|
||||
padding: "8px 20px"
|
||||
},
|
||||
}).showToast();
|
||||
}
|
||||
|
||||
function toastError(msg) {
|
||||
Toastify({
|
||||
text: msg,
|
||||
duration: 1000,
|
||||
newWindow: true,
|
||||
gravity: "top",
|
||||
position: "center",
|
||||
style: {
|
||||
background: "#fef0f0",
|
||||
border: "1px solid #fcd6d6",
|
||||
color: "#ed4630",
|
||||
borderRadius: "6px",
|
||||
boxShadow: "1px 1px 10px #e0e0e0",
|
||||
padding: "8px 20px"
|
||||
},
|
||||
}).showToast();
|
||||
}
|
||||
|
||||
|
||||
// Click
|
||||
;(function (goCaptcha){
|
||||
const getDataApi = "/samwaf_captcha/click_basic";
|
||||
const checkDataApi = "/samwaf_captcha/verify";
|
||||
|
||||
const el = document.getElementById("click-wrap");
|
||||
const capt = new goCaptcha.Click({
|
||||
width: 300,
|
||||
height: 220,
|
||||
// iconSize: 30,
|
||||
});
|
||||
|
||||
var captKey = ''
|
||||
|
||||
capt.mount(el)
|
||||
|
||||
capt.setEvents({
|
||||
click(x, y) {
|
||||
},
|
||||
confirm(dots, reset) {
|
||||
// 添加机器人检测数据
|
||||
const botData = {
|
||||
mouseTrack: window.mouseTrack || [],
|
||||
timing: Date.now(),
|
||||
pattern: dots.map(d => ({ x: d.x, y: d.y }))
|
||||
};
|
||||
|
||||
confirmEvent(dots, botData)
|
||||
},
|
||||
refresh() {
|
||||
capt.clear()
|
||||
requestCaptchaData()
|
||||
},
|
||||
close() {
|
||||
}
|
||||
})
|
||||
|
||||
const requestCaptchaData = function() {
|
||||
capt.clear()
|
||||
captKey = ''
|
||||
axios({
|
||||
method: 'get',
|
||||
url: getDataApi,
|
||||
}).then(function(response){
|
||||
const data = response.data || {};
|
||||
if (data && (data['code'] || 0) === 0) {
|
||||
capt.setData({
|
||||
image: data['image_base64'] || '',
|
||||
thumb: data['thumb_base64'] || '',
|
||||
})
|
||||
captKey = data['captcha_key'] || ''
|
||||
} else {
|
||||
toastError(`获取数据失败`)
|
||||
}
|
||||
}).catch((e)=>{
|
||||
console.warn(e)
|
||||
})
|
||||
}
|
||||
|
||||
const confirmEvent = function (dots, botData) {
|
||||
const dotArr = []
|
||||
for (let i = 0; i < dots.length; i++) {
|
||||
const dot = dots[i]
|
||||
dotArr.push(dot.x, dot.y)
|
||||
}
|
||||
|
||||
axios({
|
||||
method: 'post',
|
||||
url: checkDataApi,
|
||||
data: Qs.stringify({
|
||||
dots: dotArr.join(','),
|
||||
key: captKey || '',
|
||||
botCheck: JSON.stringify(botData || {})
|
||||
}),
|
||||
}).then(function (response){
|
||||
const data = response.data || {};
|
||||
if (data && (data['code'] || 0) === 0) {
|
||||
// 隐藏验证码元素
|
||||
document.getElementById("click-wrap").style.display = "none";
|
||||
// 添加成功提示
|
||||
const successEl = document.createElement("div");
|
||||
successEl.className = "box";
|
||||
successEl.innerHTML = "<h3 style='color: #5eaa2f; text-align: center;'>验证成功,页面即将刷新...</h3>";
|
||||
document.querySelector(".example").appendChild(successEl);
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
} else {
|
||||
toastError(`校验失败`)
|
||||
|
||||
setTimeout(() => {
|
||||
requestCaptchaData()
|
||||
}, 500)
|
||||
}
|
||||
}).catch((e)=>{
|
||||
console.warn(e)
|
||||
setTimeout(() => {
|
||||
requestCaptchaData()
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
|
||||
requestCaptchaData()
|
||||
})(window.GoCaptcha || {})
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,445 @@
|
||||
/*!
|
||||
* Toastify js 1.12.0
|
||||
* https://github.com/apvarun/toastify-js
|
||||
* @license MIT licensed
|
||||
*
|
||||
* Copyright (C) 2018 Varun A P
|
||||
*/
|
||||
(function(root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
module.exports = factory();
|
||||
} else {
|
||||
root.Toastify = factory();
|
||||
}
|
||||
})(this, function(global) {
|
||||
// Object initialization
|
||||
var Toastify = function(options) {
|
||||
// Returning a new init object
|
||||
return new Toastify.lib.init(options);
|
||||
},
|
||||
// Library version
|
||||
version = "1.12.0";
|
||||
|
||||
// Set the default global options
|
||||
Toastify.defaults = {
|
||||
oldestFirst: true,
|
||||
text: "Toastify is awesome!",
|
||||
node: undefined,
|
||||
duration: 3000,
|
||||
selector: undefined,
|
||||
callback: function () {
|
||||
},
|
||||
destination: undefined,
|
||||
newWindow: false,
|
||||
close: false,
|
||||
gravity: "toastify-top",
|
||||
positionLeft: false,
|
||||
position: '',
|
||||
backgroundColor: '',
|
||||
avatar: "",
|
||||
className: "",
|
||||
stopOnFocus: true,
|
||||
onClick: function () {
|
||||
},
|
||||
offset: {x: 0, y: 0},
|
||||
escapeMarkup: true,
|
||||
ariaLive: 'polite',
|
||||
style: {background: ''}
|
||||
};
|
||||
|
||||
// Defining the prototype of the object
|
||||
Toastify.lib = Toastify.prototype = {
|
||||
toastify: version,
|
||||
|
||||
constructor: Toastify,
|
||||
|
||||
// Initializing the object with required parameters
|
||||
init: function(options) {
|
||||
// Verifying and validating the input object
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
// Creating the options object
|
||||
this.options = {};
|
||||
|
||||
this.toastElement = null;
|
||||
|
||||
// Validating the options
|
||||
this.options.text = options.text || Toastify.defaults.text; // Display message
|
||||
this.options.node = options.node || Toastify.defaults.node; // Display content as node
|
||||
this.options.duration = options.duration === 0 ? 0 : options.duration || Toastify.defaults.duration; // Display duration
|
||||
this.options.selector = options.selector || Toastify.defaults.selector; // Parent selector
|
||||
this.options.callback = options.callback || Toastify.defaults.callback; // Callback after display
|
||||
this.options.destination = options.destination || Toastify.defaults.destination; // On-click destination
|
||||
this.options.newWindow = options.newWindow || Toastify.defaults.newWindow; // Open destination in new window
|
||||
this.options.close = options.close || Toastify.defaults.close; // Show toast close icon
|
||||
this.options.gravity = options.gravity === "bottom" ? "toastify-bottom" : Toastify.defaults.gravity; // toast position - top or bottom
|
||||
this.options.positionLeft = options.positionLeft || Toastify.defaults.positionLeft; // toast position - left or right
|
||||
this.options.position = options.position || Toastify.defaults.position; // toast position - left or right
|
||||
this.options.backgroundColor = options.backgroundColor || Toastify.defaults.backgroundColor; // toast background color
|
||||
this.options.avatar = options.avatar || Toastify.defaults.avatar; // img element src - url or a path
|
||||
this.options.className = options.className || Toastify.defaults.className; // additional class names for the toast
|
||||
this.options.stopOnFocus = options.stopOnFocus === undefined ? Toastify.defaults.stopOnFocus : options.stopOnFocus; // stop timeout on focus
|
||||
this.options.onClick = options.onClick || Toastify.defaults.onClick; // Callback after click
|
||||
this.options.offset = options.offset || Toastify.defaults.offset; // toast offset
|
||||
this.options.escapeMarkup = options.escapeMarkup !== undefined ? options.escapeMarkup : Toastify.defaults.escapeMarkup;
|
||||
this.options.ariaLive = options.ariaLive || Toastify.defaults.ariaLive;
|
||||
this.options.style = options.style || Toastify.defaults.style;
|
||||
if(options.backgroundColor) {
|
||||
this.options.style.background = options.backgroundColor;
|
||||
}
|
||||
|
||||
// Returning the current object for chaining functions
|
||||
return this;
|
||||
},
|
||||
|
||||
// Building the DOM element
|
||||
buildToast: function() {
|
||||
// Validating if the options are defined
|
||||
if (!this.options) {
|
||||
throw "Toastify is not initialized";
|
||||
}
|
||||
|
||||
// Creating the DOM object
|
||||
var divElement = document.createElement("div");
|
||||
divElement.className = "toastify on " + this.options.className;
|
||||
|
||||
// Positioning toast to left or right or center
|
||||
if (!!this.options.position) {
|
||||
divElement.className += " toastify-" + this.options.position;
|
||||
} else {
|
||||
// To be depreciated in further versions
|
||||
if (this.options.positionLeft === true) {
|
||||
divElement.className += " toastify-left";
|
||||
console.warn('Property `positionLeft` will be depreciated in further versions. Please use `position` instead.')
|
||||
} else {
|
||||
// Default position
|
||||
divElement.className += " toastify-right";
|
||||
}
|
||||
}
|
||||
|
||||
// Assigning gravity of element
|
||||
divElement.className += " " + this.options.gravity;
|
||||
|
||||
if (this.options.backgroundColor) {
|
||||
// This is being deprecated in favor of using the style HTML DOM property
|
||||
console.warn('DEPRECATION NOTICE: "backgroundColor" is being deprecated. Please use the "style.background" property.');
|
||||
}
|
||||
|
||||
// Loop through our style object and apply styles to divElement
|
||||
for (var property in this.options.style) {
|
||||
divElement.style[property] = this.options.style[property];
|
||||
}
|
||||
|
||||
// Announce the toast to screen readers
|
||||
if (this.options.ariaLive) {
|
||||
divElement.setAttribute('aria-live', this.options.ariaLive)
|
||||
}
|
||||
|
||||
// Adding the toast message/node
|
||||
if (this.options.node && this.options.node.nodeType === Node.ELEMENT_NODE) {
|
||||
// If we have a valid node, we insert it
|
||||
divElement.appendChild(this.options.node)
|
||||
} else {
|
||||
if (this.options.escapeMarkup) {
|
||||
divElement.innerText = this.options.text;
|
||||
} else {
|
||||
divElement.innerHTML = this.options.text;
|
||||
}
|
||||
|
||||
if (this.options.avatar !== "") {
|
||||
var avatarElement = document.createElement("img");
|
||||
avatarElement.src = this.options.avatar;
|
||||
|
||||
avatarElement.className = "toastify-avatar";
|
||||
|
||||
if (this.options.position == "left" || this.options.positionLeft === true) {
|
||||
// Adding close icon on the left of content
|
||||
divElement.appendChild(avatarElement);
|
||||
} else {
|
||||
// Adding close icon on the right of content
|
||||
divElement.insertAdjacentElement("afterbegin", avatarElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adding a close icon to the toast
|
||||
if (this.options.close === true) {
|
||||
// Create a span for close element
|
||||
var closeElement = document.createElement("button");
|
||||
closeElement.type = "button";
|
||||
closeElement.setAttribute("aria-label", "Close");
|
||||
closeElement.className = "toast-close";
|
||||
closeElement.innerHTML = "✖";
|
||||
|
||||
// Triggering the removal of toast from DOM on close click
|
||||
closeElement.addEventListener(
|
||||
"click",
|
||||
function(event) {
|
||||
event.stopPropagation();
|
||||
this.removeElement(this.toastElement);
|
||||
window.clearTimeout(this.toastElement.timeOutValue);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
//Calculating screen width
|
||||
var width = window.innerWidth > 0 ? window.innerWidth : screen.width;
|
||||
|
||||
// Adding the close icon to the toast element
|
||||
// Display on the right if screen width is less than or equal to 360px
|
||||
if ((this.options.position == "left" || this.options.positionLeft === true) && width > 360) {
|
||||
// Adding close icon on the left of content
|
||||
divElement.insertAdjacentElement("afterbegin", closeElement);
|
||||
} else {
|
||||
// Adding close icon on the right of content
|
||||
divElement.appendChild(closeElement);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear timeout while toast is focused
|
||||
if (this.options.stopOnFocus && this.options.duration > 0) {
|
||||
var self = this;
|
||||
// stop countdown
|
||||
divElement.addEventListener(
|
||||
"mouseover",
|
||||
function(event) {
|
||||
window.clearTimeout(divElement.timeOutValue);
|
||||
}
|
||||
)
|
||||
// add back the timeout
|
||||
divElement.addEventListener(
|
||||
"mouseleave",
|
||||
function() {
|
||||
divElement.timeOutValue = window.setTimeout(
|
||||
function() {
|
||||
// Remove the toast from DOM
|
||||
self.removeElement(divElement);
|
||||
},
|
||||
self.options.duration
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Adding an on-click destination path
|
||||
if (typeof this.options.destination !== "undefined") {
|
||||
divElement.addEventListener(
|
||||
"click",
|
||||
function(event) {
|
||||
event.stopPropagation();
|
||||
if (this.options.newWindow === true) {
|
||||
window.open(this.options.destination, "_blank");
|
||||
} else {
|
||||
window.location = this.options.destination;
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof this.options.onClick === "function" && typeof this.options.destination === "undefined") {
|
||||
divElement.addEventListener(
|
||||
"click",
|
||||
function(event) {
|
||||
event.stopPropagation();
|
||||
this.options.onClick();
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
// Adding offset
|
||||
if(typeof this.options.offset === "object") {
|
||||
|
||||
var x = getAxisOffsetAValue("x", this.options);
|
||||
var y = getAxisOffsetAValue("y", this.options);
|
||||
|
||||
var xOffset = this.options.position == "left" ? x : "-" + x;
|
||||
var yOffset = this.options.gravity == "toastify-top" ? y : "-" + y;
|
||||
|
||||
divElement.style.transform = "translate(" + xOffset + "," + yOffset + ")";
|
||||
|
||||
}
|
||||
|
||||
// Returning the generated element
|
||||
return divElement;
|
||||
},
|
||||
|
||||
// Displaying the toast
|
||||
showToast: function() {
|
||||
// Creating the DOM object for the toast
|
||||
this.toastElement = this.buildToast();
|
||||
|
||||
// Getting the root element to with the toast needs to be added
|
||||
var rootElement;
|
||||
if (typeof this.options.selector === "string") {
|
||||
rootElement = document.getElementById(this.options.selector);
|
||||
} else if (this.options.selector instanceof HTMLElement || (typeof ShadowRoot !== 'undefined' && this.options.selector instanceof ShadowRoot)) {
|
||||
rootElement = this.options.selector;
|
||||
} else {
|
||||
rootElement = document.body;
|
||||
}
|
||||
|
||||
// Validating if root element is present in DOM
|
||||
if (!rootElement) {
|
||||
throw "Root element is not defined";
|
||||
}
|
||||
|
||||
// Adding the DOM element
|
||||
var elementToInsert = Toastify.defaults.oldestFirst ? rootElement.firstChild : rootElement.lastChild;
|
||||
rootElement.insertBefore(this.toastElement, elementToInsert);
|
||||
|
||||
// Repositioning the toasts in case multiple toasts are present
|
||||
Toastify.reposition();
|
||||
|
||||
if (this.options.duration > 0) {
|
||||
this.toastElement.timeOutValue = window.setTimeout(
|
||||
function() {
|
||||
// Remove the toast from DOM
|
||||
this.removeElement(this.toastElement);
|
||||
}.bind(this),
|
||||
this.options.duration
|
||||
); // Binding `this` for function invocation
|
||||
}
|
||||
|
||||
// Supporting function chaining
|
||||
return this;
|
||||
},
|
||||
|
||||
hideToast: function() {
|
||||
if (this.toastElement.timeOutValue) {
|
||||
clearTimeout(this.toastElement.timeOutValue);
|
||||
}
|
||||
this.removeElement(this.toastElement);
|
||||
},
|
||||
|
||||
// Removing the element from the DOM
|
||||
removeElement: function(toastElement) {
|
||||
// Hiding the element
|
||||
// toastElement.classList.remove("on");
|
||||
toastElement.className = toastElement.className.replace(" on", "");
|
||||
|
||||
// Removing the element from DOM after transition end
|
||||
window.setTimeout(
|
||||
function() {
|
||||
// remove options node if any
|
||||
if (this.options.node && this.options.node.parentNode) {
|
||||
this.options.node.parentNode.removeChild(this.options.node);
|
||||
}
|
||||
|
||||
// Remove the element from the DOM, only when the parent node was not removed before.
|
||||
if (toastElement.parentNode) {
|
||||
toastElement.parentNode.removeChild(toastElement);
|
||||
}
|
||||
|
||||
// Calling the callback function
|
||||
this.options.callback.call(toastElement);
|
||||
|
||||
// Repositioning the toasts again
|
||||
Toastify.reposition();
|
||||
}.bind(this),
|
||||
400
|
||||
); // Binding `this` for function invocation
|
||||
},
|
||||
};
|
||||
|
||||
// Positioning the toasts on the DOM
|
||||
Toastify.reposition = function() {
|
||||
|
||||
// Top margins with gravity
|
||||
var topLeftOffsetSize = {
|
||||
top: 15,
|
||||
bottom: 15,
|
||||
};
|
||||
var topRightOffsetSize = {
|
||||
top: 15,
|
||||
bottom: 15,
|
||||
};
|
||||
var offsetSize = {
|
||||
top: 15,
|
||||
bottom: 15,
|
||||
};
|
||||
|
||||
// Get all toast messages on the DOM
|
||||
var allToasts = document.getElementsByClassName("toastify");
|
||||
|
||||
var classUsed;
|
||||
|
||||
// Modifying the position of each toast element
|
||||
for (var i = 0; i < allToasts.length; i++) {
|
||||
// Getting the applied gravity
|
||||
if (containsClass(allToasts[i], "toastify-top") === true) {
|
||||
classUsed = "toastify-top";
|
||||
} else {
|
||||
classUsed = "toastify-bottom";
|
||||
}
|
||||
|
||||
var height = allToasts[i].offsetHeight;
|
||||
classUsed = classUsed.substr(9, classUsed.length-1)
|
||||
// Spacing between toasts
|
||||
var offset = 15;
|
||||
|
||||
var width = window.innerWidth > 0 ? window.innerWidth : screen.width;
|
||||
|
||||
// Show toast in center if screen with less than or equal to 360px
|
||||
if (width <= 360) {
|
||||
// Setting the position
|
||||
allToasts[i].style[classUsed] = offsetSize[classUsed] + "px";
|
||||
|
||||
offsetSize[classUsed] += height + offset;
|
||||
} else {
|
||||
if (containsClass(allToasts[i], "toastify-left") === true) {
|
||||
// Setting the position
|
||||
allToasts[i].style[classUsed] = topLeftOffsetSize[classUsed] + "px";
|
||||
|
||||
topLeftOffsetSize[classUsed] += height + offset;
|
||||
} else {
|
||||
// Setting the position
|
||||
allToasts[i].style[classUsed] = topRightOffsetSize[classUsed] + "px";
|
||||
|
||||
topRightOffsetSize[classUsed] += height + offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Supporting function chaining
|
||||
return this;
|
||||
};
|
||||
|
||||
// Helper function to get offset.
|
||||
function getAxisOffsetAValue(axis, options) {
|
||||
|
||||
if(options.offset[axis]) {
|
||||
if(isNaN(options.offset[axis])) {
|
||||
return options.offset[axis];
|
||||
}
|
||||
else {
|
||||
return options.offset[axis] + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
return '0px';
|
||||
|
||||
}
|
||||
|
||||
function containsClass(elem, yourClass) {
|
||||
if (!elem || typeof yourClass !== "string") {
|
||||
return false;
|
||||
} else if (
|
||||
elem.className &&
|
||||
elem.className
|
||||
.trim()
|
||||
.split(/\s+/gi)
|
||||
.indexOf(yourClass) > -1
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Setting up the prototype for the init object
|
||||
Toastify.lib.init.prototype = Toastify.lib;
|
||||
|
||||
// Returning the Toastify function to be assigned to the window object/module
|
||||
return Toastify;
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
1.0.0
|
||||
@@ -69,6 +69,9 @@ var owaspAssets embed.FS
|
||||
//go:embed exedata/spiderbot
|
||||
var spiderBotAssets embed.FS
|
||||
|
||||
//go:embed exedata/captcha
|
||||
var captchaAssets embed.FS
|
||||
|
||||
// wafSystenService 实现了 service.Service 接口
|
||||
type wafSystenService struct{}
|
||||
|
||||
@@ -154,16 +157,17 @@ func (m *wafSystenService) run() {
|
||||
global.GWAF_DLP_CONFIG = ldpConfig
|
||||
global.GWAF_REG_PUBLIC_KEY = publicKey
|
||||
|
||||
//方式owasp资源
|
||||
//设置目标目录(当前路径下的 data/owasp)
|
||||
targetDir := utils.GetCurrentDir() + "/data/owasp"
|
||||
|
||||
// 调用 wafinit 包中的方法检查并释放数据集
|
||||
err = wafinit.CheckAndReleaseDataset(owaspAssets, targetDir)
|
||||
//owasp资源 释放
|
||||
err = wafinit.CheckAndReleaseDataset(owaspAssets, utils.GetCurrentDir()+"/data/owasp", "owasp")
|
||||
if err != nil {
|
||||
zlog.Error("owasp", err.Error())
|
||||
}
|
||||
|
||||
// 验证码资源释放
|
||||
err = wafinit.CheckAndReleaseDataset(captchaAssets, utils.GetCurrentDir()+"/data/captcha", "captcha")
|
||||
if err != nil {
|
||||
zlog.Error("captcha", err.Error())
|
||||
}
|
||||
//TODO 准备释放最新spider bot
|
||||
|
||||
//初始化cache
|
||||
|
||||
+42
-25
@@ -10,13 +10,11 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 版本文件路径在内嵌资源中
|
||||
const versionFilePath = "exedata/owasp/version"
|
||||
|
||||
// CheckAndReleaseDataset 检查目标目录的版本,并根据条件决定是否释放数据集
|
||||
func CheckAndReleaseDataset(owaspAssets embed.FS, targetDir string) error {
|
||||
innerLogName := "CheckOWASPRelease"
|
||||
zlog.Info(innerLogName, "检测OWASP数据集合")
|
||||
func CheckAndReleaseDataset(assets embed.FS, targetDir string, resourceType string) error {
|
||||
innerLogName := "CheckResourceRelease"
|
||||
zlog.Info(innerLogName, "检测"+resourceType+"数据集合")
|
||||
|
||||
// 设置 lock.txt 文件路径
|
||||
lockFilePath := filepath.Join(targetDir, "lock.txt")
|
||||
|
||||
@@ -27,7 +25,7 @@ func CheckAndReleaseDataset(owaspAssets embed.FS, targetDir string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create directory: %w", err)
|
||||
}
|
||||
zlog.Info(innerLogName, "OWASP Directory created:"+targetDir)
|
||||
zlog.Info(innerLogName, resourceType+" Directory created:"+targetDir)
|
||||
}
|
||||
|
||||
// 检查 lock.txt 是否存在
|
||||
@@ -37,21 +35,26 @@ func CheckAndReleaseDataset(owaspAssets embed.FS, targetDir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 构建版本文件路径
|
||||
versionFilePath := fmt.Sprintf("exedata/%s/version", strings.ToLower(resourceType))
|
||||
|
||||
// 获取当前版本号,从内嵌的版本文件中读取
|
||||
currentVersion, err := getCurrentVersion(owaspAssets)
|
||||
currentVersion, err := getCurrentVersion(assets, versionFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading current version: %w", err)
|
||||
// 如果没有版本文件,则默认为初始版本
|
||||
currentVersion = "1.0.0"
|
||||
zlog.Info(innerLogName, "No version file found, using default version: "+currentVersion)
|
||||
}
|
||||
|
||||
// 检查版本号文件是否存在以及版本号
|
||||
var targetVersion string
|
||||
versionFilePath := filepath.Join(targetDir, "version")
|
||||
if _, err := os.Stat(versionFilePath); os.IsNotExist(err) {
|
||||
localVersionFilePath := filepath.Join(targetDir, "version")
|
||||
if _, err := os.Stat(localVersionFilePath); os.IsNotExist(err) {
|
||||
// version 文件不存在,认为是第一次运行,释放文件
|
||||
targetVersion = ""
|
||||
} else {
|
||||
// 读取版本号文件内容
|
||||
data, err := ioutil.ReadFile(versionFilePath)
|
||||
data, err := ioutil.ReadFile(localVersionFilePath)
|
||||
if err != nil {
|
||||
zlog.Info(innerLogName, "Error reading version file:", err.Error())
|
||||
}
|
||||
@@ -61,28 +64,29 @@ func CheckAndReleaseDataset(owaspAssets embed.FS, targetDir string) error {
|
||||
// 如果版本号不存在,或者目标版本号较旧,则释放文件
|
||||
if targetVersion == "" || compareVersions(targetVersion, currentVersion) < 0 {
|
||||
// 释放文件
|
||||
err := ReleaseFiles(owaspAssets, "exedata/owasp", targetDir)
|
||||
srcPath := fmt.Sprintf("exedata/%s", strings.ToLower(resourceType))
|
||||
err := ReleaseFiles(assets, srcPath, targetDir, resourceType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error releasing files: %w", err)
|
||||
}
|
||||
|
||||
// 释放后更新版本号
|
||||
err = ioutil.WriteFile(versionFilePath, []byte(currentVersion), 0644)
|
||||
err = ioutil.WriteFile(localVersionFilePath, []byte(currentVersion), 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing version file: %w", err)
|
||||
}
|
||||
zlog.Info(innerLogName, "数据集已更新.")
|
||||
zlog.Info(innerLogName, resourceType+"数据集已更新.")
|
||||
} else {
|
||||
// 版本号是最新的,不需要释放
|
||||
zlog.Info(innerLogName, "版本号是最新的,不需要释放.")
|
||||
zlog.Info(innerLogName, resourceType+"版本号是最新的,不需要释放.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取当前版本号,从内嵌的版本文件中读取
|
||||
func getCurrentVersion(owaspAssets embed.FS) (string, error) {
|
||||
data, err := owaspAssets.ReadFile(versionFilePath)
|
||||
func getCurrentVersion(assets embed.FS, versionFilePath string) (string, error) {
|
||||
data, err := assets.ReadFile(versionFilePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read version file from embedded assets: %w", err)
|
||||
}
|
||||
@@ -94,10 +98,23 @@ func compareVersions(version1, version2 string) int {
|
||||
v1 := strings.Split(version1, ".")
|
||||
v2 := strings.Split(version2, ".")
|
||||
|
||||
// 确保两个版本号都有至少3个部分
|
||||
for len(v1) < 3 {
|
||||
v1 = append(v1, "0")
|
||||
}
|
||||
for len(v2) < 3 {
|
||||
v2 = append(v2, "0")
|
||||
}
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
if v1[i] < v2[i] {
|
||||
// 转换为整数进行比较
|
||||
var num1, num2 int
|
||||
fmt.Sscanf(v1[i], "%d", &num1)
|
||||
fmt.Sscanf(v2[i], "%d", &num2)
|
||||
|
||||
if num1 < num2 {
|
||||
return -1
|
||||
} else if v1[i] > v2[i] {
|
||||
} else if num1 > num2 {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
@@ -105,7 +122,7 @@ func compareVersions(version1, version2 string) int {
|
||||
}
|
||||
|
||||
// ReleaseFiles 释放嵌入的文件到目标目录
|
||||
func ReleaseFiles(owaspAssets embed.FS, srcPath, destPath string) error {
|
||||
func ReleaseFiles(assets embed.FS, srcPath, destPath, resourceType string) error {
|
||||
// 检查目标文件夹是否存在,不存在则创建
|
||||
innerLogName := "ReleaseFiles"
|
||||
if err := os.MkdirAll(destPath, os.ModePerm); err != nil {
|
||||
@@ -113,7 +130,7 @@ func ReleaseFiles(owaspAssets embed.FS, srcPath, destPath string) error {
|
||||
}
|
||||
|
||||
// 遍历嵌入的文件
|
||||
entries, err := owaspAssets.ReadDir(srcPath)
|
||||
entries, err := assets.ReadDir(srcPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read embedded directory: %w", err)
|
||||
}
|
||||
@@ -127,12 +144,12 @@ func ReleaseFiles(owaspAssets embed.FS, srcPath, destPath string) error {
|
||||
|
||||
// 如果是目录,递归提取
|
||||
if entry.IsDir() {
|
||||
if err := ReleaseFiles(owaspAssets, source, destination); err != nil {
|
||||
if err := ReleaseFiles(assets, source, destination, resourceType); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// 如果是文件,检查是否存在,如果不存在则写入
|
||||
data, err := owaspAssets.ReadFile(source)
|
||||
data, err := assets.ReadFile(source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read file from embed: %w", err)
|
||||
}
|
||||
@@ -141,7 +158,7 @@ func ReleaseFiles(owaspAssets embed.FS, srcPath, destPath string) error {
|
||||
if err := os.WriteFile(destination, data, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write file: %w", err)
|
||||
}
|
||||
zlog.Info(innerLogName, "OWASP Extracted:"+destination)
|
||||
zlog.Info(innerLogName, resourceType+" Extracted:"+destination)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user