1
0
Fork 0
mirror of https://github.com/wallabag/wallabag.git synced 2025-06-27 16:36:00 +00:00

Protect remove_tag with a CSRF token

This commit is contained in:
Yassine Guedidi 2025-03-23 13:46:38 +01:00
parent d1e128900a
commit ddf2e80842
4 changed files with 19 additions and 14 deletions

View file

@ -10,6 +10,7 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
use Wallabag\CoreBundle\Entity\Entry;
@ -87,12 +88,16 @@ class TagController extends AbstractController
/**
* Removes tag from entry.
*
* @Route("/remove-tag/{entry}/{tag}", requirements={"entry" = "\d+", "tag" = "\d+"}, name="remove_tag")
* @Route("/remove-tag/{entry}/{tag}", name="remove_tag", methods={"POST"}, requirements={"entry" = "\d+", "tag" = "\d+"})
*
* @return Response
*/
public function removeTagFromEntry(Request $request, Entry $entry, Tag $tag)
{
if (!$this->isCsrfTokenValid('remove-tag', $request->request->get('token'))) {
throw new BadRequestHttpException('Bad CSRF token.');
}
$this->checkUserAction($entry);
$entry->removeTag($tag);

View file

@ -5,9 +5,13 @@
<a class="chip-label" href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a>
{% if withRemove is defined and withRemove == true %}
{% set current_path = path(app.request.attributes.get('_route'), app.request.attributes.get('_route_params')) %}
<a class="chip-action" href="{{ path('remove_tag', {'entry': entryId, 'tag': tag.id, redirect: current_path}) }}" onclick="return confirm('{{ 'entry.confirm.delete_tag'|trans|escape('js') }}')">
<i class="material-icons vertical-align-middle">delete</i>
</a>
<form action="{{ path('remove_tag', {'entry': entryId, 'tag': tag.id, redirect: current_path}) }}" method="post">
<input type="hidden" name="token" value="{{ csrf_token('remove-tag') }}"/>
<button type="submit" class="btn-link chip-action" onclick="return confirm('{{ 'entry.confirm.delete_tag'|trans|escape('js') }}')">
<i class="material-icons vertical-align-middle">delete</i>
</button>
</form>
{% endif %}
</li>
{% endfor %}

View file

@ -1694,7 +1694,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$this->assertSame('example.com', $content->getDomainName());
}
public function testEntryDeleteTagLink()
public function testEntryDeleteTagForm()
{
$this->logInAs('admin');
$client = $this->getTestClient();
@ -1705,10 +1705,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->request('GET', '/view/' . $entry->getId());
// As long as the deletion link of a tag is following
// a link to the tag view, we take the second one to retrieve
// the deletion link of the first tag
$link = $crawler->filter('body div#article div.tools ul.tags li.chip a')->extract(['href'])[1];
$link = $crawler->filter('body div#article div.tools ul.tags li.chip form')->extract(['action'])[0];
$this->assertStringStartsWith(sprintf('/remove-tag/%s/%s', $entry->getId(), $tag->getId()), $link);
}

View file

@ -126,8 +126,8 @@ class TagControllerTest extends WallabagCoreTestCase
$crawler = $client->request('GET', '/view/' . $entry->getId());
$entryUri = $client->getRequest()->getRequestUri();
$link = $crawler->filter('a[href^="/remove-tag/' . $entry->getId() . '/' . $tag->getId() . '"]')->link();
$client->click($link);
$form = $crawler->filter('form[action^="/remove-tag/' . $entry->getId() . '/' . $tag->getId() . '"]')->form();
$client->submit($form);
$this->assertSame(302, $client->getResponse()->getStatusCode());
$this->assertSame($entryUri, $client->getResponse()->getTargetUrl());
@ -136,9 +136,8 @@ class TagControllerTest extends WallabagCoreTestCase
$entry = $this->getEntityManager()->getRepository(Entry::class)->find($entry->getId());
$this->assertNotContains($this->tagName, $entry->getTagsLabel());
$client->request('GET', '/remove-tag/' . $entry->getId() . '/' . $tag->getId());
$this->assertSame(404, $client->getResponse()->getStatusCode());
$client->request('GET', '/view/' . $entry->getId());
$this->assertStringNotContainsString('/remove-tag/' . $entry->getId() . '/' . $tag->getId(), $client->getResponse()->getContent());
$tag = $client->getContainer()
->get(EntityManagerInterface::class)