-
Notifications
You must be signed in to change notification settings - Fork 0
Automated Test: feature-html-sanitizer-implementation #306
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,15 +17,22 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package org.keycloak.themeverifier; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.maven.plugin.MojoExecutionException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.owasp.html.PolicyFactory; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.io.BufferedReader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.io.File; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.io.FileInputStream; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.io.StringReader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.nio.file.Files; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.ArrayList; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.HashSet; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.MissingResourceException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Objects; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.PropertyResourceBundle; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.regex.Matcher; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.regex.Pattern; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class VerifyMessageProperties { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -41,12 +48,129 @@ public List<String> verify() throws MojoExecutionException { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String contents = Files.readString(file.toPath()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| verifyNoDuplicateKeys(contents); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| verifySafeHtml(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (IOException e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new MojoExecutionException("Can not read file " + file, e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return messages; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PolicyFactory POLICY_SOME_HTML = new org.owasp.html.HtmlPolicyBuilder() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .allowElements( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "br", "p", "strong", "b" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).toFactory(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PolicyFactory POLICY_NO_HTML = new org.owasp.html.HtmlPolicyBuilder().toFactory(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+58
to
+63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Policy fields should be
♻️ Proposed fix- PolicyFactory POLICY_SOME_HTML = new org.owasp.html.HtmlPolicyBuilder()
+ private static final PolicyFactory POLICY_SOME_HTML = new org.owasp.html.HtmlPolicyBuilder()
.allowElements(
"br", "p", "strong", "b"
).toFactory();
- PolicyFactory POLICY_NO_HTML = new org.owasp.html.HtmlPolicyBuilder().toFactory();
+ private static final PolicyFactory POLICY_NO_HTML = new org.owasp.html.HtmlPolicyBuilder().toFactory();And for Line 139: - Pattern HTML_TAGS = Pattern.compile("<[a-z]+[^>]*>");
+ private static final Pattern HTML_TAGS = Pattern.compile("<[a-z]+[^>]*>");📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void verifySafeHtml() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PropertyResourceBundle bundle; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try (FileInputStream fis = new FileInputStream(file)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bundle = new PropertyResourceBundle(fis); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (IOException e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new RuntimeException("unable to read file " + file, e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PropertyResourceBundle bundleEnglish; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String englishFile = file.getAbsolutePath().replaceAll("resources-community", "resources") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .replaceAll("_[a-zA-Z-_]*\\.properties", "_en.properties"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try (FileInputStream fis = new FileInputStream(englishFile)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bundleEnglish = new PropertyResourceBundle(fis); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (IOException e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new RuntimeException("unable to read file " + englishFile, e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+73
to
+80
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing English file causes an uncaught If the English 🛡️ Proposed fix — graceful fallback PropertyResourceBundle bundleEnglish;
String englishFile = file.getAbsolutePath().replaceAll("resources-community", "resources")
.replaceAll("_[a-zA-Z-_]*\\.properties", "_en.properties");
- try (FileInputStream fis = new FileInputStream(englishFile)) {
- bundleEnglish = new PropertyResourceBundle(fis);
- } catch (IOException e) {
- throw new RuntimeException("unable to read file " + englishFile, e);
+ File englishPath = new File(englishFile);
+ if (!englishPath.exists()) {
+ messages.add("English reference file not found: " + englishFile + " — skipping HTML safety check for " + file);
+ return;
+ }
+ try (FileInputStream fis = new FileInputStream(englishPath)) {
+ bundleEnglish = new PropertyResourceBundle(fis);
+ } catch (IOException e) {
+ messages.add("Unable to read English reference file " + englishFile + ": " + e.getMessage());
+ return;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bundle.getKeys().asIterator().forEachRemaining(key -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String value = bundle.getString(key); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value = normalizeValue(key, value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String englishValue = getEnglishValue(key, bundleEnglish); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| englishValue = normalizeValue(key, englishValue); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value = santizeAnchors(key, value, englishValue); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Only if the English source string contains HTML we also allow HTML in the translation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PolicyFactory policy = containsHtml(englishValue) ? POLICY_SOME_HTML : POLICY_NO_HTML; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String sanitized = policy.sanitize(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Sanitizer will escape HTML entities for quotes and also for numberic tags like '<1>' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sanitized = org.apache.commons.text.StringEscapeUtils.unescapeHtml4(sanitized); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Sanitizer will add them when there are double curly braces | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sanitized = sanitized.replace("<!-- -->", ""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!Objects.equals(sanitized, value)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Strip identical characters from the beginning and the end to show where the difference is | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int start = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while (start < sanitized.length() && start < value.length() && value.charAt(start) == sanitized.charAt(start)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start++; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int end = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while (end < sanitized.length() && end < value.length() && value.charAt(value.length() - end - 1) == sanitized.charAt(sanitized.length() - end - 1)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| end++; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messages.add("Illegal HTML in key " + key + " for file " + file + ": '" + value.substring(start, value.length() - end) + "' vs. '" + sanitized.substring(start, sanitized.length() - end) + "'"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+99
to
+111
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential If the matching prefix ( 🐛 Proposed fix — clamp end to avoid overlap int end = 0;
- while (end < sanitized.length() && end < value.length() && value.charAt(value.length() - end - 1) == sanitized.charAt(sanitized.length() - end - 1)) {
+ while (end < sanitized.length() - start && end < value.length() - start && value.charAt(value.length() - end - 1) == sanitized.charAt(sanitized.length() - end - 1)) {
end++;
}🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private String normalizeValue(String key, String value) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (key.equals("templateHelp")) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Allow "CLAIM.<NAME>" here | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value = value.replaceAll("CLAIM\\.<[A-Z]*>", ""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (key.equals("optimizeLookupHelp")) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Allow "<Extensions>" here | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value = value.replaceAll("<Extensions>", ""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (key.startsWith("linkExpirationFormatter.timePeriodUnit") || key.equals("error-invalid-multivalued-size")) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The problem is the "<" that appears in the choice | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value = value.replaceAll("\\{[0-9]+,choice,[^}]*}", "..."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Unescape HTML entities, as we later also unescape HTML entities in the sanitized value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value = org.apache.commons.text.StringEscapeUtils.unescapeHtml4(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (file.getAbsolutePath().contains("email")) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: move the RTL information for emails | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value = value.replaceAll(Pattern.quote(" style=\"direction: rtl;\""), ""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Pattern HTML_TAGS = Pattern.compile("<[a-z]+[^>]*>"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private boolean containsHtml(String englishValue) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return HTML_TAGS.matcher(englishValue).find(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static final Pattern ANCHOR_PATTERN = Pattern.compile("</?a[^>]*>"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Allow only those anchor tags from the source key to also appear in the target key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private String santizeAnchors(String key, String value, String englishValue) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Matcher matcher = ANCHOR_PATTERN.matcher(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Matcher englishMatcher = ANCHOR_PATTERN.matcher(englishValue); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while (matcher.find()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (englishMatcher.find() && Objects.equals(matcher.group(), englishMatcher.group())) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value = value.replaceFirst(Pattern.quote(englishMatcher.group()), ""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messages.add("Didn't find anchor tag " + matcher.group() + " in original string"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+150
to
+162
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo: Also, the Consider rebuilding the matcher after each replacement, or collecting all anchors first and validating them separately from the removal step. 🐛 Proposed fix — rename and rebuild matcher- private String santizeAnchors(String key, String value, String englishValue) {
- Matcher matcher = ANCHOR_PATTERN.matcher(value);
+ private String sanitizeAnchors(String key, String value, String englishValue) {
Matcher englishMatcher = ANCHOR_PATTERN.matcher(englishValue);
- while (matcher.find()) {
- if (englishMatcher.find() && Objects.equals(matcher.group(), englishMatcher.group())) {
+ Matcher matcher = ANCHOR_PATTERN.matcher(value);
+ while (matcher.find()) {
+ if (englishMatcher.find() && Objects.equals(matcher.group(), englishMatcher.group())) {
value = value.replaceFirst(Pattern.quote(englishMatcher.group()), "");
+ matcher = ANCHOR_PATTERN.matcher(value);
} else {
messages.add("Didn't find anchor tag " + matcher.group() + " in original string");
break;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static String getEnglishValue(String key, PropertyResourceBundle bundleEnglish) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String englishValue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| englishValue = bundleEnglish.getString(key); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (MissingResourceException ex) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| englishValue = ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return englishValue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void verifyNoDuplicateKeys(String contents) throws IOException { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BufferedReader bufferedReader = new BufferedReader(new StringReader(contents)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String line; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # | ||
| # Copyright 2025 Red Hat, Inc. and/or its affiliates | ||
| # and other contributors as indicated by the @author tags. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| # | ||
| key=Some <a href="http://malicious.com">link</a> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # | ||
| # Copyright 2025 Red Hat, Inc. and/or its affiliates | ||
| # and other contributors as indicated by the @author tags. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| # | ||
| key=Some <a href="http://example.com">link</a> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # | ||
| # Copyright 2025 Red Hat, Inc. and/or its affiliates | ||
| # and other contributors as indicated by the @author tags. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| # | ||
| key=Some <div>tag</div | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Malformed closing tag The closing 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # | ||
| # Copyright 2025 Red Hat, Inc. and/or its affiliates | ||
| # and other contributors as indicated by the @author tags. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| # | ||
| key=Some <b>HTML</b> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # | ||
| # Copyright 2025 Red Hat, Inc. and/or its affiliates | ||
| # and other contributors as indicated by the @author tags. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| # | ||
| key=No HTML |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -98,7 +98,7 @@ revoke=Atšaukti įgaliojimą | |||||
|
|
||||||
| configureAuthenticators=Sukonfigūruotas autentifikatorius | ||||||
| mobile=Mobilus | ||||||
| totpStep1=Įdiekite <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> arba Google Authenticator savo įrenginyje. Programėlės prieinamos <a href="https://play.google.com">Google Play</a> ir Apple App Store. | ||||||
| totpStep1=Installa una delle seguenti applicazioni sul tuo cellulare: | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrong language: Italian text in Lithuanian ( The Restore the Lithuanian translation, e.g.: Proposed fix-totpStep1=Installa una delle seguenti applicazioni sul tuo cellulare:
+totpStep1=Įdiekite vieną iš šių programėlių į savo mobilųjį telefoną:📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| totpStep2=Atidarykite programėlę ir nuskenuokite barkodą arba įveskite kodą. | ||||||
| totpStep3=Įveskite programėlėje sugeneruotą vieną kartą galiojantį kodą ir paspauskite Saugoti norėdami prisijungti. | ||||||
|
|
||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -109,7 +109,7 @@ revoke=收回授权 | |||||
|
|
||||||
| configureAuthenticators=配置的认证者 | ||||||
| mobile=手机 | ||||||
| totpStep1=在你的设备上安装 <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> 或者 Google Authenticator.两个应用可以从 <a href="https://play.google.com">Google Play</a> 和 Apple App Store下载。 | ||||||
| totpStep1=在您的手機上安裝以下應用程式之一: | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Traditional Chinese characters used in Simplified Chinese ( This line uses Traditional Chinese characters (機, 裝, 應用程式) whereas the entire file—and the 🌐 Proposed fix: convert to Simplified Chinese-totpStep1=在您的手機上安裝以下應用程式之一:
+totpStep1=在您的手机上安装以下应用程序之一:📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| totpStep2=打开应用扫描二维码输入验证码 | ||||||
| totpStep3=输入应用提供的一次性验证码单击保存 | ||||||
|
|
||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -6,7 +6,7 @@ emailTestBody=Dette er en test besked | |||||
| emailTestBodyHtml=<p>Dette er en test besked</p> | ||||||
| identityProviderLinkSubject=Link {0} | ||||||
| identityProviderLinkBody=Nogen vil forbinde din "{1}" konto med "{0}" kontoen som er tilknyttet brugeren {2}. Hvis dette var dig, bedes du klikke på forbindet herunder for at forbinde de to konti\n\n{3}\n\nDette link vil udløbe efter {5}.\n\nHvis du ikke vil forbinde disse konti, kan du bare ignore denne besked. Hvis du vælger at forbinde de to konti, kan du logge ind som {1} via {0}. | ||||||
| identityProviderLinkBodyHtml=<p>Nogen vil forbinde din <b>{1}</b> konto med <b>{0}</b> kontoen som er tilknyttet brugeren {2}. Hvis dette var dig, bedes du klikke på forbindet herunder for at forbinde de to konti</p><p><a href="{3}">Bekræft</a></p><p>Dette link vil udløbe efter {5}.</p><p>nHvis du ikke vil forbinde disse konti, kan du bare ignore denne besked. Hvis du vælger at forbinde de to konti, kan du logge ind som {1} via {0}. | ||||||
| identityProviderLinkBodyHtml=<p>Nogen vil forbinde din <b>{1}</b> konto med <b>{0}</b> kontoen som er tilknyttet brugeren {2}. Hvis dette var dig, bedes du klikke på forbindet herunder for at forbinde de to konti</p><p><a href="{3}">Bekræft</a></p><p>Dette link vil udløbe efter {5}.</p><p>nHvis du ikke vil forbinde disse konti, kan du bare ignore denne besked. Hvis du vælger at forbinde de to konti, kan du logge ind som {1} via {0}.</p> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix typo in localized sentence ("nHvis"). The paragraph starts with “nHvis”, which looks like an accidental leading character and will render incorrectly. Please correct to “Hvis”. ✏️ Suggested fix-...<p>nHvis du ikke vil forbinde disse konti, kan du bare ignore denne besked. Hvis du vælger at forbinde de to konti, kan du logge ind som {1} via {0}.</p>
+...<p>Hvis du ikke vil forbinde disse konti, kan du bare ignore denne besked. Hvis du vælger at forbinde de to konti, kan du logge ind som {1} via {0}.</p>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| passwordResetSubject=Gendan adgangskode | ||||||
| passwordResetBody=Nogen har forsøgt at nulstille adgangskoden til {2}. Hvis dette var dig, bedes du klikke på linket herunder for at nulstille adgangskoden.\n\n{0}\n\nDette link og kode vil udløbe efter {3}.\n\nHvis du ikke ønsker at nulstille din adgangskode, kan du se bort fra denne besked. | ||||||
| passwordResetBodyHtml=<p>Nogen har forsøgt at nulstille adgangskoden til {2}. Hvis dette var dig, bedes du klikke på linket herunder for at nulstille adgangskoden.</p><p><a href="{0}">Nulstil adgangskode</a></p><p>Dette link og kode vil udløbe efter {3}.</p><p>Hvis du ikke ønsker at nulstille din adgangskode, kan du se bort fra denne besked.</p> | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: admin-coderabbit/coderabbit-keycloak-mirror
Length of output: 116
🏁 Script executed:
Repository: admin-coderabbit/coderabbit-keycloak-mirror
Length of output: 900
🌐 Web query:
owasp-java-html-sanitizer 20240325.1 maven latest version💡 Result:
The latest Maven Central release of
com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizeris20260102.1(released Jan 3, 2026). [1][2]If you’re currently on
20240325.1, that is an older release (Mar 25, 2024). [3]Maven:
Sources
[1] https://repo1.maven.org/maven2/com/googlecode/owasp-java-html-sanitizer/owasp-java-html-sanitizer/
[2] https://mvnrepository.com/artifact/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/20260102.1
[3] https://repo1.maven.org/maven2/com/googlecode/owasp-java-html-sanitizer/owasp-java-html-sanitizer/20240325.1/
🌐 Web query:
commons-text 1.13.0 CVE vulnerabilities security💡 Result:
commons-text 1.13.0 — CVE / security status (as of Feb 2026)
org.apache.commons:commons-text1.13.0: None listed by the Apache Commons Text project. The project’s published security page only calls out CVE-2022-42889, which affects versions prior to 1.10.0—so 1.13.0 is not in the affected range. (commons.apache.org)commons-text1.13.0 has a compile dependency onorg.apache.commons:commons-lang3:3.17.0, which is flagged as vulnerable. (mvnrepository.com)ClassUtils.getClass(...)on very long inputs, affecting commons-lang3 < 3.18.0. (nvd.nist.gov)Recommended remediation
commons-lang3to 3.18.0+ in your dependency management (Maven/Gradle). (nvd.nist.gov)Sources
[1] Apache Commons Text — Security Vulnerabilities (commons.apache.org)
[2] MvnRepository — commons-text 1.13.0 dependencies (shows commons-lang3 3.17.0) (mvnrepository.com)
[3] NVD — CVE-2025-48924 (Apache Commons Lang) (nvd.nist.gov)
[4] Apache Commons Text — Project Dependencies (1.15.0 uses commons-lang3 3.20.0) (commons.apache.org)
Citations:
Critical: Outdated and vulnerable dependencies require immediate attention.
The
owasp-java-html-sanitizerversion20240325.1is outdated (current:20260102.1, released Jan 3, 2026). More critically,commons-text1.13.0introduces a transitive dependency oncommons-lang33.17.0, which is vulnerable to CVE-2025-48924 (uncontrolled recursion / DoS inClassUtils.getClass(...)).Fixes required:
commons-textto1.15.0or newer (which depends oncommons-lang3 3.20.0), OR overridecommons-lang3to3.18.0+in dependency management.owasp-java-html-sanitizerto20260102.1or later.Additionally, the
owasp-java-html-sanitizerdependency lacks an explicit<scope>compile</scope>declaration for consistency withcommons-text.🤖 Prompt for AI Agents