PluginProbe
Stream – Activity Log & Audit Trail / 3.9.2
Stream – Activity Log & Audit Trail v3.9.2
4.4.0 4.3.0 4.2.2 4.2.1 trunk 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.1 3.1.1 3.10.0 3.2.0 3.2.1 3.2.2 3.2.3 All 50 releases
← All changes | classes/class-connector.php +10 -172 trunk3.9.2 View file →
@@ -109,9 +109,9 @@
109 109 * Looks for a class method with the convention: "callback_{action name}"
110 110 */
111 111 public function callback() {
112 112 $action = current_filter();
113 - $callback = array( $this, 'callback_' . preg_replace( '/[^a-z0-9_]/', '_', $action ) );
113 + $callback = array( $this, 'callback_' . preg_replace( '/[^a-z0-9_\-]/', '_', $action ) );
114 114
115 115 // For the sake of testing, trigger an action with the name of the callback.
116 116 if ( defined( 'WP_STREAM_TESTS' ) && WP_STREAM_TESTS ) {
117 117 /**
@@ -145,14 +145,14 @@
145 145
146 146 /**
147 147 * Log handler
148 148 *
149 - * @param string $message sprintf-ready error message string.
150 - * @param array $args sprintf (and extra) arguments to use.
151 - * @param int|null $object_id Target object id (if any).
152 - * @param string $context Context of the event.
153 - * @param string $action Action of the event.
154 - * @param int $user_id User responsible for the event.
149 + * @param string $message sprintf-ready error message string.
150 + * @param array $args sprintf (and extra) arguments to use.
151 + * @param int $object_id Target object id.
152 + * @param string $context Context of the event.
153 + * @param string $action Action of the event.
154 + * @param int $user_id User responsible for the event.
155 155 *
156 156 * @return bool
157 157 */
158 158 public function log( $message, $args, $object_id, $context, $action, $user_id = null ) {
@@ -157,16 +157,8 @@
157 157 */
158 158 public function log( $message, $args, $object_id, $context, $action, $user_id = null ) {
159 159 $connector = $this->name;
160 160
161 - /**
162 - * Override the data logged. Returning false to this filter will stop the data from being logged.
163 - * Examples of this filter in use can be found in some of the custom connectors.
164 - *
165 - * @see Connector_ACF::log_override()
166 - *
167 - * @return array|false An array of the data to be logged or false if it should not be logged.
168 - */
169 161 $data = apply_filters(
170 162 'wp_stream_log_data',
171 163 compact( 'connector', 'message', 'args', 'object_id', 'context', 'action', 'user_id' )
172 164 );
@@ -186,152 +178,8 @@
186 178 return call_user_func_array( array( wp_stream_get_instance()->log, 'log' ), compact( 'connector', 'message', 'args', 'object_id', 'context', 'action', 'user_id' ) );
187 179 }
188 180
189 181 /**
190 - * Substrings that mark a setting name as holding a credential.
191 - *
192 - * Deliberately matched as substrings so unknown third-party settings are
193 - * covered by default: connectors log arbitrary option arrays from other
194 - * plugins, and an allowlist cannot anticipate every field a payment gateway
195 - * or integration might add. Over-redacting a harmless field only costs a
196 - * little detail in the audit log; under-redacting persists a live credential
197 - * in a table that lower-privileged Stream viewers can read.
198 - *
199 - * @const array
200 - */
201 - const SECRET_KEY_PATTERNS = array(
202 - 'pass',
203 - 'secret',
204 - 'private_key',
205 - 'apikey',
206 - 'token',
207 - 'webhook',
208 - 'license',
209 - 'salt',
210 - 'credential',
211 - 'oauth',
212 - // PayPal NVP `api_signature`; over-matches e.g. `email_signature`.
213 - 'signature',
214 - );
215 -
216 - /**
217 - * Setting names, or suffixes of them, that hold a credential but do not
218 - * contain any of the substrings above.
219 - *
220 - * Matched against the end of the name so option prefixes used by individual
221 - * plugins (rg_gforms_key, woocommerce_..._key) are covered without treating
222 - * every name that merely contains "key" as sensitive.
223 - *
224 - * @const array
225 - */
226 - const SECRET_KEY_SUFFIXES = array(
227 - '_key',
228 - );
229 -
230 - /**
231 - * Names that end in a secret-looking suffix but are not credentials.
232 - *
233 - * Public halves of key pairs are meant to be published, and redacting them
234 - * removes useful audit detail for no benefit. Matched as substrings so the
235 - * various plugin prefixes are covered.
236 - *
237 - * Only consulted after SECRET_KEY_PATTERNS, so a name containing an
238 - * explicit secret marker is never exempted by appearing "public" too.
239 - *
240 - * @const array
241 - */
242 - const PUBLIC_KEY_PATTERNS = array(
243 - 'public_key',
244 - 'publishable_key',
245 - 'site_key',
246 - );
247 -
248 - /**
249 - * Placeholder stored in place of a redacted value.
250 - *
251 - * A distinct marker rather than an empty string, so a reader of the audit
252 - * trail can tell "this credential changed, value withheld" apart from "this
253 - * field was cleared" -- an empty string would conflate the two.
254 - *
255 - * @const string
256 - */
257 - const REDACTED_PLACEHOLDER = '[redacted]';
258 -
259 - /**
260 - * Whether a setting/field name looks like it holds a credential.
261 - *
262 - * @param string $key Setting or field name.
263 - * @return bool
264 - */
265 - public function is_secret_key( $key ) {
266 - if ( ! is_string( $key ) || '' === $key ) {
267 - return false;
268 - }
269 -
270 - $needle = strtolower( $key );
271 -
272 - // An explicit secret marker always wins. The public-name exemption
273 - // below is only there to stop the broad "_key" suffix rule from
274 - // swallowing published key halves, so it must not be able to rescue a
275 - // name that also says "secret", "private_key" or "webhook" -- that
276 - // would invert the over-redact-rather-than-under-redact preference.
277 - foreach ( self::SECRET_KEY_PATTERNS as $pattern ) {
278 - if ( false !== strpos( $needle, $pattern ) ) {
279 - return true;
280 - }
281 - }
282 -
283 - foreach ( self::PUBLIC_KEY_PATTERNS as $pattern ) {
284 - if ( false !== strpos( $needle, $pattern ) ) {
285 - return false;
286 - }
287 - }
288 -
289 - foreach ( self::SECRET_KEY_SUFFIXES as $suffix ) {
290 - if ( substr( $needle, -strlen( $suffix ) ) === $suffix ) {
291 - return true;
292 - }
293 - }
294 -
295 - return false;
296 - }
297 -
298 - /**
299 - * Redact credential values before they are persisted as record metadata.
300 - *
301 - * Accepts either a scalar (redacted when $key itself looks secret) or an
302 - * array of settings (each secret-looking member redacted, recursively).
303 - * Values are replaced rather than removed so the audit trail still shows
304 - * that the field changed, without retaining the credential.
305 - *
306 - * @param mixed $value Value about to be logged.
307 - * @param string $key Setting or field name the value belongs to.
308 - * @return mixed
309 - */
310 - public function redact_secret_values( $value, $key = '' ) {
311 - if ( is_array( $value ) ) {
312 - // A secret parent may key credentials by opaque IDs (e.g. Jetpack
313 - // `user_tokens` by user ID), so pass it down over child names.
314 - $inherited_key = $this->is_secret_key( $key ) ? $key : null;
315 -
316 - foreach ( $value as $child_key => $child_value ) {
317 - $value[ $child_key ] = $this->redact_secret_values(
318 - $child_value,
319 - null !== $inherited_key ? $inherited_key : (string) $child_key
320 - );
321 - }
322 -
323 - return $value;
324 - }
325 -
326 - if ( $this->is_secret_key( $key ) && ! empty( $value ) ) {
327 - return self::REDACTED_PLACEHOLDER;
328 - }
329 -
330 - return $value;
331 - }
332 -
333 - /**
334 182 * Save log data till shutdown, so other callbacks would be able to override
335 183 *
336 184 * @param string $handle Special slug to be shared with other actions.
337 185 * @note param mixed $arg1 Extra arguments to sent to log()
@@ -380,9 +228,9 @@
380 228
381 229 $diff = array_udiff_assoc(
382 230 $old_value,
383 231 $new_value,
384 - function ( $value1, $value2 ) {
232 + function( $value1, $value2 ) {
385 233 // Compare potentially complex nested arrays.
386 234 return wp_json_encode( $value1 ) !== wp_json_encode( $value2 );
387 235 }
388 236 );
@@ -398,9 +246,9 @@
398 246
399 247 // Remove numeric indexes.
400 248 $result = array_filter(
401 249 $result,
402 - function ( $value ) {
250 + function( $value ) {
403 251 // @codingStandardsIgnoreStart
404 252 // check if is not valid number (is_int, is_numeric and ctype_digit are not enough)
405 253 return (string) (int) $value !== (string) $value;
406 254 // @codingStandardsIgnoreEnd
@@ -423,9 +271,9 @@
423 271 } elseif ( $deep ) { // Changed, find what changed, only if we're allowed to explore a new level.
424 272 if ( is_array( $old_value[ $key ] ) && is_array( $new_value[ $key ] ) ) {
425 273 $inner = array();
426 274 $parent = $key;
427 - --$deep;
275 + $deep--;
428 276 $changed = $this->get_changed_keys( $old_value[ $key ], $new_value[ $key ], $deep );
429 277 foreach ( $changed as $child => $change ) {
430 278 $inner[ $parent . '::' . $child ] = $change;
431 279 }
@@ -444,16 +292,6 @@
444 292 * @return bool
445 293 */
446 294 public function is_dependency_satisfied() {
447 295 return true;
448 - }
449 -
450 - /**
451 - * Escape % characters in a string to avoid Uncaught ValueErrors in $this->log().
452 - *
453 - * @param string $value The string value to be escaped.
454 - * @return string The escaped string.
455 - */
456 - public function escape_percentages( $value ) {
457 - return str_replace( '%', '%%', $value );
458 296 }
459 297 }