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.
- package/README.md +114 -48
- package/dist/ajv-patch.js +34 -0
- package/dist/cache/CacheInvalidation.d.ts +3 -1
- package/dist/cache/CacheInvalidation.d.ts.map +1 -1
- package/dist/cache/CacheInvalidation.js +10 -4
- package/dist/cache/CacheInvalidation.js.map +1 -1
- package/dist/cache/CacheManager.d.ts +3 -2
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +11 -3
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/HttpCacheWrapper.d.ts +7 -6
- package/dist/cache/HttpCacheWrapper.d.ts.map +1 -1
- package/dist/cache/HttpCacheWrapper.js +8 -5
- package/dist/cache/HttpCacheWrapper.js.map +1 -1
- package/dist/cache/__tests__/HttpCacheWrapper.test.js +6 -5
- package/dist/cache/__tests__/HttpCacheWrapper.test.js.map +1 -1
- package/dist/cache/index.d.ts +3 -3
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +1 -1
- package/dist/cache/index.js.map +1 -1
- package/dist/client/CachedWordPressClient.d.ts +23 -9
- package/dist/client/CachedWordPressClient.d.ts.map +1 -1
- package/dist/client/CachedWordPressClient.js +4 -1
- package/dist/client/CachedWordPressClient.js.map +1 -1
- package/dist/client/MockWordPressClient.d.ts +2 -1
- package/dist/client/MockWordPressClient.d.ts.map +1 -1
- package/dist/client/MockWordPressClient.js +3 -1
- package/dist/client/MockWordPressClient.js.map +1 -1
- package/dist/client/api.d.ts +17 -13
- package/dist/client/api.d.ts.map +1 -1
- package/dist/client/api.js +135 -30
- package/dist/client/api.js.map +1 -1
- package/dist/client/auth.d.ts.map +1 -1
- package/dist/client/auth.js +2 -3
- package/dist/client/auth.js.map +1 -1
- package/dist/client/managers/AuthenticationManager.d.ts +55 -2
- package/dist/client/managers/AuthenticationManager.d.ts.map +1 -1
- package/dist/client/managers/AuthenticationManager.js +269 -71
- package/dist/client/managers/AuthenticationManager.js.map +1 -1
- package/dist/client/managers/BaseManager.d.ts +3 -3
- package/dist/client/managers/BaseManager.d.ts.map +1 -1
- package/dist/client/managers/BaseManager.js +11 -5
- package/dist/client/managers/BaseManager.js.map +1 -1
- package/dist/client/managers/RequestManager.d.ts +2 -2
- package/dist/client/managers/RequestManager.d.ts.map +1 -1
- package/dist/client/managers/RequestManager.js +25 -12
- package/dist/client/managers/RequestManager.js.map +1 -1
- package/dist/config/Config.d.ts +155 -0
- package/dist/config/Config.d.ts.map +1 -0
- package/dist/config/Config.js +215 -0
- package/dist/config/Config.js.map +1 -0
- package/dist/config/ConfigurationSchema.d.ts +21 -21
- package/dist/config/ConfigurationSchema.d.ts.map +1 -1
- package/dist/config/ConfigurationSchema.js +19 -2
- package/dist/config/ConfigurationSchema.js.map +1 -1
- package/dist/config/ServerConfiguration.d.ts +2 -1
- package/dist/config/ServerConfiguration.d.ts.map +1 -1
- package/dist/config/ServerConfiguration.js +50 -41
- package/dist/config/ServerConfiguration.js.map +1 -1
- package/dist/docs/DocumentationGenerator.d.ts +9 -8
- package/dist/docs/DocumentationGenerator.d.ts.map +1 -1
- package/dist/docs/DocumentationGenerator.js +10 -7
- package/dist/docs/DocumentationGenerator.js.map +1 -1
- package/dist/docs/MarkdownFormatter.d.ts.map +1 -1
- package/dist/docs/MarkdownFormatter.js +3 -2
- package/dist/docs/MarkdownFormatter.js.map +1 -1
- package/dist/dxt-entry.cjs +81 -0
- package/dist/dxt-entry.js +15 -14
- package/dist/dxt-entry.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -21
- package/dist/index.js.map +1 -1
- package/dist/performance/MetricsCollector.d.ts +13 -7
- package/dist/performance/MetricsCollector.d.ts.map +1 -1
- package/dist/performance/MetricsCollector.js +69 -27
- package/dist/performance/MetricsCollector.js.map +1 -1
- package/dist/performance/PerformanceAnalytics.d.ts +8 -2
- package/dist/performance/PerformanceAnalytics.d.ts.map +1 -1
- package/dist/performance/PerformanceAnalytics.js +17 -47
- package/dist/performance/PerformanceAnalytics.js.map +1 -1
- package/dist/performance/PerformanceMonitor.d.ts +2 -1
- package/dist/performance/PerformanceMonitor.d.ts.map +1 -1
- package/dist/performance/PerformanceMonitor.js +12 -13
- package/dist/performance/PerformanceMonitor.js.map +1 -1
- package/dist/performance/index.d.ts +2 -2
- package/dist/performance/index.d.ts.map +1 -1
- package/dist/security/AISecurityScanner.d.ts +1 -0
- package/dist/security/AISecurityScanner.d.ts.map +1 -1
- package/dist/security/AISecurityScanner.js +22 -12
- package/dist/security/AISecurityScanner.js.map +1 -1
- package/dist/security/AutomatedRemediation.d.ts +4 -3
- package/dist/security/AutomatedRemediation.d.ts.map +1 -1
- package/dist/security/AutomatedRemediation.js +46 -15
- package/dist/security/AutomatedRemediation.js.map +1 -1
- package/dist/security/InputValidator.d.ts +13 -9
- package/dist/security/InputValidator.d.ts.map +1 -1
- package/dist/security/InputValidator.js +4 -2
- package/dist/security/InputValidator.js.map +1 -1
- package/dist/security/SecurityCIPipeline.d.ts +1 -1
- package/dist/security/SecurityCIPipeline.d.ts.map +1 -1
- package/dist/security/SecurityCIPipeline.js +38 -29
- package/dist/security/SecurityCIPipeline.js.map +1 -1
- package/dist/security/SecurityConfig.d.ts +3 -3
- package/dist/security/SecurityConfig.d.ts.map +1 -1
- package/dist/security/SecurityConfig.js +13 -9
- package/dist/security/SecurityConfig.js.map +1 -1
- package/dist/security/SecurityConfigManager.d.ts +2 -2
- package/dist/security/SecurityConfigManager.d.ts.map +1 -1
- package/dist/security/SecurityConfigManager.js +20 -15
- package/dist/security/SecurityConfigManager.js.map +1 -1
- package/dist/security/SecurityMonitoring.d.ts +2 -2
- package/dist/security/SecurityMonitoring.d.ts.map +1 -1
- package/dist/security/SecurityMonitoring.js +19 -17
- package/dist/security/SecurityMonitoring.js.map +1 -1
- package/dist/security/SecurityReviewer.d.ts.map +1 -1
- package/dist/security/SecurityReviewer.js +10 -7
- package/dist/security/SecurityReviewer.js.map +1 -1
- package/dist/security/index.d.ts +24 -23
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +52 -23
- package/dist/security/index.js.map +1 -1
- package/dist/server/ConnectionTester.d.ts +12 -4
- package/dist/server/ConnectionTester.d.ts.map +1 -1
- package/dist/server/ConnectionTester.js +96 -22
- package/dist/server/ConnectionTester.js.map +1 -1
- package/dist/server/ToolRegistry.d.ts +2 -2
- package/dist/server/ToolRegistry.d.ts.map +1 -1
- package/dist/server/ToolRegistry.js +10 -5
- package/dist/server/ToolRegistry.js.map +1 -1
- package/dist/tools/BaseToolManager.d.ts +47 -11
- package/dist/tools/BaseToolManager.d.ts.map +1 -1
- package/dist/tools/BaseToolManager.js +168 -29
- package/dist/tools/BaseToolManager.js.map +1 -1
- package/dist/tools/auth.d.ts +16 -10
- package/dist/tools/auth.d.ts.map +1 -1
- package/dist/tools/auth.js +3 -2
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/cache.d.ts +30 -30
- package/dist/tools/cache.d.ts.map +1 -1
- package/dist/tools/cache.js +1 -6
- package/dist/tools/cache.js.map +1 -1
- package/dist/tools/comments.d.ts +20 -20
- package/dist/tools/comments.d.ts.map +1 -1
- package/dist/tools/comments.js +16 -9
- package/dist/tools/comments.js.map +1 -1
- package/dist/tools/media.d.ts +18 -16
- package/dist/tools/media.d.ts.map +1 -1
- package/dist/tools/media.js +16 -15
- package/dist/tools/media.js.map +1 -1
- package/dist/tools/pages.d.ts +19 -17
- package/dist/tools/pages.d.ts.map +1 -1
- package/dist/tools/pages.js +16 -12
- package/dist/tools/pages.js.map +1 -1
- package/dist/tools/performance.d.ts +11 -1
- package/dist/tools/performance.d.ts.map +1 -1
- package/dist/tools/performance.js +67 -34
- package/dist/tools/performance.js.map +1 -1
- package/dist/tools/posts/PostHandlers.d.ts +46 -0
- package/dist/tools/posts/PostHandlers.d.ts.map +1 -0
- package/dist/tools/posts/PostHandlers.js +400 -0
- package/dist/tools/posts/PostHandlers.js.map +1 -0
- package/dist/tools/posts/PostToolDefinitions.d.ts +37 -0
- package/dist/tools/posts/PostToolDefinitions.d.ts.map +1 -0
- package/dist/tools/posts/PostToolDefinitions.js +236 -0
- package/dist/tools/posts/PostToolDefinitions.js.map +1 -0
- package/dist/tools/posts/index.d.ts +138 -0
- package/dist/tools/posts/index.d.ts.map +1 -0
- package/dist/tools/posts/index.js +163 -0
- package/dist/tools/posts/index.js.map +1 -0
- package/dist/tools/posts.d.ts +10 -246
- package/dist/tools/posts.d.ts.map +1 -1
- package/dist/tools/posts.js +11 -723
- package/dist/tools/posts.js.map +1 -1
- package/dist/tools/site.d.ts +19 -18
- package/dist/tools/site.d.ts.map +1 -1
- package/dist/tools/site.js +14 -10
- package/dist/tools/site.js.map +1 -1
- package/dist/tools/taxonomies.d.ts +23 -24
- package/dist/tools/taxonomies.d.ts.map +1 -1
- package/dist/tools/taxonomies.js +24 -18
- package/dist/tools/taxonomies.js.map +1 -1
- package/dist/tools/users.d.ts +20 -15
- package/dist/tools/users.d.ts.map +1 -1
- package/dist/tools/users.js +12 -8
- package/dist/tools/users.js.map +1 -1
- package/dist/types/client.d.ts +48 -41
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/client.js +30 -5
- package/dist/types/client.js.map +1 -1
- package/dist/types/enhanced.d.ts +237 -0
- package/dist/types/enhanced.d.ts.map +1 -0
- package/dist/types/enhanced.js +49 -0
- package/dist/types/enhanced.js.map +1 -0
- package/dist/types/index.d.ts +15 -12
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/mcp.d.ts +12 -12
- package/dist/types/mcp.d.ts.map +1 -1
- package/dist/types/requests.d.ts +322 -0
- package/dist/types/requests.d.ts.map +1 -0
- package/dist/types/requests.js +8 -0
- package/dist/types/requests.js.map +1 -0
- package/dist/types/tools.d.ts +506 -0
- package/dist/types/tools.d.ts.map +1 -0
- package/dist/types/tools.js +8 -0
- package/dist/types/tools.js.map +1 -0
- package/dist/types/wordpress.d.ts +43 -15
- package/dist/types/wordpress.d.ts.map +1 -1
- package/dist/types/wordpress.js +8 -1
- package/dist/types/wordpress.js.map +1 -1
- package/dist/utils/debug.d.ts +19 -11
- package/dist/utils/debug.d.ts.map +1 -1
- package/dist/utils/debug.js +46 -10
- package/dist/utils/debug.js.map +1 -1
- package/dist/utils/enhancedError.d.ts +8 -8
- package/dist/utils/enhancedError.d.ts.map +1 -1
- package/dist/utils/enhancedError.js.map +1 -1
- package/dist/utils/error.d.ts +2 -4
- package/dist/utils/error.d.ts.map +1 -1
- package/dist/utils/error.js +42 -5
- package/dist/utils/error.js.map +1 -1
- package/dist/utils/logger.d.ts +106 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +280 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/streaming.d.ts +9 -9
- package/dist/utils/streaming.d.ts.map +1 -1
- package/dist/utils/streaming.js +71 -52
- package/dist/utils/streaming.js.map +1 -1
- package/dist/utils/toolWrapper.d.ts +9 -7
- package/dist/utils/toolWrapper.d.ts.map +1 -1
- package/dist/utils/toolWrapper.js.map +1 -1
- package/dist/utils/validation/core.d.ts +21 -0
- package/dist/utils/validation/core.d.ts.map +1 -0
- package/dist/utils/validation/core.js +71 -0
- package/dist/utils/validation/core.js.map +1 -0
- package/dist/utils/validation/index.d.ts +25 -0
- package/dist/utils/validation/index.d.ts.map +1 -0
- package/dist/utils/validation/index.js +29 -0
- package/dist/utils/validation/index.js.map +1 -0
- package/dist/utils/validation/network.d.ts +19 -0
- package/dist/utils/validation/network.d.ts.map +1 -0
- package/dist/utils/validation/network.js +93 -0
- package/dist/utils/validation/network.js.map +1 -0
- package/dist/utils/validation/rateLimit.d.ts +21 -0
- package/dist/utils/validation/rateLimit.d.ts.map +1 -0
- package/dist/utils/validation/rateLimit.js +43 -0
- package/dist/utils/validation/rateLimit.js.map +1 -0
- package/dist/utils/validation/security.d.ts +29 -0
- package/dist/utils/validation/security.d.ts.map +1 -0
- package/dist/utils/validation/security.js +327 -0
- package/dist/utils/validation/security.js.map +1 -0
- package/dist/utils/validation/wordpress.d.ts +31 -0
- package/dist/utils/validation/wordpress.d.ts.map +1 -0
- package/dist/utils/validation/wordpress.js +146 -0
- package/dist/utils/validation/wordpress.js.map +1 -0
- package/dist/utils/validation.d.ts +13 -82
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +25 -343
- package/dist/utils/validation.js.map +1 -1
- package/docs/BADGE_UPDATES.md +132 -0
- package/docs/CI_CD_IMPROVEMENTS.md +191 -0
- package/docs/INCREMENTAL_COVERAGE.md +183 -0
- package/docs/api/README.md +3 -1
- package/docs/api/openapi.json +5 -1
- package/docs/api/summary.json +1 -1
- package/docs/api/tools/wp_create_post.md +12 -14
- package/docs/examples/claude-desktop-config.md +1 -1
- package/docs/examples/docker-production.md +100 -93
- package/docs/examples/multi-site-setup.md +5 -4
- package/docs/examples/single-site-setup.md +3 -4
- package/docs/examples/use-case-workflows.md +4 -5
- package/docs/integrations/claude-desktop.md +31 -31
- package/docs/integrations/cline.md +4 -4
- package/docs/integrations/vs-code.md +9 -8
- package/docs/user-guides/SMITHERY_SETUP.md +10 -10
- package/package.json +44 -25
- package/src/cache/CacheInvalidation.ts +12 -5
- package/src/cache/CacheManager.ts +18 -15
- package/src/cache/HttpCacheWrapper.ts +30 -59
- package/src/cache/__tests__/HttpCacheWrapper.test.ts +6 -5
- package/src/cache/index.ts +3 -14
- package/src/client/CachedWordPressClient.ts +32 -30
- package/src/client/MockWordPressClient.ts +4 -2
- package/src/client/api.ts +186 -64
- package/src/client/auth.ts +15 -40
- package/src/client/managers/AuthenticationManager.ts +337 -77
- package/src/client/managers/BaseManager.ts +18 -30
- package/src/client/managers/RequestManager.ts +39 -44
- package/src/config/Config.ts +308 -0
- package/src/config/ConfigurationSchema.ts +23 -2
- package/src/config/ServerConfiguration.ts +51 -47
- package/src/docs/DocumentationGenerator.ts +50 -39
- package/src/docs/MarkdownFormatter.ts +19 -29
- package/src/dxt-entry.cjs +26 -16
- package/src/dxt-entry.ts +17 -27
- package/src/index.ts +42 -28
- package/src/performance/MetricsCollector.ts +108 -86
- package/src/performance/PerformanceAnalytics.ts +69 -164
- package/src/performance/PerformanceMonitor.ts +32 -47
- package/src/performance/index.ts +2 -10
- package/src/security/AISecurityScanner.ts +22 -12
- package/src/security/AutomatedRemediation.ts +49 -18
- package/src/security/InputValidator.ts +9 -6
- package/src/security/SecurityCIPipeline.ts +53 -37
- package/src/security/SecurityConfig.ts +22 -22
- package/src/security/SecurityConfigManager.ts +23 -19
- package/src/security/SecurityMonitoring.ts +24 -21
- package/src/security/SecurityReviewer.ts +10 -7
- package/src/security/index.ts +64 -29
- package/src/server/ConnectionTester.ts +120 -31
- package/src/server/ToolRegistry.ts +31 -21
- package/src/tools/BaseToolManager.ts +286 -33
- package/src/tools/auth.ts +20 -8
- package/src/tools/cache.ts +5 -15
- package/src/tools/comments.ts +34 -48
- package/src/tools/media.ts +41 -53
- package/src/tools/pages.ts +32 -54
- package/src/tools/performance.ts +141 -176
- package/src/tools/posts/PostHandlers.ts +474 -0
- package/src/tools/posts/PostToolDefinitions.ts +250 -0
- package/src/tools/posts/index.ts +192 -0
- package/src/tools/posts.ts +24 -780
- package/src/tools/site.ts +34 -19
- package/src/tools/taxonomies.ts +41 -57
- package/src/tools/users.ts +28 -16
- package/src/types/client.ts +114 -138
- package/src/types/enhanced.ts +318 -0
- package/src/types/index.ts +51 -30
- package/src/types/mcp.ts +20 -42
- package/src/types/requests.ts +378 -0
- package/src/types/tools.ts +608 -0
- package/src/types/wordpress.ts +56 -34
- package/src/utils/debug.ts +77 -59
- package/src/utils/enhancedError.ts +8 -8
- package/src/utils/error.ts +53 -31
- package/src/utils/logger.ts +351 -0
- package/src/utils/streaming.ts +86 -68
- package/src/utils/toolWrapper.ts +10 -12
- package/src/utils/validation/core.ts +108 -0
- package/src/utils/validation/index.ts +36 -0
- package/src/utils/validation/network.ts +132 -0
- package/src/utils/validation/rateLimit.ts +54 -0
- package/src/utils/validation/security.ts +361 -0
- package/src/utils/validation/wordpress.ts +180 -0
- package/src/utils/validation.ts +47 -470
package/src/utils/validation.ts
CHANGED
|
@@ -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
|
-
*
|
|
293
|
-
*
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
export
|
|
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";
|