Skip to main content
Glama

n8n-MCP

by 88-888
parse-config.test.tsβ€’12 kB
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { execSync } from 'child_process'; import fs from 'fs'; import path from 'path'; import os from 'os'; describe('parse-config.js', () => { let tempDir: string; let configPath: string; const parseConfigPath = path.resolve(__dirname, '../../../docker/parse-config.js'); // Clean environment for tests - only include essential variables const cleanEnv = { PATH: process.env.PATH, HOME: process.env.HOME, NODE_ENV: process.env.NODE_ENV }; beforeEach(() => { // Create temporary directory for test config files tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'parse-config-test-')); configPath = path.join(tempDir, 'config.json'); }); afterEach(() => { // Clean up temporary directory if (fs.existsSync(tempDir)) { fs.rmSync(tempDir, { recursive: true }); } }); describe('Basic functionality', () => { it('should parse simple flat config', () => { const config = { mcp_mode: 'http', auth_token: 'test-token-123', port: 3000 }; fs.writeFileSync(configPath, JSON.stringify(config)); const output = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env: cleanEnv }); expect(output).toContain("export MCP_MODE='http'"); expect(output).toContain("export AUTH_TOKEN='test-token-123'"); expect(output).toContain("export PORT='3000'"); }); it('should handle nested objects by flattening with underscores', () => { const config = { database: { host: 'localhost', port: 5432, credentials: { user: 'admin', pass: 'secret' } } }; fs.writeFileSync(configPath, JSON.stringify(config)); const output = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env: cleanEnv }); expect(output).toContain("export DATABASE_HOST='localhost'"); expect(output).toContain("export DATABASE_PORT='5432'"); expect(output).toContain("export DATABASE_CREDENTIALS_USER='admin'"); expect(output).toContain("export DATABASE_CREDENTIALS_PASS='secret'"); }); it('should convert boolean values to strings', () => { const config = { debug: true, verbose: false }; fs.writeFileSync(configPath, JSON.stringify(config)); const output = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env: cleanEnv }); expect(output).toContain("export DEBUG='true'"); expect(output).toContain("export VERBOSE='false'"); }); it('should convert numbers to strings', () => { const config = { timeout: 5000, retry_count: 3, float_value: 3.14 }; fs.writeFileSync(configPath, JSON.stringify(config)); const output = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env: cleanEnv }); expect(output).toContain("export TIMEOUT='5000'"); expect(output).toContain("export RETRY_COUNT='3'"); expect(output).toContain("export FLOAT_VALUE='3.14'"); }); }); describe('Environment variable precedence', () => { it('should not export variables that are already set in environment', () => { const config = { existing_var: 'config-value', new_var: 'new-value' }; fs.writeFileSync(configPath, JSON.stringify(config)); // Set environment variable for the child process const env = { ...cleanEnv, EXISTING_VAR: 'env-value' }; const output = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env }); expect(output).not.toContain("export EXISTING_VAR="); expect(output).toContain("export NEW_VAR='new-value'"); }); it('should respect nested environment variables', () => { const config = { database: { host: 'config-host', port: 5432 } }; fs.writeFileSync(configPath, JSON.stringify(config)); const env = { ...cleanEnv, DATABASE_HOST: 'env-host' }; const output = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env }); expect(output).not.toContain("export DATABASE_HOST="); expect(output).toContain("export DATABASE_PORT='5432'"); }); }); describe('Shell escaping and security', () => { it('should escape single quotes properly', () => { const config = { message: "It's a test with 'quotes'", command: "echo 'hello'" }; fs.writeFileSync(configPath, JSON.stringify(config)); const output = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env: cleanEnv }); // Single quotes should be escaped as '"'"' expect(output).toContain(`export MESSAGE='It'"'"'s a test with '"'"'quotes'"'"'`); expect(output).toContain(`export COMMAND='echo '"'"'hello'"'"'`); }); it('should handle command injection attempts safely', () => { const config = { malicious1: "'; rm -rf /; echo '", malicious2: "$( rm -rf / )", malicious3: "`rm -rf /`", malicious4: "test\nrm -rf /\necho" }; fs.writeFileSync(configPath, JSON.stringify(config)); const output = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env: cleanEnv }); // All malicious content should be safely quoted expect(output).toContain(`export MALICIOUS1=''"'"'; rm -rf /; echo '"'"'`); expect(output).toContain(`export MALICIOUS2='$( rm -rf / )'`); expect(output).toContain(`export MALICIOUS3='`); expect(output).toContain(`export MALICIOUS4='test\nrm -rf /\necho'`); // Verify that when we evaluate the exports in a shell, the malicious content // is safely contained as string values and not executed // Test this by creating a temp script that sources the exports and echoes a success message const testScript = ` #!/bin/sh set -e ${output} echo "SUCCESS: No commands were executed" `; const tempScript = path.join(tempDir, 'test-safety.sh'); fs.writeFileSync(tempScript, testScript); fs.chmodSync(tempScript, '755'); // If the quoting is correct, this should succeed // If any commands leak out, the script will fail const result = execSync(tempScript, { encoding: 'utf8', env: cleanEnv }); expect(result.trim()).toBe('SUCCESS: No commands were executed'); }); it('should handle special shell characters safely', () => { const config = { special1: "test$VAR", special2: "test${VAR}", special3: "test\\path", special4: "test|command", special5: "test&background", special6: "test>redirect", special7: "test<input", special8: "test;command" }; fs.writeFileSync(configPath, JSON.stringify(config)); const output = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env: cleanEnv }); // All special characters should be preserved within single quotes expect(output).toContain("export SPECIAL1='test$VAR'"); expect(output).toContain("export SPECIAL2='test${VAR}'"); expect(output).toContain("export SPECIAL3='test\\path'"); expect(output).toContain("export SPECIAL4='test|command'"); expect(output).toContain("export SPECIAL5='test&background'"); expect(output).toContain("export SPECIAL6='test>redirect'"); expect(output).toContain("export SPECIAL7='test<input'"); expect(output).toContain("export SPECIAL8='test;command'"); }); }); describe('Edge cases and error handling', () => { it('should exit silently if config file does not exist', () => { const nonExistentPath = path.join(tempDir, 'non-existent.json'); const result = execSync(`node "${parseConfigPath}" "${nonExistentPath}"`, { encoding: 'utf8', env: cleanEnv }); expect(result).toBe(''); }); it('should exit silently on invalid JSON', () => { fs.writeFileSync(configPath, '{ invalid json }'); const result = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env: cleanEnv }); expect(result).toBe(''); }); it('should handle empty config file', () => { fs.writeFileSync(configPath, '{}'); const result = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env: cleanEnv }); expect(result.trim()).toBe(''); }); it('should ignore arrays in config', () => { const config = { valid_string: 'test', invalid_array: ['item1', 'item2'], nested: { valid_number: 42, invalid_array: [1, 2, 3] } }; fs.writeFileSync(configPath, JSON.stringify(config)); const output = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env: cleanEnv }); expect(output).toContain("export VALID_STRING='test'"); expect(output).toContain("export NESTED_VALID_NUMBER='42'"); expect(output).not.toContain('INVALID_ARRAY'); }); it('should ignore null values', () => { const config = { valid_string: 'test', null_value: null, nested: { another_null: null, valid_bool: true } }; fs.writeFileSync(configPath, JSON.stringify(config)); const output = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env: cleanEnv }); expect(output).toContain("export VALID_STRING='test'"); expect(output).toContain("export NESTED_VALID_BOOL='true'"); expect(output).not.toContain('NULL_VALUE'); expect(output).not.toContain('ANOTHER_NULL'); }); it('should handle deeply nested structures', () => { const config = { level1: { level2: { level3: { level4: { level5: 'deep-value' } } } } }; fs.writeFileSync(configPath, JSON.stringify(config)); const output = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env: cleanEnv }); expect(output).toContain("export LEVEL1_LEVEL2_LEVEL3_LEVEL4_LEVEL5='deep-value'"); }); it('should handle empty strings', () => { const config = { empty_string: '', nested: { another_empty: '' } }; fs.writeFileSync(configPath, JSON.stringify(config)); const output = execSync(`node "${parseConfigPath}" "${configPath}"`, { encoding: 'utf8', env: cleanEnv }); expect(output).toContain("export EMPTY_STRING=''"); expect(output).toContain("export NESTED_ANOTHER_EMPTY=''"); }); }); describe('Default behavior', () => { it('should use /app/config.json as default path when no argument provided', () => { // This test would need to be run in a Docker environment or mocked // For now, we just verify the script accepts no arguments try { const result = execSync(`node "${parseConfigPath}"`, { encoding: 'utf8', stdio: 'pipe', env: cleanEnv }); // Should exit silently if /app/config.json doesn't exist expect(result).toBe(''); } catch (error) { // Expected to fail outside Docker environment expect(true).toBe(true); } }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/88-888/n8n-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server