| @@ -186,8 +186,17 @@ | ||
| 186 | 186 | |
| 187 | 187 | // Force permission checks — prevents check_private=false injection |
| 188 | 188 | $post_args['check_private'] = true; |
| 189 | 189 | |
| 190 | + // SECURITY: Strip forumid (singular) to force use of forumids (array) | |
| 191 | + // forumid bypasses access_filter() which only runs when forumid is null | |
| 192 | + unset( $post_args['forumid'] ); | |
| 193 | + | |
| 194 | + // Cap row_count to prevent resource exhaustion | |
| 195 | + if( isset( $post_args['row_count'] ) ) { | |
| 196 | + $post_args['row_count'] = min( 50, max( 1, intval( $post_args['row_count'] ) ) ); | |
| 197 | + } | |
| 198 | + | |
| 190 | 199 | // Validate 'orderby' parameter against whitelist |
| 191 | 200 | if( isset( $post_args['orderby'] ) ) { |
| 192 | 201 | if( ! key_exists( $post_args['orderby'], $this->orderby_fields ) ) { |
| 193 | 202 | $post_args['orderby'] = $this->default_instance['orderby']; |
| @@ -197,8 +206,20 @@ | ||
| 197 | 206 | // Validate 'order' parameter against whitelist |
| 198 | 207 | if( isset( $post_args['order'] ) ) { |
| 199 | 208 | if( ! key_exists( $post_args['order'], $this->order_fields ) ) { |
| 200 | 209 | $post_args['order'] = $this->default_instance['order']; |
| 210 | + } | |
| 211 | + } | |
| 212 | + | |
| 213 | + // SECURITY: coerce id-list fields to integer arrays so a serialized | |
| 214 | + // payload from an unauthenticated POST can never reach | |
| 215 | + // wpforo_parse_args() / unserialize() downstream. Defense in depth | |
| 216 | + // alongside the allowed_classes=>false hardening in wpforo_parse_args. | |
| 217 | + foreach( [ 'forumids', 'include', 'exclude', 'postids' ] as $idfield ) { | |
| 218 | + if( isset( $post_args[ $idfield ] ) ) { | |
| 219 | + $post_args[ $idfield ] = is_array( $post_args[ $idfield ] ) | |
| 220 | + ? array_map( 'intval', $post_args[ $idfield ] ) | |
| 221 | + : []; | |
| 201 | 222 | } |
| 202 | 223 | } |
| 203 | 224 | } |
| 204 | 225 | |