PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.20
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.20
1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 All 164 releases
← All changes | includes/Services/Pdf_Renderer.php +1 -176 1.9.16 → 1.10.20 View file →
@@ -163,9 +163,9 @@
163 163 private function embed_local_images( string $html ): string {
164 164 return (string) preg_replace_callback(
165 165 '/(<img\b[^>]*\bsrc\s*=\s*["\'])([^"\']+)(["\'][^>]*>)/i',
166 166 function ( array $matches ): string {
167 - $data_uri = $this->image_src_to_data_uri( html_entity_decode( $matches[2], ENT_QUOTES, 'UTF-8' ) );
167 + $data_uri = ( new Local_Image_Resolver() )->data_uri( html_entity_decode( $matches[2], ENT_QUOTES, 'UTF-8' ) );
168 168 if ( null === $data_uri ) {
169 169 return $matches[0];
170 170 }
171 171
@@ -172,183 +172,8 @@
172 172 return $matches[1] . esc_attr( $data_uri ) . $matches[3];
173 173 },
174 174 $html
175 175 );
176 - }
177 -
178 - /**
179 - * Convert a local image source to a data URI.
180 - *
181 - * @param string $src Image source.
182 - *
183 - * @return string|null Data URI, or null when the source is not embeddable.
184 - */
185 - private function image_src_to_data_uri( string $src ): ?string {
186 - if ( '' === $src || 0 === strpos( $src, 'data:' ) ) {
187 - return null;
188 - }
189 -
190 - $path = $this->local_image_path_from_src( $src );
191 - if ( null === $path || ! is_readable( $path ) || ! is_file( $path ) ) {
192 - return null;
193 - }
194 -
195 - $bytes = file_get_contents( $path );
196 - if ( false === $bytes || '' === $bytes ) {
197 - return null;
198 - }
199 -
200 - $mime = $this->image_mime_type( $path );
201 - if ( null === $mime ) {
202 - return null;
203 - }
204 -
205 - return 'data:' . $mime . ';base64,' . base64_encode( $bytes );
206 - }
207 -
208 - /**
209 - * Resolve an image src to a safe local filesystem path.
210 - *
211 - * @param string $src Image source.
212 - *
213 - * @return string|null Local path, or null when the src is external/unknown.
214 - */
215 - private function local_image_path_from_src( string $src ): ?string {
216 - $src = trim( $src );
217 - $src = explode( '#', $src, 2 )[0];
218 - $src = explode( '?', $src, 2 )[0];
219 -
220 - if ( 0 === strpos( $src, '/' ) && 0 !== strpos( $src, '//' ) && \defined( 'ABSPATH' ) ) {
221 - $path = wp_normalize_path( ABSPATH . ltrim( $src, '/' ) );
222 - return $this->is_allowed_local_image_path( $path ) ? $path : null;
223 - }
224 -
225 - $mappings = $this->local_url_path_mappings();
226 - foreach ( $mappings as $url_base => $path_base ) {
227 - if ( 0 !== strpos( $src, $url_base ) ) {
228 - continue;
229 - }
230 -
231 - $relative = ltrim( substr( $src, \strlen( $url_base ) ), '/\\' );
232 - $path = wp_normalize_path( trailingslashit( $path_base ) . $relative );
233 -
234 - return $this->is_allowed_local_image_path( $path ) ? $path : null;
235 - }
236 -
237 - return null;
238 - }
239 -
240 - /**
241 - * Build URL-to-path mappings for local WordPress assets.
242 - *
243 - * @return array<string,string>
244 - */
245 - private function local_url_path_mappings(): array {
246 - $uploads = wp_upload_dir();
247 - $plugin = dirname( __DIR__, 2 );
248 -
249 - $mappings = array(
250 - $uploads['baseurl'] => $uploads['basedir'],
251 - content_url() => \defined( 'WP_CONTENT_DIR' ) ? WP_CONTENT_DIR : '',
252 - plugins_url( '', $plugin . '/woocommerce-pos.php' ) => $plugin,
253 - );
254 -
255 - $normalized = array();
256 - foreach ( $mappings as $url => $path ) {
257 - if ( '' === $url || '' === $path ) {
258 - continue;
259 - }
260 -
261 - $normalized[ trailingslashit( $url ) ] = wp_normalize_path( $path );
262 - }
263 -
264 - uksort(
265 - $normalized,
266 - static function ( string $a, string $b ): int {
267 - return \strlen( $b ) <=> \strlen( $a );
268 - }
269 - );
270 -
271 - return $normalized;
272 - }
273 -
274 - /**
275 - * Check that a resolved path stays within known local asset roots.
276 - *
277 - * @param string $path Resolved path.
278 - *
279 - * @return bool
280 - */
281 - private function is_allowed_local_image_path( string $path ): bool {
282 - $real_path = realpath( $path );
283 - if ( false === $real_path ) {
284 - return false;
285 - }
286 -
287 - $real_path = wp_normalize_path( $real_path );
288 - foreach ( $this->allowed_local_image_roots() as $root ) {
289 - if ( 0 === strpos( $real_path, trailingslashit( $root ) ) || $real_path === $root ) {
290 - return true;
291 - }
292 - }
293 -
294 - return false;
295 - }
296 -
297 - /**
298 - * Allowed local image roots.
299 - *
300 - * @return string[]
301 - */
302 - private function allowed_local_image_roots(): array {
303 - $uploads = wp_upload_dir();
304 - $roots = array(
305 - $uploads['basedir'],
306 - \defined( 'WP_CONTENT_DIR' ) ? WP_CONTENT_DIR : '',
307 - dirname( __DIR__, 2 ),
308 - );
309 -
310 - return array_values(
311 - array_filter(
312 - array_map(
313 - static function ( string $root ): string {
314 - $real = realpath( $root );
315 - return false === $real ? '' : wp_normalize_path( $real );
316 - },
317 - $roots
318 - )
319 - )
320 - );
321 - }
322 -
323 - /**
324 - * Determine a supported image MIME type from path.
325 - *
326 - * @param string $path Local image path.
327 - *
328 - * @return string|null MIME type.
329 - */
330 - private function image_mime_type( string $path ): ?string {
331 - $type = wp_check_filetype( $path );
332 - $mime = false !== $type['type'] ? (string) $type['type'] : '';
333 -
334 - if ( '' === $mime ) {
335 - $extension = strtolower( pathinfo( $path, PATHINFO_EXTENSION ) );
336 - $mime = array(
337 - 'gif' => 'image/gif',
338 - 'jpg' => 'image/jpeg',
339 - 'jpeg' => 'image/jpeg',
340 - 'png' => 'image/png',
341 - 'svg' => 'image/svg+xml',
342 - 'webp' => 'image/webp',
343 - )[ $extension ] ?? '';
344 - }
345 -
346 - if ( 0 !== strpos( $mime, 'image/' ) ) {
347 - return null;
348 - }
349 -
350 - return $mime;
351 176 }
352 177
353 178 /**
354 179 * Build and render a Dompdf instance.