mcp-wordpress 1.2.0 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,68 +2,68 @@
2
2
  * Security configuration and constants for MCP WordPress
3
3
  */
4
4
 
5
- import { randomBytes } from "crypto";
5
+ import { randomBytes } from 'crypto';
6
6
 
7
7
  export const SecurityConfig = {
8
8
  // Rate limiting
9
9
  rateLimiting: {
10
10
  default: {
11
11
  windowMs: 60 * 1000, // 1 minute
12
- maxRequests: 60,
12
+ maxRequests: 60
13
13
  },
14
14
  authentication: {
15
15
  windowMs: 5 * 60 * 1000, // 5 minutes
16
- maxAttempts: 5,
16
+ maxAttempts: 5
17
17
  },
18
18
  upload: {
19
19
  windowMs: 60 * 1000, // 1 minute
20
- maxRequests: 10,
21
- },
20
+ maxRequests: 10
21
+ }
22
22
  },
23
23
 
24
24
  // File upload restrictions
25
25
  fileUpload: {
26
26
  maxSizeMB: 10,
27
27
  allowedMimeTypes: [
28
- "image/jpeg",
29
- "image/png",
30
- "image/gif",
31
- "image/webp",
32
- "image/svg+xml",
33
- "application/pdf",
34
- "application/msword",
35
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
36
- "application/vnd.ms-excel",
37
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
38
- "text/plain",
39
- "text/csv",
28
+ 'image/jpeg',
29
+ 'image/png',
30
+ 'image/gif',
31
+ 'image/webp',
32
+ 'image/svg+xml',
33
+ 'application/pdf',
34
+ 'application/msword',
35
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
36
+ 'application/vnd.ms-excel',
37
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
38
+ 'text/plain',
39
+ 'text/csv'
40
40
  ],
41
41
  // Dangerous file extensions to block
42
42
  blockedExtensions: [
43
- ".exe",
44
- ".bat",
45
- ".cmd",
46
- ".com",
47
- ".pif",
48
- ".scr",
49
- ".vbs",
50
- ".js",
51
- ".jar",
52
- ".zip",
53
- ".rar",
54
- ".tar",
55
- ".php",
56
- ".php3",
57
- ".php4",
58
- ".php5",
59
- ".phtml",
60
- ".sh",
61
- ".bash",
62
- ".zsh",
63
- ".fish",
64
- ".ps1",
65
- ".psm1",
66
- ],
43
+ '.exe',
44
+ '.bat',
45
+ '.cmd',
46
+ '.com',
47
+ '.pif',
48
+ '.scr',
49
+ '.vbs',
50
+ '.js',
51
+ '.jar',
52
+ '.zip',
53
+ '.rar',
54
+ '.tar',
55
+ '.php',
56
+ '.php3',
57
+ '.php4',
58
+ '.php5',
59
+ '.phtml',
60
+ '.sh',
61
+ '.bash',
62
+ '.zsh',
63
+ '.fish',
64
+ '.ps1',
65
+ '.psm1'
66
+ ]
67
67
  },
68
68
 
69
69
  // Input validation
@@ -76,58 +76,58 @@ export const SecurityConfig = {
76
76
  maxUsernameLength: 60,
77
77
  minUsernameLength: 3,
78
78
  maxPasswordLength: 128,
79
- minPasswordLength: 8,
79
+ minPasswordLength: 8
80
80
  },
81
81
 
82
82
  // Request timeouts (milliseconds)
83
83
  timeouts: {
84
84
  default: 30000, // 30 seconds
85
85
  upload: 600000, // 10 minutes
86
- auth: 10000, // 10 seconds
86
+ auth: 10000 // 10 seconds
87
87
  },
88
88
 
89
89
  // Security headers
90
90
  headers: {
91
- "X-Content-Type-Options": "nosniff",
92
- "X-Frame-Options": "DENY",
93
- "X-XSS-Protection": "1; mode=block",
94
- "Strict-Transport-Security": "max-age=31536000; includeSubDomains",
95
- "Content-Security-Policy": "default-src 'self'",
91
+ 'X-Content-Type-Options': 'nosniff',
92
+ 'X-Frame-Options': 'DENY',
93
+ 'X-XSS-Protection': '1; mode=block',
94
+ 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
95
+ 'Content-Security-Policy': 'default-src \'self\''
96
96
  },
97
97
 
98
98
  // Error messages (generic to avoid information disclosure)
99
99
  errorMessages: {
100
- authentication: "Authentication failed. Please check your credentials.",
101
- authorization: "You do not have permission to perform this action.",
102
- validation: "Invalid input provided.",
103
- rateLimit: "Too many requests. Please try again later.",
104
- serverError: "An error occurred processing your request.",
105
- notFound: "The requested resource was not found.",
100
+ authentication: 'Authentication failed. Please check your credentials.',
101
+ authorization: 'You do not have permission to perform this action.',
102
+ validation: 'Invalid input provided.',
103
+ rateLimit: 'Too many requests. Please try again later.',
104
+ serverError: 'An error occurred processing your request.',
105
+ notFound: 'The requested resource was not found.'
106
106
  },
