All checks were successful
Deploy mahnung / deploy (push) Successful in 14s
Umlaute in allen lang-Dateien korrigiert. Alle hardcodierten deutschen Strings
in 22 PHP-Dateien durch $langs->trans('Key') ersetzt. Neue Schlüssel für
Cron-Meldungen, Dokument-Aktionen, Bonität, Vorschlag-Status, Template-Vars u.a.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
89 lines
2.7 KiB
PHP
89 lines
2.7 KiB
PHP
<?php
|
|
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
|
*
|
|
* GPL v3 (siehe COPYING).
|
|
*/
|
|
|
|
/**
|
|
* \file htdocs/custom/mahnung/ajax/regex_preview.php
|
|
* \ingroup mahnung
|
|
* \brief AJAX-Endpoint: Live-Vorschau für Tracking-Regex auf der Setup-Seite.
|
|
*
|
|
* POST: regex (string), sample (string, max 10 KB), url_template (string)
|
|
* Response (JSON): { valid: bool, match: string|null, preview_url: string|null, error: string|null }
|
|
*/
|
|
|
|
if (!defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1');
|
|
if (!defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1');
|
|
|
|
require_once $_SERVER['DOCUMENT_ROOT'].'/main.inc.php';
|
|
|
|
global $user, $langs;
|
|
$langs->load('mahnung@mahnung');
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
if (!$user->admin && !$user->hasRight('mahnung', 'setup')) {
|
|
echo json_encode(array('valid' => false, 'error' => 'access denied'));
|
|
exit;
|
|
}
|
|
|
|
// ReDoS-Schutz: Eingaben begrenzen + Zeitlimit
|
|
$regex = (string) GETPOST('regex', 'nohtml');
|
|
$sample = (string) GETPOST('sample', 'nohtml');
|
|
$urlTemplate = (string) GETPOST('url_template', 'alphanohtml');
|
|
|
|
if (strlen($regex) > 255) {
|
|
echo json_encode(array('valid' => false, 'error' => 'regex too long (max 255)'));
|
|
exit;
|
|
}
|
|
if (strlen($sample) > 10240) {
|
|
$sample = substr($sample, 0, 10240);
|
|
}
|
|
|
|
// Whitelist: Delimiter / # ~
|
|
if ($regex === '' || !in_array($regex[0], array('/', '#', '~'), true)) {
|
|
echo json_encode(array('valid' => false, 'error' => 'allowed delimiters: / # ~'));
|
|
exit;
|
|
}
|
|
|
|
// ReDoS-Schutz via PCRE-Backtrack-Limit (klein halten)
|
|
$prevBacktrack = ini_get('pcre.backtrack_limit');
|
|
$prevRecursion = ini_get('pcre.recursion_limit');
|
|
@ini_set('pcre.backtrack_limit', '100000');
|
|
@ini_set('pcre.recursion_limit', '10000');
|
|
|
|
$matches = array();
|
|
$ret = @preg_match($regex, '', $matches); // erst leerer String — testet Syntax
|
|
if ($ret === false) {
|
|
@ini_set('pcre.backtrack_limit', (string) $prevBacktrack);
|
|
@ini_set('pcre.recursion_limit', (string) $prevRecursion);
|
|
$err = preg_last_error_msg();
|
|
echo json_encode(array('valid' => false, 'error' => 'invalid regex'.($err && $err !== 'No error' ? ': '.$err : '')));
|
|
exit;
|
|
}
|
|
|
|
// Echtes Sample matchen
|
|
$match = null;
|
|
if ($sample !== '') {
|
|
$ret = @preg_match($regex, $sample, $matches);
|
|
if ($ret === 1) {
|
|
$match = !empty($matches[1]) ? $matches[1] : $matches[0];
|
|
}
|
|
}
|
|
|
|
@ini_set('pcre.backtrack_limit', (string) $prevBacktrack);
|
|
@ini_set('pcre.recursion_limit', (string) $prevRecursion);
|
|
|
|
$previewUrl = null;
|
|
if ($match !== null && strpos($urlTemplate, 'https://') === 0 && strpos($urlTemplate, '{nr}') !== false) {
|
|
$previewUrl = str_replace('{nr}', rawurlencode($match), $urlTemplate);
|
|
}
|
|
|
|
echo json_encode(array(
|
|
'valid' => true,
|
|
'match' => $match,
|
|
'preview_url' => $previewUrl,
|
|
'error' => null,
|
|
));
|
|
exit;
|