symfony-security-voters — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited symfony-security-voters (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You are an expert in Symfony security with Voters pattern within hexagonal architecture.
No endpoint is publicly accessible without explicit `#[IsGranted]` or Voter check.
#[Route('/api/users', methods: ['GET'])]
#[IsGranted('ROLE_USER_LIST')] // ALWAYS required
public function list(): JsonResponse { ... }Define roles in security.yaml:
security:
role_hierarchy:
ROLE_ADMIN: [ROLE_USER_CREATE, ROLE_USER_EDIT, ROLE_USER_DELETE, ROLE_USER_LIST, ROLE_USER_VIEW]
ROLE_MANAGER: [ROLE_USER_LIST, ROLE_USER_VIEW, ROLE_ORDER_MANAGE]
ROLE_USER: [ROLE_USER_VIEW]ROLE_{MODULE}_{ACTION}ROLE_USER_CREATE, ROLE_ORDER_VIEW, ROLE_REPORT_GENERATE#[IsGranted]#[IsGranted('ROLE_USER_CREATE')]
public function create(): JsonResponse { ... }When authorization depends on the resource (e.g., "can this user edit THIS order?"):
namespace App\Infrastructure\{Module}\Security;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
final class {Entity}Voter extends Voter
{
public const VIEW = 'VIEW';
public const EDIT = 'EDIT';
public const DELETE = 'DELETE';
protected function supports(string $attribute, mixed $subject): bool
{
return in_array($attribute, [self::VIEW, self::EDIT, self::DELETE])
&& $subject instanceof {Entity};
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof UserInterface) {
return false;
}
return match ($attribute) {
self::VIEW => $this->canView($subject, $user),
self::EDIT => $this->canEdit($subject, $user),
self::DELETE => $this->canDelete($subject, $user),
default => false,
};
}
private function canView({Entity} $entity, UserInterface $user): bool
{
// Owner or admin can view
return $entity->ownerId() === $user->getId()
|| in_array('ROLE_ADMIN', $user->getRoles());
}
private function canEdit({Entity} $entity, UserInterface $user): bool
{
return $entity->ownerId() === $user->getId();
}
private function canDelete({Entity} $entity, UserInterface $user): bool
{
return in_array('ROLE_ADMIN', $user->getRoles());
}
}#[Route('/{id}', methods: ['PUT'])]
public function update(string $id): JsonResponse
{
$order = $this->getOrder($id);
$this->denyAccessUnlessGranted('EDIT', $order);
// ... proceed
}See references/ for detailed guides:
voter-patterns.md — Full voter examples and testingrole-hierarchy.md — Role hierarchy design patterns~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.