feat: SAC 0.7.2 security hardening (rate limit, JWT DB check, DOMPurify)
Path traversal fix in SPA fallback, admin-only host delete, login rate limit with 3 attempts and Telegram alert, JWT validated against active users in DB, and DOMPurify for agent report HTML. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Generated
+31
-2
@@ -1,17 +1,19 @@
|
||||
{
|
||||
"name": "sac-ui",
|
||||
"version": "0.1.0",
|
||||
"version": "0.7.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sac-ui",
|
||||
"version": "0.1.0",
|
||||
"version": "0.7.2",
|
||||
"dependencies": {
|
||||
"dompurify": "^3.2.4",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/dompurify": "^3.2.0",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"typescript": "~5.7.2",
|
||||
"vite": "^6.0.3",
|
||||
@@ -901,6 +903,17 @@
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@types/dompurify": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.2.0.tgz",
|
||||
"integrity": "sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==",
|
||||
"deprecated": "This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dompurify": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
@@ -908,6 +921,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
|
||||
@@ -1130,6 +1150,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.4.7",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.7.tgz",
|
||||
"integrity": "sha512-2jBxDJY4RR06tQNy4w5FlFH7kfxsQZlufd0sbv+chfHCxeJwrFw2baUDsSwvBISD4K4RDbd0PTfy3uNXsR6siA==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "sac-ui",
|
||||
"private": true,
|
||||
"version": "0.7.1",
|
||||
"version": "0.7.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -9,6 +9,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"dompurify": "^3.2.4",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
|
||||
@@ -1,20 +1,30 @@
|
||||
/** Безопасный вывод HTML из агента (только базовая разметка). */
|
||||
/** Безопасный вывод HTML из агента (DOMPurify, белый список тегов). */
|
||||
import DOMPurify from "dompurify";
|
||||
|
||||
const ALLOWED_REPORT_TAGS = [
|
||||
"b",
|
||||
"strong",
|
||||
"i",
|
||||
"em",
|
||||
"u",
|
||||
"code",
|
||||
"pre",
|
||||
"br",
|
||||
"p",
|
||||
"div",
|
||||
"span",
|
||||
"ul",
|
||||
"ol",
|
||||
"li",
|
||||
"hr",
|
||||
];
|
||||
|
||||
export function sanitizeAgentHtml(html: string): string {
|
||||
const allowed = /^(b|strong|i|em|u|code|pre|br|p|div|span|ul|ol|li|hr)$/i;
|
||||
return html.replace(/<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, (tag, name: string) => {
|
||||
if (allowed.test(name)) {
|
||||
if (tag.toLowerCase().startsWith("</")) {
|
||||
return `</${name.toLowerCase()}>`;
|
||||
}
|
||||
if (name.toLowerCase() === "br") {
|
||||
return "<br>";
|
||||
}
|
||||
return `<${name.toLowerCase()}>`;
|
||||
}
|
||||
return "";
|
||||
return DOMPurify.sanitize(html, {
|
||||
ALLOWED_TAGS: ALLOWED_REPORT_TAGS,
|
||||
ALLOWED_ATTR: [],
|
||||
});
|
||||
}
|
||||
|
||||
export interface DailyReportStats {
|
||||
platform?: "ssh" | "windows";
|
||||
successful_logins?: number;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export const APP_NAME = "Security Alert Center";
|
||||
export const APP_VERSION = "0.7.1";
|
||||
export const APP_VERSION = "0.7.2";
|
||||
export const APP_VERSION_LABEL = `${APP_NAME} v.${APP_VERSION}`;
|
||||
|
||||
Reference in New Issue
Block a user