The TagController::delete() endpoint at DELETE /admin/api/content/tags/{tagId} only verifies that the user is logged in (userIsAuthenticated()), but does not check any permission. Any authenticated user — including regular non-admin frontend users — can delete any tag by ID. This contrasts with TagController::update() and TagController::search(), which both enforce the FAQ_EDIT permission.
In phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/TagController.php, the delete() method (line 121-133) uses only $this->userIsAuthenticated():
#[Route(path: 'content/tags/{tagId}', name: 'admin.api.content.tags.id', methods: ['DELETE'])]
public function delete(Request $request): JsonResponse
{
$this->userIsAuthenticated(); // Only checks isLoggedIn() — no permission check
$tagId = (int) Filter::filterVar($request->attributes->get('tagId'), FILTER_VALIDATE_INT);
if ($this->tags->delete($tagId)) {
return $this->json(['success' => Translation::get(key: 'ad_tag_delete_success')], Response::HTTP_OK);
}
return $this->json(['error' => Translation::get(key: 'ad_tag_delete_error')], Response::HTTP_BAD_REQUEST);
}
Compare with update() (line 48-71) which properly enforces authorization:
public function update(Request $request): JsonResponse
{
$this->userHasPermission(PermissionType::FAQ_EDIT); // Proper permission check
// ... also verifies CSRF token ...
}
The userIsAuthenticated() method in AbstractController (line 258-263) only checks $this->currentUser->isLoggedIn():
protected function userIsAuthenticated(): void
{
if (!$this->currentUser->isLoggedIn()) {
throw new UnauthorizedHttpException(challenge: 'User is not authenticated.');
}
}
There is no admin-level middleware in the Kernel — it registers only RouterListener, LanguageListener, ControllerContainerListener, and exception listeners. The admin API entry point...
4.1.24.1.2Exploitability
AV:NAC:LPR:LUI:NScope
S:UImpact
C:NI:LA:L5.4/CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L