Skip to content

Commit a6ba4c4

Browse files
committed
Merge branch 'torusrxxx--patch-5' into next
2 parents 523d84b + 2bc2fc2 commit a6ba4c4

File tree

7 files changed

+459
-234
lines changed

7 files changed

+459
-234
lines changed

src/freenet/client/filter/CSSTokenizerFilter.java

Lines changed: 116 additions & 44 deletions
Large diffs are not rendered by default.

src/freenet/client/filter/ElementInfo.java

Lines changed: 67 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Collections;
55
import java.util.HashSet;
66
import java.util.Set;
7+
import java.util.regex.Pattern;
78

89
public class ElementInfo {
910

@@ -53,42 +54,6 @@ public class ElementInfo {
5354
"button"
5455
)));
5556

56-
// FIXME add some more languages.
57-
public static final Set<String> LANGUAGES =
58-
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
59-
"az",
60-
"be",
61-
"bg",
62-
"cs",
63-
"de",
64-
"el",
65-
"en",
66-
"es",
67-
"fi",
68-
"fr",
69-
"id",
70-
"it",
71-
"ja",
72-
"ka",
73-
"kk",
74-
"ky",
75-
"lv",
76-
"mo",
77-
"nl",
78-
"no",
79-
"pl",
80-
"pt",
81-
"ro",
82-
"ru",
83-
"sv",
84-
"tl",
85-
"tr",
86-
"tt",
87-
"uk",
88-
"zh-hans",
89-
"zh-hant"
90-
)));
91-
9257
public static final Set<String> MEDIA =
9358
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
9459
"all",
@@ -182,13 +147,22 @@ public class ElementInfo {
182147
"new york6"
183148
)));
184149

150+
// https://developer.mozilla.org/en-US/docs/Web/CSS/font-family
185151
public static final Set<String> GENERIC_FONT_KEYWORDS =
186152
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
187153
"serif",
188154
"sans-serif",
189155
"cursive",
190156
"fantasy",
191-
"monospace"
157+
"monospace",
158+
"system-ui",
159+
"ui-serif",
160+
"ui-sans-serif",
161+
"ui-monospace",
162+
"ui-rounded",
163+
"emoji",
164+
"math",
165+
"fangsong"
192166
)));
193167

194168
public static final Set<String> GENERIC_VOICE_KEYWORDS =
@@ -206,19 +180,37 @@ public class ElementInfo {
206180
"nth-last-child",
207181
"nth-of-type",
208182
"nth-last-of-type",
209-
"link",
210-
"visited",
183+
"link", // inverse of visited (see BANNED_PSEUDOCLASS below)
184+
"visited", // privacy risk (see BANNED_PSEUDOCLASS below)
211185
"hover",
212186
"active",
213-
"checked",
187+
"checked", // forms
214188
"focus",
215189
"focus-within",
216-
"lang",
217190
"first-line",
218191
"first-letter",
219192
"before",
220193
"after",
221-
"target"
194+
"target",
195+
"any-link",
196+
"default", // forms
197+
"defined", // Javascript only (BANNED_PSEUDOCLASS)
198+
"disabled", // forms
199+
"empty",
200+
"enabled", // forms
201+
"focus-visible",
202+
"indeterminate", // forms
203+
"in-range", // forms
204+
"invalid", // forms
205+
"only-child",
206+
"only-of-type",
207+
"optional", // forms
208+
"out-of-range", // forms
209+
"placeholder-shown", // forms
210+
"read-only", // forms
211+
"read-write", // forms
212+
"required", // forms
213+
"root"
222214
)));
223215

224216
public static final Set<String> BANNED_PSEUDOCLASS =
@@ -249,7 +241,10 @@ public class ElementInfo {
249241
// is considered too much of a danger, so we scrub that pseudoclass.
250242
//
251243
// [1] http://lcamtuf.coredump.cx/css_calc/
252-
"visited"
244+
"link",
245+
"visited",
246+
// Javascript only
247+
"defined"
253248
)));
254249

