fix(security): resolve Snyk Code findings — Tar Slip, Path Traversal, Open Redirect, XSS
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled

Tar Slip (CWE-22) — 3 import services:
  threat_actor, lolbas, caldera: add path validation before extractall()
  to prevent malicious zip members with ../ escaping the target directory.
  (sigma, elastic, atomic already had this protection)

Path Traversal (CWE-23) — professional_reports.py:
  Add _assert_safe_report_path() check on all 5 report endpoints to
  verify the generated filepath stays within REPORT_OUTPUT_DIR.

Open Redirect (CWE-601) — sso.py:
  Validate IdP redirect URL scheme (must be http/https) before
  issuing RedirectResponse, blocking javascript: and data: redirects.

DOM XSS (CWE-79) — 4 frontend pages:
  Create src/utils/url.ts with safeUrl() that rejects non-http/https
  protocols; apply to actor.mitre_url, ref.url, intel.url.
  Sanitize framework name to alphanumeric-only before DOM insertion.
  Restrict evidence MIME types to an explicit safe allowlist (png/jpg/gif/webp).

Hardcoded credentials (CWE-798):
  verify_gaps.py, create_wiki.py: replace literal passwords with
  environment variable reads (AEGIS_ADMIN_PASSWORD, GITEA_PASSWORD).
This commit is contained in:
kitos
2026-06-12 13:15:36 +02:00
parent f54dc0d342
commit dcd4bebc92
12 changed files with 82 additions and 28 deletions
+5 -1
View File
@@ -102,9 +102,13 @@ export default function CompliancePage() {
const json = JSON.stringify(frameworkStatus, null, 2);
const blob = new Blob([json], { type: "application/json" });
const url = URL.createObjectURL(blob);
const safeName = frameworkStatus.framework.name
.replace(/[^a-zA-Z0-9\s_-]/g, "")
.replace(/\s+/g, "_")
.substring(0, 64);
const a = document.createElement("a");
a.href = url;
a.download = `compliance_${frameworkStatus.framework.name.replace(/\s+/g, "_")}.json`;
a.download = `compliance_${safeName}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
+6 -2
View File
@@ -373,8 +373,12 @@ export default function ImportRTPage() {
<div className="flex items-center gap-1.5">
<div className="flex gap-1">
{t.evidence.slice(0, 3).map((ev: RTEvidenceEntry, ei: number) => {
const ext = ev.filename.split(".").pop()?.toLowerCase() ?? "png";
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext}`;
const SAFE_MIMES: Record<string, string> = {
png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg",
gif: "image/gif", webp: "image/webp",
};
const ext = ev.filename.split(".").pop()?.toLowerCase() ?? "";
const mime = SAFE_MIMES[ext] ?? "image/png";
return (
<img
key={ei}
+2 -1
View File
@@ -1,6 +1,7 @@
import { useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import MarkdownText from "../components/MarkdownText";
import { safeUrl } from "../utils/url";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import {
Loader2,
@@ -640,7 +641,7 @@ export default function TechniqueDetailPage() {
</p>
</div>
<a
href={intel.url}
href={safeUrl(intel.url)}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1 text-sm text-cyan-400 hover:underline"
+3 -2
View File
@@ -1,6 +1,7 @@
import { useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import MarkdownText from "../components/MarkdownText";
import { safeUrl } from "../utils/url";
import MotivationBadge from "../components/MotivationBadge";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import {
@@ -258,7 +259,7 @@ export default function ThreatActorDetailPage() {
)}
{actor.mitre_url && (
<a
href={actor.mitre_url}
href={safeUrl(actor.mitre_url)}
target="_blank"
rel="noreferrer"
className="flex items-center gap-1.5 rounded-lg border border-gray-700 bg-gray-800 px-3 py-2 text-xs text-gray-400 hover:text-white transition-colors"
@@ -605,7 +606,7 @@ export default function ThreatActorDetailPage() {
<li key={i} className="text-xs">
{ref.url ? (
<a
href={ref.url}
href={safeUrl(ref.url)}
target="_blank"
rel="noreferrer"
className="text-cyan-400 hover:text-cyan-300 hover:underline"
+10
View File
@@ -0,0 +1,10 @@
const SAFE_SCHEMES = new Set(["http:", "https:", "mailto:", "tel:"]);
export function safeUrl(url: string | null | undefined): string {
if (!url) return "#";
try {
return SAFE_SCHEMES.has(new URL(url).protocol) ? url : "#";
} catch {
return "#";
}
}