PluginProbe
Authorsy – Author Box, Multiple Authors, Guest Authors & Post Rating / trunk
Authorsy – Author Box, Multiple Authors, Guest Authors & Post Rating vtrunk
trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8
authorsy / core / settings / api-settings.php

api-settings.php in Authorsy – Author Box, Multiple Authors, Guest Authors & Post Rating trunk, at core/settings/api-settings.php

219 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Api Settings
4 *
5 * @package Authorsy
6 */
7 namespace Authorsy\Core\Settings;
8 defined( 'ABSPATH' ) || exit;
9
10 use Authorsy\Base\Api;
11 use Authorsy\Utils\Singleton;
12
13 class Api_Settings extends Api {
14 use Singleton;
15
16 /**
17 * Store api namespace
18 *
19 * @var string
20 */
21 protected $namespace = 'authorsy/v1';
22
23 /**
24 * Store rest base
25 *
26 * @var string
27 */
28 protected $rest_base = 'settings';
29
30 /**
31 * Register rest route
32 *
33 * @return void
34 */
35 public function register_routes() {
36 register_rest_route(
37 $this->namespace, $this->rest_base, [
38 [
39 'methods' => \WP_REST_Server::READABLE,
40 'callback' => [$this, 'get_settings'],
41 'permission_callback' => function () {
42 return current_user_can('manage_options');
43 },
44 ],
45 [
46 'methods' => \WP_REST_Server::EDITABLE,
47 'callback' => [$this, 'update_settings'],
48 'permission_callback' => function () {
49 return current_user_can('manage_options');
50 },
51 ],
52 ]
53 );
54
55 }
56
57 /**
58 * Get settings
59 *
60 * @return JSON
61 */
62 public function get_settings() {
63 $settings = apply_filters( 'authorsy_settings', authorsy_get_settings() );
64
65 $data = [
66 'status_code' => 200,
67 'success' => 1,
68 'message' => esc_html__( 'Get all settings', 'authorsy' ),
69 'data' => $settings,
70 ];
71
72 return rest_ensure_response( $settings );
73 }
74
75 /**
76 * Update settings
77 *
78 * @param WP_Rest_Request $request
79 *
80 * @return JSON
81 */
82 public function update_settings( $request ) {
83 // Rate limiting to prevent abuse
84 $user_id = get_current_user_id();
85 $rate_limit_key = 'authorsy_settings_rate_limit_' . $user_id;
86 $rate_limit = get_transient( $rate_limit_key );
87
88 if ( $rate_limit && $rate_limit >= 10 ) { // Max 10 requests per hour
89 return new \WP_Error(
90 'rate_limit_exceeded',
91 __( 'Too many requests. Please try again later.', 'authorsy' ),
92 [ 'status' => 429 ]
93 );
94 }
95
96 // Increment rate limit counter
97 if ( $rate_limit ) {
98 set_transient( $rate_limit_key, $rate_limit + 1, HOUR_IN_SECONDS );
99 } else {
100 set_transient( $rate_limit_key, 1, HOUR_IN_SECONDS );
101 }
102
103 $options = json_decode( $request->get_body(), true );
104
105 $nonce_check = $this->verify_nonce( $request );
106 if ( is_wp_error( $nonce_check ) ) {
107 return $nonce_check;
108 }
109 /**
110 * Added temporary for leagacy sass. It will remove in future.
111 */
112 $data = [
113 'status_code' => 200,
114 'success' => 1,
115 'message' => esc_html__( 'Settings successfully updated', 'authorsy' ),
116 'data' => authorsy_get_settings(),
117 ];
118
119
120
121 if ( $options ) {
122 foreach ( $options as $key => $value ) {
123 // Sanitize CSS fields to prevent XSS
124 if ( $key === 'ea_custom_css' && ! empty( $value ) ) {
125 $value = $this->sanitize_css( $value );
126
127 // Additional validation - ensure it's valid CSS
128 if ( ! $this->is_valid_css( $value ) ) {
129 return new \WP_Error(
130 'invalid_css',
131 __( 'Invalid CSS provided. Please check your CSS syntax.', 'authorsy' ),
132 [ 'status' => 400 ]
133 );
134 }
135 }
136 authorsy_update_option( $key, $value );
137 }
138 }
139
140 $data['data'] = authorsy_get_settings();
141
142 return rest_ensure_response( $data );
143 }
144
145 /**
146 * Sanitize CSS input to prevent XSS attacks
147 *
148 * @param string $css The CSS string to sanitize
149 * @return string Sanitized CSS
150 */
151 private function sanitize_css( $css ) {
152 // Remove any script tags and their content
153 $css = preg_replace( '/<script[^>]*>.*?<\/script>/is', '', $css );
154
155 // Remove any HTML tags
156 $css = strip_tags( $css );
157
158 // Remove JavaScript protocol handlers
159 $css = preg_replace( '/javascript\s*:/i', '', $css );
160
161 // Remove any expression() functions that could execute code
162 $css = preg_replace( '/expression\s*\(/i', '', $css );
163
164 // Remove any url() functions with javascript: protocol
165 $css = preg_replace( '/url\s*\(\s*["\']?\s*javascript\s*:/i', '', $css );
166
167 // Remove any @import statements that could be dangerous
168 $css = preg_replace( '/@import\s+url\s*\(\s*["\']?\s*javascript\s*:/i', '', $css );
169
170 // Remove any CSS comments that might contain malicious content
171 $css = preg_replace( '/\/\*.*?\*\//s', '', $css );
172
173 // Remove any newlines and tabs to prevent potential injection
174 $css = str_replace( [ "\n", "\r", "\t" ], '', $css );
175
176 return trim( $css );
177 }
178
179 /**
180 * Validate if the provided string contains valid CSS
181 *
182 * @param string $css The CSS string to validate
183 * @return bool True if valid CSS, false otherwise
184 */
185 private function is_valid_css( $css ) {
186 // Basic CSS validation - check for common CSS patterns
187 // This is a simplified validation - in production you might want more sophisticated validation
188
189 // Check if it contains only allowed CSS characters and basic structure
190 if ( empty( $css ) ) {
191 return true; // Empty CSS is valid
192 }
193
194 // Remove all whitespace for easier validation
195 $css = preg_replace( '/\s+/', '', $css );
196
197 // Basic CSS structure validation
198 // Should contain at least one selector and property
199 if ( ! preg_match( '/^[a-zA-Z0-9\-\_\#\.\,\s\{\}\:\;\=\"\'\(\)\[\]\>\<\+\~\|\&\*\^\$\%\!\?\/\\]+$/', $css ) ) {
200 return false;
201 }
202
203 // Check for balanced braces
204 $brace_count = 0;
205 for ( $i = 0; $i < strlen( $css ); $i++ ) {
206 if ( $css[$i] === '{' ) {
207 $brace_count++;
208 } elseif ( $css[$i] === '}' ) {
209 $brace_count--;
210 if ( $brace_count < 0 ) {
211 return false; // Unbalanced braces
212 }
213 }
214 }
215
216 return $brace_count === 0; // All braces should be balanced
217 }
218 }
219