or link deleted objects using the * victim's session cookie. They must arrive by POST with a token now: the * server rejects the GET, and the UI submits them through the token POST path. */ $root = dirname(__DIR__, 2); test('the server rejects these delete actions on GET without a token', function () use ($root) { $src = file_get_contents($root . '/include/global.php'); // The GET-method block covers the original delete actions even as the // protected list grows to include other state-changing actions. foreach (['save', 'update_data', 'changepassword', 'delete_node', 'gt_remove', 'query_remove', 'remove', 'change_leaf'] as $action) { expect($src)->toContain("'$action'"); } // the block still requires the csrf token on POST expect($src)->toContain("!isset(\$_POST['__csrf_magic'])"); }); test('tree.php deletes a node via a token POST, not GET', function () use ($root) { $src = file_get_contents($root . '/tree.php'); expect($src)->toContain("\$.post('?action=delete_node', { 'id' : data.node.id, 'tree_id' : \$('#id').val(), '__csrf_magic' : csrfMagicToken })"); expect($src)->not->toContain("\$.get('?action=delete_node'"); }); test('host.php removes graph templates and data queries via a token POST', function () use ($root) { $src = file_get_contents($root . '/host.php'); // gt_remove and query_remove no longer build a GET url through loadUrl expect($src)->not->toContain("'host.php?action=gt_remove&id='"); expect($src)->not->toContain("'host.php?action=query_remove&id='"); // they go through postUrl with the csrf token expect($src)->toContain("url: 'host.php?action=gt_remove'"); expect($src)->toContain("url: 'host.php?action=query_remove'"); expect(substr_count($src, '__csrf_magic: csrfMagicToken'))->toBeGreaterThanOrEqual(4); }); test('the automation actions guarded here are not linked by GET anywhere', function () use ($root) { /* 'remove' and 'change_leaf' can be required to post because no page emits either as a link. An action still reached by a GET link would break the moment it joined that list, which is why item_remove is not in it. */ $linked = []; foreach (glob($root . '/*.php') as $page) { $src = file_get_contents($page); foreach (["action=remove'", 'action=remove"', 'action=remove&', 'action=change_leaf'] as $needle) { if (str_contains($src, $needle)) { $linked[] = basename($page) . ' => ' . $needle; } } } expect($linked)->toBe([]); });