mcp-wordpress 2.4.2 → 2.5.0

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 (348) hide show
  1. package/README.md +114 -48
  2. package/dist/ajv-patch.js +34 -0
  3. package/dist/cache/CacheInvalidation.d.ts +3 -1
  4. package/dist/cache/CacheInvalidation.d.ts.map +1 -1
  5. package/dist/cache/CacheInvalidation.js +10 -4
  6. package/dist/cache/CacheInvalidation.js.map +1 -1
  7. package/dist/cache/CacheManager.d.ts +3 -2
  8. package/dist/cache/CacheManager.d.ts.map +1 -1
  9. package/dist/cache/CacheManager.js +11 -3
  10. package/dist/cache/CacheManager.js.map +1 -1
  11. package/dist/cache/HttpCacheWrapper.d.ts +7 -6
  12. package/dist/cache/HttpCacheWrapper.d.ts.map +1 -1
  13. package/dist/cache/HttpCacheWrapper.js +8 -5
  14. package/dist/cache/HttpCacheWrapper.js.map +1 -1
  15. package/dist/cache/__tests__/HttpCacheWrapper.test.js +6 -5
  16. package/dist/cache/__tests__/HttpCacheWrapper.test.js.map +1 -1
  17. package/dist/cache/index.d.ts +3 -3
  18. package/dist/cache/index.d.ts.map +1 -1
  19. package/dist/cache/index.js +1 -1
  20. package/dist/cache/index.js.map +1 -1
  21. package/dist/client/CachedWordPressClient.d.ts +23 -9
  22. package/dist/client/CachedWordPressClient.d.ts.map +1 -1
  23. package/dist/client/CachedWordPressClient.js +4 -1
  24. package/dist/client/CachedWordPressClient.js.map +1 -1
  25. package/dist/client/MockWordPressClient.d.ts +2 -1
  26. package/dist/client/MockWordPressClient.d.ts.map +1 -1
  27. package/dist/client/MockWordPressClient.js +3 -1
  28. package/dist/client/MockWordPressClient.js.map +1 -1
  29. package/dist/client/api.d.ts +17 -13
  30. package/dist/client/api.d.ts.map +1 -1
  31. package/dist/client/api.js +135 -30
  32. package/dist/client/api.js.map +1 -1
  33. package/dist/client/auth.d.ts.map +1 -1
  34. package/dist/client/auth.js +2 -3
  35. package/dist/client/auth.js.map +1 -1
  36. package/dist/client/managers/AuthenticationManager.d.ts +55 -2
  37. package/dist/client/managers/AuthenticationManager.d.ts.map +1 -1
  38. package/dist/client/managers/AuthenticationManager.js +269 -71
  39. package/dist/client/managers/AuthenticationManager.js.map +1 -1
  40. package/dist/client/managers/BaseManager.d.ts +3 -3
  41. package/dist/client/managers/BaseManager.d.ts.map +1 -1
  42. package/dist/client/managers/BaseManager.js +11 -5
  43. package/dist/client/managers/BaseManager.js.map +1 -1
  44. package/dist/client/managers/RequestManager.d.ts +2 -2
  45. package/dist/client/managers/RequestManager.d.ts.map +1 -1
  46. package/dist/client/managers/RequestManager.js +25 -12
  47. package/dist/client/managers/RequestManager.js.map +1 -1
  48. package/dist/config/Config.d.ts +155 -0
  49. package/dist/config/Config.d.ts.map +1 -0
  50. package/dist/config/Config.js +215 -0
  51. package/dist/config/Config.js.map +1 -0
  52. package/dist/config/ConfigurationSchema.d.ts +21 -21
  53. package/dist/config/ConfigurationSchema.d.ts.map +1 -1
  54. package/dist/config/ConfigurationSchema.js +19 -2
  55. package/dist/config/ConfigurationSchema.js.map +1 -1
  56. package/dist/config/ServerConfiguration.d.ts +2 -1
  57. package/dist/config/ServerConfiguration.d.ts.map +1 -1
  58. package/dist/config/ServerConfiguration.js +50 -41
  59. package/dist/config/ServerConfiguration.js.map +1 -1
  60. package/dist/docs/DocumentationGenerator.d.ts +9 -8
  61. package/dist/docs/DocumentationGenerator.d.ts.map +1 -1
  62. package/dist/docs/DocumentationGenerator.js +10 -7
  63. package/dist/docs/DocumentationGenerator.js.map +1 -1
  64. package/dist/docs/MarkdownFormatter.d.ts.map +1 -1
  65. package/dist/docs/MarkdownFormatter.js +3 -2
  66. package/dist/docs/MarkdownFormatter.js.map +1 -1
  67. package/dist/dxt-entry.cjs +81 -0
  68. package/dist/dxt-entry.js +15 -14
  69. package/dist/dxt-entry.js.map +1 -1
  70. package/dist/index.d.ts +3 -1
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +37 -21
  73. package/dist/index.js.map +1 -1
  74. package/dist/performance/MetricsCollector.d.ts +13 -7
  75. package/dist/performance/MetricsCollector.d.ts.map +1 -1
  76. package/dist/performance/MetricsCollector.js +69 -27
  77. package/dist/performance/MetricsCollector.js.map +1 -1
  78. package/dist/performance/PerformanceAnalytics.d.ts +8 -2
  79. package/dist/performance/PerformanceAnalytics.d.ts.map +1 -1
  80. package/dist/performance/PerformanceAnalytics.js +17 -47
  81. package/dist/performance/PerformanceAnalytics.js.map +1 -1
  82. package/dist/performance/PerformanceMonitor.d.ts +2 -1
  83. package/dist/performance/PerformanceMonitor.d.ts.map +1 -1
  84. package/dist/performance/PerformanceMonitor.js +12 -13
  85. package/dist/performance/PerformanceMonitor.js.map +1 -1
  86. package/dist/performance/index.d.ts +2 -2
  87. package/dist/performance/index.d.ts.map +1 -1
  88. package/dist/security/AISecurityScanner.d.ts +1 -0
  89. package/dist/security/AISecurityScanner.d.ts.map +1 -1
  90. package/dist/security/AISecurityScanner.js +22 -12
  91. package/dist/security/AISecurityScanner.js.map +1 -1
  92. package/dist/security/AutomatedRemediation.d.ts +4 -3
  93. package/dist/security/AutomatedRemediation.d.ts.map +1 -1
  94. package/dist/security/AutomatedRemediation.js +46 -15
  95. package/dist/security/AutomatedRemediation.js.map +1 -1
  96. package/dist/security/InputValidator.d.ts +13 -9
  97. package/dist/security/InputValidator.d.ts.map +1 -1
  98. package/dist/security/InputValidator.js +4 -2
  99. package/dist/security/InputValidator.js.map +1 -1
  100. package/dist/security/SecurityCIPipeline.d.ts +1 -1
  101. package/dist/security/SecurityCIPipeline.d.ts.map +1 -1
  102. package/dist/security/SecurityCIPipeline.js +38 -29
  103. package/dist/security/SecurityCIPipeline.js.map +1 -1
  104. package/dist/security/SecurityConfig.d.ts +3 -3
  105. package/dist/security/SecurityConfig.d.ts.map +1 -1
  106. package/dist/security/SecurityConfig.js +13 -9
  107. package/dist/security/SecurityConfig.js.map +1 -1
  108. package/dist/security/SecurityConfigManager.d.ts +2 -2
  109. package/dist/security/SecurityConfigManager.d.ts.map +1 -1
  110. package/dist/security/SecurityConfigManager.js +20 -15
  111. package/dist/security/SecurityConfigManager.js.map +1 -1
  112. package/dist/security/SecurityMonitoring.d.ts +2 -2
  113. package/dist/security/SecurityMonitoring.d.ts.map +1 -1
  114. package/dist/security/SecurityMonitoring.js +19 -17
  115. package/dist/security/SecurityMonitoring.js.map +1 -1
  116. package/dist/security/SecurityReviewer.d.ts.map +1 -1
  117. package/dist/security/SecurityReviewer.js +10 -7
  118. package/dist/security/SecurityReviewer.js.map +1 -1
  119. package/dist/security/index.d.ts +24 -23
  120. package/dist/security/index.d.ts.map +1 -1
  121. package/dist/security/index.js +52 -23
  122. package/dist/security/index.js.map +1 -1
  123. package/dist/server/ConnectionTester.d.ts +12 -4
  124. package/dist/server/ConnectionTester.d.ts.map +1 -1
  125. package/dist/server/ConnectionTester.js +96 -22
  126. package/dist/server/ConnectionTester.js.map +1 -1
  127. package/dist/server/ToolRegistry.d.ts +2 -2
  128. package/dist/server/ToolRegistry.d.ts.map +1 -1
  129. package/dist/server/ToolRegistry.js +10 -5
  130. package/dist/server/ToolRegistry.js.map +1 -1
  131. package/dist/tools/BaseToolManager.d.ts +47 -11
  132. package/dist/tools/BaseToolManager.d.ts.map +1 -1
  133. package/dist/tools/BaseToolManager.js +168 -29
  134. package/dist/tools/BaseToolManager.js.map +1 -1
  135. package/dist/tools/auth.d.ts +16 -10
  136. package/dist/tools/auth.d.ts.map +1 -1
  137. package/dist/tools/auth.js +3 -2
  138. package/dist/tools/auth.js.map +1 -1
  139. package/dist/tools/cache.d.ts +30 -30
  140. package/dist/tools/cache.d.ts.map +1 -1
  141. package/dist/tools/cache.js +1 -6
  142. package/dist/tools/cache.js.map +1 -1
  143. package/dist/tools/comments.d.ts +20 -20
  144. package/dist/tools/comments.d.ts.map +1 -1
  145. package/dist/tools/comments.js +16 -9
  146. package/dist/tools/comments.js.map +1 -1
  147. package/dist/tools/media.d.ts +18 -16
  148. package/dist/tools/media.d.ts.map +1 -1
  149. package/dist/tools/media.js +16 -15
  150. package/dist/tools/media.js.map +1 -1
  151. package/dist/tools/pages.d.ts +19 -17
  152. package/dist/tools/pages.d.ts.map +1 -1
  153. package/dist/tools/pages.js +16 -12
  154. package/dist/tools/pages.js.map +1 -1
  155. package/dist/tools/performance.d.ts +11 -1
  156. package/dist/tools/performance.d.ts.map +1 -1
  157. package/dist/tools/performance.js +67 -34
  158. package/dist/tools/performance.js.map +1 -1
  159. package/dist/tools/posts/PostHandlers.d.ts +46 -0
  160. package/dist/tools/posts/PostHandlers.d.ts.map +1 -0
  161. package/dist/tools/posts/PostHandlers.js +400 -0
  162. package/dist/tools/posts/PostHandlers.js.map +1 -0
  163. package/dist/tools/posts/PostToolDefinitions.d.ts +37 -0
  164. package/dist/tools/posts/PostToolDefinitions.d.ts.map +1 -0
  165. package/dist/tools/posts/PostToolDefinitions.js +236 -0
  166. package/dist/tools/posts/PostToolDefinitions.js.map +1 -0
  167. package/dist/tools/posts/index.d.ts +138 -0
  168. package/dist/tools/posts/index.d.ts.map +1 -0
  169. package/dist/tools/posts/index.js +163 -0
  170. package/dist/tools/posts/index.js.map +1 -0
  171. package/dist/tools/posts.d.ts +10 -246
  172. package/dist/tools/posts.d.ts.map +1 -1
  173. package/dist/tools/posts.js +11 -723
  174. package/dist/tools/posts.js.map +1 -1
  175. package/dist/tools/site.d.ts +19 -18
  176. package/dist/tools/site.d.ts.map +1 -1
  177. package/dist/tools/site.js +14 -10
  178. package/dist/tools/site.js.map +1 -1
  179. package/dist/tools/taxonomies.d.ts +23 -24
  180. package/dist/tools/taxonomies.d.ts.map +1 -1
  181. package/dist/tools/taxonomies.js +24 -18
  182. package/dist/tools/taxonomies.js.map +1 -1
  183. package/dist/tools/users.d.ts +20 -15
  184. package/dist/tools/users.d.ts.map +1 -1
  185. package/dist/tools/users.js +12 -8
  186. package/dist/tools/users.js.map +1 -1
  187. package/dist/types/client.d.ts +48 -41
  188. package/dist/types/client.d.ts.map +1 -1
  189. package/dist/types/client.js +30 -5
  190. package/dist/types/client.js.map +1 -1
  191. package/dist/types/enhanced.d.ts +237 -0
  192. package/dist/types/enhanced.d.ts.map +1 -0
  193. package/dist/types/enhanced.js +49 -0
  194. package/dist/types/enhanced.js.map +1 -0
  195. package/dist/types/index.d.ts +15 -12
  196. package/dist/types/index.d.ts.map +1 -1
  197. package/dist/types/index.js +2 -0
  198. package/dist/types/index.js.map +1 -1
  199. package/dist/types/mcp.d.ts +12 -12
  200. package/dist/types/mcp.d.ts.map +1 -1
  201. package/dist/types/requests.d.ts +322 -0
  202. package/dist/types/requests.d.ts.map +1 -0
  203. package/dist/types/requests.js +8 -0
  204. package/dist/types/requests.js.map +1 -0
  205. package/dist/types/tools.d.ts +506 -0
  206. package/dist/types/tools.d.ts.map +1 -0
  207. package/dist/types/tools.js +8 -0
  208. package/dist/types/tools.js.map +1 -0
  209. package/dist/types/wordpress.d.ts +43 -15
  210. package/dist/types/wordpress.d.ts.map +1 -1
  211. package/dist/types/wordpress.js +8 -1
  212. package/dist/types/wordpress.js.map +1 -1
  213. package/dist/utils/debug.d.ts +19 -11
  214. package/dist/utils/debug.d.ts.map +1 -1
  215. package/dist/utils/debug.js +46 -10
  216. package/dist/utils/debug.js.map +1 -1
  217. package/dist/utils/enhancedError.d.ts +8 -8
  218. package/dist/utils/enhancedError.d.ts.map +1 -1
  219. package/dist/utils/enhancedError.js.map +1 -1
  220. package/dist/utils/error.d.ts +2 -4
  221. package/dist/utils/error.d.ts.map +1 -1
  222. package/dist/utils/error.js +42 -5
  223. package/dist/utils/error.js.map +1 -1
  224. package/dist/utils/logger.d.ts +106 -0
  225. package/dist/utils/logger.d.ts.map +1 -0
  226. package/dist/utils/logger.js +280 -0
  227. package/dist/utils/logger.js.map +1 -0
  228. package/dist/utils/streaming.d.ts +9 -9
  229. package/dist/utils/streaming.d.ts.map +1 -1
  230. package/dist/utils/streaming.js +71 -52
  231. package/dist/utils/streaming.js.map +1 -1
  232. package/dist/utils/toolWrapper.d.ts +9 -7
  233. package/dist/utils/toolWrapper.d.ts.map +1 -1
  234. package/dist/utils/toolWrapper.js.map +1 -1
  235. package/dist/utils/validation/core.d.ts +21 -0
  236. package/dist/utils/validation/core.d.ts.map +1 -0
  237. package/dist/utils/validation/core.js +71 -0
  238. package/dist/utils/validation/core.js.map +1 -0
  239. package/dist/utils/validation/index.d.ts +25 -0
  240. package/dist/utils/validation/index.d.ts.map +1 -0
  241. package/dist/utils/validation/index.js +29 -0
  242. package/dist/utils/validation/index.js.map +1 -0
  243. package/dist/utils/validation/network.d.ts +19 -0
  244. package/dist/utils/validation/network.d.ts.map +1 -0
  245. package/dist/utils/validation/network.js +93 -0
  246. package/dist/utils/validation/network.js.map +1 -0
  247. package/dist/utils/validation/rateLimit.d.ts +21 -0
  248. package/dist/utils/validation/rateLimit.d.ts.map +1 -0
  249. package/dist/utils/validation/rateLimit.js +43 -0
  250. package/dist/utils/validation/rateLimit.js.map +1 -0
  251. package/dist/utils/validation/security.d.ts +29 -0
  252. package/dist/utils/validation/security.d.ts.map +1 -0
  253. package/dist/utils/validation/security.js +327 -0
  254. package/dist/utils/validation/security.js.map +1 -0
  255. package/dist/utils/validation/wordpress.d.ts +31 -0
  256. package/dist/utils/validation/wordpress.d.ts.map +1 -0
  257. package/dist/utils/validation/wordpress.js +146 -0
  258. package/dist/utils/validation/wordpress.js.map +1 -0
  259. package/dist/utils/validation.d.ts +13 -82
  260. package/dist/utils/validation.d.ts.map +1 -1
  261. package/dist/utils/validation.js +25 -343
  262. package/dist/utils/validation.js.map +1 -1
  263. package/docs/BADGE_UPDATES.md +132 -0
  264. package/docs/CI_CD_IMPROVEMENTS.md +191 -0
  265. package/docs/INCREMENTAL_COVERAGE.md +183 -0
  266. package/docs/api/README.md +3 -1
  267. package/docs/api/openapi.json +5 -1
  268. package/docs/api/summary.json +1 -1
  269. package/docs/api/tools/wp_create_post.md +12 -14
  270. package/docs/examples/claude-desktop-config.md +1 -1
  271. package/docs/examples/docker-production.md +100 -93
  272. package/docs/examples/multi-site-setup.md +5 -4
  273. package/docs/examples/single-site-setup.md +3 -4
  274. package/docs/examples/use-case-workflows.md +4 -5
  275. package/docs/integrations/claude-desktop.md +31 -31
  276. package/docs/integrations/cline.md +4 -4
  277. package/docs/integrations/vs-code.md +9 -8
  278. package/docs/user-guides/SMITHERY_SETUP.md +10 -10
  279. package/package.json +44 -25
  280. package/src/cache/CacheInvalidation.ts +12 -5
  281. package/src/cache/CacheManager.ts +18 -15
  282. package/src/cache/HttpCacheWrapper.ts +30 -59
  283. package/src/cache/__tests__/HttpCacheWrapper.test.ts +6 -5
  284. package/src/cache/index.ts +3 -14
  285. package/src/client/CachedWordPressClient.ts +32 -30
  286. package/src/client/MockWordPressClient.ts +4 -2
  287. package/src/client/api.ts +186 -64
  288. package/src/client/auth.ts +15 -40
  289. package/src/client/managers/AuthenticationManager.ts +337 -77
  290. package/src/client/managers/BaseManager.ts +18 -30
  291. package/src/client/managers/RequestManager.ts +39 -44
  292. package/src/config/Config.ts +308 -0
  293. package/src/config/ConfigurationSchema.ts +23 -2
  294. package/src/config/ServerConfiguration.ts +51 -47
  295. package/src/docs/DocumentationGenerator.ts +50 -39
  296. package/src/docs/MarkdownFormatter.ts +19 -29
  297. package/src/dxt-entry.cjs +26 -16
  298. package/src/dxt-entry.ts +17 -27
  299. package/src/index.ts +42 -28
  300. package/src/performance/MetricsCollector.ts +108 -86
  301. package/src/performance/PerformanceAnalytics.ts +69 -164
  302. package/src/performance/PerformanceMonitor.ts +32 -47
  303. package/src/performance/index.ts +2 -10
  304. package/src/security/AISecurityScanner.ts +22 -12
  305. package/src/security/AutomatedRemediation.ts +49 -18
  306. package/src/security/InputValidator.ts +9 -6
  307. package/src/security/SecurityCIPipeline.ts +53 -37
  308. package/src/security/SecurityConfig.ts +22 -22
  309. package/src/security/SecurityConfigManager.ts +23 -19
  310. package/src/security/SecurityMonitoring.ts +24 -21
  311. package/src/security/SecurityReviewer.ts +10 -7
  312. package/src/security/index.ts +64 -29
  313. package/src/server/ConnectionTester.ts +120 -31
  314. package/src/server/ToolRegistry.ts +31 -21
  315. package/src/tools/BaseToolManager.ts +286 -33
  316. package/src/tools/auth.ts +20 -8
  317. package/src/tools/cache.ts +5 -15
  318. package/src/tools/comments.ts +34 -48
  319. package/src/tools/media.ts +41 -53
  320. package/src/tools/pages.ts +32 -54
  321. package/src/tools/performance.ts +141 -176
  322. package/src/tools/posts/PostHandlers.ts +474 -0
  323. package/src/tools/posts/PostToolDefinitions.ts +250 -0
  324. package/src/tools/posts/index.ts +192 -0
  325. package/src/tools/posts.ts +24 -780
  326. package/src/tools/site.ts +34 -19
  327. package/src/tools/taxonomies.ts +41 -57
  328. package/src/tools/users.ts +28 -16
  329. package/src/types/client.ts +114 -138
  330. package/src/types/enhanced.ts +318 -0
  331. package/src/types/index.ts +51 -30
  332. package/src/types/mcp.ts +20 -42
  333. package/src/types/requests.ts +378 -0
  334. package/src/types/tools.ts +608 -0
  335. package/src/types/wordpress.ts +56 -34
  336. package/src/utils/debug.ts +77 -59
  337. package/src/utils/enhancedError.ts +8 -8
  338. package/src/utils/error.ts +53 -31
  339. package/src/utils/logger.ts +351 -0
  340. package/src/utils/streaming.ts +86 -68
  341. package/src/utils/toolWrapper.ts +10 -12
  342. package/src/utils/validation/core.ts +108 -0
  343. package/src/utils/validation/index.ts +36 -0
  344. package/src/utils/validation/network.ts +132 -0
  345. package/src/utils/validation/rateLimit.ts +54 -0
  346. package/src/utils/validation/security.ts +361 -0
  347. package/src/utils/validation/wordpress.ts +180 -0
  348. package/src/utils/validation.ts +47 -470
