Spaces:
Sleeping
Sleeping
| /** | |
| * Proof of Concept: Verify Command Injection Fix (Issue #525) | |
| * | |
| * This script demonstrates that the vulnerability has been fixed. | |
| * Original PoC: curl -X POST http://127.0.0.1:3003/sys/install \ | |
| * -H "Content-Type: application/json" \ | |
| * -d '{"packageName": "express; touch /tmp/pwned #"}' | |
| * | |
| * Expected behavior AFTER fix: | |
| * - Request is rejected with 400 Bad Request (invalid package name) | |
| * - No command execution occurs | |
| */ | |
| import http from 'http'; | |
| const HOST = '127.0.0.1'; | |
| const PORT = 3003; | |
| // Test cases | |
| const tests = [ | |
| { | |
| name: 'Valid package name (with auth)', | |
| path: '/sys/install', | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'X-API-Key': 'test-api-key' | |
| }, | |
| body: JSON.stringify({ packageName: '@activepieces/piece-github' }), | |
| expectedStatus: 200, // Should proceed to npm install | |
| description: 'Should allow valid package with authentication' | |
| }, | |
| { | |
| name: 'Command injection: semicolon (without auth)', | |
| path: '/sys/install', | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ packageName: 'express; touch /tmp/pwned #' }), | |
| expectedStatus: 401, | |
| description: 'Should reject without authentication' | |
| }, | |
| { | |
| name: 'Command injection: semicolon (with auth)', | |
| path: '/sys/install', | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'X-API-Key': 'test-api-key' | |
| }, | |
| body: JSON.stringify({ packageName: 'express; touch /tmp/pwned #' }), | |
| expectedStatus: 400, | |
| description: 'Should reject invalid package name even with auth' | |
| }, | |
| { | |
| name: 'Command injection: pipe (with auth)', | |
| path: '/sys/install', | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'X-API-Key': 'test-api-key' | |
| }, | |
| body: JSON.stringify({ packageName: 'express | cat /etc/passwd' }), | |
| expectedStatus: 400, | |
| description: 'Should reject pipe injection' | |
| }, | |
| { | |
| name: 'Command injection: backticks (with auth)', | |
| path: '/sys/install', | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'X-API-Key': 'test-api-key' | |
| }, | |
| body: JSON.stringify({ packageName: 'express`rm -rf /`' }), | |
| expectedStatus: 400, | |
| description: 'Should reject backtick injection' | |
| }, | |
| { | |
| name: 'Command injection: command substitution (with auth)', | |
| path: '/sys/install', | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'X-API-Key': 'test-api-key' | |
| }, | |
| body: JSON.stringify({ packageName: 'express$(whoami)' }), | |
| expectedStatus: 400, | |
| description: 'Should reject $() command substitution' | |
| }, | |
| { | |
| name: 'Health check (no auth required)', | |
| path: '/health', | |
| method: 'GET', | |
| headers: {}, | |
| body: null, | |
| expectedStatus: 200, | |
| description: 'Health check should work without authentication' | |
| } | |
| ]; | |
| async function makeRequest(test) { | |
| return new Promise((resolve) => { | |
| const options = { | |
| hostname: HOST, | |
| port: PORT, | |
| path: test.path, | |
| method: test.method, | |
| headers: test.headers | |
| }; | |
| const req = http.request(options, (res) => { | |
| let data = ''; | |
| res.on('data', (chunk) => { data += chunk; }); | |
| res.on('end', () => { | |
| resolve({ | |
| status: res.statusCode, | |
| body: data | |
| }); | |
| }); | |
| }); | |
| req.on('error', (error) => { | |
| resolve({ | |
| status: 'ERROR', | |
| error: error.message | |
| }); | |
| }); | |
| if (test.body) { | |
| req.write(test.body); | |
| } | |
| req.end(); | |
| }); | |
| } | |
| async function runTests() { | |
| console.log('╔══════════════════════════════════════════════════════════════╗'); | |
| console.log('║ Security Fix Verification: Command Injection (Issue #525) ║'); | |
| console.log('╚══════════════════════════════════════════════════════════════╝\n'); | |
| console.log(`Target: http://${HOST}:${PORT}`); | |
| console.log('Starting tests...\n'); | |
| let passed = 0; | |
| let failed = 0; | |
| for (const test of tests) { | |
| const result = await makeRequest(test); | |
| const statusMatch = result.status === test.expectedStatus; | |
| if (statusMatch) { | |
| passed++; | |
| console.log(`✅ PASS: ${test.name}`); | |
| console.log(` Expected: ${test.expectedStatus}, Got: ${result.status}`); | |
| console.log(` ${test.description}\n`); | |
| } else { | |
| failed++; | |
| console.log(`❌ FAIL: ${test.name}`); | |
| console.log(` Expected: ${test.expectedStatus}, Got: ${result.status}`); | |
| console.log(` ${test.description}`); | |
| if (result.error) { | |
| console.log(` Error: ${result.error}`); | |
| } else { | |
| console.log(` Response: ${result.body.substring(0, 100)}`); | |
| } | |
| console.log(); | |
| } | |
| } | |
| console.log('╔══════════════════════════════════════════════════════════════╗'); | |
| console.log(`║ Results: ${passed} passed, ${failed} failed out of ${tests.length} tests ║`); | |
| console.log('╚══════════════════════════════════════════════════════════════╝'); | |
| if (failed === 0) { | |
| console.log('\n✅ All security tests passed! The vulnerability has been fixed.'); | |
| process.exit(0); | |
| } else { | |
| console.log('\n❌ Some tests failed. The vulnerability may not be fully fixed.'); | |
| process.exit(1); | |
| } | |
| } | |
| // Run tests | |
| runTests().catch(console.error); | |