107
107
 
108
108
  // Logging configuration
109
109
  logging: {
110
110
  // Fields to exclude from logs
111
111
  excludeFields: [
112
- "password",
113
- "appPassword",
114
- "app_password",
115
- "token",
116
- "secret",
117
- "authorization",
118
- "cookie",
119
- "session",
120
- "key",
121
- "apiKey",
122
- "api_key",
112
+ 'password',
113
+ 'appPassword',
114
+ 'app_password',
115
+ 'token',
116
+ 'secret',
117
+ 'authorization',
118
+ 'cookie',
119
+ 'session',
120
+ 'key',
121
+ 'apiKey',
122
+ 'api_key'
123
123
  ],
124
124
  // Patterns to redact in log messages
125
125
  redactPatterns: [
126
126
  /password["\s:=]+["']?([^"'\s]+)["']?/gi,
127
127
  /token["\s:=]+["']?([^"'\s]+)["']?/gi,
128
128
  /secret["\s:=]+["']?([^"'\s]+)["']?/gi,
129
- /key["\s:=]+["']?([^"'\s]+)["']?/gi,
130
- ],
129
+ /key["\s:=]+["']?([^"'\s]+)["']?/gi
130
+ ]
131
131
  },
132
132
 
133
133
  // Cache configuration
@@ -145,16 +145,16 @@ export const SecurityConfig = {
145
145
  semiStatic: 2 * 60 * 60 * 1000, // 2 hours - categories, tags, user profiles
146
146
  dynamic: 15 * 60 * 1000, // 15 minutes - posts, pages, comments
147
147
  session: 30 * 60 * 1000, // 30 minutes - authentication, current user
148
- realtime: 60 * 1000, // 1 minute - real-time data
148
+ realtime: 60 * 1000 // 1 minute - real-time data
149
149
  },
150
150
 
151
151
  // Cache-Control headers by data type
152
152
  cacheHeaders: {
153
- static: "public, max-age=14400", // 4 hours
154
- semiStatic: "public, max-age=7200", // 2 hours
155
- dynamic: "public, max-age=900", // 15 minutes
156
- session: "private, max-age=1800", // 30 minutes
157
- realtime: "public, max-age=60", // 1 minute
153
+ static: 'public, max-age=14400', // 4 hours
154
+ semiStatic: 'public, max-age=7200', // 2 hours
155
+ dynamic: 'public, max-age=900', // 15 minutes
156
+ session: 'private, max-age=1800', // 30 minutes
157
+ realtime: 'public, max-age=60' // 1 minute
158
158
  },
159
159
 
160
160
  // Invalidation settings
@@ -162,16 +162,16 @@ export const SecurityConfig = {
162
162
  enabled: true,
163
163
  batchSize: 100, // Max events to process in one batch
164
164
  queueTimeout: 5000, // Max time to wait before processing queue (ms)
165
- enableCascading: true, // Allow cascading invalidations
165
+ enableCascading: true // Allow cascading invalidations
166
166
  },
167
167
 
168
168
  // Memory management
169
169
  cleanup: {
170
170
  interval: 60 * 1000, // Cleanup interval in milliseconds (1 minute)
171
171
  maxMemoryMB: 50, // Maximum memory usage for cache
172
- evictionThreshold: 0.8, // Start evicting when 80% full
173
- },
174
- },
172
+ evictionThreshold: 0.8 // Start evicting when 80% full
173
+ }
174
+ }
175
175
  };
176
176
 
177
177
  /**
@@ -182,7 +182,7 @@ export class SecurityUtils {
182
182
  * Redact sensitive information from objects
183
183
  */
