Download tests/api_fuzz.test.js from mason-keller/vulnforge-mcp: direct link, hf CLI and curl.
- Browser
- Download file 10.1 kB
-
https://huggingface.co/mason-keller/vulnforge-mcp/resolve/main/tests/api_fuzz.test.js
- Command line
-
hf download hf://mason-keller/vulnforge-mcp/tests/api_fuzz.test.js
-
curl -L -o api_fuzz.test.js https://huggingface.co/mason-keller/vulnforge-mcp/resolve/main/tests/api_fuzz.test.js
10.1 kB
| import test from 'node:test'; | |
| import assert from 'node:assert/strict'; | |
| import { | |
| generateMutationSuite, | |
| auditApiFuzzResilience, | |
| BOUNDARY_INTEGER_PAYLOADS, | |
| PROTOTYPE_POLLUTION_PAYLOADS | |
| } from '../dist/fuzz/api_fuzz.js'; | |
| import { synthesizeAndInjectPatch, synthesizeAndVerifyPatch } from '../dist/patch/patch_engine.js'; | |
| import { runInCanarySandbox } from '../dist/sandbox/canary_sandbox.js'; | |
| import { executeCanaryVerification } from '../dist/sandbox/canary_runner.js'; | |
| import { createVulnForgeServer } from '../dist/mcp/server.js'; | |
| import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; | |
| test('Fuzz Generator: generateMutationSuite generates all strategy test cases', () => { | |
| const fields = { id: 'number', name: 'string', metadata: 'object' }; | |
| const mutations = generateMutationSuite(fields, 'ALL', 5); | |
| assert.ok(mutations.length > 10, 'Should generate multiple mutation cases'); | |
| assert.ok( | |
| mutations.some((m) => m.mutationType === 'BOUNDARY_INT'), | |
| 'Should include boundary integer mutations' | |
| ); | |
| assert.ok( | |
| mutations.some((m) => m.mutationType === 'TYPE_CONFUSION'), | |
| 'Should include type confusion mutations' | |
| ); | |
| assert.ok( | |
| mutations.some((m) => m.mutationType === 'PROTOTYPE_POLLUTION'), | |
| 'Should include prototype pollution mutations' | |
| ); | |
| assert.ok( | |
| mutations.some((m) => m.mutationType === 'OVERSIZED_PAYLOAD' || m.mutationType === 'MALFORMED_ENCODING'), | |
| 'Should include encoding / oversized payload mutations' | |
| ); | |
| }); | |
| test('API Fuzz Auditor: Detects missing schema validation on request body (CWE-20)', () => { | |
| const code = ` | |
| app.post('/api/checkout', (req, res) => { | |
| const { itemId, quantity, userToken } = req.body; | |
| res.json({ status: 'ok', itemId, quantity }); | |
| }); | |
| `; | |
| const result = auditApiFuzzResilience({ routeCode: code }); | |
| assert.equal(result.complianceStatus, 'FAIL'); | |
| assert.ok( | |
| result.findings.some((f) => f.id === 'API-FUZZ-MISSING-SCHEMA-VALIDATION'), | |
| 'Should report missing schema validation finding' | |
| ); | |
| assert.equal(result.summary.missingSchemaValidationCount, 1); | |
| }); | |
| test('API Fuzz Auditor: Detects prototype pollution in unsafe object merge (CWE-1321)', () => { | |
| const code = ` | |
| app.post('/api/settings', (req, res) => { | |
| const userSettings = {}; | |
| Object.assign(userSettings, req.body); | |
| res.json({ updated: true }); | |
| }); | |
| `; | |
| const result = auditApiFuzzResilience({ routeCode: code }); | |
| assert.equal(result.complianceStatus, 'FAIL'); | |
| assert.ok( | |
| result.findings.some((f) => f.id === 'API-FUZZ-PROTOTYPE-POLLUTION'), | |
| 'Should detect prototype pollution finding' | |
| ); | |
| assert.equal(result.summary.prototypePollutionRisks, 1); | |
| }); | |
| test('API Fuzz Auditor: Detects unhandled async exception in route handler (CWE-754)', () => { | |
| const code = ` | |
| app.post('/api/transfer', async (req, res) => { | |
| const result = await bankService.sendMoney(req.body); | |
| res.json(result); | |
| }); | |
| `; | |
| const result = auditApiFuzzResilience({ routeCode: code }); | |
| assert.ok( | |
| result.findings.some((f) => f.id === 'API-FUZZ-UNHANDLED-ASYNC-EXCEPTION'), | |
| 'Should detect unhandled async exception risk' | |
| ); | |
| assert.equal(result.summary.unhandledExceptionRisks, 1); | |
| }); | |
| test('API Fuzz Auditor: Detects unrestricted JSON body parser size (CWE-400)', () => { | |
| const code = ` | |
| const app = express(); | |
| app.use(express.json()); | |
| `; | |
| const result = auditApiFuzzResilience({ routeCode: code }); | |
| assert.ok( | |
| result.findings.some((f) => f.id === 'API-FUZZ-BODY-PARSER-UNRESTRICTED-SIZE'), | |
| 'Should detect unrestricted body parser size' | |
| ); | |
| assert.equal(result.summary.oversizedPayloadDosRisks, 1); | |
| }); | |
| test('API Fuzz Auditor: Detects unbounded integer parsing (CWE-190)', () => { | |
| const code = ` | |
| app.get('/api/items', (req, res) => { | |
| const offset = parseInt(req.query.offset); | |
| res.json({ offset }); | |
| }); | |
| `; | |
| const result = auditApiFuzzResilience({ routeCode: code }); | |
| assert.ok( | |
| result.findings.some((f) => f.id === 'API-FUZZ-UNBOUNDED-INTEGER-PARSE'), | |
| 'Should detect unbounded integer parse' | |
| ); | |
| assert.equal(result.summary.typeConfusionRisks, 1); | |
| }); | |
| test('API Fuzz Auditor: Fully hardened and validated API endpoint passes cleanly', () => { | |
| const hardenedCode = ` | |
| import { z } from 'zod'; | |
| const UserSchema = z.object({ | |
| id: z.number().int().min(1).max(10000), | |
| username: z.string().min(3).max(50) | |
| }).strict(); | |
| app.post('/api/user', async (req, res, next) => { | |
| try { | |
| const validated = UserSchema.parse(req.body); | |
| const safeTarget = JSON.parse(JSON.stringify(validated, (k, v) => (k === '__proto__' ? undefined : v))); | |
| res.json({ user: safeTarget }); | |
| } catch (err) { | |
| next(err); | |
| } | |
| }); | |
| `; | |
| const result = auditApiFuzzResilience({ routeCode: hardenedCode }); | |
| assert.equal(result.complianceStatus, 'PASS'); | |
| assert.equal(result.totalFindings, 0); | |
| }); | |
| test('Patch Engine: Injects Zod schema validation guard into vulnerable handler', () => { | |
| const vulnerable = ` | |
| const { username, email } = req.body; | |
| `; | |
| const patched = synthesizeAndInjectPatch({ sourceCode: vulnerable, filename: 'routes/user.ts' }); | |
| assert.equal(patched.applied, true); | |
| assert.ok(patched.patchedCode.includes("import { z } from 'zod';")); | |
| assert.ok(patched.patchedCode.includes('BodySchema = z.object')); | |
| assert.ok(patched.findingsResolved.includes('API-FUZZ-MISSING-SCHEMA-VALIDATION')); | |
| }); | |
| test('Patch Engine: Injects safe prototype pollution guard before Object.assign', () => { | |
| const vulnerable = ` | |
| const target = {}; | |
| Object.assign(target, req.body); | |
| `; | |
| const patched = synthesizeAndInjectPatch({ sourceCode: vulnerable, filename: 'routes/proto.ts' }); | |
| assert.equal(patched.applied, true); | |
| assert.ok(patched.patchedCode.includes('const safeBody = JSON.parse')); | |
| assert.ok(patched.patchedCode.includes('__proto__')); | |
| assert.ok(patched.findingsResolved.includes('API-FUZZ-PROTOTYPE-POLLUTION')); | |
| }); | |
| test('Patch Engine: Restricts body parser size limit to 100kb', () => { | |
| const vulnerable = ` | |
| app.use(express.json()); | |
| `; | |
| const patched = synthesizeAndInjectPatch({ sourceCode: vulnerable, filename: 'server.ts' }); | |
| assert.equal(patched.applied, true); | |
| assert.ok(patched.patchedCode.includes("express.json({ limit: '100kb' })")); | |
| assert.ok(patched.findingsResolved.includes('API-FUZZ-BODY-PARSER-UNRESTRICTED-SIZE')); | |
| }); | |
| test('Canary Sandbox: MockFuzzHarness records route invocations and validates prototype security', async () => { | |
| const code = ` | |
| fuzzHarness.testRoute(() => {}, { body: { title: 'safe_post', __proto__: { polluted: false } } }); | |
| `; | |
| const result = await runInCanarySandbox(code); | |
| assert.equal(result.success, true); | |
| assert.equal(result.fuzzHarness.executedRoutes.length, 1); | |
| assert.equal(result.fuzzHarness.executedRoutes[0].body.title, 'safe_post'); | |
| }); | |
| test('Canary Verification: Dual canary verifies API fuzzing patch mitigates vulnerability and preserves benign requests', async () => { | |
| const original = ` | |
| const target = {}; | |
| Object.assign(target, req.body); | |
| `; | |
| const patched = ` | |
| const target = {}; | |
| const safeBody = JSON.parse(JSON.stringify(req.body, (k, v) => (k === '__proto__' ? undefined : v))); | |
| Object.assign(target, safeBody); | |
| `; | |
| const canaryReport = await executeCanaryVerification({ | |
| originalCode: original, | |
| patchedCode: patched, | |
| vulnType: 'API_FUZZ_RESILIENCE', | |
| attackPayload: '{"__proto__":{"polluted":true}}', | |
| benignPayload: '{"name":"valid_user"}' | |
| }); | |
| assert.equal(canaryReport.canaryStatus, 'VERIFIED_SECURE'); | |
| assert.equal(canaryReport.attackBlocked, true); | |
| assert.equal(canaryReport.benignPreserved, true); | |
| }); | |
| test('MCP Server: Registers vulnforge_fuzz_api tool with comprehensive schema', async () => { | |
| const server = createVulnForgeServer(); | |
| const listHandler = server._requestHandlers.get('tools/list'); | |
| assert.ok(listHandler, 'ListTools handler must exist'); | |
| const result = await listHandler({ method: 'tools/list', params: {} }); | |
| const fuzzTool = result.tools.find((t) => t.name === 'vulnforge_fuzz_api'); | |
| assert.ok(fuzzTool, 'vulnforge_fuzz_api tool must be registered'); | |
| assert.ok(fuzzTool.inputSchema.properties.routeCode, 'routeCode property must be defined'); | |
| assert.ok(fuzzTool.inputSchema.properties.fuzzStrategy, 'fuzzStrategy property must be defined'); | |
| }); | |
| test('MCP Server: vulnforge_fuzz_api executes and returns valid JSON security report', async () => { | |
| const server = createVulnForgeServer(); | |
| const callHandler = server._requestHandlers.get('tools/call'); | |
| assert.ok(callHandler, 'CallTool handler must exist'); | |
| const sampleRoute = `app.post('/api/item', (req, res) => { const { name } = req.body; res.json({ name }); });`; | |
| const response = await callHandler( | |
| { | |
| method: 'tools/call', | |
| params: { | |
| name: 'vulnforge_fuzz_api', | |
| arguments: { routeCode: sampleRoute } | |
| } | |
| }, | |
| {} | |
| ); | |
| assert.ok(response.content && response.content[0].text); | |
| const parsed = JSON.parse(response.content[0].text); | |
| assert.equal(parsed.complianceStatus, 'FAIL'); | |
| assert.ok(parsed.totalFindings > 0); | |
| assert.ok(parsed.summary.endpointsAudited >= 1); | |
| }); | |
| test('MCP Server: vulnforge_full_scan processes fuzzRouteCode and attaches findings', async () => { | |
| const server = createVulnForgeServer(); | |
| const callHandler = server._requestHandlers.get('tools/call'); | |
| assert.ok(callHandler, 'CallTool handler must exist'); | |
| const sampleRoute = `app.post('/api/order', (req, res) => { const { amount } = req.body; res.json({ amount }); });`; | |
| const response = await callHandler( | |
| { | |
| method: 'tools/call', | |
| params: { | |
| name: 'vulnforge_full_scan', | |
| arguments: { fuzzRouteCode: sampleRoute } | |
| } | |
| }, | |
| {} | |
| ); | |
| assert.ok(response.content && response.content[0].text); | |
| const summary = JSON.parse(response.content[0].text); | |
| assert.ok(summary.totalFindings > 0); | |
| assert.ok(summary.findings.some((f) => f.category === 'api_fuzzing')); | |
| }); | |