mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-02-06 05:23:08 +00:00
fix: address critical code review issues
Fix security and reliability issues identified in code review: 1. Security: Remove non-null assertions in credentials.ts - Add proper validation before returning credentials - Throw early with clear error messages showing which vars are missing - Prevents runtime failures with cryptic undefined errors 2. Reliability: Add pagination safety limits - Add MAX_PAGES limit (1000) to all pagination loops - Prevents infinite loops if API returns same cursor repeatedly - Applies to: cleanupOrphanedWorkflows, cleanupOldExecutions, cleanupExecutionsByWorkflow Changes ensure safer credential handling and prevent potential infinite loops in cleanup operations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
BIN
data/nodes.db
BIN
data/nodes.db
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "n8n-mcp-runtime",
|
"name": "n8n-mcp-runtime",
|
||||||
"version": "2.15.0",
|
"version": "2.15.1",
|
||||||
"description": "n8n MCP Server Runtime Dependencies Only",
|
"description": "n8n MCP Server Runtime Dependencies Only",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -32,11 +32,18 @@ export async function cleanupOrphanedWorkflows(): Promise<string[]> {
|
|||||||
let allWorkflows: any[] = [];
|
let allWorkflows: any[] = [];
|
||||||
let cursor: string | undefined;
|
let cursor: string | undefined;
|
||||||
let pageCount = 0;
|
let pageCount = 0;
|
||||||
|
const MAX_PAGES = 1000; // Safety limit to prevent infinite loops
|
||||||
|
|
||||||
// Fetch all workflows with pagination
|
// Fetch all workflows with pagination
|
||||||
try {
|
try {
|
||||||
do {
|
do {
|
||||||
pageCount++;
|
pageCount++;
|
||||||
|
|
||||||
|
if (pageCount > MAX_PAGES) {
|
||||||
|
logger.error(`Exceeded maximum pages (${MAX_PAGES}). Possible infinite loop or API issue.`);
|
||||||
|
throw new Error('Pagination safety limit exceeded while fetching workflows');
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug(`Fetching workflows page ${pageCount}...`);
|
logger.debug(`Fetching workflows page ${pageCount}...`);
|
||||||
|
|
||||||
const response = await client.listWorkflows({
|
const response = await client.listWorkflows({
|
||||||
@@ -101,11 +108,18 @@ export async function cleanupOldExecutions(
|
|||||||
let allExecutions: any[] = [];
|
let allExecutions: any[] = [];
|
||||||
let cursor: string | undefined;
|
let cursor: string | undefined;
|
||||||
let pageCount = 0;
|
let pageCount = 0;
|
||||||
|
const MAX_PAGES = 1000; // Safety limit to prevent infinite loops
|
||||||
|
|
||||||
// Fetch all executions
|
// Fetch all executions
|
||||||
try {
|
try {
|
||||||
do {
|
do {
|
||||||
pageCount++;
|
pageCount++;
|
||||||
|
|
||||||
|
if (pageCount > MAX_PAGES) {
|
||||||
|
logger.error(`Exceeded maximum pages (${MAX_PAGES}). Possible infinite loop or API issue.`);
|
||||||
|
throw new Error('Pagination safety limit exceeded while fetching executions');
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug(`Fetching executions page ${pageCount}...`);
|
logger.debug(`Fetching executions page ${pageCount}...`);
|
||||||
|
|
||||||
const response = await client.listExecutions({
|
const response = await client.listExecutions({
|
||||||
@@ -239,9 +253,18 @@ export async function cleanupExecutionsByWorkflow(
|
|||||||
|
|
||||||
let cursor: string | undefined;
|
let cursor: string | undefined;
|
||||||
let totalCount = 0;
|
let totalCount = 0;
|
||||||
|
let pageCount = 0;
|
||||||
|
const MAX_PAGES = 1000; // Safety limit to prevent infinite loops
|
||||||
|
|
||||||
try {
|
try {
|
||||||
do {
|
do {
|
||||||
|
pageCount++;
|
||||||
|
|
||||||
|
if (pageCount > MAX_PAGES) {
|
||||||
|
logger.error(`Exceeded maximum pages (${MAX_PAGES}). Possible infinite loop or API issue.`);
|
||||||
|
throw new Error(`Pagination safety limit exceeded while fetching executions for workflow ${workflowId}`);
|
||||||
|
}
|
||||||
|
|
||||||
const response = await client.listExecutions({
|
const response = await client.listExecutions({
|
||||||
workflowId,
|
workflowId,
|
||||||
cursor,
|
cursor,
|
||||||
|
|||||||
@@ -39,15 +39,27 @@ export interface N8nTestCredentials {
|
|||||||
*/
|
*/
|
||||||
export function getN8nCredentials(): N8nTestCredentials {
|
export function getN8nCredentials(): N8nTestCredentials {
|
||||||
if (process.env.CI) {
|
if (process.env.CI) {
|
||||||
// CI: Use GitHub secrets
|
// CI: Use GitHub secrets - validate required variables first
|
||||||
|
const url = process.env.N8N_URL;
|
||||||
|
const apiKey = process.env.N8N_API_KEY;
|
||||||
|
|
||||||
|
if (!url || !apiKey) {
|
||||||
|
throw new Error(
|
||||||
|
'Missing required CI credentials:\n' +
|
||||||
|
` N8N_URL: ${url ? 'set' : 'MISSING'}\n` +
|
||||||
|
` N8N_API_KEY: ${apiKey ? 'set' : 'MISSING'}\n` +
|
||||||
|
'Please configure GitHub secrets for integration tests.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url: process.env.N8N_URL!,
|
url,
|
||||||
apiKey: process.env.N8N_API_KEY!,
|
apiKey,
|
||||||
webhookWorkflows: {
|
webhookWorkflows: {
|
||||||
get: process.env.N8N_TEST_WEBHOOK_GET_ID!,
|
get: process.env.N8N_TEST_WEBHOOK_GET_ID || '',
|
||||||
post: process.env.N8N_TEST_WEBHOOK_POST_ID!,
|
post: process.env.N8N_TEST_WEBHOOK_POST_ID || '',
|
||||||
put: process.env.N8N_TEST_WEBHOOK_PUT_ID!,
|
put: process.env.N8N_TEST_WEBHOOK_PUT_ID || '',
|
||||||
delete: process.env.N8N_TEST_WEBHOOK_DELETE_ID!
|
delete: process.env.N8N_TEST_WEBHOOK_DELETE_ID || ''
|
||||||
},
|
},
|
||||||
cleanup: {
|
cleanup: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -56,10 +68,23 @@ export function getN8nCredentials(): N8nTestCredentials {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Local: Use .env file
|
// Local: Use .env file - validate required variables first
|
||||||
|
const url = process.env.N8N_API_URL;
|
||||||
|
const apiKey = process.env.N8N_API_KEY;
|
||||||
|
|
||||||
|
if (!url || !apiKey) {
|
||||||
|
throw new Error(
|
||||||
|
'Missing required credentials in .env:\n' +
|
||||||
|
` N8N_API_URL: ${url ? 'set' : 'MISSING'}\n` +
|
||||||
|
` N8N_API_KEY: ${apiKey ? 'set' : 'MISSING'}\n\n` +
|
||||||
|
'Please add these to your .env file.\n' +
|
||||||
|
'See .env.example for configuration details.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url: process.env.N8N_API_URL!,
|
url,
|
||||||
apiKey: process.env.N8N_API_KEY!,
|
apiKey,
|
||||||
webhookWorkflows: {
|
webhookWorkflows: {
|
||||||
get: process.env.N8N_TEST_WEBHOOK_GET_ID || '',
|
get: process.env.N8N_TEST_WEBHOOK_GET_ID || '',
|
||||||
post: process.env.N8N_TEST_WEBHOOK_POST_ID || '',
|
post: process.env.N8N_TEST_WEBHOOK_POST_ID || '',
|
||||||
|
|||||||
Reference in New Issue
Block a user