184
184
  static redactSensitiveData(obj: any): any {
185
- if (typeof obj !== "object" || obj === null) {
185
+ if (typeof obj !== 'object' || obj === null) {
186
186
  return obj;
187
187
  }
188
188
 
@@ -191,11 +191,11 @@ export class SecurityUtils {
191
191
  for (const key in redacted) {
192
192
  if (
193
193
  SecurityConfig.logging.excludeFields.some((field) =>
194
- key.toLowerCase().includes(field.toLowerCase()),
194
+ key.toLowerCase().includes(field.toLowerCase())
195
195
  )
196
196
  ) {
197
- redacted[key] = "[REDACTED]";
198
- } else if (typeof redacted[key] === "object") {
197
+ redacted[key] = '[REDACTED]';
198
+ } else if (typeof redacted[key] === 'object') {
199
199
  redacted[key] = SecurityUtils.redactSensitiveData(redacted[key]);
200
200
  }
201
201
  }
@@ -210,7 +210,7 @@ export class SecurityUtils {
210
210
  let redacted = str;
211
211
  for (const pattern of SecurityConfig.logging.redactPatterns) {
212
212
  redacted = redacted.replace(pattern, (match, value) => {
213
- return match.replace(value, "[REDACTED]");
213
+ return match.replace(value, '[REDACTED]');
214
214
  });
215
215
  }
216
216
  return redacted;
@@ -221,10 +221,10 @@ export class SecurityUtils {
221
221
  */
222
222
  static generateSecureToken(length: number = 32): string {
223
223
  const chars =
224
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
224
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
225
225
  const array = new Uint8Array(length);
226
226
 
227
- if (typeof crypto !== "undefined" && crypto.getRandomValues) {
227
+ if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
228
228
  crypto.getRandomValues(array);
229
229
  } else {
230
230
  // Fallback for Node.js
@@ -232,7 +232,7 @@ export class SecurityUtils {
232
232
  array.set(buffer);
233
233
  }
234
234
 
235
- return Array.from(array, (byte) => chars[byte % chars.length]).join("");
235
+ return Array.from(array, (byte) => chars[byte % chars.length]).join('');
236
236
  }
237
237
 
238
238
  /**
@@ -247,10 +247,10 @@ export class SecurityUtils {
247
247
  * Sanitize log output
248
248
  */
249
249
  static sanitizeForLog(data: any): any {
250
- if (typeof data === "string") {
250
+ if (typeof data === 'string') {
251
251
  return SecurityUtils.redactString(data);
252
252
  }
253
- if (typeof data === "object") {
253
+ if (typeof data === 'object') {
254
254
  return SecurityUtils.redactSensitiveData(data);
255
255
  }
256
256
  return data;
@@ -262,18 +262,18 @@ export class SecurityUtils {
262
262
  */
263
263
  export function createSecureError(
264
264
  error: any,
265
- fallbackMessage: string = SecurityConfig.errorMessages.serverError,
265
+ fallbackMessage: string = SecurityConfig.errorMessages.serverError
266
266
  ): Error {
267
267
  // Log the actual error for debugging (with sanitization)
268
- if (process.env.NODE_ENV !== "production") {
269
- console.error("Secure Error:", SecurityUtils.sanitizeForLog(error));
268
+ if (process.env.NODE_ENV !== 'production') {
269
+ console.error('Secure Error:', SecurityUtils.sanitizeForLog(error));
270
270
  }
271
271
 
272
272
  // Return generic error to prevent information disclosure
273
273
  const secureError = new Error(fallbackMessage);
274
274
 
275
275
  // Preserve error code if it's safe
276
- if (error && typeof error.code === "string" && !error.code.includes("_")) {
276
+ if (error && typeof error.code === 'string' && !error.code.includes('_')) {
277
277
  (secureError as any).code = error.code;
278
278
  }
279
279
 
@@ -281,7 +281,7 @@ export function createSecureError(
281
281
  }
282
282
 
283
283
  // Import path for file extension checking
284
- import * as path from "path";
284
+ import * as path from 'path';
285
285
 
286
286
  /**
287
287
  * Environment-specific security settings
@@ -290,12 +290,12 @@ export function getEnvironmentSecurity(): {
290
290
  strictMode: boolean;
291
291
  verboseErrors: boolean;
292
292
  enforceHttps: boolean;
293
- } {
294
- const isProduction = process.env.NODE_ENV === "production";
293
+ } {
294
+ const isProduction = process.env.NODE_ENV === 'production';
295
295
 
296
296
  return {
297
297
  strictMode: isProduction,
298
298
  verboseErrors: !isProduction,
299
- enforceHttps: isProduction,
299
+ enforceHttps: isProduction
300
300
  };
301
301
  }
@@ -2,7 +2,7 @@
2
2
  * Base utility class for tool managers to reduce code duplication
3
3
  */
4
4
 
5
- import { getErrorMessage } from "../utils/error.js";
5
+ import { getErrorMessage } from '../utils/error.js';
6
6
 
7
7
  export class BaseToolUtils {
8
8
  /**
@@ -23,7 +23,7 @@ export class BaseToolUtils {
23
23
  /**
24
24
  * Validate ID parameter
25
25
  */
26
- static validateId(id: unknown, name = "id"): number {
26
+ static validateId(id: unknown, name = 'id'): number {
27
27
  const numId = Number(id);
28
28
  if (!Number.isInteger(numId) || numId <= 0) {
29
29
  throw new Error(`Invalid ${name}: must be a positive integer`);
@@ -44,14 +44,14 @@ export class BaseToolUtils {
44
44
  */
45
45
  static generateCacheKey(
46
46
  operation: string,
47
- params: Record<string, unknown>,
47
+ params: Record<string, unknown>
48
48
  ): string {
49
- const site = params.site || "default";
49
+ const site = params.site || 'default';
50
50
  const paramStr = Object.entries(params)
51
- .filter(([key]) => key !== "site")
51
+ .filter(([key]) => key !== 'site')
52
52
  .sort(([a], [b]) => a.localeCompare(b))
53
53
  .map(([key, value]) => `${key}:${value}`)
54
- .join("|");
54
+ .join('|');
55
55
  return `${site}:${operation}:${paramStr}`;
56
56
  }
57
57