Terminplaner: Einstellungen, URL-Host einschraenken und unsichtbare Formatzeichen entfernen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9cacd5bd3b
commit
759be69250
2 files changed
+97
-11
No files matched your search
@@ -45,21 +45,33 @@ export function neuerFeedToken() {
|
||||
|
||||
// ---------------------------------------------------------------- Bausteine zum Pruefen
|
||||
|
||||
// ASCII- und C1-Steuerzeichen, Zeilen-/Absatztrenner und Richtungsumschalter (Anzeige-Spoofing in Mails und Widget)
|
||||
const STEUERZEICHEN = /[\u0000-\u001F\u007F-\u009F\u2028\u2029\u202A-\u202E\u2066-\u2069]/g
|
||||
// Steuerzeichen (\p{Cc}: ASCII und C1), unsichtbare Formatzeichen (\p{Cf}: Zero-Width Space, Richtungsmarken und
|
||||
// -umschalter, Word Joiner, BOM, Arabic Letter Mark, Soft Hyphen u. a.) sowie Zeilen-/Absatztrenner. Sie erzeugen
|
||||
// leere oder taeuschende Namen in Absendernamen, Betreffzeilen und auf oeffentlichen Seiten.
|
||||
const STEUERZEICHEN = /[\p{Cc}\p{Cf}\u2028\u2029]/gu
|
||||
// ZWNJ und ZWJ gehoeren zu Emoji-Sequenzen und persischer Schrift und bleiben in Freitexten erhalten.
|
||||
const VERBINDER = new Set(['\u200C', '\u200D'])
|
||||
|
||||
/** Entfernt Steuerzeichen und unsichtbare Formatzeichen; behalteVerbinder: ZWNJ/ZWJ bleiben, umbruch: '\n' bleibt. */
|
||||
function ohneSteuerzeichen(s, { behalteVerbinder = false, umbruch = false } = {}) {
|
||||
return s.replace(STEUERZEICHEN, (z) => ((behalteVerbinder && VERBINDER.has(z)) || (umbruch && z === '\n') ? z : ''))
|
||||
}
|
||||
|
||||
// Leerraum und ZWNJ/ZWJ am Rand entfernen: ein Text nur aus Verbindern bleibt sonst unsichtbar und besteht die Pflichtpruefung.
|
||||
const randTrim = (s) => s.replace(/^[\s\u200C\u200D]+|[\s\u200C\u200D]+$/g, '')
|
||||
|
||||
/** Bereinigter Text: ohne Steuerzeichen, getrimmt, auf max Zeichen (Codepoints) gekuerzt. Einzeilig: Umbrueche werden Leerzeichen. */
|
||||
function text(wert, max, { mehrzeilig = false } = {}) {
|
||||
if (typeof wert !== 'string') return ''
|
||||
let s = wert
|
||||
if (mehrzeilig) {
|
||||
s = s.replace(/\r\n?/g, '\n').replace(/\t/g, ' ').replace(STEUERZEICHEN, (z) => (z === '\n' ? z : '')).replace(/\n{3,}/g, '\n\n')
|
||||
s = ohneSteuerzeichen(s.replace(/\r\n?/g, '\n').replace(/\t/g, ' '), { behalteVerbinder: true, umbruch: true }).replace(/\n{3,}/g, '\n\n')
|
||||
} else {
|
||||
s = s.replace(/[\r\n\t]+/g, ' ').replace(STEUERZEICHEN, '').replace(/ {2,}/g, ' ')
|
||||
s = ohneSteuerzeichen(s.replace(/[\r\n\t]+/g, ' '), { behalteVerbinder: true }).replace(/ {2,}/g, ' ')
|
||||
}
|
||||
s = s.trim()
|
||||
s = randTrim(s)
|
||||
const zeichen = [...s]
|
||||
return zeichen.length > max ? zeichen.slice(0, max).join('').trim() : s
|
||||
return zeichen.length > max ? randTrim(zeichen.slice(0, max).join('')) : s
|
||||
}
|
||||
|
||||
/** Wert in {min..max} (ganzzahlig, Vielfaches von schritt) oder Vorgabe; fehlender Wert ohne Vorgabe ist ein Fehler. */
|
||||
@@ -96,7 +108,7 @@ function wahrheit(fehl, pfad, wert, vorgabe) {
|
||||
/** Ein Postfach: kein Leerraum, genau ein @, keine ,;<>" und >= 1 Punkt in der Domain, hoechstens 254 Zeichen. */
|
||||
function emailAdresse(wert) {
|
||||
if (typeof wert !== 'string') return null
|
||||
const s = wert.replace(STEUERZEICHEN, '').trim().toLowerCase()
|
||||
const s = ohneSteuerzeichen(wert).trim().toLowerCase()
|
||||
if (s.length === 0 || s.length > 254) return null
|
||||
if (!/^[^\s@,;<>"]+@[^\s@,;<>".]+(\.[^\s@,;<>".]+)+$/.test(s)) return null
|
||||
return s
|
||||
@@ -104,24 +116,32 @@ function emailAdresse(wert) {
|
||||
|
||||
const URL_MELDUNG = 'Bitte gib eine vollständige Adresse mit https:// an (höchstens 300 Zeichen, ohne Zugangsdaten).'
|
||||
|
||||
/** https-URL (<= 300, ohne Zugangsdaten) in kanonischer Schreibweise, leer = null. */
|
||||
// Hostname wie ihn URL liefert (klein, Umlaute als Punycode): Labels aus a-z, 0-9 und Bindestrich, oder IPv6 in Klammern.
|
||||
const HOST_RE = /^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*|\[[0-9a-f:.]+\])$/
|
||||
|
||||
/**
|
||||
* https-URL (<= 300, ohne Zugangsdaten, nur ASCII-Hostname) in kanonischer Schreibweise, leer = null. Diese Adressen
|
||||
* landen als href auf oeffentlichen Seiten und in Mails: Anfuehrungszeichen, Apostroph und Backtick im Host werden
|
||||
* abgelehnt, ein Apostroph im Pfad wird als %27 gespeichert (URL kodiert ihn dort nicht selbst).
|
||||
*/
|
||||
function urlFeld(fehl, pfad, wert) {
|
||||
if (wert === undefined || wert === null) return null
|
||||
if (typeof wert !== 'string') {
|
||||
fehl(pfad, URL_MELDUNG)
|
||||
return null
|
||||
}
|
||||
const s = wert.replace(STEUERZEICHEN, '').trim()
|
||||
const s = ohneSteuerzeichen(wert).trim()
|
||||
if (s === '') return null
|
||||
let url = null
|
||||
if (s.length <= 300 && !/\s/.test(s)) {
|
||||
try { url = new URL(s) } catch { url = null }
|
||||
}
|
||||
if (!url || url.protocol !== 'https:' || url.username || url.password || !url.hostname || url.href.length > 300) {
|
||||
const href = url ? url.href.replace(/'/g, '%27') : ''
|
||||
if (!url || url.protocol !== 'https:' || url.username || url.password || !HOST_RE.test(url.hostname) || href.length > 300) {
|
||||
fehl(pfad, URL_MELDUNG)
|
||||
return null
|
||||
}
|
||||
return url.href
|
||||
return href
|
||||
}
|
||||
|
||||
/** ID eines Eintrags: fehlt sie, entsteht eine neue; sonst Format + Eindeutigkeit je Liste. */
|
||||
|
||||
Reference in new issue
Block a user