Initial commit: IT Nexus Web-App
This commit is contained in:
148
backend/src/controllers/securityReport.controller.js
Normal file
148
backend/src/controllers/securityReport.controller.js
Normal file
@@ -0,0 +1,148 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { getDatabase } = require('../config/database');
|
||||
const multer = require('multer');
|
||||
const pdfParse = require('pdf-parse');
|
||||
const Anthropic = require('@anthropic-ai/sdk');
|
||||
|
||||
const UPLOAD_DIR = path.join(__dirname, '../../uploads/security-reports');
|
||||
if (!fs.existsSync(UPLOAD_DIR)) fs.mkdirSync(UPLOAD_DIR, { recursive: true });
|
||||
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => cb(null, UPLOAD_DIR),
|
||||
filename: (req, file, cb) => cb(null, `${Date.now()}-${file.originalname}`),
|
||||
});
|
||||
const upload = multer({
|
||||
storage,
|
||||
limits: { fileSize: 50 * 1024 * 1024 },
|
||||
fileFilter: (req, file, cb) => {
|
||||
if (file.mimetype === 'application/pdf') cb(null, true);
|
||||
else cb(new Error('Nur PDF-Dateien erlaubt'));
|
||||
},
|
||||
});
|
||||
|
||||
const ANALYSIS_PROMPT = `Du analysierst einen wöchentlichen Microsoft 365 Security Report von Arctic Wolf für Cereda Systems GmbH.
|
||||
|
||||
Extrahiere alle relevanten Informationen und gib sie als JSON zurück. Antworte NUR mit validem JSON, kein Text darum herum.
|
||||
|
||||
JSON-Struktur:
|
||||
{
|
||||
"report_period": "DD.MM.YYYY - DD.MM.YYYY",
|
||||
"risk_level": "niedrig|mittel|hoch",
|
||||
"summary": "Kurze 2-3 Satz Zusammenfassung auf Deutsch",
|
||||
"login_stats": {
|
||||
"total": 0,
|
||||
"successful": 0,
|
||||
"failed": 0,
|
||||
"success_rate": 0.0
|
||||
},
|
||||
"top_login_failures": [
|
||||
{ "user": "email@cereda-systems.de", "count": 0, "type": "Interactive|Non-Interactive" }
|
||||
],
|
||||
"non_european_logins": [
|
||||
{ "user": "email", "country": "Land", "ip": "IP", "count": 0, "flagged": true }
|
||||
],
|
||||
"sharepoint_anonymous_links": 0,
|
||||
"identity_protection_events": 0,
|
||||
"new_groups_created": [],
|
||||
"key_findings": ["Befund 1", "Befund 2"],
|
||||
"recommendations": ["Empfehlung 1", "Empfehlung 2"],
|
||||
"all_clear": true
|
||||
}
|
||||
|
||||
Wichtig:
|
||||
- risk_level "hoch" wenn: nicht-europäische Logins von unbekannten IPs, viele Login-Fehler bei kritischen Accounts (server@, admin@)
|
||||
- risk_level "mittel" wenn: einige Auffälligkeiten aber keine aktiven Bedrohungen
|
||||
- risk_level "niedrig" wenn: alles normal
|
||||
- flagged: true bei nicht-europäischen Logins die verdächtig aussehen
|
||||
- all_clear: false wenn risk_level hoch oder mittel`;
|
||||
|
||||
const analyzeWithClaude = async (text) => {
|
||||
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
||||
const response = await client.messages.create({
|
||||
model: 'claude-sonnet-4-6',
|
||||
max_tokens: 2000,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: `${ANALYSIS_PROMPT}\n\nReport-Inhalt:\n${text.slice(0, 15000)}`,
|
||||
}],
|
||||
});
|
||||
const content = response.content[0].text.trim();
|
||||
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
||||
if (!jsonMatch) throw new Error('Claude hat kein gültiges JSON zurückgegeben');
|
||||
return JSON.parse(jsonMatch[0]);
|
||||
};
|
||||
|
||||
// ── Upload + Analysieren ──────────────────────────────────────────────────────
|
||||
const uploadReport = [
|
||||
upload.single('pdf'),
|
||||
async (req, res) => {
|
||||
if (!req.file) return res.status(400).json({ error: 'Keine PDF-Datei' });
|
||||
try {
|
||||
const pdfBuffer = fs.readFileSync(req.file.path);
|
||||
const pdfData = await pdfParse(pdfBuffer);
|
||||
const text = pdfData.text;
|
||||
|
||||
if (text.length < 100) {
|
||||
fs.unlinkSync(req.file.path);
|
||||
return res.status(400).json({ error: 'PDF konnte nicht gelesen werden oder ist leer' });
|
||||
}
|
||||
|
||||
const analysis = await analyzeWithClaude(text);
|
||||
|
||||
const db = getDatabase();
|
||||
const result = db.prepare(`
|
||||
INSERT INTO security_reports
|
||||
(filename, original_filename, report_period, risk_level, summary_json, created_by_user_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
req.file.filename,
|
||||
req.file.originalname,
|
||||
analysis.report_period || 'Unbekannt',
|
||||
analysis.risk_level || 'unbekannt',
|
||||
JSON.stringify(analysis),
|
||||
req.user.id
|
||||
);
|
||||
|
||||
const report = db.prepare('SELECT * FROM security_reports WHERE id = ?').get(result.lastInsertRowid);
|
||||
res.status(201).json({ ...report, analysis });
|
||||
} catch (e) {
|
||||
if (req.file?.path && fs.existsSync(req.file.path)) fs.unlinkSync(req.file.path);
|
||||
console.error('[SecurityReport] Upload error:', e.message);
|
||||
res.status(500).json({ error: `Analyse fehlgeschlagen: ${e.message}` });
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// ── Alle Reports ──────────────────────────────────────────────────────────────
|
||||
const getAllReports = (req, res) => {
|
||||
const db = getDatabase();
|
||||
const rows = db.prepare(`
|
||||
SELECT sr.*, u.username as created_by_username
|
||||
FROM security_reports sr
|
||||
LEFT JOIN users u ON sr.created_by_user_id = u.id
|
||||
ORDER BY sr.created_at DESC
|
||||
`).all();
|
||||
res.json(rows.map(r => ({ ...r, analysis: JSON.parse(r.summary_json || '{}') })));
|
||||
};
|
||||
|
||||
// ── Einzelner Report ──────────────────────────────────────────────────────────
|
||||
const getReport = (req, res) => {
|
||||
const db = getDatabase();
|
||||
const report = db.prepare('SELECT * FROM security_reports WHERE id = ?').get(req.params.id);
|
||||
if (!report) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||
res.json({ ...report, analysis: JSON.parse(report.summary_json || '{}') });
|
||||
};
|
||||
|
||||
// ── Report löschen ────────────────────────────────────────────────────────────
|
||||
const deleteReport = (req, res) => {
|
||||
const db = getDatabase();
|
||||
const report = db.prepare('SELECT * FROM security_reports WHERE id = ?').get(req.params.id);
|
||||
if (!report) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||
const filePath = path.join(UPLOAD_DIR, report.filename);
|
||||
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
||||
db.prepare('DELETE FROM security_reports WHERE id = ?').run(req.params.id);
|
||||
res.json({ success: true });
|
||||
};
|
||||
|
||||
module.exports = { uploadReport, getAllReports, getReport, deleteReport };
|
||||
Reference in New Issue
Block a user