security.test_idor
Test web applications for IDOR vulnerabilities by checking access control on object references to identify security weaknesses.
Instructions
Test for IDOR (Insecure Direct Object Reference) vulnerabilities
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Target URL with ID parameter | |
| idParam | No | ID parameter name | id |
| testIds | No | IDs to test (e.g., [1, 2, 3]) |
Implementation Reference
- src/tools/security.ts:285-354 (handler)The handler function executes IDOR tests by sending GET requests to the target URL with varying ID values, establishes a baseline response, detects differences indicating potential unauthorized access, logs findings via saveFinding, saves test results, and returns formatted output.async ({ url, idParam = 'id', testIds = [1, 2, 3, 999, 1000] }: any): Promise<ToolResult> => { try { const results: any[] = []; let baselineResponse: AxiosResponse | null = null; for (const testId of testIds) { try { const response = await axios.get(url, { params: { [idParam]: testId }, validateStatus: () => true, timeout: 15000, }); if (!baselineResponse) { baselineResponse = response; } const isDifferent = response.status !== baselineResponse.status || response.data !== baselineResponse.data; const result = { id: testId, status: response.status, length: typeof response.data === 'string' ? response.data.length : JSON.stringify(response.data).length, isDifferent, accessible: response.status === 200, }; if (isDifferent && response.status === 200) { await saveFinding({ target: url, type: 'IDOR', severity: 'high', description: `Potential IDOR - different response for ID: ${testId}`, payload: `${idParam}=${testId}`, response: typeof response.data === 'string' ? response.data.substring(0, 1000) : JSON.stringify(response.data).substring(0, 1000), timestamp: new Date(), score: 7, }); } results.push(result); } catch (error: any) { results.push({ id: testId, error: error.message, }); } } const idorScore = results.some((r: any) => r.vulnerable) ? 8 : 4; await saveTestResult(url, 'idor_test', true, { results }, undefined, idorScore, JSON.stringify(testIds), JSON.stringify(results)); return formatToolResult(true, { results, summary: { totalTests: testIds.length, accessible: results.filter((r) => r.accessible).length, potentialVulns: results.filter((r) => r.isDifferent && r.accessible).length, }, }); } catch (error: any) { await saveTestResult(url, 'idor_test', false, null, error.message, 0, JSON.stringify(testIds), undefined); return formatToolResult(false, null, error.message); } }
- src/tools/security.ts:271-283 (schema)Input schema defining parameters for the IDOR test: required 'url', optional 'idParam' (default 'id'), and 'testIds' array of numbers.inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'Target URL with ID parameter' }, idParam: { type: 'string', description: 'ID parameter name', default: 'id' }, testIds: { type: 'array', items: { type: 'number' }, description: 'IDs to test (e.g., [1, 2, 3])', }, }, required: ['url'], },
- src/tools/security.ts:268-355 (registration)Registers the 'security.test_idor' tool on the MCP server within the registerSecurityTools function, which is called from src/index.ts.'security.test_idor', { description: 'Test for IDOR (Insecure Direct Object Reference) vulnerabilities', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'Target URL with ID parameter' }, idParam: { type: 'string', description: 'ID parameter name', default: 'id' }, testIds: { type: 'array', items: { type: 'number' }, description: 'IDs to test (e.g., [1, 2, 3])', }, }, required: ['url'], }, }, async ({ url, idParam = 'id', testIds = [1, 2, 3, 999, 1000] }: any): Promise<ToolResult> => { try { const results: any[] = []; let baselineResponse: AxiosResponse | null = null; for (const testId of testIds) { try { const response = await axios.get(url, { params: { [idParam]: testId }, validateStatus: () => true, timeout: 15000, }); if (!baselineResponse) { baselineResponse = response; } const isDifferent = response.status !== baselineResponse.status || response.data !== baselineResponse.data; const result = { id: testId, status: response.status, length: typeof response.data === 'string' ? response.data.length : JSON.stringify(response.data).length, isDifferent, accessible: response.status === 200, }; if (isDifferent && response.status === 200) { await saveFinding({ target: url, type: 'IDOR', severity: 'high', description: `Potential IDOR - different response for ID: ${testId}`, payload: `${idParam}=${testId}`, response: typeof response.data === 'string' ? response.data.substring(0, 1000) : JSON.stringify(response.data).substring(0, 1000), timestamp: new Date(), score: 7, }); } results.push(result); } catch (error: any) { results.push({ id: testId, error: error.message, }); } } const idorScore = results.some((r: any) => r.vulnerable) ? 8 : 4; await saveTestResult(url, 'idor_test', true, { results }, undefined, idorScore, JSON.stringify(testIds), JSON.stringify(results)); return formatToolResult(true, { results, summary: { totalTests: testIds.length, accessible: results.filter((r) => r.accessible).length, potentialVulns: results.filter((r) => r.isDifferent && r.accessible).length, }, }); } catch (error: any) { await saveTestResult(url, 'idor_test', false, null, error.message, 0, JSON.stringify(testIds), undefined); return formatToolResult(false, null, error.message); } } );