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

Protect archive_entry with a CSRF token

This commit is contained in:
Yassine Guedidi 2025-03-21 23:21:25 +01:00
parent 3817010e29
commit edffef8375
7 changed files with 73 additions and 17 deletions

View file

@ -6,11 +6,32 @@ nav {
line-height: initial;
}
// adapted from anchor styles from node_modules/materialize-css/sass/components/_navbar.scss
nav ul button {
transition: background-color .3s;
font-size: 1rem;
color: #fff;
display: block;
padding: 0 15px;
cursor: pointer;
background: none;
border: 0;
&:focus {
background: none;
}
&:hover {
background-color: rgba(0 0 0 / 10%);
}
}
nav {
input {
color: #aaa;
}
ul button:hover,
ul a:hover {
background-color: initial;
}
@ -34,6 +55,7 @@ nav {
justify-content: space-between;
align-items: center;
button,
a {
padding: 10px 15px;
}

View file

@ -15,7 +15,7 @@ $(document).ready(() => {
/* mark as read */
Mousetrap.bind('a', () => {
$('ul.side-nav a.markasread i')[0].click();
$('ul.side-nav button.markasread i')[0].click();
});
/* delete */

View file

@ -434,12 +434,16 @@ class EntryController extends AbstractController
/**
* Changes read status for an entry.
*
* @Route("/archive/{id}", requirements={"id" = "\d+"}, name="archive_entry")
* @Route("/archive/{id}", name="archive_entry", methods={"POST"}, requirements={"id" = "\d+"})
*
* @return RedirectResponse
*/
public function toggleArchiveAction(Request $request, Entry $entry)
{
if (!$this->isCsrfTokenValid('archive-entry', $request->request->get('token'))) {
throw new BadRequestHttpException('Bad CSRF token.');
}
$this->checkUserAction($entry);
$entry->toggleArchive();

View file

@ -14,7 +14,13 @@
<a title="{{ 'entry.list.show_same_domain'|trans }}" class="tool grey-text" href="{{ path('same_domain', {'id': entry.id, redirect: current_path}) }}" data-action="same_domain" data-entry-id="{{ entry.id }}"><i class="material-icons">language</i></a>
</li>
<li>
<a title="{{ 'entry.list.toogle_as_read'|trans }}" class="tool grey-text" href="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" data-action="archived" data-entry-id="{{ entry.id }}"><i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i></a>
<form action="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
<button type="submit" class="btn-link tool grey-text" title="{{ 'entry.list.toogle_as_read'|trans }}">
<i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
</button>
</form>
</li>
<li>
<a title="{{ 'entry.list.toogle_as_star'|trans }}" class="tool grey-text" href="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}" data-action="star" data-entry-id="{{ entry.id }}"><i class="material-icons">{% if entry.isStarred == 0 %}star_border{% else %}star{% endif %}</i></a>

View file

@ -15,7 +15,13 @@
<ul class="tools-list hide-on-small-only">
<li>
<a title="{{ 'entry.list.show_same_domain'|trans }}" class="tool grey-text" href="{{ path('same_domain', {'id': entry.id, redirect: current_path}) }}"><i class="material-icons">language</i></a>
<a title="{{ 'entry.list.toogle_as_read'|trans }}" class="tool grey-text" href="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}"><i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i></a>
<form action="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
<button type="submit" class="btn-link tool grey-text" title="{{ 'entry.list.toogle_as_read'|trans }}">
<i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
</button>
</form>
<a title="{{ 'entry.list.toogle_as_star'|trans }}" class="tool grey-text" href="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}"><i class="material-icons">{% if entry.isStarred == 0 %}star_border{% else %}star{% endif %}</i></a>
<a title="{{ 'entry.list.delete'|trans }}" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')" class="tool grey-text delete" href="{{ path('delete_entry', {'id': entry.id, redirect: current_path}) }}"><i class="material-icons">delete</i></a>
</li>

View file

@ -26,9 +26,13 @@
</ul>
<ul class="right">
<li>
<a class="waves-effect" title="{{ 'entry.view.left_menu.set_as_read'|trans }}" href="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" id="markAsRead">
<i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
</a>
<form action="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
<button type="submit" class="waves-effect" title="{{ 'entry.view.left_menu.set_as_read'|trans }}">
<i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
</button>
</form>
</li>
<li>
<a class="waves-effect" title="{{ 'entry.view.left_menu.set_as_starred'|trans }}" href="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}" id="setFav">
@ -73,10 +77,14 @@
{% endif %}
<li class="bold hide-on-med-and-down">
<a class="waves-effect collapsible-header markasread" title="{{ mark_as_read_label|trans }}" href="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" id="markAsRead">
<i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
<span>{{ mark_as_read_label|trans }}</span>
</a>
<form action="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" method="post">
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
<button type="submit" class="waves-effect collapsible-header markasread" title="{{ mark_as_read_label|trans }}">
<i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
<span>{{ mark_as_read_label|trans }}</span>
</button>
</form>
<div class="collapsible-body"></div>
</li>
@ -304,7 +312,15 @@
<i class="material-icons">menu</i>
</a>
<ul>
<li><a class="btn-floating" href="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}"><i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i></a></li>
<li>
<form action="{{ path('archive_entry', {'id': entry.id, redirect: current_path}) }}" method="post" class="inline-block">
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
<button type="submit" class="btn-floating">
<i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
</button>
</form>
</li>
<li><a class="btn-floating" href="{{ path('star_entry', {'id': entry.id, redirect: current_path}) }}"><i class="material-icons">{% if entry.isStarred == 0 %}star_outline{% else %}star{% endif %}</i></a></li>
<li><a class="btn-floating" href="{{ path('delete_entry', {'id': entry.id, redirect: current_path}) }}" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')"><i class="material-icons">delete</i></a></li>
</ul>

View file

@ -645,7 +645,9 @@ class EntryControllerTest extends WallabagCoreTestCase
$this->getEntityManager()->flush();
$this->getEntityManager()->clear();
$client->request('GET', '/archive/' . $entry->getId());
$crawler = $client->request('GET', '/view/' . $entry->getId());
$client->submit($crawler->filter('.left-bar')->selectButton('entry.view.left_menu.set_as_read')->form());
$this->assertSame(302, $client->getResponse()->getStatusCode());
@ -1283,8 +1285,9 @@ class EntryControllerTest extends WallabagCoreTestCase
$this->getEntityManager()->flush();
$client->request('GET', '/view/' . $entry->getId());
$client->request('GET', '/archive/' . $entry->getId());
$crawler = $client->request('GET', '/view/' . $entry->getId());
$client->submit($crawler->filter('.left-bar')->selectButton('entry.view.left_menu.set_as_read')->form());
$this->assertSame(302, $client->getResponse()->getStatusCode());
$this->assertSame('/', $client->getResponse()->headers->get('location'));
@ -1308,8 +1311,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->request('GET', '/view/' . $entry->getId());
$link = $crawler->filter('a[id="markAsRead"]')->link();
$client->click($link);
$client->submit($crawler->filter('.left-bar')->selectButton('entry.view.left_menu.set_as_read')->form());
$this->assertSame(302, $client->getResponse()->getStatusCode());
$this->assertStringContainsString('/view/' . $entry->getId(), $client->getResponse()->headers->get('location'));