103 lines
4.8 KiB
PHP
103 lines
4.8 KiB
PHP
<?php
|
|
/**
|
|
* WEVIA Safe Ops - path-locked file operations
|
|
* V61 · 2026-04-19 · Opus
|
|
* Doctrine #7 #14: extend WEVIA autonomy via auditable endpoint instead of dispatcher whitelist
|
|
* SECURITY:
|
|
* - Only accepts files under /var/www/html/*.html
|
|
* - Rejects *.GOLD *.bak *.gold and anything with .. or /
|
|
* - Token required (WEVADS2026)
|
|
* - Always GOLD backup before modify
|
|
* - Always chattr -i / +i wrap
|
|
*/
|
|
header('Content-Type: application/json');
|
|
header('Access-Control-Allow-Origin: *');
|
|
|
|
$token = $_POST['k'] ?? $_GET['k'] ?? '';
|
|
if ($token !== 'WEVADS2026') { http_response_code(401); die(json_encode(['error'=>'auth'])); }
|
|
|
|
$action = $_POST['action'] ?? $_GET['action'] ?? '';
|
|
$file = basename($_POST['file'] ?? $_GET['file'] ?? '');
|
|
$old = $_POST['old'] ?? $_GET['old'] ?? '';
|
|
$new = $_POST['new'] ?? $_GET['new'] ?? '';
|
|
|
|
// PATH LOCK: *.html only, no hidden, no GOLD/bak
|
|
if (!preg_match('/^[a-zA-Z0-9_-]+\.html$/', $file)) {
|
|
http_response_code(400);
|
|
die(json_encode(['error'=>'invalid_filename', 'msg'=>'must be [a-zA-Z0-9_-]+.html']));
|
|
}
|
|
$full = '/var/www/html/' . $file;
|
|
if (!file_exists($full)) {
|
|
http_response_code(404);
|
|
die(json_encode(['error'=>'file_not_found', 'file'=>$full]));
|
|
}
|
|
|
|
$out = ['ok'=>false, 'action'=>$action, 'file'=>$file, 'ts'=>date('c')];
|
|
|
|
if ($action === 'sed_patch') {
|
|
if ($old === '' || $new === '') die(json_encode(array_merge($out, ['error'=>'old_new_required'])));
|
|
// Escape regex
|
|
$old_esc = escapeshellarg($old);
|
|
$new_esc = escapeshellarg($new);
|
|
// GOLD
|
|
$gold = '/opt/wevads/vault/' . $file . '.GOLD-' . date('Ymd-His') . '-safe-ops';
|
|
@shell_exec('cp ' . escapeshellarg($full) . ' ' . escapeshellarg($gold) . ' 2>&1');
|
|
// Unlock, patch, relock
|
|
@shell_exec('sudo -n chattr -i ' . escapeshellarg($full) . ' 2>&1');
|
|
$before = (int) @shell_exec('grep -c ' . $old_esc . ' ' . escapeshellarg($full));
|
|
@shell_exec('sed -i "s|' . str_replace('|', '\\|', $old) . '|' . str_replace('|', '\\|', $new) . '|g" ' . escapeshellarg($full) . ' 2>&1');
|
|
$after = (int) @shell_exec('grep -c ' . $new_esc . ' ' . escapeshellarg($full));
|
|
@shell_exec('sudo -n chattr +i ' . escapeshellarg($full) . ' 2>&1');
|
|
@shell_exec('chown www-data:www-data ' . escapeshellarg($full) . ' 2>&1');
|
|
$out['ok'] = $after > 0;
|
|
$out['before_count'] = $before;
|
|
$out['after_count'] = $after;
|
|
$out['gold'] = $gold;
|
|
echo json_encode($out);
|
|
exit;
|
|
}
|
|
|
|
if ($action === 'splice_drill') {
|
|
// Inject drill-down block before </body>
|
|
$block_src = '/tmp/drill-block.html';
|
|
if (!file_exists($block_src)) die(json_encode(array_merge($out, ['error'=>'drill_block_missing'])));
|
|
// Already has drill?
|
|
$has = (int) @shell_exec('grep -c __opusUniversalDrill ' . escapeshellarg($full));
|
|
if ($has > 0) { $out['ok'] = true; $out['already'] = true; echo json_encode($out); exit; }
|
|
$line = (int) @shell_exec('grep -n "</body>" ' . escapeshellarg($full) . ' | head -1 | cut -d: -f1');
|
|
if ($line <= 0) die(json_encode(array_merge($out, ['error'=>'no_body_tag'])));
|
|
$gold = '/opt/wevads/vault/' . $file . '.GOLD-' . date('Ymd-His') . '-safe-splice';
|
|
@shell_exec('cp ' . escapeshellarg($full) . ' ' . escapeshellarg($gold) . ' 2>&1');
|
|
@shell_exec('sudo -n chattr -i ' . escapeshellarg($full) . ' 2>&1');
|
|
$tmp = '/tmp/splice-' . uniqid() . '.html';
|
|
@shell_exec('head -n ' . ($line-1) . ' ' . escapeshellarg($full) . ' > ' . escapeshellarg($tmp));
|
|
@shell_exec('cat ' . escapeshellarg($block_src) . ' >> ' . escapeshellarg($tmp));
|
|
@shell_exec('tail -n +' . $line . ' ' . escapeshellarg($full) . ' >> ' . escapeshellarg($tmp));
|
|
@shell_exec('cp ' . escapeshellarg($tmp) . ' ' . escapeshellarg($full));
|
|
@shell_exec('chown www-data:www-data ' . escapeshellarg($full) . ' 2>&1');
|
|
@shell_exec('sudo -n chattr +i ' . escapeshellarg($full) . ' 2>&1');
|
|
@shell_exec('rm -f ' . escapeshellarg($tmp));
|
|
$after = (int) @shell_exec('grep -c __opusUniversalDrill ' . escapeshellarg($full));
|
|
$out['ok'] = $after > 0;
|
|
$out['after_count'] = $after;
|
|
$out['injected_line'] = $line;
|
|
$out['gold'] = $gold;
|
|
echo json_encode($out);
|
|
exit;
|
|
}
|
|
|
|
if ($action === 'validate') {
|
|
$out['has_drill'] = (int) @shell_exec('grep -c __opusUniversalDrill ' . escapeshellarg($full));
|
|
$out['size_bytes'] = filesize($full);
|
|
$out['md5'] = md5_file($full);
|
|
$lsattr = trim(@shell_exec('lsattr ' . escapeshellarg($full) . ' 2>&1'));
|
|
$out['immutable'] = (strpos($lsattr, 'i') !== false);
|
|
$hs = @file_get_contents('https://weval-consulting.com/' . $file);
|
|
$out['http_ok'] = ($hs !== false && strlen($hs) > 100);
|
|
$out['ok'] = true;
|
|
echo json_encode($out);
|
|
exit;
|
|
}
|
|
|
|
die(json_encode(array_merge($out, ['error'=>'unknown_action', 'available'=>['sed_patch','splice_drill','validate']])));
|