PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 4.0.8
Visualizer – Tables & Charts Manager with Built-in AI Generator v4.0.8
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
← All changes | classes/Visualizer/Module.php +102 -2 4.0.44.0.8 View file →
@@ -649,9 +649,9 @@
649 649 }
650 650 $class_name = $id . $name;
651 651 $properties = implode( ' !important; ', array_filter( $attributes ) );
652 652 if ( ! empty( $properties ) ) {
653 - $css .= '.' . $class_name . ' {' . $properties . ' !important;}';
653 + $css .= wp_strip_all_tags( '.' . $class_name . ' {' . $properties . ' !important;}' );
654 654 $classes[ $name ] = $class_name;
655 655 }
656 656 }
657 657 $settings['cssClassNames'] = $classes;
@@ -722,8 +722,29 @@
722 722 return $q->found_posts;
723 723 }
724 724
725 725 /**
726 + * Checks whether the current user may edit a specific chart.
727 + *
728 + * @param int $chart_id Chart ID.
729 + * @return bool
730 + */
731 + public static function can_edit_chart( $chart_id ) {
732 + $chart_id = absint( $chart_id );
733 + if ( ! $chart_id ) {
734 + return false;
735 + }
736 +
737 + $chart = get_post( $chart_id );
738 + return $chart
739 + && Visualizer_Plugin::CPT_VISUALIZER === $chart->post_type
740 + && (
741 + current_user_can( 'edit_post', $chart_id )
742 + || ( (int) $chart->post_author === get_current_user_id() && current_user_can( 'edit_posts' ) )
743 + );
744 + }
745 +
746 + /**
726 747 * Checks if the PRO version is active.
727 748 *
728 749 * @since 3.3.0
729 750 */
@@ -778,8 +799,87 @@
778 799 }
779 800 }
780 801
781 802 /**
803 + * Safely unserialize chart/source content, blocking PHP object injection.
804 + *
805 + * Single guarded chokepoint shared by chart/source content sinks so the
806 + * allowed_classes guard cannot be dropped from one call site independently.
807 + *
808 + * @param mixed $content The serialized content (only strings are decoded).
809 + * @return mixed The decoded value (array for valid chart data), or false.
810 + */
811 + public static function decode_content( $content ) {
812 + if ( ! is_string( $content ) ) {
813 + return false;
814 + }
815 + $value = unserialize( trim( $content ), array( 'allowed_classes' => false ) );
816 + if ( self::contains_references( $value ) ) {
817 + return false;
818 + }
819 + return self::strip_incomplete_objects( $value );
820 + }
821 +
822 + /**
823 + * Check decoded arrays for references before recursively processing them.
824 + *
825 + * Cyclic serialized arrays necessarily contain a reference. Rejecting all
826 + * references also prevents shared references from becoming cycles later,
827 + * so strip_incomplete_objects() cannot recurse without terminating.
828 + *
829 + * @param mixed $value The decoded value.
830 + * @return bool Whether the value contains an array reference.
831 + */
832 + private static function contains_references( $value ) {
833 + if ( ! is_array( $value ) ) {
834 + return false;
835 + }
836 + foreach ( array_keys( $value ) as $key ) {
837 + if ( null !== ReflectionReference::fromArrayElement( $value, $key ) ) {
838 + return true;
839 + }
840 + if ( is_array( $value[ $key ] ) && self::contains_references( $value[ $key ] ) ) {
841 + return true;
842 + }
843 + }
844 + return false;
845 + }
846 +
847 + /**
848 + * Remove the __PHP_Incomplete_Class stubs the allowed_classes guard leaves
849 + * behind; they crash map_deep() when the decoded value is written back to
850 + * post meta. Legitimate chart content is nested arrays/scalars only.
851 + *
852 + * @param mixed $value The decoded value.
853 + * @return mixed The value without object stubs; false for a top-level stub.
854 + */
855 + private static function strip_incomplete_objects( $value ) {
856 + if ( $value instanceof __PHP_Incomplete_Class ) {
857 + return false;
858 + }
859 + if ( is_array( $value ) ) {
860 + foreach ( $value as $key => $item ) {
861 + if ( $item instanceof __PHP_Incomplete_Class ) {
862 + unset( $value[ $key ] );
863 + } elseif ( is_array( $item ) ) {
864 + $value[ $key ] = self::strip_incomplete_objects( $item );
865 + }
866 + }
867 + }
868 + return $value;
869 + }
870 +
871 + /**
872 + * Object-injection-safe drop-in for maybe_unserialize().
873 + *
874 + * @param mixed $value Raw meta/content value.
875 + * @return mixed The decoded value for serialized input, the value unchanged otherwise.
876 + */
877 + public static function maybe_decode_content( $value ) {
878 + return is_serialized( $value ) ? self::decode_content( $value ) : $value;
879 + }
880 +
881 + /**
782 882 * Gets the chart content after common manipulations.
783 883 */
784 884 public static function get_chart_data( $chart, $type, $run_filter = true ) {
785 885 // change HTML entities
@@ -792,9 +892,9 @@
792 892 }
793 893 },
794 894 $post_content
795 895 );
796 - $data = unserialize( $post_content );
896 + $data = self::decode_content( $post_content );
797 897 $altered = array();
798 898 if ( ! empty( $data ) ) {
799 899 foreach ( $data as $index => $array ) {
800 900 if ( ! is_array( $index ) && is_array( $array ) ) {