mcp-wordpress 1.1.7 → 1.2.2

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.
Files changed (255) hide show
  1. package/README.md +388 -66
  2. package/dist/cache/CacheInvalidation.d.ts +118 -0
  3. package/dist/cache/CacheInvalidation.d.ts.map +1 -0
  4. package/dist/cache/CacheInvalidation.js +349 -0
  5. package/dist/cache/CacheInvalidation.js.map +1 -0
  6. package/dist/cache/CacheManager.d.ts +143 -0
  7. package/dist/cache/CacheManager.d.ts.map +1 -0
  8. package/dist/cache/CacheManager.js +308 -0
  9. package/dist/cache/CacheManager.js.map +1 -0
  10. package/dist/cache/HttpCacheWrapper.d.ts +121 -0
  11. package/dist/cache/HttpCacheWrapper.d.ts.map +1 -0
  12. package/dist/cache/HttpCacheWrapper.js +280 -0
  13. package/dist/cache/HttpCacheWrapper.js.map +1 -0
  14. package/dist/cache/__tests__/CacheInvalidation.test.d.ts +5 -0
  15. package/dist/cache/__tests__/CacheInvalidation.test.d.ts.map +1 -0
  16. package/dist/cache/__tests__/CacheInvalidation.test.js +236 -0
  17. package/dist/cache/__tests__/CacheInvalidation.test.js.map +1 -0
  18. package/dist/cache/__tests__/CacheManager.test.d.ts +5 -0
  19. package/dist/cache/__tests__/CacheManager.test.d.ts.map +1 -0
  20. package/dist/cache/__tests__/CacheManager.test.js +233 -0
  21. package/dist/cache/__tests__/CacheManager.test.js.map +1 -0
  22. package/dist/cache/__tests__/CachedWordPressClient.test.d.ts +5 -0
  23. package/dist/cache/__tests__/CachedWordPressClient.test.d.ts.map +1 -0
  24. package/dist/cache/__tests__/CachedWordPressClient.test.js +228 -0
  25. package/dist/cache/__tests__/CachedWordPressClient.test.js.map +1 -0
  26. package/dist/cache/__tests__/HttpCacheWrapper.test.d.ts +5 -0
  27. package/dist/cache/__tests__/HttpCacheWrapper.test.d.ts.map +1 -0
  28. package/dist/cache/__tests__/HttpCacheWrapper.test.js +296 -0
  29. package/dist/cache/__tests__/HttpCacheWrapper.test.js.map +1 -0
  30. package/dist/cache/index.d.ts +12 -0
  31. package/dist/cache/index.d.ts.map +1 -0
  32. package/dist/cache/index.js +9 -0
  33. package/dist/cache/index.js.map +1 -0
  34. package/dist/client/CachedWordPressClient.d.ts +160 -0
  35. package/dist/client/CachedWordPressClient.d.ts.map +1 -0
  36. package/dist/client/CachedWordPressClient.js +338 -0
  37. package/dist/client/CachedWordPressClient.js.map +1 -0
  38. package/dist/client/WordPressClient.d.ts +81 -0
  39. package/dist/client/WordPressClient.d.ts.map +1 -0
  40. package/dist/client/WordPressClient.js +354 -0
  41. package/dist/client/WordPressClient.js.map +1 -0
  42. package/dist/config/ConfigurationSchema.d.ts +281 -0
  43. package/dist/config/ConfigurationSchema.d.ts.map +1 -0
  44. package/dist/config/ConfigurationSchema.js +205 -0
  45. package/dist/config/ConfigurationSchema.js.map +1 -0
  46. package/dist/config/ServerConfiguration.d.ts +38 -0
  47. package/dist/config/ServerConfiguration.d.ts.map +1 -0
  48. package/dist/config/ServerConfiguration.js +158 -0
  49. package/dist/config/ServerConfiguration.js.map +1 -0
  50. package/dist/docs/DocumentationGenerator.d.ts +184 -0
  51. package/dist/docs/DocumentationGenerator.d.ts.map +1 -0
  52. package/dist/docs/DocumentationGenerator.js +735 -0
  53. package/dist/docs/DocumentationGenerator.js.map +1 -0
  54. package/dist/docs/MarkdownFormatter.d.ts +84 -0
  55. package/dist/docs/MarkdownFormatter.d.ts.map +1 -0
  56. package/dist/docs/MarkdownFormatter.js +448 -0
  57. package/dist/docs/MarkdownFormatter.js.map +1 -0
  58. package/dist/docs/index.d.ts +8 -0
  59. package/dist/docs/index.d.ts.map +1 -0
  60. package/dist/docs/index.js +7 -0
  61. package/dist/docs/index.js.map +1 -0
  62. package/dist/index.d.ts +1 -4
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +12 -212
  65. package/dist/index.js.map +1 -1
  66. package/dist/performance/AnomalyDetector.d.ts +63 -0
  67. package/dist/performance/AnomalyDetector.d.ts.map +1 -0
  68. package/dist/performance/AnomalyDetector.js +222 -0
  69. package/dist/performance/AnomalyDetector.js.map +1 -0
  70. package/dist/performance/BenchmarkAnalyzer.d.ts +67 -0
  71. package/dist/performance/BenchmarkAnalyzer.d.ts.map +1 -0
  72. package/dist/performance/BenchmarkAnalyzer.js +301 -0
  73. package/dist/performance/BenchmarkAnalyzer.js.map +1 -0
  74. package/dist/performance/MetricsCollector.d.ts +139 -0
  75. package/dist/performance/MetricsCollector.d.ts.map +1 -0
  76. package/dist/performance/MetricsCollector.js +320 -0
  77. package/dist/performance/MetricsCollector.js.map +1 -0
  78. package/dist/performance/PerformanceAnalytics.d.ts +162 -0
  79. package/dist/performance/PerformanceAnalytics.d.ts.map +1 -0
  80. package/dist/performance/PerformanceAnalytics.js +554 -0
  81. package/dist/performance/PerformanceAnalytics.js.map +1 -0
  82. package/dist/performance/PerformanceMonitor.d.ts +202 -0
  83. package/dist/performance/PerformanceMonitor.d.ts.map +1 -0
  84. package/dist/performance/PerformanceMonitor.js +478 -0
  85. package/dist/performance/PerformanceMonitor.js.map +1 -0
  86. package/dist/performance/TrendAnalyzer.d.ts +69 -0
  87. package/dist/performance/TrendAnalyzer.d.ts.map +1 -0
  88. package/dist/performance/TrendAnalyzer.js +203 -0
  89. package/dist/performance/TrendAnalyzer.js.map +1 -0
  90. package/dist/performance/index.d.ts +11 -0
  91. package/dist/performance/index.d.ts.map +1 -0
  92. package/dist/performance/index.js +8 -0
  93. package/dist/performance/index.js.map +1 -0
  94. package/dist/security/InputValidator.d.ts +215 -0
  95. package/dist/security/InputValidator.d.ts.map +1 -0
  96. package/dist/security/InputValidator.js +278 -0
  97. package/dist/security/InputValidator.js.map +1 -0
  98. package/dist/security/SecurityConfig.d.ts +129 -0
  99. package/dist/security/SecurityConfig.d.ts.map +1 -0
  100. package/dist/security/SecurityConfig.js +262 -0
  101. package/dist/security/SecurityConfig.js.map +1 -0
  102. package/dist/server/ConnectionTester.d.ts +24 -0
  103. package/dist/server/ConnectionTester.d.ts.map +1 -0
  104. package/dist/server/ConnectionTester.js +61 -0
  105. package/dist/server/ConnectionTester.js.map +1 -0
  106. package/dist/server/ToolRegistry.d.ts +46 -0
  107. package/dist/server/ToolRegistry.d.ts.map +1 -0
  108. package/dist/server/ToolRegistry.js +148 -0
  109. package/dist/server/ToolRegistry.js.map +1 -0
  110. package/dist/tools/BaseToolClass.d.ts +76 -0
  111. package/dist/tools/BaseToolClass.d.ts.map +1 -0
  112. package/dist/tools/BaseToolClass.js +104 -0
  113. package/dist/tools/BaseToolClass.js.map +1 -0
  114. package/dist/tools/BaseToolManager.d.ts +26 -0
  115. package/dist/tools/BaseToolManager.d.ts.map +1 -0
  116. package/dist/tools/BaseToolManager.js +56 -0
  117. package/dist/tools/BaseToolManager.js.map +1 -0
  118. package/dist/tools/base.d.ts +37 -0
  119. package/dist/tools/base.d.ts.map +1 -0
  120. package/dist/tools/base.js +60 -0
  121. package/dist/tools/base.js.map +1 -0
  122. package/dist/tools/cache.d.ts +260 -0
  123. package/dist/tools/cache.d.ts.map +1 -0
  124. package/dist/tools/cache.js +237 -0
  125. package/dist/tools/cache.js.map +1 -0
  126. package/dist/tools/index.d.ts +2 -0
  127. package/dist/tools/index.d.ts.map +1 -1
  128. package/dist/tools/index.js +2 -0
  129. package/dist/tools/index.js.map +1 -1
  130. package/dist/tools/performance.d.ts +63 -0
  131. package/dist/tools/performance.d.ts.map +1 -0
  132. package/dist/tools/performance.js +865 -0
  133. package/dist/tools/performance.js.map +1 -0
  134. package/dist/types/client.d.ts +1 -0
  135. package/dist/types/client.d.ts.map +1 -1
  136. package/dist/types/client.js.map +1 -1
  137. package/dist/utils/toolWrapper.d.ts +4 -0
  138. package/dist/utils/toolWrapper.d.ts.map +1 -1
  139. package/dist/utils/toolWrapper.js +11 -0
  140. package/dist/utils/toolWrapper.js.map +1 -1
  141. package/dist/utils/validation.d.ts +68 -0
  142. package/dist/utils/validation.d.ts.map +1 -0
  143. package/dist/utils/validation.js +185 -0
  144. package/dist/utils/validation.js.map +1 -0
  145. package/docs/CACHING.md +340 -0
  146. package/docs/DOCKER.md +451 -0
  147. package/docs/PERFORMANCE_MONITORING.md +471 -0
  148. package/docs/SECURITY_TESTING.md +393 -0
  149. package/docs/api/README.md +200 -0
  150. package/docs/api/categories/auth.md +40 -0
  151. package/docs/api/categories/cache.md +41 -0
  152. package/docs/api/categories/comment.md +44 -0
  153. package/docs/api/categories/media.md +43 -0
  154. package/docs/api/categories/page.md +43 -0
  155. package/docs/api/categories/performance.md +44 -0
  156. package/docs/api/categories/post.md +43 -0
  157. package/docs/api/categories/site.md +43 -0
  158. package/docs/api/categories/taxonomy.md +47 -0
  159. package/docs/api/categories/user.md +43 -0
  160. package/docs/api/openapi.json +3305 -0
  161. package/docs/api/summary.json +12 -0
  162. package/docs/api/tools/wp_approve_comment.md +98 -0
  163. package/docs/api/tools/wp_cache_clear.md +120 -0
  164. package/docs/api/tools/wp_cache_info.md +119 -0
  165. package/docs/api/tools/wp_cache_stats.md +119 -0
  166. package/docs/api/tools/wp_cache_warm.md +119 -0
  167. package/docs/api/tools/wp_create_application_password.md +102 -0
  168. package/docs/api/tools/wp_create_category.md +102 -0
  169. package/docs/api/tools/wp_create_comment.md +128 -0
  170. package/docs/api/tools/wp_create_page.md +135 -0
  171. package/docs/api/tools/wp_create_post.md +147 -0
  172. package/docs/api/tools/wp_create_tag.md +101 -0
  173. package/docs/api/tools/wp_create_user.md +135 -0
  174. package/docs/api/tools/wp_delete_application_password.md +101 -0
  175. package/docs/api/tools/wp_delete_category.md +100 -0
  176. package/docs/api/tools/wp_delete_comment.md +101 -0
  177. package/docs/api/tools/wp_delete_media.md +108 -0
  178. package/docs/api/tools/wp_delete_page.md +108 -0
  179. package/docs/api/tools/wp_delete_post.md +117 -0
  180. package/docs/api/tools/wp_delete_tag.md +100 -0
  181. package/docs/api/tools/wp_delete_user.md +108 -0
  182. package/docs/api/tools/wp_get_application_passwords.md +103 -0
  183. package/docs/api/tools/wp_get_auth_status.md +101 -0
  184. package/docs/api/tools/wp_get_category.md +103 -0
  185. package/docs/api/tools/wp_get_comment.md +103 -0
  186. package/docs/api/tools/wp_get_current_user.md +101 -0
  187. package/docs/api/tools/wp_get_media.md +103 -0
  188. package/docs/api/tools/wp_get_page.md +103 -0
  189. package/docs/api/tools/wp_get_page_revisions.md +103 -0
  190. package/docs/api/tools/wp_get_post.md +112 -0
  191. package/docs/api/tools/wp_get_post_revisions.md +103 -0
  192. package/docs/api/tools/wp_get_site_settings.md +108 -0
  193. package/docs/api/tools/wp_get_tag.md +103 -0
  194. package/docs/api/tools/wp_get_user.md +103 -0
  195. package/docs/api/tools/wp_list_categories.md +111 -0
  196. package/docs/api/tools/wp_list_comments.md +111 -0
  197. package/docs/api/tools/wp_list_media.md +145 -0
  198. package/docs/api/tools/wp_list_pages.md +145 -0
  199. package/docs/api/tools/wp_list_posts.md +156 -0
  200. package/docs/api/tools/wp_list_tags.md +110 -0
  201. package/docs/api/tools/wp_list_users.md +111 -0
  202. package/docs/api/tools/wp_performance_alerts.md +162 -0
  203. package/docs/api/tools/wp_performance_benchmark.md +160 -0
  204. package/docs/api/tools/wp_performance_export.md +162 -0
  205. package/docs/api/tools/wp_performance_history.md +161 -0
  206. package/docs/api/tools/wp_performance_optimize.md +162 -0
  207. package/docs/api/tools/wp_performance_stats.md +160 -0
  208. package/docs/api/tools/wp_search_site.md +99 -0
  209. package/docs/api/tools/wp_spam_comment.md +98 -0
  210. package/docs/api/tools/wp_switch_auth_method.md +122 -0
  211. package/docs/api/tools/wp_test_auth.md +96 -0
  212. package/docs/api/tools/wp_update_category.md +102 -0
  213. package/docs/api/tools/wp_update_comment.md +127 -0
  214. package/docs/api/tools/wp_update_media.md +129 -0
  215. package/docs/api/tools/wp_update_page.md +135 -0
  216. package/docs/api/tools/wp_update_post.md +144 -0
  217. package/docs/api/tools/wp_update_site_settings.md +127 -0
  218. package/docs/api/tools/wp_update_tag.md +102 -0
  219. package/docs/api/tools/wp_update_user.md +134 -0
  220. package/docs/api/tools/wp_upload_media.md +131 -0
  221. package/docs/api/types/WordPressPost.md +39 -0
  222. package/docs/contract-testing.md +183 -0
  223. package/docs/developer/NPM_AUTH_SETUP.md +3 -3
  224. package/docs/wordpress-rest-api-authentication-troubleshooting.md +218 -0
  225. package/package.json +84 -64
  226. package/src/cache/CacheInvalidation.ts +421 -0
  227. package/src/cache/CacheManager.ts +391 -0
  228. package/src/cache/HttpCacheWrapper.ts +372 -0
  229. package/src/cache/__tests__/CacheInvalidation.test.ts +299 -0
  230. package/src/cache/__tests__/CacheManager.test.ts +300 -0
  231. package/src/cache/__tests__/CachedWordPressClient.test.ts +304 -0
  232. package/src/cache/__tests__/HttpCacheWrapper.test.ts +359 -0
  233. package/src/cache/index.ts +26 -0
  234. package/src/client/CachedWordPressClient.ts +442 -0
  235. package/src/config/ConfigurationSchema.ts +246 -0
  236. package/src/config/ServerConfiguration.ts +215 -0
  237. package/src/docs/DocumentationGenerator.ts +952 -0
  238. package/src/docs/MarkdownFormatter.ts +494 -0
  239. package/src/docs/index.ts +21 -0
  240. package/src/index.ts +14 -274
  241. package/src/performance/MetricsCollector.ts +447 -0
  242. package/src/performance/PerformanceAnalytics.ts +762 -0
  243. package/src/performance/PerformanceMonitor.ts +649 -0
  244. package/src/performance/index.ts +28 -0
  245. package/src/security/InputValidator.ts +319 -0
  246. package/src/security/SecurityConfig.ts +301 -0
  247. package/src/server/ConnectionTester.ts +74 -0
  248. package/src/server/ToolRegistry.ts +194 -0
  249. package/src/tools/BaseToolManager.ts +66 -0
  250. package/src/tools/cache.ts +259 -0
  251. package/src/tools/index.ts +2 -0
  252. package/src/tools/performance.ts +948 -0
  253. package/src/types/client.ts +1 -0
  254. package/src/utils/toolWrapper.ts +11 -0
  255. package/src/utils/validation.ts +259 -0
