Sicherheits-Härtung (v1.1.1) und aktuelle README

- nur http(s)-Links, Validierung/Limits für Remote-, Storage- und Importdaten
- HTTP-Sync nur im lokalen Netz, keine Zugangsdaten in der URL
- Berechtigung bei Serverwechsel entziehen, explizite CSP, UUID-IDs

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
root
2026-10-11 18:22:26 +00:00
co-authored by Claude Sonnet 5.5
parent bb7ee831a8
commit a04aaae859
5 changed files with 185 additions and 21 deletions
+12 -5
View File
@@ -7,12 +7,12 @@ let editId = null;
let syncOn = false;
let syncState = ""; // "", "busy", "ok", "err"
const uid = () => Math.random().toString(36).slice(2, 10);
const uid = () => crypto.randomUUID();
function normalizeUrl(raw) {
raw = raw.trim();
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(raw)) raw = "http://" + raw;
try { return new URL(raw).href; } catch { return null; }
return LabSync.safeUrl(raw);
}
async function load() {
@@ -135,7 +135,11 @@ function closeForm() {
$("form").addEventListener("submit", async (e) => {
e.preventDefault();
const url = normalizeUrl($("fUrl").value);
if (!url) { $("fUrl").focus(); return; }
if (!url) {
$("fUrl").setCustomValidity("Nur gültige http(s)-URLs");
$("fUrl").reportValidity();
return;
}
const entry = { name: $("fName").value.trim(), url, icon: $("fIcon").value.trim(), updated: Date.now() };
if (editId === "new") links.push({ id: uid(), ...entry });
else Object.assign(links.find((l) => l.id === editId), entry);
@@ -143,6 +147,7 @@ $("form").addEventListener("submit", async (e) => {
closeForm();
});
$("fUrl").addEventListener("input", () => $("fUrl").setCustomValidity(""));
$("cancel").addEventListener("click", closeForm);
$("add").addEventListener("click", () => openForm());
$("search").addEventListener("input", render);
@@ -172,9 +177,11 @@ $("file").addEventListener("change", async (e) => {
try {
const arr = JSON.parse(await file.text());
const now = Date.now();
if (!Array.isArray(arr)) throw new Error("kein Array");
const imported = arr
.filter((x) => x && x.name && x.url)
.map((x) => ({ id: uid(), name: String(x.name), url: normalizeUrl(String(x.url)) || String(x.url), icon: x.icon || "", updated: now }));
.slice(0, 500)
.map((x) => LabSync.cleanLink({ ...x, id: uid(), url: x && normalizeUrl(String(x.url ?? "")), updated: now }))
.filter(Boolean);
deleted = [...deleted, ...links.map((l) => ({ id: l.id, updated: now }))];
links = imported;
await save();