Escape passkeys descriptions and labels depending on the context

Closes #44387

(cherry picked from commit 39d1fa2825c6d7b1e759968babe7713b21045c07)

Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com>
This commit is contained in:
Alexander Schwartz 2026-01-05 13:50:23 +01:00 committed by GitHub
parent 797bd2221c
commit fce07e936e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 84 additions and 71 deletions

View File

@ -160,7 +160,7 @@ public class PolicyJsInjectionTest extends AbstractWebAuthnVirtualTest {
assertThat(authenticators, notNullValue());
assertThat(authenticators.getItems(), not(Matchers.empty()));
assertThat(authenticators.getLabels().get(0), is("label`;window.prompt(\"another\");"));
assertThat(authenticators.getLabels().get(0), is(originalLabel));
}
}

View File

@ -3,7 +3,7 @@
<#if section = "title">
title
<#elseif section = "header">
${kcSanitize(msg("passkey-login-title"))?no_esc}
${msg("passkey-login-title")}
<#elseif section = "form">
<form id="webauth" action="${url.loginAction}" method="post">
<input type="hidden" id="clientDataJSON" name="clientDataJSON"/>
@ -24,7 +24,7 @@
<#if shouldDisplayAuthenticators?? && shouldDisplayAuthenticators>
<#if authenticators.authenticators?size gt 1>
<p class="${properties.kcSelectAuthListItemTitle!}">${kcSanitize(msg("passkey-available-authenticators"))?no_esc}</p>
<p class="${properties.kcSelectAuthListItemTitle!}">${msg("passkey-available-authenticators")}</p>
</#if>
<div class="${properties.kcFormClass!}">
@ -36,14 +36,14 @@
<div class="${properties.kcSelectAuthListItemBodyClass!}">
<div id="kc-webauthn-authenticator-label-${authenticator?index}"
class="${properties.kcSelectAuthListItemHeadingClass!}">
${kcSanitize(msg('${authenticator.label}'))?no_esc}
${authenticator.label}
</div>
<#if authenticator.transports?? && authenticator.transports.displayNameProperties?has_content>
<div id="kc-webauthn-authenticator-transport-${authenticator?index}"
class="${properties.kcSelectAuthListItemDescriptionClass!}">
<#list authenticator.transports.displayNameProperties as nameProperty>
<span>${kcSanitize(msg('${nameProperty!}'))?no_esc}</span>
<span>${msg(nameProperty)}</span>
<#if nameProperty?has_next>
<span>, </span>
</#if>
@ -53,10 +53,10 @@
<div class="${properties.kcSelectAuthListItemDescriptionClass!}">
<span id="kc-webauthn-authenticator-createdlabel-${authenticator?index}">
${kcSanitize(msg('passkey-createdAt-label'))?no_esc}
${msg('passkey-createdAt-label')}
</span>
<span id="kc-webauthn-authenticator-created-${authenticator?index}">
${kcSanitize(authenticator.createdAt)?no_esc}
${authenticator.createdAt}
</span>
</div>
</div>
@ -92,7 +92,7 @@
</#if>
<div id="kc-form-passkey-button" class="${properties.kcFormButtonsClass!}" style="display:none">
<input id="authenticateWebAuthnButton" type="button" autofocus="autofocus"
value="${kcSanitize(msg("passkey-doAuthenticate"))}"
value="${msg("passkey-doAuthenticate")}"
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"/>
</div>
</div>
@ -100,17 +100,18 @@
</div>
<script type="module">
<#outputformat "JavaScript">
import { authenticateByWebAuthn } from "${url.resourcesPath}/js/webauthnAuthenticate.js";
import { initAuthenticate } from "${url.resourcesPath}/js/passkeysConditionalAuth.js";
const authButton = document.getElementById('authenticateWebAuthnButton');
const input = {
isUserIdentified : ${isUserIdentified},
challenge : '${challenge}',
userVerification : '${userVerification}',
rpId : '${rpId}',
challenge : ${challenge?c},
userVerification : ${userVerification?c},
rpId : ${rpId?c},
createTimeout : ${createTimeout?c},
errmsg : "${msg("webauthn-unsupported-browser-text")?no_esc}"
errmsg : ${msg("webauthn-unsupported-browser-text")?c}
};
authButton.addEventListener("click", () => {
authenticateByWebAuthn(input);
@ -118,11 +119,11 @@
const args = {
isUserIdentified : ${isUserIdentified},
challenge : '${challenge}',
userVerification : '${userVerification}',
rpId : '${rpId}',
challenge : ${challenge?c},
userVerification : ${userVerification?c},
rpId : ${rpId?c},
createTimeout : ${createTimeout?c},
errmsg : "${msg("passkey-unsupported-browser-text")?no_esc}"
errmsg : ${msg("passkey-unsupported-browser-text")?c}
};
document.addEventListener("DOMContentLoaded", (event) => initAuthenticate(args, (available) => {
@ -132,6 +133,7 @@
document.getElementById("kc-form-passkey-button").style.display = 'block';
}
}));
</#outputformat>
</script>
<#elseif section = "info">

View File

@ -16,28 +16,30 @@
</form>
</#if>
<script type="module">
<#outputformat "JavaScript">
import { authenticateByWebAuthn } from "${url.resourcesPath}/js/webauthnAuthenticate.js";
import { initAuthenticate } from "${url.resourcesPath}/js/passkeysConditionalAuth.js";
const args = {
isUserIdentified : ${isUserIdentified},
challenge : '${challenge}',
userVerification : '${userVerification}',
rpId : '${rpId}',
challenge : ${challenge?c},
userVerification : ${userVerification?c},
rpId : ${rpId?c},
createTimeout : ${createTimeout?c}
};
document.addEventListener("DOMContentLoaded", (event) => initAuthenticate({errmsg : "${msg("passkey-unsupported-browser-text")?no_esc}", ...args}));
document.addEventListener("DOMContentLoaded", (event) => initAuthenticate({errmsg : ${msg("passkey-unsupported-browser-text")?c}, ...args}));
const authButton = document.getElementById('authenticateWebAuthnButton');
if (authButton) {
authButton.addEventListener("click", (event) => {
event.preventDefault();
authenticateByWebAuthn({errmsg : "${msg("webauthn-unsupported-browser-text")?no_esc}", ...args});
authenticateByWebAuthn({errmsg : ${msg("webauthn-unsupported-browser-text")?c}, ...args});
}, { once: true });
}
</#outputformat>
</script>
<a id="authenticateWebAuthnButton" href="#" class="${properties.kcButtonSecondaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcMarginTopClass!}">
${kcSanitize(msg("webauthn-doAuthenticate"))?no_esc}
${msg("webauthn-doAuthenticate")}
</a>
</#if>
</#macro>

View File

@ -3,7 +3,7 @@
<#if section = "title">
title
<#elseif section = "header">
${kcSanitize(msg("webauthn-login-title"))?no_esc}
${msg("webauthn-login-title")}
<#elseif section = "form">
<div id="kc-form-webauthn" class="${properties.kcFormClass!}">
<form id="webauth" action="${url.loginAction}" method="post">
@ -25,7 +25,7 @@
<#if shouldDisplayAuthenticators?? && shouldDisplayAuthenticators>
<#if authenticators.authenticators?size gt 1>
<p class="${properties.kcSelectAuthListItemTitle!}">${kcSanitize(msg("webauthn-available-authenticators"))?no_esc}</p>
<p class="${properties.kcSelectAuthListItemTitle!}">${msg("webauthn-available-authenticators")}</p>
</#if>
<div class="${properties.kcFormClass!}">
@ -37,14 +37,14 @@
<div class="${properties.kcSelectAuthListItemBodyClass!}">
<div id="kc-webauthn-authenticator-label-${authenticator?index}"
class="${properties.kcSelectAuthListItemHeadingClass!}">
${kcSanitize(msg('${authenticator.label}'))?no_esc}
${authenticator.label}
</div>
<#if authenticator.transports?? && authenticator.transports.displayNameProperties?has_content>
<div id="kc-webauthn-authenticator-transport-${authenticator?index}"
class="${properties.kcSelectAuthListItemDescriptionClass!}">
<#list authenticator.transports.displayNameProperties as nameProperty>
<span>${kcSanitize(msg('${nameProperty!}'))?no_esc}</span>
<span>${msg(nameProperty)}</span>
<#if nameProperty?has_next>
<span>, </span>
</#if>
@ -54,10 +54,10 @@
<div class="${properties.kcSelectAuthListItemDescriptionClass!}">
<span id="kc-webauthn-authenticator-createdlabel-${authenticator?index}">
${kcSanitize(msg('webauthn-createdAt-label'))?no_esc}
${msg('webauthn-createdAt-label')}
</span>
<span id="kc-webauthn-authenticator-created-${authenticator?index}">
${kcSanitize(authenticator.createdAt)?no_esc}
${authenticator.createdAt}
</span>
</div>
</div>
@ -70,26 +70,28 @@
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
<input id="authenticateWebAuthnButton" type="button" autofocus="autofocus"
value="${kcSanitize(msg("webauthn-doAuthenticate"))}"
value="${msg("webauthn-doAuthenticate")}"
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"/>
</div>
</div>
</div>
<script type="module">
<#outputformat "JavaScript">
import { authenticateByWebAuthn } from "${url.resourcesPath}/js/webauthnAuthenticate.js";
const authButton = document.getElementById('authenticateWebAuthnButton');
authButton.addEventListener("click", function() {
const input = {
isUserIdentified : ${isUserIdentified},
challenge : '${challenge}',
userVerification : '${userVerification}',
rpId : '${rpId}',
challenge : ${challenge?c},
userVerification : ${userVerification?c},
rpId : ${rpId?c},
createTimeout : ${createTimeout?c},
errmsg : "${msg("webauthn-unsupported-browser-text")?no_esc}"
errmsg : ${msg("webauthn-unsupported-browser-text")?c}
};
authenticateByWebAuthn(input);
}, { once: true });
</#outputformat>
</script>
<#elseif section = "info">

View File

@ -6,7 +6,7 @@
title
<#elseif section = "header">
<span class="${properties.kcWebAuthnKeyIcon!}"></span>
${kcSanitize(msg("webauthn-registration-title"))?no_esc}
${msg("webauthn-registration-title")}
<#elseif section = "form">
<form id="register" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
@ -22,28 +22,30 @@
</form>
<script type="module">
<#outputformat "JavaScript">
import { registerByWebAuthn } from "${url.resourcesPath}/js/webauthnRegister.js";
const registerButton = document.getElementById('registerWebAuthn');
registerButton.addEventListener("click", function() {
const input = {
challenge : '${challenge}',
userid : '${userid}',
username : '${username}',
challenge : ${challenge?c},
userid : ${userid?c},
username : ${username?c},
signatureAlgorithms : [<#list signatureAlgorithms as sigAlg>${sigAlg?c},</#list>],
rpEntityName : '${rpEntityName}',
rpId : '${rpId}',
attestationConveyancePreference : '${attestationConveyancePreference}',
authenticatorAttachment : '${authenticatorAttachment}',
requireResidentKey : '${requireResidentKey}',
userVerificationRequirement : '${userVerificationRequirement}',
rpEntityName : ${rpEntityName?c},
rpId : ${rpId?c},
attestationConveyancePreference : ${attestationConveyancePreference?c},
authenticatorAttachment : ${authenticatorAttachment?c},
requireResidentKey : ${requireResidentKey?c},
userVerificationRequirement : ${userVerificationRequirement?c},
createTimeout : ${createTimeout?c},
excludeCredentialIds : '${excludeCredentialIds}',
initLabel : "${msg("webauthn-registration-init-label")?no_esc}",
initLabelPrompt : "${msg("webauthn-registration-init-label-prompt")?no_esc}",
errmsg : "${msg("webauthn-unsupported-browser-text")?no_esc}"
excludeCredentialIds : ${excludeCredentialIds?c},
initLabel : ${msg("webauthn-registration-init-label")?c},
initLabelPrompt : ${msg("webauthn-registration-init-label-prompt")?c},
errmsg : ${msg("webauthn-unsupported-browser-text")?c}
};
registerByWebAuthn(input);
}, { once: true });
</#outputformat>
</script>
<input type="submit"

View File

@ -6,7 +6,7 @@
<#if section = "title">
title
<#elseif section = "header">
${kcSanitize(msg("webauthn-login-title"))?no_esc}
${msg("webauthn-login-title")}
<#elseif section = "form">
<div id="kc-form-webauthn" class="${properties.kcFormClass!}" >
<form id="webauth" action="${url.loginAction}" method="post" hidden="hidden">
@ -27,7 +27,7 @@
<#if shouldDisplayAuthenticators?? && shouldDisplayAuthenticators>
<#if authenticators.authenticators?size gt 1>
<p class="${properties.kcSelectAuthListItemTitle!}">${kcSanitize(msg("webauthn-available-authenticators"))?no_esc}</p>
<p class="${properties.kcSelectAuthListItemTitle!}">${msg("webauthn-available-authenticators")}</p>
</#if>
<ul class="${properties.kcSelectAuthListClass!}" role="list">
@ -70,13 +70,13 @@
<div class="${properties.kcSelectAuthListItemBodyClass!}">
<div id="kc-webauthn-authenticator-label-${authenticator?index}"
class="${properties.kcSelectAuthListItemHeadingClass!}">
${kcSanitize(msg('${authenticator.label}'))?no_esc}
${authenticator.label}
</div>
<#if authenticator.transports?? && authenticator.transports.displayNameProperties?has_content>
<div id="kc-webauthn-authenticator-transport-${authenticator?index}">
<#list authenticator.transports.displayNameProperties as nameProperty>
<span>${kcSanitize(msg('${nameProperty!}'))?no_esc}</span>
<span>${msg(nameProperty)}</span>
<#if nameProperty?has_next>
<span>, </span>
</#if>
@ -85,12 +85,13 @@
</#if>
<span id="kc-webauthn-authenticator-createdlabel-${authenticator?index}">
<i>${kcSanitize(msg('webauthn-createdAt-label'))?no_esc}</i>
<i>${msg('webauthn-createdAt-label')}</i>
</span>
<span id="kc-webauthn-authenticator-created-${authenticator?index}">
<i>${kcSanitize(authenticator.createdAt)?no_esc}</i>
<i>${authenticator.createdAt}</i>
</span>
</div>
</div>
<div class="${properties.kcSelectAuthListItemFillClass!}"></div>
</div>
</li>
@ -104,19 +105,21 @@
</@buttons.actionGroup>
</div>
<script type="module">
<#outputformat "JavaScript">
import { authenticateByWebAuthn } from "${url.resourcesPath}/js/webauthnAuthenticate.js";
const authButton = document.getElementById('authenticateWebAuthnButton');
authButton.addEventListener("click", function() {
const input = {
isUserIdentified : ${isUserIdentified},
challenge : '${challenge}',
userVerification : '${userVerification}',
rpId : '${rpId}',
challenge : ${challenge?c},
userVerification : ${userVerification?c},
rpId : ${rpId?c},
createTimeout : ${createTimeout?c},
errmsg : "${msg("webauthn-unsupported-browser-text")?no_esc}"
errmsg : ${msg("webauthn-unsupported-browser-text")?c}
};
authenticateByWebAuthn(input);
}, { once: true });
</#outputformat>
</script>
<#elseif section = "info">

View File

@ -7,7 +7,7 @@
title
<#elseif section = "header">
<span class="${properties.kcWebAuthnKeyIcon!}"></span>
${kcSanitize(msg("webauthn-registration-title"))?no_esc}
${msg("webauthn-registration-title")}
<#elseif section = "form">
<div class="${properties.kcFormClass!}">
<form id="register" action="${url.loginAction}" method="post" >
@ -23,28 +23,30 @@
</form>
<script type="module">
<#outputformat "JavaScript">
import { registerByWebAuthn } from "${url.resourcesPath}/js/webauthnRegister.js";
const registerButton = document.getElementById('registerWebAuthn');
registerButton.addEventListener("click", function() {
const input = {
challenge : '${challenge}',
userid : '${userid}',
username : '${username}',
challenge : ${challenge?c},
userid : ${userid?c},
username : ${username?c},
signatureAlgorithms : [<#list signatureAlgorithms as sigAlg>${sigAlg?c},</#list>],
rpEntityName : '${rpEntityName}',
rpId : '${rpId}',
attestationConveyancePreference : '${attestationConveyancePreference}',
authenticatorAttachment : '${authenticatorAttachment}',
requireResidentKey : '${requireResidentKey}',
userVerificationRequirement : '${userVerificationRequirement}',
rpEntityName : ${rpEntityName?c},
rpId : ${rpId?c},
attestationConveyancePreference : ${attestationConveyancePreference?c},
authenticatorAttachment : ${authenticatorAttachment?c},
requireResidentKey : ${requireResidentKey?c},
userVerificationRequirement : ${userVerificationRequirement?c},
createTimeout : ${createTimeout?c},
excludeCredentialIds : '${excludeCredentialIds}',
initLabel : "${msg("webauthn-registration-init-label")?no_esc}",
initLabelPrompt : "${msg("webauthn-registration-init-label-prompt")?no_esc}",
errmsg : "${msg("webauthn-unsupported-browser-text")?no_esc}"
excludeCredentialIds : ${excludeCredentialIds?c},
initLabel : ${msg("webauthn-registration-init-label")?c},
initLabelPrompt : ${msg("webauthn-registration-init-label-prompt")?c},
errmsg : ${msg("webauthn-unsupported-browser-text")?c}
};
registerByWebAuthn(input);
}, { once: true });
</#outputformat>
</script>
<@buttons.actionGroup horizontal=true>