@@ -87,4 +87,108 @@ function stackable_blocksy_global_color_schemes_compatibility( $styles, $scheme,
8787 }
8888
8989 add_filter ( 'stackable.global-settings.global-color-schemes.add-theme-compatibility ' , 'stackable_blocksy_global_color_schemes_compatibility ' , 10 , 6 );
90- }
90+ }
91+
92+ if ( ! function_exists ( 'stackable_blocksy_theme_global_styles ' ) ) {
93+ function stackable_sanitize_css_string ( $ css ) {
94+ if ( ! is_string ( $ css ) ) {
95+ return '' ;
96+ }
97+
98+ // sanitize css content
99+ $ css = wp_strip_all_tags ( $ css );
100+ $ css = preg_replace ('/\bexpression\s*\([^)]*\)/i ' , '' , $ css );
101+ $ css = preg_replace ('/\bjavascript\s*:/i ' , '' , $ css );
102+
103+ // Only allow URLs from the theme directory
104+ $ theme_uri = preg_quote ( get_template_directory_uri (), '/ ' );
105+ $ css = preg_replace_callback (
106+ '/url\(\s*[ \'"]?\s*(https?:\/\/[^ \'")]+)\s*[ \'"]?\s*\)/i ' ,
107+ function ( $ matches ) use ( $ theme_uri ) {
108+ if ( preg_match ( "/^ {$ theme_uri }/i " , $ matches [1 ] ) ) {
109+ return $ matches [0 ]; // Keep theme URLs
110+ }
111+ return 'url("") ' ; // Remove others
112+ },
113+ $ css
114+ );
115+
116+ // Block unsafe tokens
117+ $ css = preg_replace ('/\b(?:eval|mocha)\b(\s*:|\s*\()/i ' , '/* blocked */$1 ' , $ css );
118+
119+ // Block behavior and vendor-prefixed behavior
120+ $ css = preg_replace ('/(?<![a-zA-Z0-9-])(?:-+[a-zA-Z]*behavior|behavior)\b(\s*:|\s*\()/i ' , '/* blocked */$1 ' , $ css );
121+
122+ // Remove redundant semicolons
123+ $ css = preg_replace ('/;+/ ' , '; ' , $ css );
124+
125+ // Remove empty rule blocks (e.g. ".selector { }")
126+ $ css = preg_replace ('/[^{]+\{\s*\}/m ' , '' , $ css );
127+
128+ // Normalize spacing and line breaks
129+ $ css = preg_replace ('/\s+/ ' , ' ' , $ css );
130+ $ css = trim ($ css );
131+
132+ return $ css ;
133+ }
134+
135+ function stackable_blocksy_theme_global_styles ( $ styles ) {
136+
137+ if ( function_exists ( 'blocksy_manager ' ) ) {
138+ $ blocksy_css = blocksy_manager ()->dynamic_css ->load_backend_dynamic_css ([
139+ 'echo ' => false
140+ ] );
141+
142+ $ styles .= $ blocksy_css ;
143+ }
144+
145+ if ( class_exists ( 'Blocksy_Static_Css_Files ' ) ) {
146+ $ blocksy_static_files = ( new Blocksy_Static_Css_Files () )->all_static_files ();
147+
148+ $ blocksy_static_files = array_filter (
149+ $ blocksy_static_files ,
150+ function ( $ file ) {
151+ return isset ( $ file ['id ' ] ) && in_array ( $ file ['id ' ], array ( 'ct-main-styles ' , 'ct-stackable-styles ' ), true );
152+ }
153+ );
154+
155+ $ styles_from_files = '' ;
156+ foreach ( $ blocksy_static_files as $ file ) {
157+ if ( isset ( $ file ['url ' ] ) ) {
158+ // Normalize and validate the path to prevent traversal
159+ $ file_url = ltrim ( $ file ['url ' ], '/ ' );
160+ $ file_path = get_template_directory () . '/ ' . $ file_url ;
161+ $ file_path = realpath ( $ file_path );
162+ $ theme_dir = realpath ( get_template_directory () );
163+
164+ // Ensure the resolved path is within the theme directory
165+ if ( ! $ file_path || strpos ( $ file_path , $ theme_dir ) !== 0 ) {
166+ continue ;
167+ }
168+
169+ if ( file_exists ( $ file_path ) && is_readable ( $ file_path ) ) {
170+ $ extension = strtolower ( pathinfo ( $ file_path , PATHINFO_EXTENSION ) );
171+ if ( $ extension !== 'css ' ) {
172+ continue ;
173+ }
174+ $ content = file_get_contents ( $ file_path );
175+ if ( $ content !== false ) {
176+ $ styles_from_files .= $ content ;
177+ }
178+
179+ }
180+ }
181+ }
182+
183+ if ( $ styles_from_files ) {
184+ // sanitize styles from files
185+ $ styles_from_files = stackable_sanitize_css_string ( $ styles_from_files );
186+ $ styles .= $ styles_from_files ;
187+ }
188+ }
189+
190+ return $ styles ;
191+ }
192+
193+ add_filter ( 'stackable.design-library.global-theme-styles ' , 'stackable_blocksy_theme_global_styles ' );
194+ }
0 commit comments