fix:captcha file

#112 #3
This commit is contained in:
samwaf
2025-03-24 10:37:50 +08:00
parent 6fa7d6bc95
commit ddb8f12d49
11 changed files with 5390 additions and 31 deletions
File diff suppressed because one or more lines are too long
+85
View File
@@ -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

+360
View File
@@ -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
+445
View File
@@ -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 = "&#10006;";
// 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;
});
+1
View File
@@ -0,0 +1 @@
1.0.0
+10 -6
View File
@@ -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
View File
@@ -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