sast-idor
Detect Insecure Direct Object Reference (IDOR) vulnerabilities in a codebase using a three-phase approach: recon (find candidates), batched verify (check authorization in parallel subagents, 3 candidates each), and merge (consolidate batch results).
Install / Use
npx skills add utkusen/sast-skills --skill sast-idorInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
Development & EngineeringSupported Platforms
Tags
Our assessment of sast-idor
sast-idor scores 89/100 on our quality scale, 1189th of 4,259 Development & Engineering skills we index (top 28%).
Its SKILL.md is 19 KB long, well organised into 31 sections with 14 code examples: a thorough specification that gives an agent plenty to work with.
With 1,321 GitHub stars, it is one of the more widely adopted skills in the catalogue.
Maintenance, license and trust
- The repository was last updated about 6 months ago. That is recent enough to be usable, but agent tooling moves fast, so check the instructions against your agent's current version.
- It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 98/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
sast-idor compared with similar skills
All 4 of these similar skills score higher than sast-idor; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| sast-idor (this skill)by utkusen | 89 | 1.3k | 6mo ago | SKILL.md |
| ai-job-searchby MadsLorentzen | 100 | 44.6k | 1d ago | CLAUDE.md |
| claude-howtoby luongnv89 | 100 | 41.7k | today | CLAUDE.md |
| algorithmic-artby anthropics | 100 | 177.9k | 8d ago | SKILL.md |
| pptxby anthropics | 100 | 177.9k | 8d ago | SKILL.md |
Frequently asked questions
- How do I install sast-idor?
- Run
npx skills add utkusen/sast-skills --skill sast-idor. The install tabs above show the steps for each supported agent. - Which AI agents does sast-idor work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is sast-idor safe to use?
- It is MIT-licensed and scores 98/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is sast-idor still maintained?
- The repository was last updated about 6 months ago. That is recent enough to be usable, but agent tooling moves fast, so check the instructions against your agent's current version.
Skill content
View source on GitHubname: sast-idor description: >- Detect Insecure Direct Object Reference (IDOR) vulnerabilities in a codebase using a three-phase approach: recon (find candidates), batched verify (check authorization in parallel subagents, 3 candidates each), and merge (consolidate batch results). Checks endpoints for missing ownership or authorization checks on user-supplied identifiers. Requires sast/architecture.md (run sast-analysis first). Outputs findings to sast/idor-results.md. Use when asked to find IDOR or authorization bypass bugs.
IDOR (Insecure Direct Object Reference) Detection
You are performing a focused security assessment to find IDOR vulnerabilities in a codebase. This skill uses a three-phase approach with subagents: recon (find candidate endpoints), batched verify (check authorization in parallel batches of 3), and merge (consolidate results).
Prerequisites: sast/architecture.md must exist. Run the analysis skill first if it doesn't.
What is IDOR
IDOR occurs when an application uses a user-supplied identifier (ID, slug, filename, etc.) to directly access an object without verifying the requesting user is authorized to access that specific object. The application authenticates the user but fails to check ownership or permissions on the requested resource.
The core pattern: authenticated user A can access or modify resources belonging to user B by changing an identifier in the request.
What IDOR IS
- Changing
/api/orders/1001to/api/orders/1002and seeing another user's order - Sending
DELETE /api/documents/555to delete a document you don't own - Modifying
{"account_id": 789}in a request body to transfer money from someone else's account - Changing a file download parameter
?file_id=42to access another user's private file - Updating another user's profile via
PUT /api/users/other-user-id
What IDOR is NOT
Do not flag these as IDOR:
- Missing authentication: Endpoint requires no login at all → that's "Unauthenticated Access", a different class
- Broken function-level access control: Regular user accessing
/admin/dashboard→ that's vertical privilege escalation, not IDOR - Public resources: Accessing
/api/posts/123where posts are intentionally public is not IDOR - Parameter tampering on non-object fields: Changing
role=adminorprice=0in a request → that's mass assignment or business logic, not IDOR - SQL injection via ID fields:
?id=1 OR 1=1→ that's SQLi, not IDOR
Authorization Patterns That Prevent IDOR
When you see these patterns, the endpoint is likely not vulnerable:
1. Query scoped to current user (most common fix)
# The query itself ensures only the user's own records are returned
Order.objects.filter(id=order_id, user=request.user) # Django
current_user.orders.find(params[:id]) # Rails
Order.findOne({ _id: orderId, userId: req.user.id }) # Mongoose
SELECT * FROM orders WHERE id = ? AND user_id = ? # Raw SQL
2. Explicit ownership check after fetch
order = Order.find(order_id)
if order.user_id != current_user.id:
raise Forbidden
3. Policy / ability / authorization middleware
authorize('view', order) # Laravel Policy
can?(:read, @order) # CanCanCan (Rails)
@PreAuthorize("@auth.ownsOrder(#orderId)") # Spring Security
4. Tenant/organization scoping
# Multi-tenant apps that scope all queries to the tenant
tenant = get_current_tenant(request)
Order.objects.filter(id=order_id, tenant=tenant)
Vulnerable vs. Secure Examples
Python — Django
# VULNERABLE: fetches any order by ID, no ownership check
def get_order(request, order_id):
order = Order.objects.get(id=order_id)
return JsonResponse(model_to_dict(order))
# SECURE: query scoped to requesting user
def get_order(request, order_id):
order = get_object_or_404(Order, id=order_id, user=request.user)
return JsonResponse(model_to_dict(order))
Python — Flask / SQLAlchemy
# VULNERABLE
@app.route('/api/documents/<int:doc_id>')
@login_required
def get_document(doc_id):
doc = Document.query.get_or_404(doc_id)
return jsonify(doc.serialize())
# SECURE
@app.route('/api/documents/<int:doc_id>')
@login_required
def get_document(doc_id):
doc = Document.query.filter_by(id=doc_id, owner_id=current_user.id).first_or_404()
return jsonify(doc.serialize())
Node.js — Express / Mongoose
// VULNERABLE
router.get('/api/orders/:id', auth, async (req, res) => {
const order = await Order.findById(req.params.id);
res.json(order);
});
// SECURE
router.get('/api/orders/:id', auth, async (req, res) => {
const order = await Order.findOne({ _id: req.params.id, userId: req.user.id });
if (!order) return res.status(404).json({ error: 'Not found' });
res.json(order);
});
Node.js — Express / Prisma
// VULNERABLE
router.get('/api/invoices/:id', auth, async (req, res) => {
const invoice = await prisma.invoice.findUnique({ where: { id: req.params.id } });
res.json(invoice);
});
// SECURE
router.get('/api/invoices/:id', auth, async (req, res) => {
const invoice = await prisma.invoice.findFirst({
where: { id: req.params.id, userId: req.user.id }
});
if (!invoice) return res.status(404).json({ error: 'Not found' });
res.json(invoice);
});
Ruby on Rails
# VULNERABLE
def show
@order = Order.find(params[:id])
end
# SECURE
def show
@order = current_user.orders.find(params[:id])
end
Java — Spring Boot
// VULNERABLE
@GetMapping("/api/accounts/{id}")
public Account getAccount(@PathVariable Long id) {
return accountRepo.findById(id).orElseThrow();
}
// SECURE
@GetMapping("/api/accounts/{id}")
public Account getAccount(@PathVariable Long id, Authentication auth) {
Account acct = accountRepo.findById(id).orElseThrow();
if (!acct.getOwnerId().equals(auth.getName()))
throw new AccessDeniedException("Forbidden");
return acct;
}
Go
// VULNERABLE
func GetOrder(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
order, _ := db.GetOrder(id)
json.NewEncoder(w).Encode(order)
}
// SECURE
func GetOrder(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
userID := r.Context().Value("userID").(string)
order, _ := db.GetOrderByUser(id, userID)
json.NewEncoder(w).Encode(order)
}
PHP — Laravel
// VULNERABLE
public function show($id) {
return Invoice::findOrFail($id);
}
// SECURE (scoped query)
public function show($id) {
return auth()->user()->invoices()->findOrFail($id);
}
// SECURE (policy)
public function show($id) {
$invoice = Invoice::findOrFail($id);
$this->authorize('view', $invoice);
return $invoice;
}
C# — ASP.NET Core
// VULNERABLE
[HttpGet("api/profiles/{id}")]
public async Task<IActionResult> GetProfile(int id) {
var profile = await _db.Profiles.FindAsync(id);
return Ok(profile);
}
// SECURE
[HttpGet("api/profiles/{id}")]
public async Task<IActionResult> GetProfile(int id) {
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var profile = await _db.Profiles.FirstOrDefaultAsync(p => p.Id == id && p.UserId == userId);
if (profile == null) return NotFound();
return Ok(profile);
}
Execution
This skill runs in three phases using subagents. Pass the contents of sast/architecture.md to all subagents as context.
Phase 1: Recon — Find Candidate Endpoints
Launch a subagent with the following instructions:
Goal: Find every endpoint, controller action, or handler that retrieves, modifies, or deletes a specific object using a user-supplied identifier. Write results to
sast/idor-recon.md.Context: You will be given the project's architecture summary. Use it to understand the tech stack, frameworks, route definitions, and data access patterns.
What to search for:
Route definitions that contain ID parameters:
- Path parameters:
:id,{id},<int:id>,[id]- Search patterns: route/path/endpoint definitions with parameter placeholders
Controller/handler methods that accept ID arguments and use them to fetch or mutate objects:
- ORM lookups:
find(id),findById(),get(id=),objects.get(),findOne(),findUnique(),findFirst(),query.get(),where(id:)- Raw queries:
SELECT ... WHERE id = ?, etc.- Also look for delete, update operations with user-supplied IDs
Request body or query parameter IDs used in operations:
req.body.userId,req.query.id,request.data['account_id'], etc.GraphQL resolvers and mutations that accept ID arguments
File/resource access by user-supplied path or filename
What to ignore:
- Endpoints that are intentionally public (no auth required by design)
- Admin-only endpoints behind role-based checks (these are a different class)
- Endpoints where the only ID used is the authenticated user's own ID (e.g.,
GET /api/me/profile)- Static asset serving
Output format — write to
sast/idor-recon.md:# IDOR Recon: [Project Name] ## Summary Found [N] candidate endpoints that use user-supplied identifiers to access objects. ## Candidates ### 1. [Descriptive name] - **File**: `path/to/file.ext` (lines X-Y) - **Endpoint**: `METHOD /path/:param` - **Identifier source**: [path param / query param / body field] - **Operation**: [read / update / delete] - **Object accessed**: [model/table name] - **Code snippet**:[relevant code]
[Repeat for each candidate]
Phase 2: Verify — Check Authorization (Batched)
After Phase 1 completes, read sast/idor-recon.md and split the candidates into batches of up to 3 candidates each. Launch one subagent per batch in parallel. Each subagent verifies only its assigned candidates and writes results to its own batch file.
Batching procedure (you, the orchestrator, do this — not a subagent):
- Read
sast/idor-recon.mdand count the numbered candidate sections (### 1., ### 2., etc.). - Divide them into batches of up to 3. For example, 8 candidates → 3 batches (1-3, 4-6, 7-8).
- For each batch, extract the full text of those candidate sections from the recon file.
- Launch all batch subagents in parallel, passing each one only its assigned candidates.
- Each subagent writes to
sast/idor-batch-N.mdwhere N is the 1-based batch number. - Identify the project's primary language/framework from
sast/architecture.mdand select only the matching examples from the "Vulnerable vs. Secure Examples" section above. For example, if the project uses Node.js/Express with Prisma, include only the "Node.js — Express / Prisma" and "Node.js — Express / Mongoose" examples. Include these selected examples in each subagent's instructions where indicated by[TECH-STACK EXAMPLES]below.
Give each batch subagent the following instructions (substitute the batch-specific values):
Goal: Verify the following IDOR (Insecure Direct Object Reference) candidates and determine whether adequate authorization checks exist. Our goal is to find IDOR vulnerabilities. Write results to
sast/idor-batch-[N].md.Your assigned candidates (from the recon phase):
[Paste the full text of the assigned candidate sections here, preserving the original numbering]
Context: You will be given the project's architecture summary. Use it to understand the auth mechanism, middleware stack, and ORM patterns.
IDOR Reference — What to look for:
IDOR occurs when an authenticated user can access or modify resources belonging to another user by changing an identifier in the request. Focus on *horizontal privilege escalation
Truncated for display — read the full file on GitHub.
Related Skills
ai-job-search
44.6kThe job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.
claude-howto
41.7kA visual, example-driven guide to Claude Code — from basic concepts to advanced agents, with copy-paste templates that bring immediate value.
algorithmic-art
177.9kCreating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems.
pptx
177.9kUse this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an em…
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
