Allow external web links in market rich text
This commit is contained in:
1 parent
c25d3f1b3c
commit
9f10344fa7
4 files changed
+20
-12
No files matched your search
@@ -47,8 +47,9 @@ Link: <a href="https://erpnaurua.imhochrain.ch/files.php?path=Marktmanagement%2F
|
||||
Leerzeichen, Umlaute und sonstige Sonderzeichen im URL-Parameter müssen
|
||||
vollständig URL-encodiert werden. Im Feld `notes` sind ausschließlich `<a>`
|
||||
für solche internen Dokumentlinks sowie `<br>` und `<strong>` erlaubt. Andere
|
||||
HTML-Tags, externe Links und nicht erlaubte URL-Schemata werden entfernt. Der
|
||||
Link ist schreibgeschützt und erfordert eine aktive ERP-Anmeldung.
|
||||
HTML-Tags und nicht erlaubte URL-Schemata werden entfernt. Externe `http://`-
|
||||
und `https://`-Links sind erlaubt und öffnen in einem neuen Tab. Der Link ist
|
||||
schreibgeschützt und erfordert bei internen Dokumenten eine aktive ERP-Anmeldung.
|
||||
|
||||
Die Route benötigt auf dem ERP-Webserver die Konfiguration
|
||||
`HERMES_DOCUMENT_ROOT=/volume1/Hermes/Hermes Naurua`. Dieser lokale Pfad ist
|
||||
|
||||
@@ -72,7 +72,8 @@ Modulberechtigung.
|
||||
Marktdurchführung speichert, muss die Quelldatei als interner `<a>`-Link im
|
||||
Feld `notes` referenziert werden. Zusätzlich sind in mehrzeiligen Feldern
|
||||
`<br>` und `<strong>` erlaubt. Die Position im bestehenden Freitext ist frei
|
||||
wählbar; andere HTML-Tags und externe Links sind nicht zulässig.
|
||||
wählbar. Externe `http://`- und `https://`-Links sind zulässig und öffnen in
|
||||
einem neuen Tab; andere HTML-Tags und URL-Schemata sind nicht zulässig.
|
||||
- Unsichere Erkennung wird nicht durch eine zusätzliche App-Freigabelogik
|
||||
behandelt; die fachliche Qualitätssicherung liegt im Hermes-Training.
|
||||
|
||||
|
||||
@@ -26,15 +26,21 @@ const MARKET_MANAGEMENT_MULTILINE_FIELDS = [
|
||||
'description',
|
||||
];
|
||||
|
||||
function market_management_is_allowed_document_url(string $url): bool
|
||||
function market_management_is_allowed_link_url(string $url): bool
|
||||
{
|
||||
$parts = parse_url($url);
|
||||
if (!is_array($parts)
|
||||
|| ($parts['scheme'] ?? '') !== 'https'
|
||||
|| ($parts['host'] ?? '') !== 'erpnaurua.imhochrain.ch'
|
||||
|| ($parts['path'] ?? '') !== '/files.php'
|
||||
|| isset($parts['port'], $parts['user'], $parts['pass'], $parts['fragment'])
|
||||
|| !preg_match('/^path=([^&]+)$/', (string) ($parts['query'] ?? ''), $matches)) {
|
||||
|| !in_array(($parts['scheme'] ?? ''), ['http', 'https'], true)
|
||||
|| ($parts['host'] ?? '') === ''
|
||||
|| isset($parts['user'], $parts['pass'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (($parts['host'] ?? '') !== 'erpnaurua.imhochrain.ch' || ($parts['path'] ?? '') !== '/files.php') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isset($parts['port'], $parts['fragment']) || !preg_match('/^path=([^&]+)$/', (string) ($parts['query'] ?? ''), $matches)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -88,12 +94,12 @@ function market_management_sanitize_multiline_html(?string $value): ?string
|
||||
continue;
|
||||
}
|
||||
if (preg_match('/^<a\b[^>]*\bhref\s*=\s*(["\'])(.*?)\1[^>]*>$/isu', $token, $matches)
|
||||
&& market_management_is_allowed_document_url($matches[2])) {
|
||||
&& market_management_is_allowed_link_url($matches[2])) {
|
||||
if ($anchorOpen) {
|
||||
$result .= '</a>';
|
||||
}
|
||||
$href = htmlspecialchars($matches[2], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
$result .= '<a class="sg-hyperlink" href="' . $href . '" target="_blank" rel="noopener">';
|
||||
$result .= '<a class="sg-hyperlink" href="' . $href . '" target="_blank" rel="noopener noreferrer">';
|
||||
$anchorOpen = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1152,7 +1152,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $otcM
|
||||
echo " const getCurrentLimit = () => parseInt(getTable()?.dataset.marketsLimit || '20', 10);";
|
||||
echo " const getPageSize = () => parseInt(getTable()?.dataset.marketsPageSize || '20', 10);";
|
||||
echo " const marketEscape = (value) => String(value ?? '').replace(/[&<>\"']/g, (character) => ({ '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''' }[character] || character));";
|
||||
echo " const marketDisplay = (value) => { if (value === null || value === undefined || value === '') { return 'Nicht hinterlegt'; } const escaped = marketEscape(value); const safeMarkup = escaped.replace(/<br>/g, '<br>').replace(/<strong>/g, '<strong>').replace(/<\\/strong>/g, '</strong>').replace(/<a class="sg-hyperlink" href="(https:\\/\\/erpnaurua\\.imhochrain\\.ch\\/files\\.php\\?path=[^"]+)" target="_blank" rel="noopener">/g, '<a class=\"sg-hyperlink\" href=\"$1\" target=\"_blank\" rel=\"noopener\">').replace(/<\\/a>/g, '</a>'); return safeMarkup.replace(/\\r?\\n/g, '<br>'); };";
|
||||
echo " const marketDisplay = (value) => { if (value === null || value === undefined || value === '') { return 'Nicht hinterlegt'; } const escaped = marketEscape(value); const safeMarkup = escaped.replace(/<br>/g, '<br>').replace(/<strong>/g, '<strong>').replace(/<\\/strong>/g, '</strong>').replace(/<a class="sg-hyperlink" href="(https?:\\/\\/.*?)" target="_blank" rel="noopener noreferrer">/g, '<a class=\"sg-hyperlink\" href=\"$1\" target=\"_blank\" rel=\"noopener noreferrer\">').replace(/<\\/a>/g, '</a>'); return safeMarkup.replace(/\\r?\\n/g, '<br>'); };";
|
||||
echo " const marketBoolean = (value) => value === true || value === 'true' || value === 1 || value === '1' ? 'Ja' : value === false || value === 'false' || value === 0 || value === '0' ? 'Nein' : 'Nicht hinterlegt';";
|
||||
echo " const marketMoney = (value) => value === null || value === undefined || value === '' ? 'Nicht hinterlegt' : 'CHF ' + Number(value).toFixed(2);";
|
||||
echo " const marketContactName = (contact) => { if (!contact) { return 'Nicht hinterlegt'; } return marketDisplay([contact.first_name, contact.last_name].filter(Boolean).join(' ')); };";
|
||||
|
||||
Reference in new issue
Block a user