Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/business-profile/bpfwp-templates/contact-card.php
+++ b/business-profile/bpfwp-templates/contact-card.php
@@ -112,7 +112,7 @@
<?php
foreach ( $data as $data => $callback ) {
- if ( is_callable( $callback ) && bpfwp_is_callback_allowed( $callback ) ) {
+ if ( is_callable( $callback ) ) {
$return_array = call_user_func( $callback, bpfwp_get_display( 'location' ) );
if ( is_array( $return_array ) ) { $json_ld_data = array_merge_recursive( $json_ld_data, $return_array ); }
}
--- a/business-profile/business-profile.php
+++ b/business-profile/business-profile.php
@@ -3,7 +3,7 @@
* Plugin Name: Five Star Business Profile and Schema
* Plugin URI: https://www.fivestarplugins.com/plugins/business-profile/
* Description: Add schema structured data to any page or post type. Create an SEO friendly contact card with your business info and associated schema. Supports Google Map, opening hours and more.
- * Version: 2.3.19
+ * Version: 2.3.20
* Author: Five Star Plugins
* Author URI: https://www.fivestarplugins.com
* License: GPLv3
@@ -107,7 +107,7 @@
define( 'BPFWP_PLUGIN_DIR', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
define( 'BPFWP_PLUGIN_URL', untrailingslashit( plugin_dir_url( __FILE__ ) ) );
define( 'BPFWP_PLUGIN_FNAME', plugin_basename( __FILE__ ) );
- define( 'BPFWP_VERSION', '2.3.19' );
+ define( 'BPFWP_VERSION', '2.3.20' );
}
/**
--- a/business-profile/includes/schemas/class-schema-field.php
+++ b/business-profile/includes/schemas/class-schema-field.php
@@ -151,8 +151,12 @@
$command = substr($command, strpos($command, ' ') + 1);
}
- if ( function_exists($command) ) { $value = $command( ...$args ); }
- else { $value = false; }
+ if ( function_exists( $command ) && in_array( $command, $this->get_allowed_callback_functions(), true ) ) {
+ $value = $command( ...$args );
+ }
+ else {
+ $value = false;
+ }
}
elseif ( $operation == 'option' ) {
@@ -171,5 +175,44 @@
return $value;
}
+
+ /**
+ * Get the functions that schema field callbacks are allowed to invoke.
+ *
+ * @return array $allowed_functions The allowed callback function names.
+ */
+ protected function get_allowed_callback_functions() {
+
+ global $bpfwp_controller;
+
+ $allowed_functions = array(
+ 'get_permalink',
+ 'get_the_author',
+ 'get_the_author_meta',
+ 'get_the_permalink',
+ );
+
+ if ( isset( $bpfwp_controller->cpts ) ) {
+ foreach ( $bpfwp_controller->cpts->get_helper_function_options() as $helper_function ) {
+ if ( isset( $helper_function['value'] ) && is_string( $helper_function['value'] ) ) {
+ $allowed_functions[] = $helper_function['value'];
+ }
+ }
+ }
+
+ $allowed_functions = apply_filters(
+ 'bpfwp_schema_field_allowed_functions',
+ $allowed_functions
+ );
+
+ return array_values(
+ array_unique(
+ array_filter(
+ (array) $allowed_functions,
+ 'is_string'
+ )
+ )
+ );
+ }
}
endif;
--- a/business-profile/includes/template-functions.php
+++ b/business-profile/includes/template-functions.php
@@ -1071,25 +1071,42 @@
function bpfwp_get_contact_card_fields() {
global $bpfwp_controller;
-
- return array_replace(
- (array) json_decode( $bpfwp_controller->settings->get_setting( 'contact-card-elements-order' ) ),
- array(
- 'name' => 'bpwfwp_print_name',
- 'address' => 'bpwfwp_print_address',
- 'phone' => 'bpwfwp_print_phone',
- 'cell_phone' => 'bpwfwp_print_cell_phone',
- 'whatsapp' => 'bpwfwp_print_whatsapp_phone',
- 'fax_phone' => 'bpwfwp_print_fax',
- 'ordering-link' => 'bpfwp_print_ordering_link',
- 'custom_fields' => 'bpfwp_print_custom_fields',
- 'contact' => 'bpwfwp_print_contact',
- 'exceptions' => 'bpwfwp_print_exceptions',
- 'opening_hours' => 'bpwfwp_print_opening_hours', // opening-hours
- 'map' => 'bpwfwp_print_map',
- 'parent_organization' => 'bpfwp_print_parent_organization'
- )
+
+ $callbacks = array(
+ 'name' => 'bpwfwp_print_name',
+ 'address' => 'bpwfwp_print_address',
+ 'phone' => 'bpwfwp_print_phone',
+ 'cell_phone' => 'bpwfwp_print_cell_phone',
+ 'whatsapp' => 'bpwfwp_print_whatsapp_phone',
+ 'fax_phone' => 'bpwfwp_print_fax',
+ 'ordering-link' => 'bpfwp_print_ordering_link',
+ 'custom_fields' => 'bpfwp_print_custom_fields',
+ 'contact' => 'bpwfwp_print_contact',
+ 'exceptions' => 'bpwfwp_print_exceptions',
+ 'opening_hours' => 'bpwfwp_print_opening_hours', // opening-hours
+ 'map' => 'bpwfwp_print_map',
+ 'parent_organization' => 'bpfwp_print_parent_organization',
+ );
+
+ $element_order = (array) json_decode(
+ $bpfwp_controller->settings->get_setting( 'contact-card-elements-order' )
);
+
+ $ordered_callbacks = array();
+
+ /**
+ * The stored setting controls component order only. Callback values must
+ * originate from the callback registry defined in PHP and must never be
+ * derived from persisted setting values.
+ */
+ foreach ( array_keys( $element_order ) as $component ) {
+ if ( array_key_exists( $component, $callbacks ) ) {
+ $ordered_callbacks[ $component ] = $callbacks[ $component ];
+ unset( $callbacks[ $component ] );
+ }
+ }
+
+ return $ordered_callbacks + $callbacks;
}
function bpfwp_get_time_label( $time ) {
@@ -1216,104 +1233,4 @@
return is_array( json_decode( html_entity_decode( $values ) ) ) ? json_decode( html_entity_decode( $values ) ) : array();
}
-}
-
-if ( ! function_exists ( 'bpfwp_get_blacklisted_callbacks' ) ) {
-function bpfwp_get_blacklisted_callbacks() {
-
- $dangerous_functions = array(
- // Command execution / processes
- 'system',
- 'exec',
- 'shell_exec',
- 'passthru',
- 'proc_open',
- 'popen',
- 'pcntl_exec',
-
- // Dynamic code / evaluation
- 'eval', // not used as a callback, but block name anyway
- 'assert',
- 'create_function',
-
- // File read/write
- 'file_get_contents',
- 'file_put_contents',
- 'fopen',
- 'fwrite',
- 'fputs',
- 'fprintf',
- 'ftruncate',
- 'unlink',
- 'copy',
- 'rename',
- 'rmdir',
- 'mkdir',
- 'scandir',
- 'glob',
- 'readfile',
- 'file', // reads entire file into array
- 'move_uploaded_file',
- 'chmod',
- 'chown',
- 'chgrp',
- 'symlink',
- 'link',
- 'tempnam',
-
- // Network / sockets / external calls
- 'fsockopen',
- 'pfsockopen',
- 'curl_exec',
- 'curl_multi_exec',
- 'stream_socket_client',
- 'stream_socket_server',
-
- // Environment manipulation / mail (optional, but often sensitive)
- 'putenv',
- 'apache_setenv',
- 'mail',
- );
-
- return $dangerous_functions;
-}
-}
-
-
-if ( ! function_exists ( 'bpfwp_is_callback_allowed' ) ) {
-function bpfwp_is_callback_allowed( $callback ) {
- $dangerous_functions = bpfwp_get_blacklisted_callbacks();
-
- // Disallow closures entirely (you can't inspect them safely)
- if ( $callback instanceof Closure ) {
- return false;
- }
-
- // Simple function name
- if ( is_string( $callback ) ) {
- $name = strtolower( ltrim( $callback, '\' ) );
- return ! in_array( $name, $dangerous_functions, true );
- }
-
- // Array callback: [object|string, 'method']
- if ( is_array( $callback ) && count( $callback ) === 2 ) {
- $method = strtolower( $callback[1] );
-
- // Optionally reuse same list for methods
- if ( in_array( $method, $dangerous_functions, true ) ) {
- return false;
- }
-
- // Optional: block certain classes / namespaces
- if ( is_string( $callback[0] ) ) {
- $class = ltrim( $callback[0], '\' );
- // Example: disallow callbacks from some class
- // if ( $class === 'DangerousClass' ) return false;
- }
-
- return true;
- }
-
- return false;
-}
}
No newline at end of file