255250
public static boolean isSpecificFontFamily(String font) {
@@ -432,49 +427,63 @@ public static boolean isBannedPseudoClass(String cname)
432427
// Pseudo-classes can be chained, at least dynamic ones can, see CSS2.1 section 5.11.3
433428
String[] split = cname.split(":");
434429
for(String s : split)
435-
if(isBannedPseudoClass(s)) return true;
430+
if(isBannedPseudoClass2(s)) return true;
436431
return false;
432+
} else {
433+
return isBannedPseudoClass2(cname);
437434
}
438-
cname=cname.toLowerCase();
439-
return BANNED_PSEUDOCLASS.contains(cname);
440435
}
441436

437+
private static boolean isBannedPseudoClass2(String cname)
438+
{
439+
return BANNED_PSEUDOCLASS.contains(cname.toLowerCase());
440+
}
441+
442442
public static boolean isValidPseudoClass(String cname)
443443
{
444444
if(cname.indexOf(':') != -1) {
445445
// Pseudo-classes can be chained, at least dynamic ones can, see CSS2.1 section 5.11.3
446446
String[] split = cname.split(":");
447447
for(String s : split)
448-
if(!isValidPseudoClass(s)) return false;
448+
if(!isValidPseudoClass2(s)) return false;
449449
return true;
450+
} else {
451+
return isValidPseudoClass2(cname);
450452
}
453+
}
454+
455+
private static boolean isValidPseudoClass2(String cname)
456+
{
451457
cname=cname.toLowerCase();
452458
if(PSEUDOCLASS.contains(cname))
453459
return true;
454-
455-
456-
else if(cname.contains("lang") && LANGUAGES.contains(getPseudoClassArg(cname, "lang")))
460+
else if(cname.startsWith("lang") && Pattern.matches("[\\w\\-*]{1,30}", getPseudoClassArg(cname, "lang")))
457461
{
458-
// FIXME accept unknown languages as long as they are [a-z-]
462+
// More than 8000 valid BCP-47 language codes. Just let through all of them.
459463
return true;
460464
}
461-
462-
else if(cname.contains("nth-child") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-child")))
465+
else if(cname.startsWith("nth-child") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-child")))
463466
return true;
464-
else if(cname.contains("nth-last-child") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-last-child")))
467+
else if(cname.startsWith("nth-last-child") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-last-child")))
465468
return true;
466-
else if(cname.contains("nth-of-type") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-of-type")))
469+
else if(cname.startsWith("nth-of-type") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-of-type")))
467470
return true;
468-
else if(cname.contains("nth-last-of-type") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-last-of-type")))
471+
else if(cname.startsWith("nth-last-of-type") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-last-of-type")))
469472
return true;
470-
473+
else if(cname.startsWith("dir")) {
474+
String arg = getPseudoClassArg(cname, "dir");
475+
return arg.equalsIgnoreCase("ltr") || arg.equalsIgnoreCase("rtl");
476+
}
471477
return false;
472-
}
478+
}
479+
473480
public static String getPseudoClassArg(String cname, String cname_sans_arg) {
474481
String arg="";
475482
int cnameIndex=cname.indexOf(cname_sans_arg);
476483
int firstIndex=cname.indexOf('(');
477484
int secondIndex=cname.lastIndexOf(')');
485+
if(cnameIndex == -1 || firstIndex == -1 || secondIndex == -1)
486+
return "";
478487
if(cname.substring(cnameIndex + cname_sans_arg.length(), firstIndex).trim().isEmpty() && cname.substring(0, cnameIndex).trim().isEmpty() && cname.substring(secondIndex + 1, cname.length()).trim().isEmpty())
479488
{
480489
arg=CSSTokenizerFilter.removeOuterQuotes(cname.substring(firstIndex+1,secondIndex).trim());

src/freenet/client/filter/FilterUtils.java

Lines changed: 64 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.ArrayList;
55
import java.util.Arrays;
66
import java.util.HashSet;
7+
import java.util.regex.Pattern;
78

89
public class FilterUtils {
910
private static volatile boolean logDEBUG;
@@ -289,6 +290,7 @@ else if(value.contains("rad"))
289290
SVGcolorKeywords.add("whitesmoke");
290291
SVGcolorKeywords.add("yellow");
291292
SVGcolorKeywords.add("yellowgreen");
293+
SVGcolorKeywords.add("rebeccapurple"); // CSS Colors Level 4: #663399
292294
}
293295
private final static HashSet<String> CSScolorKeywords=new HashSet<String>();
294296
static
@@ -315,34 +317,34 @@ else if(value.contains("rad"))
315317
}
316318
private final static HashSet<String> CSSsystemColorKeywords=new HashSet<String>();
317319
static {
318-
CSScolorKeywords.add("ActiveBorder");
319-
CSScolorKeywords.add("ActiveCaption");
320-
CSScolorKeywords.add("AppWorkspace");
321-
CSScolorKeywords.add("Background");
322-
CSScolorKeywords.add("ButtonFace");
323-
CSScolorKeywords.add("ButtonHighlight");
324-
CSScolorKeywords.add("ButtonShadow");
325-
CSScolorKeywords.add("ButtonText");
326-
CSScolorKeywords.add("CaptionText");
327-
CSScolorKeywords.add("GrayText");
328-
CSScolorKeywords.add("Highlight");
329-
CSScolorKeywords.add("HighlightText");
330-
CSScolorKeywords.add("InactiveBorder");
331-
CSScolorKeywords.add("InactiveCaption");
332-
CSScolorKeywords.add("InactiveCaptionText");
333-
CSScolorKeywords.add("InfoBackground");
334-
CSScolorKeywords.add("InfoText");
335-
CSScolorKeywords.add("Menu");
336-
CSScolorKeywords.add("MenuText");
337-
CSScolorKeywords.add("Scrollbar");
338-
CSScolorKeywords.add("ThreeDDarkShadow");
339-
CSScolorKeywords.add("ThreeDFace");
340-
CSScolorKeywords.add("ThreeDHighlight");
341-
CSScolorKeywords.add("ThreeDLightShadow");
342-
CSScolorKeywords.add("ThreeDShadow");
343-
CSScolorKeywords.add("Window");
344-
CSScolorKeywords.add("WindowFrame");
345-
CSScolorKeywords.add("WindowText");
320+
CSScolorKeywords.add("activeborder");
321+
CSScolorKeywords.add("activecaption");
322+
CSScolorKeywords.add("appworkspace");
323+
CSScolorKeywords.add("background");
324+
CSScolorKeywords.add("buttonface");
325+
CSScolorKeywords.add("buttonhighlight");
326+
CSScolorKeywords.add("buttonshadow");
327+
CSScolorKeywords.add("buttontext");
328+
CSScolorKeywords.add("captiontext");
329+
CSScolorKeywords.add("graytext");
330+
CSScolorKeywords.add("highlight");
331+
CSScolorKeywords.add("highlighttext");
332+
CSScolorKeywords.add("inactiveborder");
333+
CSScolorKeywords.add("inactivecaption");
334+
CSScolorKeywords.add("inactivecaptiontext");
335+
CSScolorKeywords.add("infobackground");
336+
CSScolorKeywords.add("infotext");
337+
CSScolorKeywords.add("menu");
338+
CSScolorKeywords.add("menutext");
339+
CSScolorKeywords.add("scrollbar");
340+
CSScolorKeywords.add("threeddarkshadow");
341+
CSScolorKeywords.add("threedface");
342+
CSScolorKeywords.add("threedhighlight");
343+
CSScolorKeywords.add("threedlightshadow");
344+
CSScolorKeywords.add("threedshadow");
345+
CSScolorKeywords.add("window");
346+
CSScolorKeywords.add("windowframe");
347+
CSScolorKeywords.add("windowtext");
346348
}
347349
public static boolean isValidCSSShape(String value)
348350
{
@@ -370,70 +372,57 @@ public static boolean isValidCSSShape(String value)
370372
public static boolean isMedia(String media) {
371373
return cssMedia.contains(media);
372374
}
375+
376+
public static final Pattern hexColorPattern = Pattern.compile("#(?>[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3,4})", Pattern.CASE_INSENSITIVE);
377+
373378
public static boolean isColor(String value)
374379
{
375-
value=value.trim();
380+
value=value.trim().toLowerCase();
376381

377382
if(CSScolorKeywords.contains(value) || CSSsystemColorKeywords.contains(value) || SVGcolorKeywords.contains(value))
378383
return true;
379384

380385
if(value.indexOf('#')==0)
381386
{
382-
383-
if(value.length()==4)
387+
return hexColorPattern.matcher(value).matches();
388+
}
389+
if((value.startsWith("rgb(") || value.startsWith("rgba(")) && value.indexOf(')')==value.length()-1)
390+
{
391+
// rgba is an alias to rgb
392+
if(value.contains(","))
384393
{
385-
try{
386-
Integer.valueOf(value.substring(1,2),16).intValue();
387-
Integer.valueOf(value.substring(2,3),16).intValue();
388-
Integer.valueOf(value.substring(3,4),16).intValue();
389-
return true;
390-
}
391-
catch(Exception e)
394+
// Legacy format rgba(r,g,b,a)
395+
String[] colorParts=value.substring(value.indexOf("(")+1,value.length()-1).split(",");
396+
if(colorParts.length!=3&&colorParts.length!=4)
397+
return false;
398+
for(int i=0; i<3; i++)
392399
{
400+
if(!(isPercentage(colorParts[i].trim()) || isInteger(colorParts[i].trim())))
401+
return false;
393402
}
394-
395-
}
396-
else if(value.length()==7)
397-
{
398-
399-
try{
400-
Integer.valueOf(value.substring(1,3),16).intValue();
401-
Integer.valueOf(value.substring(3,5),16).intValue();
402-
Integer.valueOf(value.substring(5,7),16).intValue();
403+
if(colorParts.length<=3 || isNumber(colorParts[3]))
403404
return true;
405+
}else{
406+
if(value.contains("/")){
407+
// Modern format rgba(r g b / a)
408+
String alphaPart=value.substring(value.indexOf("/")+1,value.length()-1).trim();
409+
if(!alphaPart.isEmpty() && !isPercentage(alphaPart) && !isNumber(alphaPart) && !alphaPart.equalsIgnoreCase("none"))
410+
return false;
411+
value=value.substring(0,value.indexOf("/"))+")"; // Strip alpha value, proceed to the following tests
412+
}
413+
// Modern format rgba(r g b)
414+
String[] colorParts=value.substring(value.indexOf("(")+1,value.length()-1).split(" ");
415+
if(colorParts.length!=3) {
416+
return false;
404417
}
405-
catch(Exception e)
418+
for(int i=0; i<3; i++)
406419
{
420+
String trimmed = colorParts[i].trim();
421+
if(!(trimmed.equalsIgnoreCase("none") || isPercentage(trimmed) || (isInteger(trimmed) && isIntegerInRange(trimmed, 0, 255))))
422+
return false;
407423
}
408-
}
409-
}
410-
if(value.indexOf("rgb(")==0 && value.indexOf(')')==value.length()-1)
411-
{
412-
String[] colorParts=value.substring(4,value.length()-1).split(",");
413-
if(colorParts.length!=3)
414-
return false;
415-
boolean isValidColorParts=true;
416-
for(int i=0; i<colorParts.length && isValidColorParts;i++)
417-
{
418-
if(!(isPercentage(colorParts[i].trim()) || isInteger(colorParts[i].trim())))
419-
isValidColorParts = false;
420-
}
421-
if(isValidColorParts)
422424
return true;
423-
}
424-
if(value.indexOf("rgba(")==0 && value.indexOf(')')==value.length()-1)
425-
{
426-
String[] colorParts=value.substring(5,value.length()-1).split(",");
427-
if(colorParts.length!=4)
428-
return false;
429-
boolean isValidColorParts=true;
430-
for(int i=0; i<colorParts.length-1 && isValidColorParts;i++)
431-
{
432-
if(!(isPercentage(colorParts[i].trim()) || isInteger(colorParts[i].trim())))
433-
isValidColorParts = false;
434425
}
435-
if(isValidColorParts && isNumber(colorParts[3]))
436-
return true;
437426
}
438427

439428
if(value.indexOf("hsl(")==0 && value.indexOf(')')==value.length()-1)

0 commit comments

Comments
 (0)