@@ -1359,4 +1359,212 @@ public static function get_transformations_title( $context ) {
13591359
13601360 return $ transformations_title ;
13611361 }
1362+
1363+ /**
1364+ * Get inline SVG content safely.
1365+ *
1366+ * @param string $file_path The absolute or relative path to the SVG file.
1367+ * @param bool $echo Whether to echo the SVG content or return it. Default true.
1368+ *
1369+ * @return string|void The SVG content if $echo is false, void otherwise.
1370+ */
1371+ public static function get_inline_svg ( $ file_path , $ echo = true ) {
1372+ // If relative path, make it absolute from plugin root.
1373+ if ( ! file_exists ( $ file_path ) ) {
1374+ $ plugin_dir = dirname ( __DIR__ );
1375+ $ file_path = $ plugin_dir . '/ ' . ltrim ( $ file_path , '/ ' );
1376+ }
1377+
1378+ // Check if file exists and is an SVG.
1379+ if ( ! file_exists ( $ file_path ) || 'svg ' !== pathinfo ( $ file_path , PATHINFO_EXTENSION ) ) {
1380+ return $ echo ? '' : '' ;
1381+ }
1382+
1383+ // Get the SVG content.
1384+ $ svg_content = file_get_contents ( $ file_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
1385+
1386+ if ( false === $ svg_content ) {
1387+ return $ echo ? '' : '' ;
1388+ }
1389+
1390+ // Sanitize SVG content to prevent XSS attacks.
1391+ $ svg_content = self ::sanitize_svg ( $ svg_content );
1392+
1393+ if ( $ echo ) {
1394+ echo $ svg_content ; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Sanitized by sanitize_svg.
1395+ } else {
1396+ return $ svg_content ;
1397+ }
1398+ }
1399+
1400+ /**
1401+ * Sanitize SVG content to prevent XSS attacks.
1402+ *
1403+ * @param string $svg_content The SVG content to sanitize.
1404+ *
1405+ * @return string The sanitized SVG content.
1406+ */
1407+ public static function sanitize_svg ( $ svg_content ) {
1408+ // Define allowed SVG tags and attributes.
1409+ $ allowed_tags = array (
1410+ 'svg ' => array (
1411+ 'class ' => true ,
1412+ 'aria-hidden ' => true ,
1413+ 'aria-labelledby ' => true ,
1414+ 'role ' => true ,
1415+ 'xmlns ' => true ,
1416+ 'width ' => true ,
1417+ 'height ' => true ,
1418+ 'viewbox ' => true ,
1419+ 'fill ' => true ,
1420+ 'stroke ' => true ,
1421+ ),
1422+ 'g ' => array (
1423+ 'fill ' => true ,
1424+ 'stroke ' => true ,
1425+ 'transform ' => true ,
1426+ 'class ' => true ,
1427+ ),
1428+ 'title ' => array ( 'title ' => true ),
1429+ 'desc ' => array (),
1430+ 'path ' => array (
1431+ 'd ' => true ,
1432+ 'fill ' => true ,
1433+ 'stroke ' => true ,
1434+ 'stroke-width ' => true ,
1435+ 'stroke-linecap ' => true ,
1436+ 'stroke-linejoin ' => true ,
1437+ 'transform ' => true ,
1438+ 'class ' => true ,
1439+ ),
1440+ 'rect ' => array (
1441+ 'x ' => true ,
1442+ 'y ' => true ,
1443+ 'width ' => true ,
1444+ 'height ' => true ,
1445+ 'rx ' => true ,
1446+ 'ry ' => true ,
1447+ 'fill ' => true ,
1448+ 'stroke ' => true ,
1449+ 'stroke-width ' => true ,
1450+ 'transform ' => true ,
1451+ 'class ' => true ,
1452+ ),
1453+ 'circle ' => array (
1454+ 'cx ' => true ,
1455+ 'cy ' => true ,
1456+ 'r ' => true ,
1457+ 'fill ' => true ,
1458+ 'stroke ' => true ,
1459+ 'stroke-width ' => true ,
1460+ 'transform ' => true ,
1461+ 'class ' => true ,
1462+ ),
1463+ 'ellipse ' => array (
1464+ 'cx ' => true ,
1465+ 'cy ' => true ,
1466+ 'rx ' => true ,
1467+ 'ry ' => true ,
1468+ 'fill ' => true ,
1469+ 'stroke ' => true ,
1470+ 'stroke-width ' => true ,
1471+ 'transform ' => true ,
1472+ 'class ' => true ,
1473+ ),
1474+ 'line ' => array (
1475+ 'x1 ' => true ,
1476+ 'y1 ' => true ,
1477+ 'x2 ' => true ,
1478+ 'y2 ' => true ,
1479+ 'stroke ' => true ,
1480+ 'stroke-width ' => true ,
1481+ 'transform ' => true ,
1482+ 'class ' => true ,
1483+ ),
1484+ 'polyline ' => array (
1485+ 'points ' => true ,
1486+ 'fill ' => true ,
1487+ 'stroke ' => true ,
1488+ 'stroke-width ' => true ,
1489+ 'transform ' => true ,
1490+ 'class ' => true ,
1491+ ),
1492+ 'polygon ' => array (
1493+ 'points ' => true ,
1494+ 'fill ' => true ,
1495+ 'stroke ' => true ,
1496+ 'stroke-width ' => true ,
1497+ 'transform ' => true ,
1498+ 'class ' => true ,
1499+ ),
1500+ 'defs ' => array (),
1501+ 'lineargradient ' => array (
1502+ 'id ' => true ,
1503+ 'x1 ' => true ,
1504+ 'y1 ' => true ,
1505+ 'x2 ' => true ,
1506+ 'y2 ' => true ,
1507+ 'gradientunits ' => true ,
1508+ ),
1509+ 'radialgradient ' => array (
1510+ 'id ' => true ,
1511+ 'cx ' => true ,
1512+ 'cy ' => true ,
1513+ 'r ' => true ,
1514+ 'gradientunits ' => true ,
1515+ ),
1516+ 'stop ' => array (
1517+ 'offset ' => true ,
1518+ 'stop-color ' => true ,
1519+ 'stop-opacity ' => true ,
1520+ ),
1521+ 'use ' => array (
1522+ 'xlink:href ' => true ,
1523+ 'href ' => true ,
1524+ ),
1525+ 'clippath ' => array ( 'id ' => true ),
1526+ 'mask ' => array ( 'id ' => true ),
1527+ 'pattern ' => array (
1528+ 'id ' => true ,
1529+ 'x ' => true ,
1530+ 'y ' => true ,
1531+ 'width ' => true ,
1532+ 'height ' => true ,
1533+ 'patternunits ' => true ,
1534+ ),
1535+ 'text ' => array (
1536+ 'x ' => true ,
1537+ 'y ' => true ,
1538+ 'fill ' => true ,
1539+ 'font-size ' => true ,
1540+ 'font-family ' => true ,
1541+ 'text-anchor ' => true ,
1542+ 'transform ' => true ,
1543+ 'class ' => true ,
1544+ ),
1545+ 'tspan ' => array (
1546+ 'x ' => true ,
1547+ 'y ' => true ,
1548+ 'fill ' => true ,
1549+ 'font-size ' => true ,
1550+ 'font-family ' => true ,
1551+ 'class ' => true ,
1552+ ),
1553+ );
1554+
1555+ /**
1556+ * Filter the allowed SVG tags and attributes.
1557+ *
1558+ * @hook cloudinary_allowed_svg_tags
1559+ * @since 3.2.15
1560+ *
1561+ * @param $allowed_tags {array} The allowed SVG tags and their attributes.
1562+ *
1563+ * @return {array}
1564+ */
1565+ $ allowed_tags = apply_filters ( 'cloudinary_allowed_svg_tags ' , $ allowed_tags );
1566+
1567+ // Use wp_kses to sanitize the SVG content.
1568+ return wp_kses ( $ svg_content , $ allowed_tags );
1569+ }
13621570}
0 commit comments