@@ -1,471 +1,48 @@
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 with comprehensive edge case handling
10
- */
11
- export function validateId(id: any, fieldName: string = "id"): number {
12
- // Handle null/undefined
13
- if (id === null || id === undefined) {
14
- throw new WordPressAPIError(`${fieldName} is required`, 400, "MISSING_PARAMETER");
15
- }
16
-
17
- // Convert to string first to handle various input types
18
- const strId = String(id).trim();
19
-
20
- // Check for empty string after trim
21
- if (strId === "") {
22
- throw new WordPressAPIError(`${fieldName} cannot be empty`, 400, "INVALID_PARAMETER");
23
- }
24
-
25
- // Handle decimal inputs
26
- if (strId.includes(".")) {
27
- throw new WordPressAPIError(`${fieldName} must be a whole number, not a decimal`, 400, "INVALID_PARAMETER");
28
- }
29
-
30
- const numId = parseInt(strId, 10);
31
-
32
- // Check for NaN
33
- if (isNaN(numId)) {
34
- throw new WordPressAPIError(`Invalid ${fieldName}: "${id}" is not a valid number`, 400, "INVALID_PARAMETER");
35
- }
36
-
37
- // Check for negative or zero
38
- if (numId <= 0) {
39
- throw new WordPressAPIError(
40
- `Invalid ${fieldName}: must be a positive number (got ${numId})`,
41
- 400,
42
- "INVALID_PARAMETER",
43
- );
44
- }
45
-
46
- // Check for max int32 limit (WordPress database limit)
47
- if (numId > 2147483647) {
48
- throw new WordPressAPIError(
49
- `Invalid ${fieldName}: exceeds maximum allowed value (2147483647)`,
50
- 400,
51
- "INVALID_PARAMETER",
52
- );
53
- }
54
-
55
- return numId;
56
- }
57
-
58
- /**
59
- * Validates string length within bounds
60
- */
61
- export function validateString(value: any, fieldName: string, minLength: number = 1, maxLength: number = 1000): string {
62
- if (typeof value !== "string") {
63
- throw new WordPressAPIError(`Invalid ${fieldName}: must be a string`, 400, "INVALID_PARAMETER");
64
- }
65
-
66
- const trimmed = value.trim();
67
- if (trimmed.length < minLength || trimmed.length > maxLength) {
68
- throw new WordPressAPIError(
69
- `Invalid ${fieldName}: length must be between ${minLength} and ${maxLength} characters`,
70
- 400,
71
- "INVALID_PARAMETER",
72
- );
73
- }
74
-
75
- return trimmed;
76
- }
77
-
78
- /**
79
- * Validates and sanitizes file paths to prevent directory traversal
80
- */
81
- export function validateFilePath(userPath: string, allowedBasePath: string): string {
82
- // Normalize the path to remove ../ and other dangerous patterns
83
- const normalizedPath = path.normalize(userPath);
84
- const resolvedPath = path.resolve(allowedBasePath, normalizedPath);
85
-
86
- // Ensure the resolved path is within the allowed directory
87
- if (!resolvedPath.startsWith(path.resolve(allowedBasePath))) {
88
- throw new WordPressAPIError("Invalid file path: access denied", 403, "PATH_TRAVERSAL_ATTEMPT");
89
- }
90
-
91
- return resolvedPath;
92
- }
93
-
94
- /**
95
- * Validates WordPress post status values
96
- */
97
- export function validatePostStatus(status: string): string {
98
- const validStatuses = ["publish", "draft", "pending", "private", "future", "auto-draft", "trash"];
99
- if (!validStatuses.includes(status)) {
100
- throw new WordPressAPIError(`Invalid status: must be one of ${validStatuses.join(", ")}`, 400, "INVALID_PARAMETER");
101
- }
102
- return status;
103
- }
104
-
105
- /**
106
- * Validates and sanitizes URLs with enhanced edge case handling
107
- */
108
- export function validateUrl(url: string, fieldName: string = "url"): string {
109
- // Check for empty or whitespace-only URLs
110
- const trimmedUrl = url.trim();
111
- if (!trimmedUrl) {
112
- throw new WordPressAPIError(`${fieldName} cannot be empty`, 400, "INVALID_PARAMETER");
113
- }
114
-
115
- // Remove trailing slashes for consistency
116
- const cleanUrl = trimmedUrl.replace(/\/+$/, "");
117
-
118
- // Check for common URL mistakes
119
- if (!cleanUrl.match(/^https?:\/\//i)) {
120
- throw new WordPressAPIError(
121
- `Invalid ${fieldName}: must start with http:// or https:// (got "${cleanUrl}")`,
122
- 400,
123
- "INVALID_PARAMETER",
124
- );
125
- }
126
-
127
- try {
128
- const urlObj = new URL(cleanUrl);
129
-
130
- // Only allow http and https protocols
131
- if (!["http:", "https:"].includes(urlObj.protocol)) {
132
- throw new WordPressAPIError(
133
- `Invalid ${fieldName}: only HTTP and HTTPS protocols are allowed`,
134
- 400,
135
- "INVALID_PARAMETER",
136
- );
137
- }
138
-
139
- // Validate hostname
140
- if (!urlObj.hostname || urlObj.hostname.length < 3) {
141
- throw new WordPressAPIError(`Invalid ${fieldName}: hostname is missing or too short`, 400, "INVALID_PARAMETER");
142
- }
143
-
144
- // Check for localhost in production
145
- if (process.env.NODE_ENV === "production" && (urlObj.hostname === "localhost" || urlObj.hostname === "127.0.0.1")) {
146
- throw new WordPressAPIError(
147
- `Invalid ${fieldName}: localhost URLs are not allowed in production`,
148
- 400,
149
- "INVALID_PARAMETER",
150
- );
151
- }
152
-
153
- // Validate port if present
154
- if (urlObj.port) {
155
- const port = parseInt(urlObj.port);
156
- if (port < 1 || port > 65535) {
157
- throw new WordPressAPIError(
158
- `Invalid ${fieldName}: port number must be between 1 and 65535`,
159
- 400,
160
- "INVALID_PARAMETER",
161
- );
162
- }
163
- }
164
-
165
- return cleanUrl;
166
- } catch (error) {
167
- if (error instanceof WordPressAPIError) {
168
- throw error;
169
- }
170
- throw new WordPressAPIError(`Invalid ${fieldName}: malformed URL "${cleanUrl}"`, 400, "INVALID_PARAMETER");
171
- }
172
- }
173
-
174
- /**
175
- * Validates file size
176
- */
177
- export function validateFileSize(sizeInBytes: number, maxSizeInMB: number = 10): void {
178
- const maxSizeInBytes = maxSizeInMB * 1024 * 1024;
179
- if (sizeInBytes > maxSizeInBytes) {
180
- throw new WordPressAPIError(`File size exceeds maximum allowed size of ${maxSizeInMB}MB`, 413, "FILE_TOO_LARGE");
181
- }
182
- }
183
-
184
- /**
185
- * Validates MIME types for file uploads
186
- */
187
- export function validateMimeType(mimeType: string, allowedTypes: string[]): void {
188
- if (!allowedTypes.includes(mimeType)) {
189
- throw new WordPressAPIError(
190
- `Invalid file type: ${mimeType}. Allowed types: ${allowedTypes.join(", ")}`,
191
- 415,
192
- "UNSUPPORTED_MEDIA_TYPE",
193
- );
194
- }
195
- }
196
-
197
- /**
198
- * Sanitizes HTML content to prevent XSS
199
- * Note: This is a basic implementation. For production use,
200
- * consider using a library like DOMPurify
201
- */
202
- export function sanitizeHtml(html: string): string {
203
- // Remove script tags and their content
204
- let sanitized = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "");
205
-
206
- // Remove event handlers
207
- sanitized = sanitized.replace(/\s*on\w+\s*=\s*["'][^"']*["']/gi, "");
208
-
209
- // Remove javascript: protocol
210
- sanitized = sanitized.replace(/javascript:/gi, "");
211
-
212
- // Remove data: protocol (can be used for XSS)
213
- sanitized = sanitized.replace(/data:text\/html/gi, "");
214
-
215
- return sanitized;
216
- }
217
-
218
- /**
219
- * Validates array input
220
- */
221
- export function validateArray<T>(value: any, fieldName: string, minItems: number = 0, maxItems: number = 100): T[] {
222
- if (!Array.isArray(value)) {
223
- throw new WordPressAPIError(`Invalid ${fieldName}: must be an array`, 400, "INVALID_PARAMETER");
224
- }
225
-
226
- if (value.length < minItems || value.length > maxItems) {
227
- throw new WordPressAPIError(
228
- `Invalid ${fieldName}: array must contain between ${minItems} and ${maxItems} items`,
229
- 400,
230
- "INVALID_PARAMETER",
231
- );
232
- }
233
-
234
- return value;
235
- }
236
-
237
- /**
238
- * Validates email addresses
239
- */
240
- export function validateEmail(email: string): string {
241
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
242
- if (!emailRegex.test(email)) {
243
- throw new WordPressAPIError("Invalid email address format", 400, "INVALID_PARAMETER");
244
- }
245
- return email.toLowerCase();
246
- }
247
-
248
- /**
249
- * Validates username format with enhanced security checks
250
- */
251
- export function validateUsername(username: string): string {
252
- // Trim and check for empty
253
- const trimmed = username.trim();
254
- if (!trimmed) {
255
- throw new WordPressAPIError("Username cannot be empty", 400, "INVALID_PARAMETER");
256
- }
257
-
258
- // WordPress username rules: alphanumeric, space, underscore, hyphen, period, @ symbol
259
- const usernameRegex = /^[a-zA-Z0-9 _.\-@]+$/;
260
- if (!usernameRegex.test(trimmed)) {
261
- throw new WordPressAPIError(
262
- "Invalid username: can only contain letters, numbers, spaces, and _.-@ symbols",
263
- 400,
264
- "INVALID_PARAMETER",
265
- );
266
- }
267
-
268
- // Length validation
269
- if (trimmed.length < 3 || trimmed.length > 60) {
270
- throw new WordPressAPIError(
271
- `Invalid username: must be between 3 and 60 characters (got ${trimmed.length})`,
272
- 400,
273
- "INVALID_PARAMETER",
274
- );
275
- }
276
-
277
- // Check for consecutive spaces
278
- if (/\s{2,}/.test(trimmed)) {
279
- throw new WordPressAPIError("Invalid username: cannot contain consecutive spaces", 400, "INVALID_PARAMETER");
280
- }
281
-
282
- // Security: Prevent common problematic usernames
283
- const blacklist = ["admin", "root", "wordpress", "wp-admin", "administrator"];
284
- if (blacklist.includes(trimmed.toLowerCase())) {
285
- throw new WordPressAPIError(`Username "${trimmed}" is reserved and cannot be used`, 400, "RESERVED_USERNAME");
286
- }
287
-
288
- return trimmed;
289
- }
290
-
291
1
  /**
292
- * Rate limiting tracker (simple in-memory implementation)
293
- * For production, use Redis or similar
294
- */
295
- class RateLimiter {
296
- private attempts: Map<string, { count: number; resetTime: number }> = new Map();
297
-
298
- constructor(
299
- private maxAttempts: number = 5,
300
- private windowMs: number = 60000, // 1 minute
301
- ) {}
302
-
303
- check(identifier: string): void {
304
- const now = Date.now();
305
- const record = this.attempts.get(identifier);
306
-
307
- if (!record || record.resetTime < now) {
308
- this.attempts.set(identifier, {
309
- count: 1,
310
- resetTime: now + this.windowMs,
311
- });
312
- return;
313
- }
314
-
315
- if (record.count >= this.maxAttempts) {
316
- const waitTime = Math.ceil((record.resetTime - now) / 1000);
317
- throw new WordPressAPIError(
318
- `Rate limit exceeded. Please wait ${waitTime} seconds before trying again.`,
319
- 429,
320
- "RATE_LIMIT_EXCEEDED",
321
- );
322
- }
323
-
324
- record.count++;
325
- }
326
-
327
- reset(identifier: string): void {
328
- this.attempts.delete(identifier);
329
- }
330
- }
331
-
332
- // Export a default rate limiter for authentication attempts
333
- export const authRateLimiter = new RateLimiter(5, 300000); // 5 attempts per 5 minutes
334
-
335
- /**
336
- * Validates and sanitizes search queries
337
- */
338
- export function validateSearchQuery(query: string): string {
339
- // Remove potentially dangerous characters while preserving search functionality
340
- let sanitized = query.trim();
341
-
342
- // Limit length to prevent DoS
343
- if (sanitized.length > 200) {
344
- sanitized = sanitized.substring(0, 200);
345
- }
346
-
347
- // Remove SQL-like patterns (basic protection)
348
- sanitized = sanitized.replace(/(\b(union|select|insert|update|delete|drop|create)\b)/gi, "");
349
-
350
- // Remove special characters that might be used for injection
351
- sanitized = sanitized.replace(/[<>'"`;\\]/g, "");
352
-
353
- return sanitized;
354
- }
355
-
356
- /**
357
- * Validates pagination parameters as a set
358
- */
359
- export function validatePaginationParams(params: { page?: any; per_page?: any; offset?: any }): {
360
- page?: number;
361
- per_page?: number;
362
- offset?: number;
363
- } {
364
- const validated: { page?: number; per_page?: number; offset?: number } = {};
365
-
366
- // Validate page
367
- if (params.page !== undefined) {
368
- const page = parseInt(String(params.page), 10);
369
- if (isNaN(page) || page < 1) {
370
- throw new WordPressAPIError("Page must be a positive integer", 400, "INVALID_PARAMETER");
371
- }
372
- if (page > 10000) {
373
- throw new WordPressAPIError("Page number too high (max 10000)", 400, "INVALID_PARAMETER");
374
- }
375
- validated.page = page;
376
- }
377
-
378
- // Validate per_page
379
- if (params.per_page !== undefined) {
380
- const perPage = parseInt(String(params.per_page), 10);
381
- if (isNaN(perPage) || perPage < 1) {
382
- throw new WordPressAPIError("Per page must be a positive integer", 400, "INVALID_PARAMETER");
383
- }
384
- if (perPage > 100) {
385
- throw new WordPressAPIError(`Per page exceeds maximum allowed (100), got ${perPage}`, 400, "INVALID_PARAMETER");
386
- }
387
- validated.per_page = perPage;
388
- }
389
-
390
- // Validate offset
391
- if (params.offset !== undefined) {
392
- const offset = parseInt(String(params.offset), 10);
393
- if (isNaN(offset) || offset < 0) {
394
- throw new WordPressAPIError("Offset must be a non-negative integer", 400, "INVALID_PARAMETER");
395
- }
396
- if (offset > 1000000) {
397
- throw new WordPressAPIError("Offset too large (max 1000000)", 400, "INVALID_PARAMETER");
398
- }
399
- validated.offset = offset;
400
- }
401
-
402
- // Check for conflicting parameters
403
- if (validated.page && validated.offset) {
404
- throw new WordPressAPIError(
405
- "Cannot use both 'page' and 'offset' parameters together",
406
- 400,
407
- "CONFLICTING_PARAMETERS",
408
- );
409
- }
410
-
411
- return validated;
412
- }
413
-
414
- /**
415
- * Validates complex post creation parameters
416
- */
417
- export function validatePostParams(params: any): any {
418
- const validated: any = {};
419
-
420
- // Title validation
421
- if (!params.title || typeof params.title !== "string") {
422
- throw new WordPressAPIError("Post title is required and must be a string", 400, "INVALID_PARAMETER");
423
- }
424
- validated.title = validateString(params.title, "title", 1, 200);
425
-
426
- // Content validation
427
- if (params.content !== undefined) {
428
- validated.content = sanitizeHtml(String(params.content));
429
- }
430
-
431
- // Status validation with context
432
- if (params.status) {
433
- validated.status = validatePostStatus(params.status);
434
-
435
- // Future posts need a date
436
- if (validated.status === "future" && !params.date) {
437
- throw new WordPressAPIError("Future posts require a 'date' parameter", 400, "MISSING_PARAMETER");
438
- }
439
- }
440
-
441
- // Categories and tags validation
442
- if (params.categories) {
443
- validated.categories = validateArray(params.categories, "categories", 0, 50);
444
- validated.categories = validated.categories.map((id: any) => validateId(id, "category ID"));
445
- }
446
-
447
- if (params.tags) {
448
- validated.tags = validateArray(params.tags, "tags", 0, 100);
449
- validated.tags = validated.tags.map((id: any) => validateId(id, "tag ID"));
450
- }
451
-
452
- // Date validation for scheduled posts
453
- if (params.date) {
454
- try {
455
- const date = new Date(params.date);
456
- if (isNaN(date.getTime())) {
457
- throw new Error("Invalid date");
458
- }
459
- // WordPress expects ISO 8601 format
460
- validated.date = date.toISOString();
461
- } catch {
462
- throw new WordPressAPIError(
463
- "Invalid date format. Use ISO 8601 format (YYYY-MM-DDTHH:mm:ss)",
464
- 400,
465
- "INVALID_PARAMETER",
466
- );
467
- }
468
- }
469
-
470
- return validated;
471
- }
2
+ * Enhanced Security-Focused Validation Utilities - Legacy Export Module
3
+ *
4
+ * This file maintains backward compatibility while the codebase transitions
5
+ * to the new modular structure. The actual implementations have been refactored
6
+ * into focused modules under ./validation/ directory.
7
+ *
8
+ * @deprecated Use direct imports from ./validation/ modules instead
9
+ * @see ./validation/index.ts for the new modular implementation
10
+ */
11
+
12
+ // Re-export everything from the modular structure for backward compatibility
13
+ export {
14
+ // Core validators
15
+ validateId,
16
+ validateString,
17
+ validateArray,
18
+
19
+ // Security validators
20
+ validateFilePath,
21
+ validateFileSize,
22
+ validateMimeType,
23
+ sanitizeHtml,
24
+
25
+ // Network validators
26
+ validateUrl,
27
+ validateEmail,
28
+ validateUsername,
29
+
30
+ // WordPress-specific validators
31
+ validatePostStatus,
32
+ validateSearchQuery,
33
+ validatePaginationParams,
34
+ validatePostParams,
35
+
36
+ // Rate limiting
37
+ RateLimiter,
38
+ authRateLimiter,
39
+
40
+ // Type re-exports
41
+ type WordPressId,
42
+ WordPressAPIError,
43
+ } from "./validation/index.js";
44
+
45
+ // Legacy re-exports for specific components (advanced usage)
46
+ export { validateId as validateWordPressId } from "./validation/core.js";
47
+ export { validatePostParams as validatePostData } from "./validation/wordpress.js";
48
+ export { sanitizeHtml as cleanHtml } from "./validation/security.js";