Merge pull request #404 from f3rs3n/fix/lynis-removal-threshold-guidance

Clarify Lynis removal outcomes and printable/threshold guidance
This commit is contained in:
MacRimi
2026-09-29 07:25:49 +02:00
committed by GitHub
15 changed files with 485 additions and 75 deletions
@@ -0,0 +1,76 @@
// Execute the actual Security handlers against inert UI state and transport.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const vm = require('node:vm');
const path = require('node:path');
const root = path.resolve(__dirname, '../../../..');
const source = fs.readFileSync(path.join(root, 'AppImage/components/security.tsx'), 'utf8');
const catalogs = Object.fromEntries(['en','de','es','fr','it','pt','sk','sv'].map(lang => [lang, require(path.join(root, `AppImage/messages/${lang}/common.json`)).securityPage]));
const begin = source.indexOf(' const handleUninstallLynis = async () => {');
const end = source.indexOf('\n const loadFail2banDetails =', begin);
assert(begin !== -1 && end > begin);
const eraseTypes = text => text.replace(/fetchApi<\{[^}]+\}>/g, 'fetchApi')
.replace(/ as \{ partial\?: boolean \} \| undefined/g, '');
const uninstall = eraseTypes(source.slice(begin, end));
const anchor = source.indexOf('if (confirm(st("confirm.deleteAuditReport")))');
assert(anchor !== -1);
const clickBegin = source.lastIndexOf('onClick={(e) => {', anchor);
const clickEnd = source.indexOf('\n className=', anchor);
assert(clickBegin !== -1 && clickEnd > anchor);
const click = eraseTypes(source.slice(clickBegin + 'onClick={'.length, clickEnd).trim().replace(/}\s*$/, ''));
async function exercise(lang, action, reply, priorSuccess = '') {
const state = {report: {id: 'inert'}, expanded: true, success: priorSuccess, error: '', loads: 0, calls: []};
const st = (key) => key.split('.').reduce((v, k) => v?.[k], catalogs[lang]);
const ctx = {
st, Error, confirm: () => true,
setLynisReport: v => {state.report = v}, setLynisShowReport: v => {state.expanded = v},
setSuccess: v => {state.success = v}, setError: v => {state.error = v},
setUninstallingLynis: () => {}, setShowLynisUninstallConfirm: () => {},
loadSecurityTools: () => {state.loads++},
fetchApi: (endpoint, options) => {
assert.equal(endpoint, action === 'delete' ? '/api/security/lynis/report' : '/api/security/lynis/uninstall');
assert.equal(options.method, action === 'delete' ? 'DELETE' : 'POST');
state.calls.push(options.method);
return reply instanceof Error ? Promise.reject(reply) : Promise.resolve(reply);
},
};
const fn = vm.runInNewContext(action === 'delete' ? `(${click})` : `${uninstall}; handleUninstallLynis`, ctx);
await fn({stopPropagation() {}});
await new Promise(resolve => setImmediate(resolve));
return state;
}
(async () => {
for (const lang of Object.keys(catalogs)) {
for (const action of ['delete', 'uninstall']) {
const success = await exercise(lang, action, {success:true, message:'inert'});
assert.equal(success.report, null, `${lang} ${action} clean report`);
assert(success.success, `${lang} ${action} clean success`);
assert.equal(success.error, '', `${lang} ${action} clean no error`);
assert.equal(success.loads, 1);
for (const [kind, reply] of [
['no_files', {success:false,outcome:'no_files',message:'No report files found to delete'}],
['malformed_success', {success:'false',message:'inert malformed response'}],
['first_failure', {success:false,partial:false,message:'inert denied'}],
['partial_failure', {success:false,partial:true,message:'inert denied'}],
['http_failure', Object.assign(new Error('inert denied'), {body:{success:false,partial:true,message:'inert denied'}})],
['nonjson_failure', new Error('Invalid JSON response')],
['network_failure', new Error('network failure')],
]) {
const state = await exercise(lang, action, reply);
assert.notEqual(state.report, null, `${lang} ${action} ${kind}: report retained`);
assert.equal(state.success, '', `${lang} ${action} ${kind}: no success`);
assert.equal(state.loads, 0, `${lang} ${action} ${kind}: no success refresh`);
assert(state.error, `${lang} ${action} ${kind}: error visible`);
const expectedKey = kind === 'no_files' ? (action === 'delete' ? 'deleteReportNoFiles' : 'lynisUninstallNoFiles')
: kind === 'partial_failure' || kind === 'http_failure' ? 'lynisRemovalPartial'
: 'lynisRemovalUnconfirmed';
assert.equal(state.error, catalogs[lang].errors[expectedKey], `${lang} ${action} ${kind}: native whole-message feedback`);
if (action === 'delete' && kind === 'no_files') {
const stale = await exercise(lang, action, reply, 'Earlier unrelated success');
assert.equal(stale.success, '', `${lang} delete clears stale success before failure`);
}
}
}
}
console.log('actual Lynis handlers: eight locales, clean/no-files/pre-first/partial/HTTP/non-JSON/network state');
})().catch(err => { console.error(err); process.exitCode = 1 });
@@ -0,0 +1,57 @@
// Execute the actual printable-report generator, without mounting the app.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const vm = require('node:vm');
const path = require('node:path');
const root = path.resolve(__dirname, '../../../..');
const source = fs.readFileSync(path.join(root, 'AppImage/components/security.tsx'), 'utf8');
const begin = source.indexOf(' const generatePrintableReport = (report: LynisReport) => {');
const end = source.indexOf('\n const loadSslStatus =', begin);
assert(begin > 0 && end > begin, 'print consumer not found');
const body = source.slice(begin, end)
.replace('(report: LynisReport)', '(report)')
.replace('(raw: unknown): string', '(raw)');
const lang = process.env.LYNIS_FIXTURE_LANG || 'en';
assert(['en','de','es','fr','it','pt','sk','sv'].includes(lang));
const catalog = require(path.join(root, `AppImage/messages/${lang}/common.json`));
const translate = (key, params = {}) => {
const value = key.split('.').reduce((v, k) => v?.[k], catalog.securityPage);
assert.equal(typeof value, 'string', `missing ${lang} key ${key}`);
return value.replace(/\{(\w+)\}/g, (_, k) => String(params[k]));
};
const context = {
st: translate,
window: { location: { origin: 'https://offline.invalid' } },
document: { documentElement: { lang } },
getLynisScoreState: () => ({rawScore: 80, displayScore: 80, reportComplete: true, hasAdjustment: false}),
getActionableCount: (total, expected) => Math.max(0, total - expected),
lynisCountText: (key, count) => translate(`lynis.counts.${key}.${count === 1 ? 'one' : 'many'}`, { count }),
};
const render = vm.runInNewContext(`${body}\ngeneratePrintableReport`, context);
const report = {
hostname: 'inert', os_name: 'Linux', os_version: '', os_fullname: 'Linux',
kernel_version: 'test', lynis_version: 'test', datetime_start: '2026-01-01',
hardening_index: 80, tests_performed: 1,
warnings: [
{test_id: 'EXPECTED-W', description: 'expected warning', solution: '', proxmox_expected: true},
{test_id: 'ACTION-W', description: 'actionable warning', solution: '', proxmox_expected: false},
],
suggestions: [
{test_id: 'EXPECTED-S', description: 'expected suggestion', solution: '', proxmox_expected: true},
{test_id: 'ACTION-S', description: 'actionable suggestion', solution: '', proxmox_expected: false},
],
proxmox_expected_warnings: 1, proxmox_expected_suggestions: 1, sections: [],
};
const html = render(report);
if (process.env.LYNIS_FIXTURE_HTML) fs.writeFileSync(process.env.LYNIS_FIXTURE_HTML, html);
for (const id of ['EXPECTED-W', 'ACTION-W', 'EXPECTED-S', 'ACTION-S']) {
assert(html.includes(id), `missing actual finding ${id}`);
}
assert.match(html, /finding f-pve[\s\S]*?EXPECTED-W[\s\S]*?f-tag-pve/);
assert.match(html, /finding f-pve[\s\S]*?EXPECTED-S[\s\S]*?f-tag-pve/);
for (const key of ['lynis.report.warningsDescription', 'lynis.report.suggestionsDescription']) {
const description = translate(key);
assert(!/hidden|nascosti|ocult|caché|skryt|dold|versteckt/i.test(description));
assert(html.includes(description), `missing rendered description ${key}`);
}
console.log(`print HTML (${lang}): expected/actionable findings shown and badged`);
@@ -0,0 +1,78 @@
"""Inert branch tests for the real Lynis deletion/uninstall producers."""
import ast
import os
import subprocess
import unittest
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[3]
def extracted(path, name, namespace):
tree = ast.parse((ROOT / path).read_text())
fn = next(x for x in tree.body if isinstance(x, ast.FunctionDef) and x.name == name)
fn.decorator_list = []
exec(compile(ast.fix_missing_locations(ast.Module(body=[fn], type_ignores=[])), str(path), 'exec'), namespace)
return namespace[name]
class LynisFeedbackProducer(unittest.TestCase):
def test_actual_ui_feedback_handlers_all_shipped_locales(self):
result = subprocess.run(['node', str(ROOT / '.github/scripts/tests/fixtures/lynis_feedback_contract.cjs')],
cwd=ROOT, capture_output=True, text=True, env=os.environ.copy())
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
def test_uninstall_nothing_to_remove_is_not_reported_as_uninstalled(self):
fake_os = SimpleNamespace(path=SimpleNamespace(exists=lambda p: False, join=lambda *p: '/'.join(p)), remove=lambda p: self.fail(p))
result = extracted('AppImage/scripts/security_manager.py', 'uninstall_lynis', {'os': fake_os})()
self.assertFalse(result[0])
self.assertEqual(result[3], 'no_files')
manager = SimpleNamespace(uninstall_lynis=lambda: result)
route = extracted('AppImage/scripts/flask_security_routes.py', 'lynis_uninstall', {'security_manager': manager, 'jsonify': lambda data: data})
self.assertEqual(route()['outcome'], 'no_files')
def test_report_outcomes(self):
paths = ['/var/log/lynis-report.dat', '/var/log/lynis.log', '/var/log/lynis-output.log']
for fail_at in (None, 0, 1, 'missing'):
with self.subTest(fail_at=fail_at):
removed = []
def remove(p):
if fail_at in (0, 1) and p == paths[fail_at]:
raise PermissionError('inert denial')
removed.append(p)
fake_os = SimpleNamespace(path=SimpleNamespace(isfile=lambda p: fail_at != 'missing'), remove=remove)
ns = {'security_manager': object(), 'jsonify': lambda data: data}
orig_import = __import__
with patch('builtins.__import__', side_effect=lambda n, *a, **kw: fake_os if n == 'os' else orig_import(n, *a, **kw)):
result = extracted('AppImage/scripts/flask_security_routes.py', 'lynis_report_delete', ns)()
body, status = result if isinstance(result, tuple) else (result, 200)
self.assertEqual(removed, paths[:fail_at] if fail_at in (0, 1) else ([] if fail_at == 'missing' else paths))
self.assertEqual(body['success'], fail_at is None)
if fail_at == 'missing':
self.assertEqual(body['outcome'], 'no_files')
elif fail_at in (0, 1):
self.assertEqual(status, 500)
self.assertEqual(body['partial'], fail_at == 1)
self.assertIn('inert denial', body['message'])
def test_uninstall_outcomes_and_route(self):
targets = ['/opt/lynis', '/usr/local/bin/lynis', '/var/log/lynis-report.dat', '/var/log/lynis.log', '/var/log/lynis-output.log']
for fail_at in (None, 0, 2):
with self.subTest(fail_at=fail_at):
removed = []
def remove(path):
if fail_at is not None and path == targets[fail_at]: raise PermissionError('inert denial')
removed.append(path)
fake_os = SimpleNamespace(path=SimpleNamespace(exists=lambda p: p in targets, join=lambda *parts: '/'.join(parts)), remove=remove)
fake_shutil = SimpleNamespace(rmtree=remove)
orig_import = __import__
with patch('builtins.__import__', side_effect=lambda n, *a, **kw: fake_shutil if n == 'shutil' else orig_import(n, *a, **kw)):
result = extracted('AppImage/scripts/security_manager.py', 'uninstall_lynis', {'os': fake_os})()
self.assertEqual(removed, targets[:fail_at] if fail_at is not None else targets)
self.assertEqual(result[0], fail_at is None)
self.assertEqual(result[2], fail_at == 2)
manager = SimpleNamespace(uninstall_lynis=lambda: result)
route = extracted('AppImage/scripts/flask_security_routes.py', 'lynis_uninstall', {'security_manager': manager, 'jsonify': lambda data: data})
self.assertEqual(route()['partial'], fail_at == 2)
if __name__ == '__main__': unittest.main()
@@ -0,0 +1,148 @@
"""Inert source-contract checks; never import operational modules or touch host paths."""
import ast
import json
import os
import subprocess
import unittest
from collections import defaultdict
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[3]
APP = ROOT / 'AppImage'
def extract_function(path, name, *, owner=None):
tree = ast.parse(path.read_text())
container = next(n for n in tree.body if isinstance(n, ast.ClassDef) and n.name == owner) if owner else tree
node = next(n for n in container.body if isinstance(n, ast.FunctionDef) and n.name == name)
node.decorator_list = []
namespace = {}
return node, ast.fix_missing_locations(ast.Module(body=[node], type_ignores=[]))
def leaf(pointer):
data = json.loads((APP / 'messages/en/common.json').read_text())
for segment in pointer.split('.'):
data = data[segment]
return data
class LynisThresholdContracts(unittest.TestCase):
def test_delete_confirmation_names_all_three_actual_targets(self):
node, module = extract_function(APP / 'scripts/flask_security_routes.py', 'lynis_report_delete')
removed = []
fake_os = SimpleNamespace(path=SimpleNamespace(isfile=lambda p: True), remove=removed.append)
original_import = __import__
def fake_import(name, *args, **kwargs):
if name == 'os':
return fake_os
return original_import(name, *args, **kwargs)
namespace = {'security_manager': object(), 'jsonify': lambda x: x}
with patch('builtins.__import__', side_effect=fake_import):
exec(compile(module, str(APP / 'scripts/flask_security_routes.py'), 'exec'), namespace)
result = namespace['lynis_report_delete']()
self.assertEqual(result['success'], True)
self.assertEqual(removed, ['/var/log/lynis-report.dat', '/var/log/lynis.log', '/var/log/lynis-output.log'])
text = leaf('securityPage.confirm.deleteAuditReport').lower()
self.assertIn('report', text)
self.assertIn('logs', text)
self.assertNotIn('this audit report?', text)
def test_delete_route_partial_failure_is_not_reported_as_full_success(self):
_, module = extract_function(APP / 'scripts/flask_security_routes.py', 'lynis_report_delete')
removed = []
def remove(path):
if path.endswith('lynis.log'):
raise PermissionError('inert denial')
removed.append(path)
fake_os = SimpleNamespace(path=SimpleNamespace(isfile=lambda p: True), remove=remove)
original_import = __import__
def fake_import(name, *args, **kwargs):
return fake_os if name == 'os' else original_import(name, *args, **kwargs)
namespace = {'security_manager': object(), 'jsonify': lambda x: x}
with patch('builtins.__import__', side_effect=fake_import):
exec(compile(module, '<inert lynis route>', 'exec'), namespace)
result, status = namespace['lynis_report_delete']()
self.assertEqual(removed, ['/var/log/lynis-report.dat'])
self.assertEqual(status, 500)
self.assertFalse(result['success'])
self.assertIn('inert denial', result['message'])
def test_delete_route_with_no_files_returns_success_false_not_a_clean_result(self):
_, module = extract_function(APP / 'scripts/flask_security_routes.py', 'lynis_report_delete')
fake_os = SimpleNamespace(path=SimpleNamespace(isfile=lambda p: False), remove=lambda p: self.fail(p))
original_import = __import__
def fake_import(name, *args, **kwargs):
return fake_os if name == 'os' else original_import(name, *args, **kwargs)
namespace = {'security_manager': object(), 'jsonify': lambda x: x}
with patch('builtins.__import__', side_effect=fake_import):
exec(compile(module, '<inert lynis route>', 'exec'), namespace)
result = namespace['lynis_report_delete']()
self.assertFalse(result['success'])
self.assertIn('No report files', result['message'])
def test_uninstall_notice_describes_existing_cleanup_targets(self):
_, module = extract_function(APP / 'scripts/security_manager.py', 'uninstall_lynis')
removed = []
fake_os = SimpleNamespace(path=SimpleNamespace(exists=lambda p: p != '/usr/local/share/proxmenux/components_status.json', join=lambda *s: '/'.join(s)), remove=removed.append)
class FakeShutil:
@staticmethod
def rmtree(path):
removed.append(path)
original_import = __import__
def fake_import(name, *args, **kwargs):
return FakeShutil if name == 'shutil' else original_import(name, *args, **kwargs)
namespace = {'os': fake_os}
with patch('builtins.__import__', side_effect=fake_import):
exec(compile(module, '<inert lynis uninstall>', 'exec'), namespace)
result = namespace['uninstall_lynis']()
self.assertTrue(result[0])
self.assertEqual(removed, ['/opt/lynis', '/usr/local/bin/lynis', '/var/log/lynis-report.dat', '/var/log/lynis.log', '/var/log/lynis-output.log'])
for key in ('securityPage.lynis.removeReports', 'securityPage.lynis.uninstallConfirmDescription'):
text = leaf(key).lower()
self.assertIn('report', text)
self.assertIn('logs', text)
self.assertNotIn('monitor-created', text)
def test_swap_hint_qualifies_sampling_and_independent_ram_alarm(self):
_, module = extract_function(APP / 'scripts/health_monitor.py', '_check_memory_comprehensive', owner='HealthMonitor')
# The method is bound to an inert class, not the production constructor.
self_obj = SimpleNamespace(state_history=defaultdict(list), MEMORY_DURATION=300, SWAP_CRITICAL_DURATION=300,
MEMORY_WARNING=80, SWAP_HIGH_PERCENT=80, AVAILABLE_MIN_PERCENT=20)
memory = SimpleNamespace(percent=50, available=10, total=100)
swap = SimpleNamespace(percent=90, used=90, total=100)
namespace = {'Dict': dict, 'Any': object, 'psutil': SimpleNamespace(virtual_memory=lambda: memory, swap_memory=lambda: swap),
'time': SimpleNamespace(time=lambda: 1000)}
exec(compile(module, '<inert memory check>', 'exec'), namespace)
first = namespace['_check_memory_comprehensive'](self_obj)
second = namespace['_check_memory_comprehensive'](self_obj)
self.assertEqual(first['status'], 'OK')
self.assertEqual(second['checks']['swap_usage']['status'], 'CRITICAL')
self_obj.state_history.clear()
memory.percent = 91
swap.percent = 0
for _ in range(8):
last = namespace['_check_memory_comprehensive'](self_obj)
self.assertEqual(last['status'], 'CRITICAL')
self.assertEqual(last['checks']['swap_usage']['status'], 'OK')
hint = leaf('settings.healthThresholds.swapPressureHint').lower()
self.assertIn('swap usage', hint)
self.assertIn('repeated', hint)
self.assertIn('ram', hint)
self.assertNotIn('swap file', hint)
self.assertNotIn('swap file', leaf('settings.healthThresholds.swapHighLabel').lower())
self.assertNotIn('only when both', hint)
def test_print_generator_and_descriptions(self):
node = ROOT / '.github/scripts/tests/fixtures/lynis_print_contract.cjs'
for lang in ('en', 'de', 'es', 'fr', 'it', 'pt', 'sk', 'sv'):
result = subprocess.run(['node', str(node)], cwd=ROOT, text=True, capture_output=True,
env={**os.environ, 'LYNIS_FIXTURE_LANG': lang})
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertIn(f'print HTML ({lang}):', result.stdout)
if __name__ == '__main__':
unittest.main()
+23 -11
View File
@@ -382,18 +382,19 @@ export function Security() {
setSuccess("")
setShowLynisUninstallConfirm(false)
try {
const data = await fetchApi("/api/security/lynis/uninstall", {
const data = await fetchApi<{ success: boolean; partial?: boolean; outcome?: string }>("/api/security/lynis/uninstall", {
method: "POST",
})
if (data.success) {
if (data.success === true) {
setSuccess(st("messages.lynisUninstalled"))
loadSecurityTools()
setLynisReport(null)
} else {
setError(data.message || st("errors.lynisUninstallFailed"))
setError(data.outcome === "no_files" ? st("errors.lynisUninstallNoFiles") : data.partial ? st("errors.lynisRemovalPartial") : st("errors.lynisRemovalUnconfirmed"))
}
} catch (err) {
setError(err instanceof Error ? err.message : st("errors.lynisUninstallFailed"))
const body = err instanceof Error && "body" in err ? err.body as { partial?: boolean } | undefined : undefined
setError(body?.partial ? st("errors.lynisRemovalPartial") : st("errors.lynisRemovalUnconfirmed"))
} finally {
setUninstallingLynis(false)
}
@@ -4377,14 +4378,25 @@ ${(report.sections && report.sections.length > 0) ? `
onClick={(e) => {
e.stopPropagation()
if (confirm(st("confirm.deleteAuditReport"))) {
fetchApi("/api/security/lynis/report", { method: "DELETE" })
.then(() => {
setLynisReport(null)
setLynisShowReport(false)
setSuccess(st("messages.reportDeleted"))
loadSecurityTools()
setError("")
setSuccess("")
fetchApi<{ success: boolean; outcome?: string; partial?: boolean }>("/api/security/lynis/report", { method: "DELETE" })
.then((data) => {
if (data.success === true) {
setLynisReport(null)
setLynisShowReport(false)
setSuccess(st("messages.reportDeleted"))
loadSecurityTools()
} else if (data.outcome === "no_files") {
setError(st("errors.deleteReportNoFiles"))
} else {
setError(data.partial ? st("errors.lynisRemovalPartial") : st("errors.lynisRemovalUnconfirmed"))
}
})
.catch((err) => {
const body = err instanceof Error && "body" in err ? err.body as { partial?: boolean } | undefined : undefined
setError(body?.partial ? st("errors.lynisRemovalPartial") : st("errors.lynisRemovalUnconfirmed"))
})
.catch(() => setError(st("errors.deleteReportFailed")))
}
}}
className="h-7 px-2 text-xs text-red-500 hover:text-red-400 hover:bg-red-500/10 ml-2 sm:ml-0"
+11 -7
View File
@@ -2251,8 +2251,8 @@
}
},
"swapPressure": "Speicherdruck",
"swapPressureHint": "Markiert ein kritisches Ereignis nur, wenn BEIDE Bedingungen gleichzeitig zutreffen — Swap-Nutzung über dem ersten Schwellenwert UND verfügbarer RAM unter dem zweiten. Alarmierung nur wegen Swap war auf Proxmox-Hosts laut, da Linux inaktive Seiten auslagert, während der RAM reichlich verfügbar bleibt.",
"swapHighLabel": "Swap-Datei-Nutzung",
"swapPressureHint": "Diese Prüfung des Swap-Drucks markiert nach wiederholten Messungen ein kritisches Ereignis, wenn die gesamte Swap-Nutzung den ersten Schwellenwert überschreitet und der verfügbare RAM unter den zweiten fällt. Anhaltend hohe RAM-Nutzung kann unabhängig davon ein weiteres kritisches Ereignis auslösen.",
"swapHighLabel": "Gesamte Swap-Nutzung",
"availableMinLabel": "Mindestens verfügbarer RAM"
},
"navOrder": {
@@ -2373,6 +2373,10 @@
"down": "Runter"
},
"errors": {
"deleteReportNoFiles": "Es wurden keine Lynis-Berichts- oder Protokolldateien zum Entfernen gefunden.",
"lynisUninstallNoFiles": "Es wurden keine Lynis-Installations-, Berichts- oder Protokolldateien zum Entfernen gefunden.",
"lynisRemovalPartial": "Das Entfernen wurde nicht abgeschlossen. Einige Lynis-Dateien wurden entfernt; prüfen Sie vor einem erneuten Versuch, was noch vorhanden ist.",
"lynisRemovalUnconfirmed": "Das Ergebnis der Lynis-Entfernung konnte nicht bestätigt werden. Einige Dateien könnten bereits entfernt worden sein; prüfen Sie dies vor einem erneuten Versuch.",
"passwordMinLength": "Das Passwort muss mindestens 10 Zeichen lang sein.",
"passwordComplexity": "Verwenden Sie mindestens drei davon: Kleinbuchstaben, Großbuchstaben, Zahlen, Symbole.",
"passwordCommon": "Dieses Passwort ist zu häufig. Wählen Sie etwas, das schwerer zu erraten ist.",
@@ -2448,7 +2452,7 @@
"disableAuth": "Authentifizierung deaktivieren? Jeder, der das Dashboard erreichen kann, kann es öffnen.",
"revokeToken": "Dieses API-Token widerrufen?",
"disableHttps": "HTTPS deaktivieren und zu einfachem HTTP zurückkehren?",
"deleteAuditReport": "Diesen Prüfbericht löschen?",
"deleteAuditReport": "Lynis-Prüfbericht und Protokolle löschen?",
"cannotBeUndone": "Dies kann nicht rückgängig gemacht werden."
},
"auth": {
@@ -2811,10 +2815,10 @@
"installationTitle": "Installieren Sie Lynis",
"installationDescription": "Das Terminal installiert Lynis und bereitet die Berichtsunterstützung vor.",
"uninstallConfirmTitle": "Lynis deinstallieren?",
"uninstallConfirmDescription": "Dadurch werden von Lynis und Monitor erstellte Prüfberichtsdateien entfernt.",
"uninstallConfirmDescription": "Lynis deinstallieren und den Prüfbericht sowie die Protokolle entfernen.",
"removeInstallation": "Entfernen Sie die Lynis-Installation",
"removeWrapper": "Entfernen Sie den Monitor Lynis-Wrapper",
"removeReports": "Entfernen Sie gespeicherte Prüfberichte",
"removeReports": "Lynis-Prüfbericht und Protokolle entfernen",
"counts": {
"tests": {
"one": "{count}-Test",
@@ -2888,10 +2892,10 @@
"proxmoxScoreWithLabel": "Proxmox-Score ({label})",
"actionableWarningsLabel": "Umsetzbare Warnungen",
"actionableSuggestionsLabel": "Umsetzbare Vorschläge",
"warningsDescription": "Warnungen, die es wert sind, zuerst gelesen zu werden. Die von Proxmox erwarteten Erkenntnisse sind hier versteckt.",
"warningsDescription": "Warnungen, die zuerst geprüft werden sollten. Auf Proxmox erwartete Befunde werden mit einer Kennzeichnung angezeigt.",
"noWarningsDetected": "Es wurden keine umsetzbaren Warnungen erkannt.",
"recommendation": "Empfehlung",
"suggestionsDescription": "Härtungsvorschläge mit niedrigerer Priorität. Auch hier verbergen sich die von Proxmox erwarteten Erkenntnisse.",
"suggestionsDescription": "Weniger dringende Vorschläge zur Härtung. Auf Proxmox erwartete Befunde werden ebenfalls mit einer Kennzeichnung angezeigt.",
"incompleteDescription": "Lynis hat einen unvollständigen Bericht ohne Ergebnis oder Testanzahl erstellt. Führen Sie die Prüfung erneut durch, um ein zuverlässiges Ergebnis anzuzeigen.",
"detailedChecks": "Detaillierte Kontrollen",
"categoriesCount": "{count} Kategorien",
+11 -7
View File
@@ -2250,8 +2250,8 @@
}
},
"swapPressure": "Swap pressure",
"swapPressureHint": "Marks a critical event only when BOTH conditions hold at the same time — swap file usage above the first threshold AND available RAM below the second. Alerting on swap alone is noisy on Proxmox hosts where Linux swaps out inactive pages while RAM is still plentifully available.",
"swapHighLabel": "Swap file usage",
"swapPressureHint": "This swap-pressure check marks a critical event after repeated samples where overall swap usage exceeds the first threshold and available RAM falls below the second. Sustained high RAM usage can also trigger a separate critical event.",
"swapHighLabel": "Overall swap usage",
"availableMinLabel": "Minimum available RAM"
},
"navOrder": {
@@ -2372,6 +2372,10 @@
"down": "Down"
},
"errors": {
"deleteReportNoFiles": "No Lynis report or log files were found to remove.",
"lynisUninstallNoFiles": "No Lynis installation, report or log files were found to remove.",
"lynisRemovalPartial": "Removal did not finish. Some Lynis files were removed; check what remains before trying again.",
"lynisRemovalUnconfirmed": "Could not confirm the Lynis removal result. Some files may already have been removed; check before trying again.",
"passwordMinLength": "Password must be at least 10 characters long.",
"passwordComplexity": "Use at least three of these: lowercase, uppercase, number, symbol.",
"passwordCommon": "This password is too common. Choose something harder to guess.",
@@ -2447,7 +2451,7 @@
"disableAuth": "Disable authentication? Anyone who can reach the dashboard will be able to open it.",
"revokeToken": "Revoke this API token?",
"disableHttps": "Disable HTTPS and go back to plain HTTP?",
"deleteAuditReport": "Delete this audit report?",
"deleteAuditReport": "Delete the Lynis audit report and logs?",
"cannotBeUndone": "This cannot be undone."
},
"auth": {
@@ -2810,10 +2814,10 @@
"installationTitle": "Install Lynis",
"installationDescription": "The terminal will install Lynis and prepare report support.",
"uninstallConfirmTitle": "Uninstall Lynis?",
"uninstallConfirmDescription": "This removes Lynis and Monitor-created audit report files.",
"uninstallConfirmDescription": "Uninstall Lynis and remove its audit report and logs.",
"removeInstallation": "Remove Lynis installation",
"removeWrapper": "Remove Monitor Lynis wrapper",
"removeReports": "Remove saved audit reports",
"removeReports": "Remove Lynis audit report and logs",
"counts": {
"tests": {
"one": "{count} test",
@@ -2887,10 +2891,10 @@
"proxmoxScoreWithLabel": "Proxmox score ({label})",
"actionableWarningsLabel": "Actionable warnings",
"actionableSuggestionsLabel": "Actionable suggestions",
"warningsDescription": "Warnings worth reviewing first. Proxmox-expected findings are hidden here.",
"warningsDescription": "Warnings worth reviewing first. Proxmox-expected findings are shown with a badge.",
"noWarningsDetected": "No actionable warnings were detected.",
"recommendation": "Recommendation",
"suggestionsDescription": "Lower-priority hardening suggestions. Proxmox-expected findings are hidden here too.",
"suggestionsDescription": "Lower-priority hardening suggestions. Proxmox-expected findings are shown with a badge too.",
"incompleteDescription": "Lynis created an incomplete report without a score or test count. Run the audit again to show a reliable result.",
"detailedChecks": "Detailed checks",
"categoriesCount": "{count} categories",
+11 -7
View File
@@ -2251,8 +2251,8 @@
}
},
"swapPressure": "Presión de memoria",
"swapPressureHint": "Marca un evento crítico solo cuando se cumplen AMBAS condiciones a la vez: uso del archivo de swap por encima del primer umbral Y RAM disponible por debajo del segundo. Alertar solo por swap era ruidoso en Proxmox porque Linux swapea páginas inactivas mientras la RAM sigue disponible.",
"swapHighLabel": "Uso del archivo de swap",
"swapPressureHint": "Esta comprobación de presión de swap marca un evento crítico tras muestras repetidas en las que el uso total de swap supera el primer umbral y la RAM disponible cae por debajo del segundo. Un uso de RAM elevado y sostenido también puede activar otro evento crítico por separado.",
"swapHighLabel": "Uso total de swap",
"availableMinLabel": "RAM disponible mínima"
},
"navOrder": {
@@ -2373,6 +2373,10 @@
"down": "Abajo"
},
"errors": {
"deleteReportNoFiles": "No se encontraron archivos de informe ni registros de Lynis para eliminar.",
"lynisUninstallNoFiles": "No se encontraron archivos de instalación, informe ni registros de Lynis para eliminar.",
"lynisRemovalPartial": "La eliminación no terminó. Se eliminaron algunos archivos de Lynis; comprueba lo que queda antes de intentarlo de nuevo.",
"lynisRemovalUnconfirmed": "No se pudo confirmar el resultado de la eliminación de Lynis. Puede que ya se hayan eliminado algunos archivos; compruébalo antes de intentarlo de nuevo.",
"passwordMinLength": "La contraseña debe tener al menos 10 caracteres.",
"passwordComplexity": "Utilice al menos tres de estos: minúscula, mayúscula, número y símbolo.",
"passwordCommon": "Esta contraseña es demasiado común. Elija algo más difícil de adivinar.",
@@ -2448,7 +2452,7 @@
"disableAuth": "¿Desactivar la autenticación? Cualquiera que pueda alcanzar el tablero podrá abrirlo.",
"revokeToken": "¿Revocar este token API?",
"disableHttps": "¿Desactivar HTTPS y volver a HTTP simple?",
"deleteAuditReport": "¿Eliminar este informe de auditoría?",
"deleteAuditReport": "¿Eliminar el informe de auditoría y los registros de Lynis?",
"cannotBeUndone": "Esto no se puede deshacer."
},
"auth": {
@@ -2811,10 +2815,10 @@
"installationTitle": "Instalar Lynis",
"installationDescription": "La terminal instalará Lynis y preparará el informe de soporte.",
"uninstallConfirmTitle": "¿Desinstalar Lynis?",
"uninstallConfirmDescription": "Esto elimina los archivos de informes de auditoría creados por Lynis y Monitor.",
"uninstallConfirmDescription": "Desinstalar Lynis y eliminar su informe de auditoría y sus registros.",
"removeInstallation": "Quitar la instalación de Lynis",
"removeWrapper": "Eliminar el contenedor Monitor Lynis",
"removeReports": "Eliminar informes de auditoría guardados",
"removeReports": "Eliminar el informe de auditoría y los registros de Lynis",
"counts": {
"tests": {
"one": "{count} prueba",
@@ -2888,10 +2892,10 @@
"proxmoxScoreWithLabel": "Puntuación de Proxmox ({label})",
"actionableWarningsLabel": "Advertencias procesables",
"actionableSuggestionsLabel": "Sugerencias procesables",
"warningsDescription": "Advertencias que vale la pena revisar primero. Los hallazgos esperados por Proxmox están ocultos aquí.",
"warningsDescription": "Advertencias que conviene revisar primero. Los hallazgos esperados en Proxmox se muestran con una etiqueta.",
"noWarningsDetected": "No se detectaron advertencias procesables.",
"recommendation": "Recomendación",
"suggestionsDescription": "Sugerencias de endurecimiento de menor prioridad. Los hallazgos esperados por Proxmox también están ocultos aquí.",
"suggestionsDescription": "Sugerencias de refuerzo de menor prioridad. Los hallazgos esperados en Proxmox también se muestran con una etiqueta.",
"incompleteDescription": "Lynis creó un informe incompleto sin puntuación ni recuento de pruebas. Ejecute la auditoría nuevamente para mostrar un resultado confiable.",
"detailedChecks": "Controles detallados",
"categoriesCount": "{count} categorías",
+11 -7
View File
@@ -2251,8 +2251,8 @@
}
},
"swapPressure": "Pression mémoire",
"swapPressureHint": "Marque un événement critique uniquement quand LES DEUX conditions sont vraies en même temps — utilisation du fichier swap au-dessus du premier seuil ET RAM disponible en-dessous du second. Alerter uniquement sur le swap était bruyant sur les hôtes Proxmox où Linux échange des pages inactives alors que la RAM reste abondamment disponible.",
"swapHighLabel": "Utilisation du fichier swap",
"swapPressureHint": "Ce contrôle de la pression sur le swap signale un événement critique après plusieurs mesures où l’utilisation totale du swap dépasse le premier seuil et la RAM disponible passe sous le second. Une utilisation de la RAM élevée et persistante peut aussi déclencher un événement critique distinct.",
"swapHighLabel": "Utilisation totale du swap",
"availableMinLabel": "RAM disponible minimale"
},
"navOrder": {
@@ -2373,6 +2373,10 @@
"down": "Vers le bas"
},
"errors": {
"deleteReportNoFiles": "Aucun rapport ni journal Lynis à supprimer n’a été trouvé.",
"lynisUninstallNoFiles": "Aucun fichier d’installation, rapport ou journal Lynis à supprimer n’a été trouvé.",
"lynisRemovalPartial": "La suppression ne s’est pas terminée. Certains fichiers Lynis ont été supprimés ; vérifiez ce qui reste avant de réessayer.",
"lynisRemovalUnconfirmed": "Le résultat de la suppression de Lynis n’a pas pu être confirmé. Certains fichiers ont peut-être déjà été supprimés ; vérifiez avant de réessayer.",
"passwordMinLength": "Le mot de passe doit comporter au moins 10 caractères.",
"passwordComplexity": "Utilisez-en au moins trois : minuscules, majuscules, chiffres, symboles.",
"passwordCommon": "Ce mot de passe est trop courant. Choisissez quelque chose de plus difficile à deviner.",
@@ -2448,7 +2452,7 @@
"disableAuth": "Désactiver l'authentification ? Toute personne pouvant accéder au tableau de bord pourra l’ouvrir.",
"revokeToken": "Révoquer ce jeton API ?",
"disableHttps": "Désactiver HTTPS et revenir au HTTP simple ?",
"deleteAuditReport": "Supprimer ce rapport d'audit ?",
"deleteAuditReport": "Supprimer le rapport d’audit et les journaux Lynis ?",
"cannotBeUndone": "Cela ne peut pas être annulé."
},
"auth": {
@@ -2811,10 +2815,10 @@
"installationTitle": "Installer Lynis",
"installationDescription": "Le terminal installera Lynis et préparera le support des rapports.",
"uninstallConfirmTitle": "Désinstaller Lynis ?",
"uninstallConfirmDescription": "Cela supprime les fichiers de rapport d'audit créés par Lynis et Monitor.",
"uninstallConfirmDescription": "Désinstaller Lynis et supprimer son rapport d’audit ainsi que ses journaux.",
"removeInstallation": "Supprimer l'installation de Lynis",
"removeWrapper": "Supprimer le wrapper Monitor Lynis",
"removeReports": "Supprimer les rapports d'audit enregistrés",
"removeReports": "Supprimer le rapport d’audit et les journaux Lynis",
"counts": {
"tests": {
"one": "Test {count}",
@@ -2888,10 +2892,10 @@
"proxmoxScoreWithLabel": "Score Proxmox ({label})",
"actionableWarningsLabel": "Avertissements exploitables",
"actionableSuggestionsLabel": "Suggestions exploitables",
"warningsDescription": "Avertissements qui méritent d’être examinés en premier. Les résultats attendus de Proxmox sont cachés ici.",
"warningsDescription": "Avertissements à examiner en priorité. Les résultats attendus sur Proxmox sont affichés avec un badge.",
"noWarningsDetected": "Aucun avertissement exploitable n’a été détecté.",
"recommendation": "Recommandation",
"suggestionsDescription": "Suggestions de durcissement de moindre priorité. Les résultats attendus de Proxmox sont également cachés ici.",
"suggestionsDescription": "Suggestions de renforcement moins prioritaires. Les résultats attendus sur Proxmox sont aussi affichés avec un badge.",
"incompleteDescription": "Lynis a créé un rapport incomplet sans score ni décompte de tests. Réexécutez l’audit pour afficher un résultat fiable.",
"detailedChecks": "Vérifications détaillées",
"categoriesCount": "Catégories {count}",
+11 -7
View File
@@ -2251,8 +2251,8 @@
}
},
"swapPressure": "Pressione memoria",
"swapPressureHint": "Segnala un evento critico solo quando ENTRAMBE le condizioni sono vere contemporaneamente — utilizzo del file di swap sopra la prima soglia E RAM disponibile sotto la seconda. Allertare solo sullo swap era rumoroso sugli host Proxmox dove Linux scambia pagine inattive mentre la RAM resta abbondantemente disponibile.",
"swapHighLabel": "Utilizzo del file di swap",
"swapPressureHint": "Questo controllo della pressione sulla memoria segnala un evento critico dopo più campioni in cui l’utilizzo complessivo dello swap supera la prima soglia e la RAM disponibile scende sotto la seconda. Un utilizzo elevato e persistente della RAM può attivare separatamente un evento critico.",
"swapHighLabel": "Utilizzo complessivo dello swap",
"availableMinLabel": "RAM disponibile minima"
},
"navOrder": {
@@ -2373,6 +2373,10 @@
"down": "Giù"
},
"errors": {
"deleteReportNoFiles": "Nessun rapporto o log di Lynis da rimuovere è stato trovato.",
"lynisUninstallNoFiles": "Nessun file di installazione, rapporto o log di Lynis da rimuovere è stato trovato.",
"lynisRemovalPartial": "La rimozione non è stata completata. Alcuni file di Lynis sono stati rimossi; controlla quelli rimasti prima di riprovare.",
"lynisRemovalUnconfirmed": "Impossibile confermare l’esito della rimozione di Lynis. Alcuni file potrebbero essere già stati rimossi; controlla prima di riprovare.",
"passwordMinLength": "La password deve contenere almeno 10 caratteri.",
"passwordComplexity": "Usane almeno tre: minuscolo, maiuscolo, numero, simbolo.",
"passwordCommon": "Questa password è troppo comune. Scegli qualcosa di più difficile da indovinare.",
@@ -2448,7 +2452,7 @@
"disableAuth": "Disabilitare l'autenticazione? Chiunque riesca a raggiungere la dashboard potrà aprirla.",
"revokeToken": "Revocare questo token API?",
"disableHttps": "Disabilitare HTTPS e tornare al semplice HTTP?",
"deleteAuditReport": "Eliminare questo rapporto di controllo?",
"deleteAuditReport": "Eliminare il rapporto di controllo e i log di Lynis?",
"cannotBeUndone": "Questa operazione non può essere annullata."
},
"auth": {
@@ -2811,10 +2815,10 @@
"installationTitle": "Installa Lynis",
"installationDescription": "Il terminale installerà Lynis e preparerà il supporto dei report.",
"uninstallConfirmTitle": "Disinstallare Lynis?",
"uninstallConfirmDescription": "Ciò rimuove i file di rapporto di controllo creati da Lynis e Monitor.",
"uninstallConfirmDescription": "La disinstallazione rimuove Lynis, il rapporto di controllo e i relativi log.",
"removeInstallation": "Rimuovere l'installazione di Lynis",
"removeWrapper": "Rimuovere il wrapper di Monitor Lynis",
"removeReports": "Rimuovi i rapporti di controllo salvati",
"removeReports": "Rimuovi il rapporto di controllo e i log di Lynis",
"counts": {
"tests": {
"one": "{count}prova",
@@ -2888,10 +2892,10 @@
"proxmoxScoreWithLabel": "Punteggio Proxmox ({label})",
"actionableWarningsLabel": "Avvisi attuabili",
"actionableSuggestionsLabel": "Suggerimenti attuabili",
"warningsDescription": "Avvertenze che vale la pena rivedere prima. I risultati attesi da Proxmox sono nascosti qui.",
"warningsDescription": "Avvertenze da esaminare per prime. I risultati attesi su Proxmox sono mostrati con un'etichetta.",
"noWarningsDetected": "Non è stato rilevato alcun avviso utilizzabile.",
"recommendation": "Raccomandazione",
"suggestionsDescription": "Suggerimenti di rafforzamento con priorità inferiore. Anche i risultati attesi da Proxmox sono nascosti qui.",
"suggestionsDescription": "Suggerimenti di rafforzamento meno prioritari. Anche i risultati attesi su Proxmox sono mostrati con un'etichetta.",
"incompleteDescription": "Lynis ha creato un report incompleto senza punteggio o conteggio dei test. Eseguire nuovamente l'audit per mostrare un risultato affidabile.",
"detailedChecks": "Controlli dettagliati",
"categoriesCount": "{count} categorie",
+11 -7
View File
@@ -2251,8 +2251,8 @@
}
},
"swapPressure": "Pressão de memória",
"swapPressureHint": "Marca um evento crítico apenas quando AMBAS as condições forem verdadeiras ao mesmo tempo — uso do arquivo de swap acima do primeiro limite E RAM disponível abaixo do segundo. Alertar apenas por swap era ruidoso em hosts Proxmox onde o Linux troca páginas inativas enquanto a RAM permanece amplamente disponível.",
"swapHighLabel": "Uso do arquivo de swap",
"swapPressureHint": "Esta verificação da pressão de swap marca um evento crítico após amostras repetidas em que o uso total de swap ultrapassa o primeiro limite e a RAM disponível fica abaixo do segundo. O uso elevado e persistente de RAM também pode acionar outro evento crítico separadamente.",
"swapHighLabel": "Uso total de swap",
"availableMinLabel": "RAM disponível mínima"
},
"navOrder": {
@@ -2373,6 +2373,10 @@
"down": "Abaixo"
},
"errors": {
"deleteReportNoFiles": "Nenhum arquivo de relatório ou log do Lynis foi encontrado para remoção.",
"lynisUninstallNoFiles": "Nenhum arquivo de instalação, relatório ou log do Lynis foi encontrado para remoção.",
"lynisRemovalPartial": "A remoção não foi concluída. Alguns arquivos do Lynis foram removidos; verifique o que resta antes de tentar novamente.",
"lynisRemovalUnconfirmed": "Não foi possível confirmar o resultado da remoção do Lynis. Alguns arquivos podem já ter sido removidos; verifique antes de tentar novamente.",
"passwordMinLength": "A senha deve ter pelo menos 10 caracteres.",
"passwordComplexity": "Use pelo menos três destes: letras minúsculas, maiúsculas, número, símbolo.",
"passwordCommon": "Esta senha é muito comum. Escolha algo mais difícil de adivinhar.",
@@ -2448,7 +2452,7 @@
"disableAuth": "Desativar autenticação? Qualquer pessoa que conseguir acessar o painel poderá abri-lo.",
"revokeToken": "Revogar este token de API?",
"disableHttps": "Desativar HTTPS e voltar para HTTP simples?",
"deleteAuditReport": "Excluir este relatório de auditoria?",
"deleteAuditReport": "Excluir o relatório de auditoria e os logs do Lynis?",
"cannotBeUndone": "Isto não pode ser desfeito."
},
"auth": {
@@ -2811,10 +2815,10 @@
"installationTitle": "Instale o Lynis",
"installationDescription": "O terminal instalará o Lynis e preparará o suporte de relatório.",
"uninstallConfirmTitle": "Desinstalar Lynis?",
"uninstallConfirmDescription": "Isso remove os arquivos de relatório de auditoria criados pelo Lynis e pelo Monitor.",
"uninstallConfirmDescription": "Desinstalar o Lynis e remover seu relatório de auditoria e seus logs.",
"removeInstallation": "Remover instalação do Lynis",
"removeWrapper": "Remover o wrapper do Monitor Lynis",
"removeReports": "Remover relatórios de auditoria salvos",
"removeReports": "Remover o relatório de auditoria e os logs do Lynis",
"counts": {
"tests": {
"one": "Teste {count}",
@@ -2888,10 +2892,10 @@
"proxmoxScoreWithLabel": "Pontuação Proxmox ({label})",
"actionableWarningsLabel": "Avisos acionáveis",
"actionableSuggestionsLabel": "Sugestões acionáveis",
"warningsDescription": "Avisos que valem a pena revisar primeiro. As descobertas esperadas do Proxmox estão ocultas aqui.",
"warningsDescription": "Avisos que devem ser analisados primeiro. Os resultados esperados no Proxmox são exibidos com um marcador.",
"noWarningsDetected": "Nenhum aviso acionável foi detectado.",
"recommendation": "Recomendação",
"suggestionsDescription": "Sugestões de proteção de baixa prioridade. As descobertas esperadas do Proxmox também estão ocultas aqui.",
"suggestionsDescription": "Sugestões de reforço de menor prioridade. Os resultados esperados no Proxmox também são exibidos com um marcador.",
"incompleteDescription": "Lynis criou um relatório incompleto sem pontuação ou contagem de testes. Execute a auditoria novamente para mostrar um resultado confiável.",
"detailedChecks": "Verificações detalhadas",
"categoriesCount": "{count} categorias",
+11 -7
View File
@@ -2250,8 +2250,8 @@
}
},
"swapPressure": "Tlak na pamäť",
"swapPressureHint": "Kritickú udalosť označí len vtedy, keď sú splnené obe podmienky: využitie súboru swap je nad prvým limitom a dostupná RAM pod druhým. Upozorňovanie iba na swap bolo na serveroch Proxmox príliš hlučné, pretože Linux odsúva neaktívne stránky aj pri dostatočne dostupnej RAM.",
"swapHighLabel": "Využitie súboru swap",
"swapPressureHint": "Táto kontrola zaťaženia swapu označí kritickú udalosť po opakovaných meraniach, pri ktorých celkové využitie swapu prekročí prvý limit a dostupná RAM klesne pod druhý. Dlhodobo vysoké využitie RAM môže nezávisle spustiť ďalšiu kritickú udalosť.",
"swapHighLabel": "Celkové využitie swapu",
"availableMinLabel": "Minimálna dostupná RAM"
},
"navOrder": {
@@ -2372,6 +2372,10 @@
"down": "Nebeží"
},
"errors": {
"deleteReportNoFiles": "Nenašli sa žiadne súbory správy ani logov Lynis na odstránenie.",
"lynisUninstallNoFiles": "Nenašli sa žiadne inštalačné súbory, správy ani logy Lynis na odstránenie.",
"lynisRemovalPartial": "Odstránenie sa nedokončilo. Niektoré súbory Lynis boli odstránené; pred ďalším pokusom skontrolujte, čo zostalo.",
"lynisRemovalUnconfirmed": "Výsledok odstránenia Lynis sa nepodarilo potvrdiť. Niektoré súbory už mohli byť odstránené; pred ďalším pokusom to skontrolujte.",
"passwordMinLength": "Heslo musí mať aspoň 10 znakov.",
"passwordComplexity": "Použite aspoň tri z týchto možností: malé písmeno, veľké písmeno, číslo, symbol.",
"passwordCommon": "Toto heslo je príliš bežné. Vyberte niečo ťažšie uhádnuteľné.",
@@ -2447,7 +2451,7 @@
"disableAuth": "Vypnúť prihlásenie? Každý, kto sa dostane k dashboardu, ho bude môcť otvoriť.",
"revokeToken": "Zrušiť tento API token?",
"disableHttps": "Vypnúť HTTPS a vrátiť sa na obyčajné HTTP?",
"deleteAuditReport": "Vymazať túto správu z auditu?",
"deleteAuditReport": "Odstrániť správu z auditu Lynis a jej logy?",
"cannotBeUndone": "Tento krok sa nedá vrátiť späť."
},
"auth": {
@@ -2810,10 +2814,10 @@
"installationTitle": "Inštalácia Lynis",
"installationDescription": "Terminál nainštaluje Lynis a pripraví podporu reportov.",
"uninstallConfirmTitle": "Odinštalovať Lynis?",
"uninstallConfirmDescription": "Odstráni sa Lynis aj správy z auditu vytvorené Monitorom.",
"uninstallConfirmDescription": "Odinštalovať Lynis a odstrániť jeho správu z auditu a logy.",
"removeInstallation": "Odstrániť inštaláciu Lynis",
"removeWrapper": "Odstrániť Monitor wrapper pre Lynis",
"removeReports": "Odstrániť uložené správy z auditu",
"removeReports": "Odstrániť správu z auditu Lynis a logy",
"counts": {
"tests": {
"one": "{count} test",
@@ -2887,10 +2891,10 @@
"proxmoxScoreWithLabel": "Skóre pre Proxmox ({label})",
"actionableWarningsLabel": "Varovania na riešenie",
"actionableSuggestionsLabel": "Odporúčania na riešenie",
"warningsDescription": "Varovania, ktoré sa oplatí skontrolovať ako prvé. Očakávané Proxmox nálezy sú tu skryté.",
"warningsDescription": "Varovania, ktoré treba preveriť ako prvé. Nálezy očakávané na Proxmoxe sa zobrazujú s označením.",
"noWarningsDetected": "Nenašli sa žiadne varovania na riešenie.",
"recommendation": "Odporúčanie",
"suggestionsDescription": "Menej urgentné odporúčania na spevnenie. Očakávané Proxmox nálezy sú skryté aj tu.",
"suggestionsDescription": "Menej naliehavé odporúčania na spevnenie. Nálezy očakávané na Proxmoxe sa tiež zobrazujú s označením.",
"incompleteDescription": "Lynis vytvoril neúplnú správu bez skóre alebo počtu testov. Spustite audit znova, aby sa zobrazil spoľahlivý výsledok.",
"detailedChecks": "Podrobné kontroly",
"categoriesCount": "{count} kategórií",
+11 -7
View File
@@ -2251,8 +2251,8 @@
}
},
"swapPressure": "Minnestryck",
"swapPressureHint": "Markerar en kritisk händelse endast när BÅDA villkoren är sanna samtidigt — swap-filanvändning över första tröskeln OCH tillgängligt RAM under andra. Att larma enbart på swap var störande på Proxmox-värdar där Linux växlar ut inaktiva sidor medan RAM förblir rikligt tillgängligt.",
"swapHighLabel": "Swap-filanvändning",
"swapPressureHint": "Den här kontrollen av swaptryck markerar en kritisk händelse efter upprepade mätningar där den totala swapanvändningen överskrider den första tröskeln och tillgängligt RAM understiger den andra. Ihållande hög RAM-användning kan också utlösa en separat kritisk händelse.",
"swapHighLabel": "Total swapanvändning",
"availableMinLabel": "Minsta tillgängliga RAM"
},
"navOrder": {
@@ -2373,6 +2373,10 @@
"down": "Ner"
},
"errors": {
"deleteReportNoFiles": "Inga Lynis-rapport- eller loggfiler hittades att ta bort.",
"lynisUninstallNoFiles": "Inga installations-, rapport- eller loggfiler för Lynis hittades att ta bort.",
"lynisRemovalPartial": "Borttagningen slutfördes inte. Vissa Lynis-filer togs bort; kontrollera vad som finns kvar innan du försöker igen.",
"lynisRemovalUnconfirmed": "Det gick inte att bekräfta resultatet av borttagningen av Lynis. Vissa filer kan redan ha tagits bort; kontrollera innan du försöker igen.",
"passwordMinLength": "Lösenordet måste vara minst 10 tecken långt.",
"passwordComplexity": "Använd minst tre av dessa: gemener, versaler, nummer, symbol.",
"passwordCommon": "Det här lösenordet är för vanligt. Välj något som är svårare att gissa.",
@@ -2448,7 +2452,7 @@
"disableAuth": "Inaktivera autentisering? Alla som kan nå instrumentpanelen kommer att kunna öppna den.",
"revokeToken": "Återkalla denna API-token?",
"disableHttps": "Inaktivera HTTPS och gå tillbaka till vanlig HTTP?",
"deleteAuditReport": "Vill du ta bort den här revisionsrapporten?",
"deleteAuditReport": "Ta bort Lynis granskningsrapport och loggar?",
"cannotBeUndone": "Detta kan inte ångras."
},
"auth": {
@@ -2811,10 +2815,10 @@
"installationTitle": "Installera Lynis",
"installationDescription": "Terminalen kommer att installera Lynis och förbereda rapportstöd.",
"uninstallConfirmTitle": "Avinstallera Lynis?",
"uninstallConfirmDescription": "Detta tar bort revisionsrapportfiler som skapats av Lynis och Monitor.",
"uninstallConfirmDescription": "Avinstallera Lynis och ta bort dess granskningsrapport och loggar.",
"removeInstallation": "Ta bort Lynis-installationen",
"removeWrapper": "Ta bort Monitor Lynis-omslaget",
"removeReports": "Ta bort sparade revisionsrapporter",
"removeReports": "Ta bort Lynis granskningsrapport och loggar",
"counts": {
"tests": {
"one": "{count} test",
@@ -2888,10 +2892,10 @@
"proxmoxScoreWithLabel": "Proxmox-poäng ({label})",
"actionableWarningsLabel": "Handlingsbara varningar",
"actionableSuggestionsLabel": "Handlingsbara förslag",
"warningsDescription": "Varningar värda att granska först. Proxmox-förväntade fynd är dolda här.",
"warningsDescription": "Varningar som bör granskas först. Förväntade fynd på Proxmox visas med en markering.",
"noWarningsDetected": "Inga åtgärdsbara varningar upptäcktes.",
"recommendation": "Rekommendation",
"suggestionsDescription": "Förslag på lägre prioritet härdning. Proxmox-förväntade fynd är dolda här också.",
"suggestionsDescription": "Förslag på härdning med lägre prioritet. Förväntade fynd på Proxmox visas också med en markering.",
"incompleteDescription": "Lynis skapade en ofullständig rapport utan poäng eller testräkning. Kör granskningen igen för att visa ett tillförlitligt resultat.",
"detailedChecks": "Detaljerade kontroller",
"categoriesCount": "{count} kategorier",
+5 -5
View File
@@ -388,9 +388,9 @@ def lynis_report_delete():
"""Delete Lynis audit report files"""
if not security_manager:
return jsonify({"success": False, "message": "Security manager not available"}), 500
deleted = []
try:
import os
deleted = []
for f in ["/var/log/lynis-report.dat", "/var/log/lynis.log", "/var/log/lynis-output.log"]:
if os.path.isfile(f):
os.remove(f)
@@ -398,9 +398,9 @@ def lynis_report_delete():
if deleted:
return jsonify({"success": True, "message": f"Deleted: {', '.join(deleted)}"})
else:
return jsonify({"success": False, "message": "No report files found to delete"})
return jsonify({"success": False, "outcome": "no_files", "message": "No report files found to delete"})
except Exception as e:
return jsonify({"success": False, "message": str(e)}), 500
return jsonify({"success": False, "partial": bool(deleted), "message": str(e)}), 500
# -------------------------------------------------------------------
@@ -427,8 +427,8 @@ def lynis_uninstall():
if not security_manager:
return jsonify({"success": False, "message": "Security manager not available"}), 500
try:
success, message = security_manager.uninstall_lynis()
return jsonify({"success": success, "message": message})
success, message, partial, outcome = security_manager.uninstall_lynis()
return jsonify({"success": success, "message": message, "partial": partial, "outcome": outcome})
except Exception as e:
return jsonify({"success": False, "message": str(e)}), 500
+10 -3
View File
@@ -2577,18 +2577,21 @@ def uninstall_fail2ban():
def uninstall_lynis():
"""
Uninstall Lynis and clean up all files.
Returns (success, message).
Returns (success, message, partial, outcome). Partial records completed removals only.
"""
removed = []
try:
import shutil
# Remove installation directory
if os.path.exists("/opt/lynis"):
shutil.rmtree("/opt/lynis")
removed.append("/opt/lynis")
# Remove wrapper script
if os.path.exists("/usr/local/bin/lynis"):
os.remove("/usr/local/bin/lynis")
removed.append("/usr/local/bin/lynis")
# Remove report files
for report_file in [
@@ -2598,6 +2601,7 @@ def uninstall_lynis():
]:
if os.path.exists(report_file):
os.remove(report_file)
removed.append(report_file)
# Update component status
base_dir = "/usr/local/share/proxmenux"
@@ -2615,6 +2619,9 @@ def uninstall_lynis():
except Exception:
pass
return True, "Lynis has been uninstalled successfully"
if not removed:
return False, "No Lynis files found to remove", False, "no_files"
return True, "Lynis has been uninstalled successfully", False, "removed"
except Exception as e:
return False, f"Error uninstalling Lynis: {str(e)}"
return False, f"Error uninstalling Lynis: {str(e)}", bool(removed), "failed"