@@ -72,6 +72,7 @@ export interface RequestOptions {
72
72
  timeout?: number;
73
73
  retries?: number;
74
74
  signal?: AbortSignal;
75
+ params?: any;
75
76
  }
76
77
 
77
78
  // API Response Wrapper
@@ -103,3 +103,14 @@ export function formatErrorResponse(operation: string, error: any): never {
103
103
  const message = getErrorMessage(error);
104
104
  throw new Error(`${operation}: ${message}`);
105
105
  }
106
+
107
+ /**
108
+ * Simple tool wrapper for standalone async functions
109
+ */
110
+ export async function toolWrapper<T>(fn: () => Promise<T>): Promise<T> {
111
+ try {
112
+ return await fn();
113
+ } catch (error) {
114
+ throw new Error(getErrorMessage(error));
115
+ }
116
+ }
@@ -0,0 +1,259 @@
1
+ import * as path from 'path';
2
+ import { WordPressAPIError } from '../types/client.js';
3
+
4
+ /**
5
+ * Security-focused validation utilities for MCP WordPress
6
+ */
7
+
8
+ /**
9
+ * Validates and sanitizes numeric IDs
10
+ */
11
+ export function validateId(id: any, fieldName: string = 'id'): number {
12
+ const numId = parseInt(String(id), 10);
13
+ if (isNaN(numId) || numId <= 0) {
14
+ throw new WordPressAPIError(`Invalid ${fieldName}: must be a positive number`, 400, 'INVALID_PARAMETER');
15
+ }
16
+ return numId;
17
+ }
18
+
19
+ /**
20
+ * Validates string length within bounds
21
+ */
22
+ export function validateString(
23
+ value: any,
24
+ fieldName: string,
25
+ minLength: number = 1,
26
+ maxLength: number = 1000
27
+ ): string {
28
+ if (typeof value !== 'string') {
29
+ throw new WordPressAPIError(`Invalid ${fieldName}: must be a string`, 400, 'INVALID_PARAMETER');
30
+ }
31
+
32
+ const trimmed = value.trim();
33
+ if (trimmed.length < minLength || trimmed.length > maxLength) {
34
+ throw new WordPressAPIError(
35
+ `Invalid ${fieldName}: length must be between ${minLength} and ${maxLength} characters`,
36
+ 400,
37
+ 'INVALID_PARAMETER'
38
+ );
39
+ }
40
+
41
+ return trimmed;
42
+ }
43
+
44
+ /**
45
+ * Validates and sanitizes file paths to prevent directory traversal
46
+ */
47
+ export function validateFilePath(userPath: string, allowedBasePath: string): string {
48
+ // Normalize the path to remove ../ and other dangerous patterns
49
+ const normalizedPath = path.normalize(userPath);
50
+ const resolvedPath = path.resolve(allowedBasePath, normalizedPath);
51
+
52
+ // Ensure the resolved path is within the allowed directory
53
+ if (!resolvedPath.startsWith(path.resolve(allowedBasePath))) {
54
+ throw new WordPressAPIError('Invalid file path: access denied', 403, 'PATH_TRAVERSAL_ATTEMPT');
55
+ }
56
+
57
+ return resolvedPath;
58
+ }
59
+
60
+ /**
61
+ * Validates WordPress post status values
62
+ */
63
+ export function validatePostStatus(status: string): string {
64
+ const validStatuses = ['publish', 'draft', 'pending', 'private', 'future', 'auto-draft', 'trash'];
65
+ if (!validStatuses.includes(status)) {
66
+ throw new WordPressAPIError(
67
+ `Invalid status: must be one of ${validStatuses.join(', ')}`,
68
+ 400,
69
+ 'INVALID_PARAMETER'
70
+ );
71
+ }
72
+ return status;
73
+ }
74
+
75
+ /**
76
+ * Validates and sanitizes URLs
77
+ */
78
+ export function validateUrl(url: string, fieldName: string = 'url'): string {
79
+ try {
80
+ const urlObj = new URL(url);
81
+ // Only allow http and https protocols
82
+ if (!['http:', 'https:'].includes(urlObj.protocol)) {
83
+ throw new Error('Invalid protocol');
84
+ }
85
+ return urlObj.toString();
86
+ } catch {
87
+ throw new WordPressAPIError(`Invalid ${fieldName}: must be a valid URL`, 400, 'INVALID_PARAMETER');
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Validates file size
93
+ */
94
+ export function validateFileSize(sizeInBytes: number, maxSizeInMB: number = 10): void {
95
+ const maxSizeInBytes = maxSizeInMB * 1024 * 1024;
96
+ if (sizeInBytes > maxSizeInBytes) {
97
+ throw new WordPressAPIError(
98
+ `File size exceeds maximum allowed size of ${maxSizeInMB}MB`,
99
+ 413,
100
+ 'FILE_TOO_LARGE'
101
+ );
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Validates MIME types for file uploads
107
+ */
108
+ export function validateMimeType(mimeType: string, allowedTypes: string[]): void {
109
+ if (!allowedTypes.includes(mimeType)) {
110
+ throw new WordPressAPIError(
111
+ `Invalid file type: ${mimeType}. Allowed types: ${allowedTypes.join(', ')}`,
112
+ 415,
113
+ 'UNSUPPORTED_MEDIA_TYPE'
114
+ );
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Sanitizes HTML content to prevent XSS
120
+ * Note: This is a basic implementation. For production use,
121
+ * consider using a library like DOMPurify
122
+ */
123
+ export function sanitizeHtml(html: string): string {
124
+ // Remove script tags and their content
125
+ let sanitized = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
126
+
127
+ // Remove event handlers
128
+ sanitized = sanitized.replace(/\s*on\w+\s*=\s*["'][^"']*["']/gi, '');
129
+
130
+ // Remove javascript: protocol
131
+ sanitized = sanitized.replace(/javascript:/gi, '');
132
+
133
+ // Remove data: protocol (can be used for XSS)
134
+ sanitized = sanitized.replace(/data:text\/html/gi, '');
135
+
136
+ return sanitized;
137
+ }
138
+
139
+ /**
140
+ * Validates array input
141
+ */
142
+ export function validateArray<T>(
143
+ value: any,
144
+ fieldName: string,
145
+ minItems: number = 0,
146
+ maxItems: number = 100
147
+ ): T[] {
148
+ if (!Array.isArray(value)) {
149
+ throw new WordPressAPIError(`Invalid ${fieldName}: must be an array`, 400, 'INVALID_PARAMETER');
150
+ }
151
+
152
+ if (value.length < minItems || value.length > maxItems) {
153
+ throw new WordPressAPIError(
154
+ `Invalid ${fieldName}: array must contain between ${minItems} and ${maxItems} items`,
155
+ 400,
156
+ 'INVALID_PARAMETER'
157
+ );
158
+ }
159
+
160
+ return value;
161
+ }
162
+
163
+ /**
164
+ * Validates email addresses
165
+ */
166
+ export function validateEmail(email: string): string {
167
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
168
+ if (!emailRegex.test(email)) {
169
+ throw new WordPressAPIError('Invalid email address format', 400, 'INVALID_PARAMETER');
170
+ }
171
+ return email.toLowerCase();
172
+ }
173
+
174
+ /**
175
+ * Validates username format
176
+ */
177
+ export function validateUsername(username: string): string {
178
+ // WordPress username rules: alphanumeric, space, underscore, hyphen, period, @ symbol
179
+ const usernameRegex = /^[a-zA-Z0-9 _.\-@]+$/;
180
+ if (!usernameRegex.test(username)) {
181
+ throw new WordPressAPIError(
182
+ 'Invalid username: can only contain letters, numbers, spaces, and _.-@ symbols',
183
+ 400,
184
+ 'INVALID_PARAMETER'
185
+ );
186
+ }
187
+
188
+ if (username.length < 3 || username.length > 60) {
189
+ throw new WordPressAPIError(
190
+ 'Invalid username: must be between 3 and 60 characters',
191
+ 400,
192
+ 'INVALID_PARAMETER'
193
+ );
194
+ }
195
+
196
+ return username;
197
+ }
198
+
199
+ /**
200
+ * Rate limiting tracker (simple in-memory implementation)
201
+ * For production, use Redis or similar
202
+ */
203
+ class RateLimiter {
204
+ private attempts: Map<string, { count: number; resetTime: number }> = new Map();
205
+
206
+ constructor(
207
+ private maxAttempts: number = 5,
208
+ private windowMs: number = 60000 // 1 minute
209
+ ) {}
210
+
211
+ check(identifier: string): void {
212
+ const now = Date.now();
213
+ const record = this.attempts.get(identifier);
214
+
215
+ if (!record || record.resetTime < now) {
216
+ this.attempts.set(identifier, { count: 1, resetTime: now + this.windowMs });
217
+ return;
218
+ }
219
+
220
+ if (record.count >= this.maxAttempts) {
221
+ const waitTime = Math.ceil((record.resetTime - now) / 1000);
222
+ throw new WordPressAPIError(
223
+ `Rate limit exceeded. Please wait ${waitTime} seconds before trying again.`,
224
+ 429,
225
+ 'RATE_LIMIT_EXCEEDED'
226
+ );
227
+ }
228
+
229
+ record.count++;
230
+ }
231
+
232
+ reset(identifier: string): void {
233
+ this.attempts.delete(identifier);
234
+ }
235
+ }
236
+
237
+ // Export a default rate limiter for authentication attempts
238
+ export const authRateLimiter = new RateLimiter(5, 300000); // 5 attempts per 5 minutes
239
+
240
+ /**
241
+ * Validates and sanitizes search queries
242
+ */
243
+ export function validateSearchQuery(query: string): string {
244
+ // Remove potentially dangerous characters while preserving search functionality
245
+ let sanitized = query.trim();
246
+
247
+ // Limit length to prevent DoS
248
+ if (sanitized.length > 200) {
249
+ sanitized = sanitized.substring(0, 200);
250
+ }
251
+
252
+ // Remove SQL-like patterns (basic protection)
253
+ sanitized = sanitized.replace(/(\b(union|select|insert|update|delete|drop|create)\b)/gi, '');
254
+
255
+ // Remove special characters that might be used for injection
256
+ sanitized = sanitized.replace(/[<>'"`;\\]/g, '');
257
+
258
+ return sanitized;
259
+ }