@@ -49,13 +49,13 @@ public class AvatarContributor implements Contributor {
4949 private static final Logger LOGGER = Logger .getLogger (AvatarContributor .class .getName ());
5050
5151 private final Set <String > domains = ConcurrentHashMap .newKeySet ();
52- private final Set <String > allowedSources = ConcurrentHashMap .newKeySet ();
52+ private final Set <String > allowedSources = ConcurrentHashMap .newKeySet ();
5353
54- @ Override
55- public void apply (CspBuilder cspBuilder ) {
56- domains .forEach (d -> cspBuilder .add ("img-src" , d ));
57- allowedSources .forEach (s -> cspBuilder .add ("img-src" , s ));
58- }
54+ @ Override
55+ public void apply (CspBuilder cspBuilder ) {
56+ domains .forEach (d -> cspBuilder .add ("img-src" , d ));
57+ allowedSources .forEach (s -> cspBuilder .add ("img-src" , s ));
58+ }
5959
6060 /**
6161 * Request addition of the domain of the specified URL to the allowed set of avatar image domains.
@@ -94,20 +94,21 @@ public static void allow(@CheckForNull String url) {
9494 * @param url The full avatar image URL to allow.
9595 */
9696 @ SuppressWarnings ("unused" )
97- public static void allowUrl (@ CheckForNull String url ) {
98- String normalized = normalizeUrl ( url );
97+ public static void allowUrl (@ CheckForNull String url ) {
98+ AvatarContributor self = ExtensionList . lookupSingleton ( AvatarContributor . class );
9999
100- if (normalized == null ) {
101- LOGGER .log (Level .FINE , "Skipping invalid or unsupported avatar URL: " + url );
102- return ;
103- }
100+ if (addNormalizedUrl ( url , self . allowedSources ) ) {
101+ LOGGER .log (Level .CONFIG , "Adding allowed avatar URL: {0}" , url );
102+ }
103+ }
104104
105- if (ExtensionList .lookupSingleton (AvatarContributor .class ).allowedSources .add (normalized )) {
106- LOGGER .log (Level .CONFIG , "Adding allowed avatar URL: " + normalized + " (from: " + url + ")" );
107- } else {
108- LOGGER .log (Level .FINEST , "Skipped adding duplicate allowed url: " + normalized + " (from: " + url + ")" );
109- }
105+ static boolean addNormalizedUrl (@ CheckForNull String url , Set <String > target ) {
106+ String normalized = normalizeUrl (url );
107+ if (normalized == null ) {
108+ return false ;
110109 }
110+ return target .add (normalized );
111+ }
111112
112113 /**
113114 * Utility method extracting the domain specification for CSP fetch directives from a specified URL.
@@ -157,14 +158,33 @@ public static String extractDomainFromUrl(@CheckForNull String url) {
157158 /**
158159 * Normalizes a URL for use in Content-Security-Policy.
159160 * <p>
160- * This method performs several steps:
161+ * This method validates and canonicalizes the URL to ensure it is safe for use in a CSP header.
161162 * </p>
162163 * <ul>
163- * <li>Only http or https are accepted.</li>
164- * <li>Removes embedded credentials for security.</li>
165- * <li>Converts IDN to ASCII.</li>
166- * <li>Normalizes IPv6 addresses.</li>
167- * <li>Removes default ports.</li>
164+ * <li>
165+ * <strong>Accepts only http and https schemes</strong> -
166+ * Avatar images are fetched over the network. Other schemes such as file, data, or JavaScript are either unsafe
167+ * or meaningless in a CSP img-src context.
168+ * </li>
169+ * <li>
170+ * <strong>Rejects URLs with embedded credentials</strong> —
171+ * URLs containing user:password@host can accidentally expose credentials in logs or browser requests. Such URLs
172+ * are then rejected.
173+ * </li>
174+ * <li>
175+ * <strong>Converts IDN to ASCII</strong> —
176+ * Browser internally normalize hostnames to their ASCII representation. Normalizing here ensures Jenkins
177+ * generates CSP entries that match browser behavior consistently.
178+ * </li>
179+ * <li>
180+ * <strong>Normalizes IPv6 literals</strong> —
181+ * IPv6 addresses must be enclosed in square brackets in URLs.
182+ * </li>
183+ * <li>
184+ * <strong>Removes default ports</strong> —
185+ * Ports 80 (HTTP) and 443 (HTTPS) are removed. Removing them avoids duplicate CSP entries that differ only
186+ * by the presence of a default port.
187+ * </li>
168188 * </ul>
169189 *
170190 * @param url The raw input URL.
0 commit comments