summaryrefslogtreecommitdiffstats
path: root/index.php
diff options
context:
space:
mode:
authorGab <24553253+gabrix73@users.noreply.github.com>2026-08-16 19:24:16 +0200
committerGab <24553253+gabrix73@users.noreply.github.com>2026-08-16 19:24:16 +0200
commit56e296e5875b10ed053cbdedbab08957fce2a461 (patch)
treee1c49b328f1fe5142f7e2b27593b0905c5e3668a /index.php
parent43fbddf016f94f4ba006d82c9a67dca61b5852a1 (diff)
downloadyamnweb-56e296e5875b10ed053cbdedbab08957fce2a461.tar.gz
yamnweb-56e296e5875b10ed053cbdedbab08957fce2a461.tar.xz
yamnweb-56e296e5875b10ed053cbdedbab08957fce2a461.zip
Harden Usenet threading and ingress deliveryHEADmain
Diffstat (limited to 'index.php')
-rw-r--r--index.php52
1 files changed, 49 insertions, 3 deletions
diff --git a/index.php b/index.php
index af646a6..bbab642 100644
--- a/index.php
+++ b/index.php
@@ -86,6 +86,43 @@ if (empty($_SESSION['csrf_token'])) {
$currentCsrfToken = $_SESSION['csrf_token'];
/**
+ * Return remailer names whose public keys are valid today.
+ *
+ * @return array<string, bool> Name lookup table.
+ */
+function getUsableRemailerKeyNames(): array {
+ static $usableNames = null;
+ if (is_array($usableNames)) {
+ return $usableNames;
+ }
+
+ $usableNames = [];
+ $keyring = yamnConfig('YAMN_PUBRING', '/opt/yamn-master/pubring.mix');
+ if (!is_readable($keyring)) {
+ return $usableNames;
+ }
+
+ $today = gmdate('Y-m-d');
+ foreach (file($keyring, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [] as $line) {
+ $parts = preg_split('/\s+/', trim($line));
+ if (count($parts) !== 7) {
+ continue;
+ }
+ [$name, , , , , $validFrom, $validUntil] = $parts;
+ if (!preg_match('/^[a-z0-9_-]+$/', $name)
+ || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $validFrom)
+ || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $validUntil)) {
+ continue;
+ }
+ if ($validFrom <= $today && $today <= $validUntil) {
+ $usableNames[$name] = true;
+ }
+ }
+
+ return $usableNames;
+}
+
+/**
* Parse remailers from file and return array by type
* Entry and Exit can use ANY remailer
* Middle should use remailers with specific flags
@@ -95,6 +132,7 @@ $currentCsrfToken = $_SESSION['csrf_token'];
*/
function getRemailers($type) {
$remailers = ['*']; // Always include Random option
+ $usableKeyNames = getUsableRemailerKeyNames();
// Try multiple file locations
$files = [
@@ -140,6 +178,7 @@ function getRemailers($type) {
// Validate name: lowercase letters, numbers, hyphens only
if (!preg_match('/^[a-z0-9-]+$/', $remailerName)) continue;
+ if (!isset($usableKeyNames[$remailerName])) continue;
// Check if last field is 'D' (middle capability flag)
$lastField = end($parts);
@@ -185,6 +224,9 @@ function resolveRemailer($remailer, $availableRemailers) {
$randomIndex = array_rand($candidates);
return $candidates[$randomIndex];
}
+ if (!in_array($remailer, $availableRemailers, true)) {
+ throw new Exception("Selected remailer is no longer available. Reload the page and choose again.");
+ }
return $remailer;
}
@@ -246,11 +288,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$middleRemailer = isset($_POST['middle_remailer']) ? filter_var($_POST['middle_remailer'], FILTER_SANITIZE_STRING) : '';
$exitRemailer = isset($_POST['exit_remailer']) ? filter_var($_POST['exit_remailer'], FILTER_SANITIZE_STRING) : '';
$from = isset($_POST['from']) ? filter_var($_POST['from'], FILTER_SANITIZE_STRING) : '';
- $replyTo = isset($_POST['reply_to']) ? filter_var($_POST['reply_to'], FILTER_SANITIZE_STRING) : '';
+ $replyTo = isset($_POST['reply_to']) && is_string($_POST['reply_to'])
+ ? trim($_POST['reply_to'])
+ : '';
$to = isset($_POST['to']) ? filter_var($_POST['to'], FILTER_SANITIZE_EMAIL) : '';
$subject = isset($_POST['subject']) ? filter_var($_POST['subject'], FILTER_SANITIZE_STRING) : '';
$newsgroups = isset($_POST['newsgroups']) ? filter_var($_POST['newsgroups'], FILTER_SANITIZE_STRING) : '';
- $references = isset($_POST['references']) ? filter_var($_POST['references'], FILTER_SANITIZE_STRING) : '';
+ $references = isset($_POST['references']) && is_string($_POST['references'])
+ ? trim($_POST['references'])
+ : '';
$data = isset($_POST['data']) ? $_POST['data'] : ''; // Keep original formatting
$copies = isset($_POST['copies']) ? intval($_POST['copies']) : 1;
@@ -766,7 +812,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<div class="form-group"><label for="to">Email recipient</label><input type="email" name="to" id="to"><small>Required for email delivery. Leave empty when publishing to a newsgroup.</small></div>
<div class="form-group"><label for="subject">Subject <span class="required">*</span></label><input type="text" name="subject" id="subject" required></div>
<div class="form-group"><label for="newsgroups">Newsgroup</label><input type="text" name="newsgroups" id="newsgroups" placeholder="misc.test"><small>Optional. When set, the message is routed through the configured Mail-to-News gateway. Leave empty for email.</small></div>
- <div class="form-group"><label for="references">References</label><input type="text" name="references" id="references" placeholder="&lt;message-id@example.org&gt;"><small>Optional. Links the article to an existing thread.</small></div>
+ <div class="form-group"><label for="references">References</label><input type="text" name="references" id="references" maxlength="900" placeholder="&lt;message-id@example.org&gt;"><small>Optional. Enter one or more space-separated Message-IDs, without the <code>References:</code> label. The last ID becomes <code>In-Reply-To</code>.</small></div>
<div class="form-group"><label for="data">Message body <span class="required">*</span></label><textarea name="data" id="data" required></textarea>
<small>The server creates the encrypted YAMN envelope in memory and sends only that packet through Nym. Remailer queues may take several hours.</small>
</div>