access-control — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited access-control (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.
Every protected resource must verify that the requesting user has permission. Default to deny.
Related: authentication, api-security, security-context
Never trust route parameters alone. Verify the user owns or can access the resource.
// WRONG — anyone can access any user's data
app.get('/users/:id/settings', async (req, res) => {
const settings = await getSettings(req.params.id);
res.json(settings);
});
// RIGHT — verify ownership
app.get('/users/:id/settings', async (req, res) => {
if (req.params.id !== req.user.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
const settings = await getSettings(req.params.id);
res.json(settings);
});Protect everything by default. Explicitly mark public routes.
# WRONG — protect individual routes (easy to forget one)
@app.route('/admin')
@login_required
def admin(): ...
# RIGHT — protect everything, whitelist public routes
@app.before_request
def require_auth():
public = ['/login', '/signup', '/health']
if request.path not in public and not current_user.is_authenticated:
return redirect('/login')Don't rely on route middleware alone. Check in the service layer too.
// WRONG — only checked at route level
router.delete('/posts/:id', requireAdmin, deletePost);
// RIGHT — checked at route AND service level
router.delete('/posts/:id', requireAdmin, deletePost);
async function deletePost(postId, requestingUser) {
const post = await getPost(postId);
if (!post) throw new NotFoundError();
if (!requestingUser.isAdmin && post.authorId !== requestingUser.id) {
throw new ForbiddenError();
}
await removePost(postId);
}Don't scatter permission checks as ad-hoc if statements. Centralize them.
// WRONG — ad-hoc checks everywhere
if (user.role === 'admin' || user.role === 'editor') { ... }
// RIGHT — centralized policy
const policies = {
'posts:delete': (user, post) => user.isAdmin || post.authorId === user.id,
'users:manage': (user) => user.isAdmin,
};
function authorize(action, user, resource) {
return policies[action]?.(user, resource) ?? false;
}Use UUIDs for public-facing identifiers. Sequential IDs reveal data volume and are easily enumerated.
// WRONG — sequential IDs in URLs
GET /api/invoices/1042
// RIGHT — UUIDs in URLs
GET /api/invoices/a1b2c3d4-e5f6-7890-abcd-ef1234567890| Do | Don't |
|---|---|
| Verify ownership on every request | Trust route parameters |
| Default to deny, whitelist public routes | Protect routes individually (easy to miss) |
| Check permissions in routes AND services | Rely on a single middleware layer |
| Centralize permission logic | Scatter if-statements across codebase |
| Use UUIDs for public-facing IDs | Expose sequential database IDs |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.