@@ -41,7 +41,14 @@ pub fn resolve_url(base: &str, href: &str) -> Option<String> {
4141 Some ( format ! ( "{}://{}{}" , & base[ ..scheme_split] , dir, href) )
4242}
4343
44- /// 内联单资源占位(style/img 空档——防空串替换破坏结构)。
44+ /// 解码 base64 为文本(CSS 内联用;失败=None 保留原引用降级)。
45+ fn decode_text ( b64 : & str ) -> Option < String > {
46+ use base64:: Engine as _;
47+ let bytes = base64:: engine:: general_purpose:: STANDARD . decode ( b64. trim ( ) ) . ok ( ) ?;
48+ Some ( String :: from_utf8_lossy ( & bytes) . into_owned ( ) )
49+ }
50+
51+ /// 内联单资源占位(img 用 data URI;style 走 decode_text 分支)。
4552fn data_or_keep (
4653 kind : & str ,
4754 resolved : Option < String > ,
@@ -90,13 +97,19 @@ pub fn inline_html(base: &str, html: &str, resolver: Resolver) -> String {
9097 if lower. starts_with ( "<link" ) {
9198 if let Some ( href) = extract_attr ( tag, "href" ) {
9299 if lower. contains ( "stylesheet" ) {
93- if let Some ( data) = data_or_keep ( "style" , resolve_url ( base, & href) , & mut * resolver) {
94- out. push_str ( & format ! ( "<style data-inlined=\" {}\" >{}</style>" , escape_attr( & href) , data) ) ;
95- continue ;
100+ // CSS 必须解码为纯文本塞入 <style>(data URI 字面量是非法规则——
101+ // 审查 M1:内联产物对外部样式要真实生效)
102+ if let Some ( url) = resolve_url ( base, & href) {
103+ if let Some ( b64) = resolver ( & url) {
104+ if let Some ( css) = decode_text ( b64. as_str ( ) ) {
105+ out. push_str ( & format ! ( "<style data-inlined=\" {}\" >{}</style>" , escape_attr( & href) , css) ) ;
106+ continue ;
107+ }
108+ }
96109 }
97110 }
98111 }
99- out. push_str ( tag) ;
112+ out. push_str ( & scrub_tag ( tag) ) ;
100113 } else if lower. starts_with ( "<img" ) {
101114 if let Some ( src) = extract_attr ( tag, "src" ) {
102115 let data = data_or_keep ( "img" , resolve_url ( base, & src) , & mut * resolver) ;
0 commit comments