mahnung/ajax/regex_preview.php
Eduard Wisch 10cf41a687
All checks were successful
Deploy mahnung / deploy (push) Successful in 14s
i18n: Alle Texte über $langs->trans() — ~100 neue Sprachschlüssel de_DE + en_US [deploy]
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>
2026-05-13 16:25:50 +02:00

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;