85 lines
2.8 KiB
PHP
85 lines
2.8 KiB
PHP
<?php
|
||
// Opus WIRE 19-avr doctrine 94 - Safe Write Helper
|
||
// Allows WEVIA to write whitelisted paths (chattr lifecycle handled by sudoers)
|
||
// REQUIRES: sudo access for www-data on chattr (to be configured by root)
|
||
// WHITELIST: only specific files or paths WEVIA can modify autonomously
|
||
// SECURITY: token required + path validation
|
||
|
||
header('Content-Type: application/json');
|
||
header('Access-Control-Allow-Origin: *');
|
||
|
||
$TOKEN = 'SAFEWRITE2026_WEVIA_DOCTRINE94';
|
||
$WHITELIST = [
|
||
'/var/www/html/wiki/', // Wiki docs
|
||
'/var/www/html/api/playwright-results/', // Test results
|
||
'/opt/weval-l99/wiki/', // L99 wiki
|
||
'/opt/obsidian-vault/sessions/', // Session files
|
||
'/opt/weval-l99/kpi-cache/', // KPI feeders output
|
||
'/tmp/wevia-', // Temp files
|
||
];
|
||
|
||
$input = json_decode(file_get_contents('php://input'), true);
|
||
$token = $input['token'] ?? '';
|
||
$path = $input['path'] ?? '';
|
||
$content = $input['content'] ?? '';
|
||
$action = $input['action'] ?? 'write';
|
||
|
||
if ($token !== $TOKEN) {
|
||
http_response_code(403);
|
||
echo json_encode(['error' => 'invalid token']);
|
||
exit;
|
||
}
|
||
|
||
if (empty($path)) {
|
||
echo json_encode(['error' => 'path required', 'whitelist' => $WHITELIST]);
|
||
exit;
|
||
}
|
||
|
||
// Resolve to absolute path safely
|
||
$path = realpath(dirname($path)) . '/' . basename($path);
|
||
$allowed = false;
|
||
foreach ($WHITELIST as $prefix) {
|
||
if (strpos($path, $prefix) === 0) { $allowed = true; break; }
|
||
}
|
||
if (!$allowed) {
|
||
http_response_code(403);
|
||
echo json_encode(['error' => 'path not in whitelist', 'path' => $path, 'whitelist' => $WHITELIST]);
|
||
exit;
|
||
}
|
||
|
||
// Reject path traversal
|
||
if (strpos($path, '..') !== false || strpos($path, " |