PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.0
Elementor Website Builder – more than just a page builder v4.3.0
4.3.1 4.3.0 4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 All 454 releases
elementor / vendor / wordpress / mcp-adapter / includes / Infrastructure / Observability / McpObservabilityHelperTrait.php

McpObservabilityHelperTrait.php in Elementor Website Builder – more than just a page builder 4.3.0, at vendor/wordpress/mcp-adapter/includes/Infrastructure/Observability/McpObservabilityHelperTrait.php

220 lines 5.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Helper trait for MCP observability handlers providing shared utility methods.
4 *
5 * @package McpAdapter
6 */
7
8 declare( strict_types=1 );
9
10 namespace WP\MCP\Infrastructure\Observability;
11
12 /**
13 * Trait McpObservabilityHelperTrait
14 *
15 * Provides shared utility methods for observability handlers including
16 * tag management, metric formatting, and sanitization functionality.
17 */
18 trait McpObservabilityHelperTrait {
19 /**
20 * Error categories keyed by throwable class name.
21 *
22 * @used-by ::categorize_error() method.
23 */
24 private static array $error_categories = array(
25 \ArgumentCountError::class => 'arguments',
26 \Error::class => 'system',
27 \InvalidArgumentException::class => 'validation',
28 \LogicException::class => 'logic',
29 \RuntimeException::class => 'execution',
30 \TypeError::class => 'type',
31 );
32
33 /**
34 * Patterns that indicate sensitive data in keys or values.
35 *
36 * These patterns are designed to match both camelCase and snake_case variants:
37 * - apiKey, api_key, API_KEY
38 * - authToken, auth_token, AUTH_TOKEN
39 * - secretKey, secret_key, SECRET_KEY
40 *
41 * @var string[]
42 */
43 private static array $sensitive_patterns = array(
44 'password',
45 'passwd',
46 'pwd',
47 'secret',
48 'token',
49 'bearer',
50 'credential',
51 'private',
52 'apikey',
53 'api_key',
54 'authtoken',
55 'auth_token',
56 'accesstoken',
57 'access_token',
58 'refreshtoken',
59 'refresh_token',
60 'clientsecret',
61 'client_secret',
62 'privatekey',
63 'private_key',
64 'secretkey',
65 'secret_key',
66 'authorization',
67 'authenticate',
68 'encryption',
69 );
70
71 /**
72 * Format metric name to follow consistent naming conventions.
73 *
74 * @param string $metric The raw metric name.
75 *
76 * @return string
77 */
78 public static function format_metric_name( string $metric ): string {
79 // Ensure metric starts with 'mcp.' prefix.
80 if ( ! str_starts_with( $metric, 'mcp.' ) ) {
81 $metric = 'mcp.' . $metric;
82 }
83
84 // Convert to lowercase and replace spaces/special chars with dots.
85 $metric = strtolower( $metric );
86 $metric = (string) preg_replace( '/[^a-z0-9_\.]/', '.', $metric );
87 $metric = (string) preg_replace( '/\.+/', '.', $metric ); // Remove duplicate dots.
88 // Remove leading/trailing dots.
89
90 return trim( $metric, '.' );
91 }
92
93 /**
94 * Merge default tags with provided tags.
95 *
96 * @param array $tags The user-provided tags.
97 *
98 * @return array
99 */
100 public static function merge_tags( array $tags ): array {
101 $default_tags = self::get_default_tags();
102 $merged_tags = array_merge( $default_tags, $tags );
103
104 return self::sanitize_tags( $merged_tags );
105 }
106
107 /**
108 * Get default tags that should be included with all metrics.
109 *
110 * @return array
111 */
112 public static function get_default_tags(): array {
113 return array(
114 'site_id' => function_exists( 'get_current_blog_id' ) ? get_current_blog_id() : 0,
115 'user_id' => function_exists( 'get_current_user_id' ) ? get_current_user_id() : 0,
116 'timestamp' => time(),
117 );
118 }
119
120 /**
121 * Sanitize tags to ensure they are safe for logging and don't contain sensitive data.
122 *
123 * @param array $tags The tags to sanitize.
124 *
125 * @return array
126 */
127 public static function sanitize_tags( array $tags ): array {
128 $sanitized = array();
129
130 foreach ( $tags as $key => $value ) {
131 // Convert key to string and limit length to prevent log bloat.
132 $key = substr( (string) $key, 0, 64 );
133
134 // Check if the key itself indicates sensitive data.
135 if ( self::is_sensitive_key( $key ) ) {
136 $sanitized[ $key ] = '[REDACTED]';
137 continue;
138 }
139
140 // Convert value to string, handling null specially.
141 if ( null === $value ) {
142 $value = '';
143 } elseif ( is_scalar( $value ) ) {
144 $value = (string) $value;
145 } else {
146 $value = wp_json_encode( $value );
147 // wp_json_encode can return false on failure, ensure we have a string.
148 if ( false === $value ) {
149 $value = '';
150 }
151 }
152
153 // Limit value length to prevent log bloat.
154 if ( strlen( $value ) > 1024 ) {
155 $value = substr( $value, 0, 1024 ) . '...[truncated]';
156 }
157
158 // Remove potentially sensitive information patterns from values.
159 $value = self::redact_sensitive_values( $value );
160
161 $sanitized[ $key ] = $value;
162 }
163
164 return $sanitized;
165 }
166
167 /**
168 * Check if a key name indicates sensitive data.
169 *
170 * Matches patterns in camelCase, snake_case, and SCREAMING_CASE.
171 *
172 * @param string $key The key name to check.
173 *
174 * @return bool True if the key appears to contain sensitive data.
175 */
176 public static function is_sensitive_key( string $key ): bool {
177 // Normalize: lowercase and remove underscores/hyphens for pattern matching.
178 $normalized = strtolower( str_replace( array( '_', '-' ), '', $key ) );
179
180 foreach ( self::$sensitive_patterns as $pattern ) {
181 // Remove underscores from pattern for normalized comparison.
182 $normalized_pattern = str_replace( '_', '', $pattern );
183
184 if ( str_contains( $normalized, $normalized_pattern ) ) {
185 return true;
186 }
187 }
188
189 return false;
190 }
191
192 /**
193 * Redact sensitive values from a string.
194 *
195 * Uses a more comprehensive pattern that catches compound words.
196 *
197 * @param string $value The value to redact.
198 *
199 * @return string The value with sensitive patterns redacted.
200 */
201 public static function redact_sensitive_values( string $value ): string {
202 // Build a regex pattern that matches sensitive words as substrings.
203 // This catches camelCase (apiKey), snake_case (api_key), and standalone words.
204 $pattern = '/(?:' . implode( '|', array_map( 'preg_quote', self::$sensitive_patterns ) ) . ')/i';
205
206 return (string) preg_replace( $pattern, '[REDACTED]', $value );
207 }
208
209 /**
210 * Categorize an exception into a general error category.
211 *
212 * @param \Throwable $exception The exception to categorize.
213 *
214 * @return string
215 */
216 public static function categorize_error( \Throwable $exception ): string {
217 return self::$error_categories[ get_class( $exception ) ] ?? 'unknown';
218 }
219 }
220