@@ -51,10 +51,19 @@ public void testTrustedTypesBasic() {
5151 assertEquals (3 , tt .getPolicyNames_ ().size ());
5252
5353 // Wildcard
54- p = Policy .parseSerializedCSP ("trusted-types *" , ThrowIfPolicyError );
54+ ArrayList <PolicyError > observedErrors = new ArrayList <>();
55+ Policy .PolicyErrorConsumer consumer = (severity , message , directiveIndex , valueIndex ) -> {
56+ observedErrors .add (e (severity , message , directiveIndex , valueIndex ));
57+ };
58+ p = Policy .parseSerializedCSP ("trusted-types *" , consumer );
5559 tt = p .trustedTypes ().get ();
5660 assertTrue (tt .star ());
61+ assertTrue (tt .allowsWildcardPolicyNames ());
62+ assertTrue (p .allowsWildcardPolicyNames ());
5763 assertEquals (0 , tt .getPolicyNames_ ().size ());
64+ assertEquals (1 , observedErrors .size ());
65+ assertEquals (Policy .Severity .Warning , observedErrors .get (0 ).severity_ ());
66+ assertTrue (observedErrors .get (0 ).message_ ().contains ("Wildcard policy names" ));
5867
5968 // Allow duplicates
6069 p = Policy .parseSerializedCSP ("trusted-types myPolicy 'allow-duplicates'" , ThrowIfPolicyError );
@@ -63,10 +72,18 @@ public void testTrustedTypesBasic() {
6372 assertEquals (1 , tt .getPolicyNames_ ().size ());
6473
6574 // Wildcard with allow-duplicates
66- p = Policy .parseSerializedCSP ("trusted-types * 'allow-duplicates'" , ThrowIfPolicyError );
75+ observedErrors .clear ();
76+ p = Policy .parseSerializedCSP ("trusted-types * 'allow-duplicates'" , consumer );
6777 tt = p .trustedTypes ().get ();
6878 assertTrue (tt .star ());
79+ assertTrue (tt .allowsWildcardPolicyNames ());
80+ assertTrue (p .allowsWildcardPolicyNames ());
6981 assertTrue (tt .allowDuplicates ());
82+ assertEquals (2 , observedErrors .size ());
83+ assertEquals (Policy .Severity .Warning , observedErrors .get (0 ).severity_ ());
84+ assertTrue (observedErrors .get (0 ).message_ ().contains ("Wildcard policy names" ));
85+ assertEquals (Policy .Severity .Warning , observedErrors .get (1 ).severity_ ());
86+ assertTrue (observedErrors .get (1 ).message_ ().contains ("redundant when wildcard" ));
7087
7188 // None keyword
7289 p = Policy .parseSerializedCSP ("trusted-types 'none'" , ThrowIfPolicyError );
@@ -102,23 +119,35 @@ public void testTrustedTypesPolicyNameCharacters() {
102119 public void testTrustedTypesRoundTrips () {
103120 roundTrips ("trusted-types myPolicy" );
104121 roundTrips ("trusted-types one two three" );
105- roundTrips ("trusted-types *" );
122+ roundTrips ("trusted-types *" ,
123+ e (Policy .Severity .Warning , "Wildcard policy names (*) permit any policy name, which may reduce security" , 0 , 0 ));
106124 roundTrips ("trusted-types 'none'" );
107125 roundTrips ("trusted-types myPolicy 'allow-duplicates'" );
108- roundTrips ("trusted-types * 'allow-duplicates'" );
126+ roundTrips ("trusted-types * 'allow-duplicates'" ,
127+ e (Policy .Severity .Warning , "Wildcard policy names (*) permit any policy name, which may reduce security" , 0 , 0 ),
128+ e (Policy .Severity .Warning , "'allow-duplicates' is redundant when wildcard (*) is present" , 0 , -1 ));
109129 }
110130
111131 @ Test
112132 public void testTrustedTypesCaseInsensitiveKeywords () {
113133 // Keywords are case-insensitive per ABNF
114134 inTurkey (() -> {
115135 Policy p ;
136+ ArrayList <PolicyError > observedErrors = new ArrayList <>();
137+ Policy .PolicyErrorConsumer consumer = (severity , message , directiveIndex , valueIndex ) -> {
138+ observedErrors .add (e (severity , message , directiveIndex , valueIndex ));
139+ };
116140
117141 p = Policy .parseSerializedCSP ("trusted-types 'NONE'" , ThrowIfPolicyError );
118142 assertTrue (p .trustedTypes ().get ().none ());
119143
120- p = Policy .parseSerializedCSP ("trusted-types 'ALLOW-DUPLICATES'" , ThrowIfPolicyError );
144+ // 'allow-duplicates' alone now generates a warning, so use consumer instead of ThrowIfPolicyError
145+ observedErrors .clear ();
146+ p = Policy .parseSerializedCSP ("trusted-types 'ALLOW-DUPLICATES'" , consumer );
121147 assertTrue (p .trustedTypes ().get ().allowDuplicates ());
148+ assertEquals (1 , observedErrors .size ());
149+ assertEquals (Policy .Severity .Warning , observedErrors .get (0 ).severity_ ());
150+ assertTrue (observedErrors .get (0 ).message_ ().contains ("has no effect without policy names" ));
122151
123152 p = Policy .parseSerializedCSP ("TRUSTED-TYPES myPolicy" , ThrowIfPolicyError );
124153 assertTrue (p .trustedTypes ().isPresent ());
@@ -175,14 +204,93 @@ public void testTrustedTypesErrors() {
175204 // Duplicate wildcard
176205 roundTrips (
177206 "trusted-types * *" ,
207+ e (Policy .Severity .Warning , "Wildcard policy names (*) permit any policy name, which may reduce security" , 0 , 0 ),
178208 e (Policy .Severity .Warning , "Duplicate wildcard *" , 0 , 1 )
179209 );
180210
211+ // Policy name with wildcard (wildcard makes policy names redundant)
212+ roundTrips (
213+ "trusted-types myPolicy *" ,
214+ e (Policy .Severity .Warning , "Wildcard policy names (*) permit any policy name, which may reduce security" , 0 , 1 ),
215+ e (Policy .Severity .Warning , "Wildcard (*) permits any policy name, making specific policy names redundant" , 0 , -1 )
216+ );
217+
218+ // Multiple policy names with wildcard
219+ roundTrips (
220+ "trusted-types one two *" ,
221+ e (Policy .Severity .Warning , "Wildcard policy names (*) permit any policy name, which may reduce security" , 0 , 2 ),
222+ e (Policy .Severity .Warning , "Wildcard (*) permits any policy name, making specific policy names redundant" , 0 , -1 )
223+ );
224+
181225 // Duplicate directive
182226 roundTrips (
183227 "trusted-types one; trusted-types two" ,
184228 e (Policy .Severity .Warning , "Duplicate directive trusted-types" , 1 , -1 )
185229 );
230+
231+ // Empty directive
232+ roundTrips (
233+ "trusted-types" ,
234+ e (Policy .Severity .Warning , "Empty trusted-types directive allows all policy names (use '*' or 'none' to be explicit)" , 0 , -1 )
235+ );
236+
237+ // 'allow-duplicates' alone (no policy names or wildcard)
238+ roundTrips (
239+ "trusted-types 'allow-duplicates'" ,
240+ e (Policy .Severity .Warning , "'allow-duplicates' has no effect without policy names or wildcard" , 0 , -1 )
241+ );
242+
243+ // Wildcard with allow-duplicates (redundant)
244+ roundTrips (
245+ "trusted-types * 'allow-duplicates'" ,
246+ e (Policy .Severity .Warning , "Wildcard policy names (*) permit any policy name, which may reduce security" , 0 , 0 ),
247+ e (Policy .Severity .Warning , "'allow-duplicates' is redundant when wildcard (*) is present" , 0 , -1 )
248+ );
249+
250+ // Policy names with wildcard and allow-duplicates (multiple redundancies)
251+ roundTrips (
252+ "trusted-types myPolicy * 'allow-duplicates'" ,
253+ e (Policy .Severity .Warning , "Wildcard policy names (*) permit any policy name, which may reduce security" , 0 , 1 ),
254+ e (Policy .Severity .Warning , "Wildcard (*) permits any policy name, making specific policy names redundant" , 0 , -1 ),
255+ e (Policy .Severity .Warning , "'allow-duplicates' is redundant when wildcard (*) is present" , 0 , -1 )
256+ );
257+
258+ // Order independence: wildcard before policy name
259+ roundTrips (
260+ "trusted-types * myPolicy" ,
261+ e (Policy .Severity .Warning , "Wildcard policy names (*) permit any policy name, which may reduce security" , 0 , 0 ),
262+ e (Policy .Severity .Warning , "Wildcard (*) permits any policy name, making specific policy names redundant" , 0 , -1 )
263+ );
264+ }
265+
266+ @ Test
267+ public void testTrustedTypesEdgeCases () {
268+ Policy p ;
269+ TrustedTypesDirective tt ;
270+
271+ // Single character policy name
272+ p = Policy .parseSerializedCSP ("trusted-types a" , ThrowIfPolicyError );
273+ tt = p .trustedTypes ().get ();
274+ assertEquals (1 , tt .getPolicyNames_ ().size ());
275+ assertEquals ("a" , tt .getPolicyNames_ ().get (0 ));
276+
277+ // Policy name with all allowed special characters
278+ p = Policy .parseSerializedCSP ("trusted-types A-Za-z0-9-#=_/@.%" , ThrowIfPolicyError );
279+ tt = p .trustedTypes ().get ();
280+ assertEquals (1 , tt .getPolicyNames_ ().size ());
281+ assertTrue (tt .getPolicyNames_ ().contains ("A-Za-z0-9-#=_/@.%" ));
282+
283+ // Policy name starting with special character
284+ p = Policy .parseSerializedCSP ("trusted-types -policy" , ThrowIfPolicyError );
285+ tt = p .trustedTypes ().get ();
286+ assertEquals (1 , tt .getPolicyNames_ ().size ());
287+ assertEquals ("-policy" , tt .getPolicyNames_ ().get (0 ));
288+
289+ // Policy name ending with special character
290+ p = Policy .parseSerializedCSP ("trusted-types policy-" , ThrowIfPolicyError );
291+ tt = p .trustedTypes ().get ();
292+ assertEquals (1 , tt .getPolicyNames_ ().size ());
293+ assertEquals ("policy-" , tt .getPolicyNames_ ().get (0 ));
186294 }
187295
188296 // require-trusted-types-for directive tests
@@ -286,6 +394,35 @@ public void testRequireTrustedTypesForManipulation() {
286394 assertTrue (rttf .script ());
287395 }
288396
397+ @ Test
398+ public void testAllowsWildcardPolicyNames () {
399+ Policy p ;
400+ TrustedTypesDirective tt ;
401+
402+ // Policy without wildcard
403+ p = Policy .parseSerializedCSP ("trusted-types myPolicy" , ThrowIfPolicyError );
404+ assertTrue (p .trustedTypes ().isPresent ());
405+ tt = p .trustedTypes ().get ();
406+ assertFalse (tt .allowsWildcardPolicyNames ());
407+ assertFalse (p .allowsWildcardPolicyNames ());
408+
409+ // Policy with wildcard
410+ ArrayList <PolicyError > observedErrors = new ArrayList <>();
411+ Policy .PolicyErrorConsumer consumer = (severity , message , directiveIndex , valueIndex ) -> {
412+ observedErrors .add (e (severity , message , directiveIndex , valueIndex ));
413+ };
414+ p = Policy .parseSerializedCSP ("trusted-types *" , consumer );
415+ assertTrue (p .trustedTypes ().isPresent ());
416+ tt = p .trustedTypes ().get ();
417+ assertTrue (tt .allowsWildcardPolicyNames ());
418+ assertTrue (p .allowsWildcardPolicyNames ());
419+
420+ // Policy without trusted-types directive
421+ p = Policy .parseSerializedCSP ("default-src 'self'" , ThrowIfPolicyError );
422+ assertFalse (p .trustedTypes ().isPresent ());
423+ assertFalse (p .allowsWildcardPolicyNames ());
424+ }
425+
289426 // Helper methods
290427
291428 private static void roundTrips (String input , PolicyError ... errors ) {
0 commit comments