From harness-claude
Enforces object-level authorization by scoping database queries to the authenticated user and centralizing ownership checks in a dedicated service.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:owasp-idor-preventionThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Enforce object-level authorization so users can only access resources they own or are permitted to access
Enforce object-level authorization so users can only access resources they own or are permitted to access
The most reliable pattern: include userId in every database query, not just in authorization checks after the fact.
// BAD — fetches by ID only, attacker can enumerate any order
app.get('/orders/:id', authenticate, async (req, res) => {
const order = await db.order.findUnique({ where: { id: req.params.id } });
if (!order) return res.status(404).json({ error: 'Not found' });
res.json(order); // returns order even if it belongs to another user
});
// GOOD — scope the query to the current user
app.get('/orders/:id', authenticate, async (req, res) => {
const order = await db.order.findFirst({
where: {
id: req.params.id,
userId: req.user.id, // IDOR prevention at query level
},
});
if (!order) return res.status(404).json({ error: 'Not found' }); // same error for not found and unauthorized
res.json(order);
});
Centralize ownership checks in a dedicated service to avoid scattered, inconsistent checks:
// authorization.service.ts
@Injectable()
export class AuthorizationService {
constructor(private db: PrismaService) {}
async assertOwnsOrder(userId: string, orderId: string): Promise<Order> {
const order = await this.db.order.findFirst({
where: { id: orderId, userId },
});
if (!order) throw new ForbiddenException('Access denied');
return order;
}
async assertCanAccessDocument(userId: string, docId: string): Promise<Document> {
const doc = await this.db.document.findFirst({
where: {
id: docId,
OR: [
{ ownerId: userId },
{ sharedWith: { some: { userId } } },
],
},
});
if (!doc) throw new ForbiddenException('Access denied');
return doc;
}
}
// order.controller.ts
@Get(':id')
async getOrder(@Param('id') id: string, @CurrentUser() user: User) {
return this.authz.assertOwnsOrder(user.id, id);
}
// resource-owner.guard.ts
@Injectable()
export class ResourceOwnerGuard implements CanActivate {
constructor(
private reflector: Reflector,
private authz: AuthorizationService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
const userId = req.user.id;
const resourceId = req.params.id;
const resourceType = this.reflector.get<string>('resourceType', context.getHandler());
await this.authz.assertOwns(userId, resourceType, resourceId);
return true;
}
}
// Usage
@Get(':id')
@SetMetadata('resourceType', 'order')
@UseGuards(ResourceOwnerGuard)
getOrder(@Param('id') id: string) { ... }
Use UUIDs for public-facing resource identifiers. Sequential IDs make enumeration trivial.
// PREDICTABLE — easy to enumerate
// GET /invoices/1, /invoices/2, /invoices/3 ...
// BETTER — UUID makes guessing computationally infeasible
// GET /invoices/550e8400-e29b-41d4-a716-446655440000
// Prisma schema
model Order {
id String @id @default(uuid())
userId String
// ...
}
For extra security with internal sequential IDs, map them to tokens:
// Return opaque tokens instead of raw IDs
function encodeResourceId(internalId: number, secret: string): string {
// Use a deterministic encryption (not hash) so you can decode
return Buffer.from(`${internalId}:${secret}`).toString('base64url');
}
IDOR (also called BOLA — Broken Object Level Authorization) is the #1 API security issue per OWASP API Security Top 10. Authentication tells you WHO the user is; authorization tells you WHAT they can access.
Common IDOR mistakes:
userId in request body instead of the JWT/sessionReturn 404 not 403 for unauthorized resources — returning 403 confirms the resource exists, enabling enumeration.
Audit checklist:
findById / findUnique call — does it include userId or equivalent scope?https://owasp.org/www-project-top-ten/
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeDetects missing ownership checks, broken role enforcement, and IDOR vulnerabilities in authorization code. Use when implementing access control middleware or resource ownership checks.
Verifies that API endpoints check resource ownership, not just authentication. Catches IDOR vulnerabilities and missing authorization in handlers that read or mutate user data.
Provides an IDOR testing checklist including object ID enumeration, horizontal/vertical privilege escalation, GUID prediction, and API endpoint testing. Use for web app security assessments and bug bounty recon.