VYPR
Moderate severityNVD Advisory· Published Jul 31, 2024· Updated Aug 1, 2024

Zitadel improperly sanitizes HTML in emails and Console UI

CVE-2024-41953

Description

Zitadel is an open source identity management system. ZITADEL uses HTML for emails and renders certain information such as usernames dynamically. That information can be entered by users or administrators. Due to a missing output sanitization, these emails could include malicious code. This may potentially lead to a threat where an attacker, without privileges, could send out altered notifications that are part of the registration processes. An attacker could create a malicious link, where the injected code would be rendered as part of the email. On the user's detail page, the username was also not sanitized and would also render HTML, giving an attacker the same vulnerability. While it was possible to inject HTML including javascript, the execution of such scripts would be prevented by most email clients and the Content Security Policy in Console UI. This vulnerability is fixed in 2.58.1, 2.57.1, 2.56.2, 2.55.5, 2.54.8 2.53.9, and 2.52.3.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/zitadel/zitadelGo
>= 1.80.1, < 2.52.32.52.3
github.com/zitadel/zitadelGo
>= 2.53.0, < 2.53.92.53.9
github.com/zitadel/zitadelGo
>= 2.54.0, < 2.54.82.54.8
github.com/zitadel/zitadelGo
>= 2.55.0, < 2.55.52.55.5
github.com/zitadel/zitadelGo
>= 2.56.0, < 2.56.22.56.2
github.com/zitadel/zitadelGo
>= 2.57.0, < 2.57.12.57.1
github.com/zitadel/zitadelGo
>= 2.58.0, < 2.58.12.58.1
github.com/zitadel/zitadelGo
< 0.0.0-20240731122110-189505c80fa60.0.0-20240731122110-189505c80fa6
github.com/zitadel/zitadelGo
>= 0.0.0, < 1.80.0-v2.20.0.20240731122110-189505c80fa61.80.0-v2.20.0.20240731122110-189505c80fa6

Affected products

1

Patches

7
4b59cac67bb8

fix: sanitize output for email (#8373)

https://github.com/zitadel/zitadelLivio SpringJul 31, 2024via ghsa
19 files changed · +39 23
  • console/src/app/modules/top-view/top-view.component.html+1 1 modified
    @@ -16,7 +16,7 @@ <h2 data-e2e="top-view-title" class="cnsl-title">{{ title }}</h2>
                 ></div>
               </div>
               <div class="cnsl-doc-row">
    -            <span class="cnsl-type" [innerHtml]="sub"></span>
    +            <span class="cnsl-type">{{ sub }}</span>
                 <a *ngIf="docLink" mat-icon-button [href]="docLink" rel="noreferrer" target="_blank">
                   <mat-icon class="icon">info_outline</mat-icon>
                 </a>
    
  • console/src/app/modules/top-view/top-view.component.scss+1 1 modified
    @@ -82,7 +82,7 @@
     
           .cnsl-type {
             font-size: 14px;
    -        margin-top: 2rem;
    +        margin-top: 1.5rem;
             margin-bottom: 1rem;
           }
     
    
  • console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html+1 3 modified
    @@ -2,9 +2,7 @@
       title="{{ project?.projectName }}"
       [hasActions]="false"
       docLink="https://zitadel.com/docs/guides/manage/console/projects#what-is-a-granted-project"
    -  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate }} {{ 'ACTIONS.OF' | translate }} <strong>{{
    -    project?.projectOwnerName
    -  }}</strong>"
    +  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate: { name: project?.projectOwnerName } }}"
       [isActive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE"
       [isInactive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE"
       stateTooltip="{{ 'ORG.STATE.' + project?.state | translate }}"
    
  • console/src/assets/i18n/bg.json+1 1 modified
    @@ -1765,7 +1765,7 @@
           "TYPE": {
             "OWNED": "Притежавани проекти",
             "OWNED_SINGULAR": "Собствен проект",
    -        "GRANTED_SINGULAR": "Приет проект"
    +        "GRANTED_SINGULAR": "Отпуснат проект на {{name}}"
           },
           "PRIVATELABEL": {
             "0": {
    
  • console/src/assets/i18n/cs.json+1 1 modified
    @@ -1773,7 +1773,7 @@
           "TYPE": {
             "OWNED": "Vlastní projekty",
             "OWNED_SINGULAR": "Vlastní projekt",
    -        "GRANTED_SINGULAR": "Přidělený projekt"
    +        "GRANTED_SINGULAR": "Projekt přidělený {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Nastavení brandingu",
    
  • console/src/assets/i18n/de.json+1 1 modified
    @@ -1771,7 +1771,7 @@
           "TYPE": {
             "OWNED": "Eigene Projekte",
             "OWNED_SINGULAR": "Eigenes Projekt",
    -        "GRANTED_SINGULAR": "Berechtigtes Projekt"
    +        "GRANTED_SINGULAR": "Berechtigtes Projekt von {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Verhalten",
    
  • console/src/assets/i18n/en.json+1 1 modified
    @@ -1772,7 +1772,7 @@
           "TYPE": {
             "OWNED": "Owned Projects",
             "OWNED_SINGULAR": "Owned Project",
    -        "GRANTED_SINGULAR": "Granted Project"
    +        "GRANTED_SINGULAR": "Granted Project of {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Setting",
    
  • console/src/assets/i18n/es.json+1 1 modified
    @@ -1773,7 +1773,7 @@
           "TYPE": {
             "OWNED": "Proyectos propios",
             "OWNED_SINGULAR": "Proyecto propio",
    -        "GRANTED_SINGULAR": "Proyecto concedido"
    +        "GRANTED_SINGULAR": "Proyecto asignado {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ajustes de imagen de marca",
    
  • console/src/assets/i18n/fr.json+2 2 modified
    @@ -1770,8 +1770,8 @@
           "ZITADELPROJECT": "Ceci appartient au projet ZITADEL. Attention",
           "TYPE": {
             "OWNED": "Projets possédés",
    -        "OWNED_SINGULAR": "Projet propre",
    -        "GRANTED_SINGULAR": "Projet concédé"
    +        "OWNED_SINGULAR": "Projet possédé",
    +        "GRANTED_SINGULAR": "Projet attribué à {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Image de marque",
    
  • console/src/assets/i18n/it.json+1 1 modified
    @@ -1771,7 +1771,7 @@
           "TYPE": {
             "OWNED": "Progetti proprietari",
             "OWNED_SINGULAR": "Progetto proprietario",
    -        "GRANTED_SINGULAR": "Progetto delegato"
    +        "GRANTED_SINGULAR": "Progetto concesso di {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Impostazione branding",
    
  • console/src/assets/i18n/ja.json+1 1 modified
    @@ -1767,7 +1767,7 @@
           "TYPE": {
             "OWNED": "所有プロジェクト",
             "OWNED_SINGULAR": "所有プロジェクト",
    -        "GRANTED_SINGULAR": "グラントされたプロジェクト"
    +        "GRANTED_SINGULAR": "{{name}}に付与されたプロジェクト"
           },
           "PRIVATELABEL": {
             "TITLE": "ブランディング設定",
    
  • console/src/assets/i18n/mk.json+1 1 modified
    @@ -1773,7 +1773,7 @@
           "TYPE": {
             "OWNED": "Сопствени проекти",
             "OWNED_SINGULAR": "Сопствен проект",
    -        "GRANTED_SINGULAR": "Доделен проект"
    +        "GRANTED_SINGULAR": "Проект доделен на {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Подесувања за брендирање",
    
  • console/src/assets/i18n/nl.json+1 1 modified
    @@ -1772,7 +1772,7 @@
           "TYPE": {
             "OWNED": "Eigen Projecten",
             "OWNED_SINGULAR": "Eigen Project",
    -        "GRANTED_SINGULAR": "Verleend Project"
    +        "GRANTED_SINGULAR": "Project toegewezen {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Instelling",
    
  • console/src/assets/i18n/pl.json+1 1 modified
    @@ -1771,7 +1771,7 @@
           "TYPE": {
             "OWNED": "Własne Projekty",
             "OWNED_SINGULAR": "Własny Projekt",
    -        "GRANTED_SINGULAR": "Udzielony Projekt"
    +        "GRANTED_SINGULAR": "Projekt przydzielony {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ustawienia marka",
    
  • console/src/assets/i18n/pt.json+1 1 modified
    @@ -1771,7 +1771,7 @@
           "TYPE": {
             "OWNED": "Projetos Próprios",
             "OWNED_SINGULAR": "Projeto Próprio",
    -        "GRANTED_SINGULAR": "Projeto Concedido"
    +        "GRANTED_SINGULAR": "Projeto atribuído a {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Configuração de Marca",
    
  • console/src/assets/i18n/ru.json+1 2 modified
    @@ -1853,9 +1853,8 @@
           "ZITADELPROJECT": "Это принадлежит проекту ZITADEL. Осторожно: Если вы внесёте изменения, ZITADEL может вести себя не так, как предполагалось.",
           "TYPE": {
             "OWNED": "Собственные проекты",
    -        "GRANTED": "Проекты доступа",
             "OWNED_SINGULAR": "Собственный проект",
    -        "GRANTED_SINGULAR": "Допуск проекта"
    +        "GRANTED_SINGULAR": "Проект, предоставленный {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Настройка брендинга",
    
  • console/src/assets/i18n/zh.json+1 1 modified
    @@ -1770,7 +1770,7 @@
           "TYPE": {
             "OWNED": "拥有的项目",
             "OWNED_SINGULAR": "拥有项目",
    -        "GRANTED_SINGULAR": "被授予的项目"
    +        "GRANTED_SINGULAR": "授予{{name}}的项目"
           },
           "PRIVATELABEL": {
             "TITLE": "品牌标识设置",
    
  • internal/notification/templates/templateData.go+1 2 modified
    @@ -2,7 +2,6 @@ package templates
     
     import (
     	"fmt"
    -	"html"
     
     	"github.com/zitadel/zitadel/internal/domain"
     	"github.com/zitadel/zitadel/internal/i18n"
    @@ -40,7 +39,7 @@ func (data *TemplateData) Translate(translator *i18n.Translator, msgType string,
     	data.PreHeader = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessagePreHeader), args, langs...)
     	data.Subject = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageSubject), args, langs...)
     	data.Greeting = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageGreeting), args, langs...)
    -	data.Text = html.UnescapeString(translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...))
    +	data.Text = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...)
     	data.ButtonText = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageButtonText), args, langs...)
     	// Footer text is neither included in i18n files nor defaults.yaml
     	footerText := fmt.Sprintf("%s.%s", msgType, domain.MessageFooterText)
    
  • internal/notification/types/notification.go+20 0 modified
    @@ -2,7 +2,9 @@ package types
     
     import (
     	"context"
    +	"html"
     
    +	"github.com/zitadel/zitadel/internal/database"
     	"github.com/zitadel/zitadel/internal/eventstore"
     	"github.com/zitadel/zitadel/internal/i18n"
     	"github.com/zitadel/zitadel/internal/notification/channels/smtp"
    @@ -42,6 +44,7 @@ func SendEmail(
     		allowUnverifiedNotificationChannel bool,
     	) error {
     		args = mapNotifyUserToArgs(user, args)
    +		sanitizeArgsForHTML(args)
     		data := GetTemplateData(ctx, translator, args, url, messageType, user.PreferredLanguage.String(), colors)
     		template, err := templates.GetParsedTemplate(mailhtml, data)
     		if err != nil {
    @@ -59,6 +62,23 @@ func SendEmail(
     	}
     }
     
    +func sanitizeArgsForHTML(args map[string]any) {
    +	for key, arg := range args {
    +		switch a := arg.(type) {
    +		case string:
    +			args[key] = html.EscapeString(a)
    +		case []string:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		case database.TextArray[string]:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		}
    +	}
    +}
    +
     func SendSMSTwilio(
     	ctx context.Context,
     	channels ChannelChains,
    
c1a3fc72dde1

fix: sanitize output for email (#8373)

https://github.com/zitadel/zitadelLivio SpringJul 31, 2024via ghsa
20 files changed · +39 23
  • console/src/app/modules/top-view/top-view.component.html+1 1 modified
    @@ -16,7 +16,7 @@ <h2 data-e2e="top-view-title" class="cnsl-title">{{ title }}</h2>
                 ></div>
               </div>
               <div class="cnsl-doc-row">
    -            <span class="cnsl-type" [innerHtml]="sub"></span>
    +            <span class="cnsl-type">{{ sub }}</span>
                 <a *ngIf="docLink" mat-icon-button [href]="docLink" rel="noreferrer" target="_blank">
                   <mat-icon class="icon">info_outline</mat-icon>
                 </a>
    
  • console/src/app/modules/top-view/top-view.component.scss+1 1 modified
    @@ -82,7 +82,7 @@
     
           .cnsl-type {
             font-size: 14px;
    -        margin-top: 2rem;
    +        margin-top: 1.5rem;
             margin-bottom: 1rem;
           }
     
    
  • console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html+1 3 modified
    @@ -2,9 +2,7 @@
       title="{{ project?.projectName }}"
       [hasActions]="false"
       docLink="https://zitadel.com/docs/guides/manage/console/projects#what-is-a-granted-project"
    -  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate }} {{ 'ACTIONS.OF' | translate }} <strong>{{
    -    project?.projectOwnerName
    -  }}</strong>"
    +  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate: { name: project?.projectOwnerName } }}"
       [isActive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE"
       [isInactive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE"
       stateTooltip="{{ 'ORG.STATE.' + project?.state | translate }}"
    
  • console/src/assets/i18n/bg.json+1 1 modified
    @@ -1780,7 +1780,7 @@
           "TYPE": {
             "OWNED": "Притежавани проекти",
             "OWNED_SINGULAR": "Собствен проект",
    -        "GRANTED_SINGULAR": "Приет проект"
    +        "GRANTED_SINGULAR": "Отпуснат проект на {{name}}"
           },
           "PRIVATELABEL": {
             "0": {
    
  • console/src/assets/i18n/cs.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Vlastní projekty",
             "OWNED_SINGULAR": "Vlastní projekt",
    -        "GRANTED_SINGULAR": "Přidělený projekt"
    +        "GRANTED_SINGULAR": "Projekt přidělený {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Nastavení brandingu",
    
  • console/src/assets/i18n/de.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "Eigene Projekte",
             "OWNED_SINGULAR": "Eigenes Projekt",
    -        "GRANTED_SINGULAR": "Berechtigtes Projekt"
    +        "GRANTED_SINGULAR": "Berechtigtes Projekt von {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Verhalten",
    
  • console/src/assets/i18n/en.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "Owned Projects",
             "OWNED_SINGULAR": "Owned Project",
    -        "GRANTED_SINGULAR": "Granted Project"
    +        "GRANTED_SINGULAR": "Granted Project of {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Setting",
    
  • console/src/assets/i18n/es.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Proyectos propios",
             "OWNED_SINGULAR": "Proyecto propio",
    -        "GRANTED_SINGULAR": "Proyecto concedido"
    +        "GRANTED_SINGULAR": "Proyecto asignado {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ajustes de imagen de marca",
    
  • console/src/assets/i18n/fr.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "Projets possédés",
             "OWNED_SINGULAR": "Projet possédé",
    -        "GRANTED_SINGULAR": "Projet octroyé"
    +        "GRANTED_SINGULAR": "Projet attribué à {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Image de marque",
    
  • console/src/assets/i18n/it.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "Progetti proprietari",
             "OWNED_SINGULAR": "Progetto proprietario",
    -        "GRANTED_SINGULAR": "Progetto delegato"
    +        "GRANTED_SINGULAR": "Progetto concesso di {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Impostazione branding",
    
  • console/src/assets/i18n/ja.json+1 1 modified
    @@ -1777,7 +1777,7 @@
           "TYPE": {
             "OWNED": "所有プロジェクト",
             "OWNED_SINGULAR": "所有プロジェクト",
    -        "GRANTED_SINGULAR": "グラントされたプロジェクト"
    +        "GRANTED_SINGULAR": "{{name}}に付与されたプロジェクト"
           },
           "PRIVATELABEL": {
             "TITLE": "ブランディング設定",
    
  • console/src/assets/i18n/mk.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Сопствени проекти",
             "OWNED_SINGULAR": "Сопствен проект",
    -        "GRANTED_SINGULAR": "Доделен проект"
    +        "GRANTED_SINGULAR": "Проект доделен на {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Подесувања за брендирање",
    
  • console/src/assets/i18n/nl.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "Eigen Projecten",
             "OWNED_SINGULAR": "Eigen Project",
    -        "GRANTED_SINGULAR": "Verleend Project"
    +        "GRANTED_SINGULAR": "Project toegewezen {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Instelling",
    
  • console/src/assets/i18n/pl.json+1 1 modified
    @@ -1780,7 +1780,7 @@
           "TYPE": {
             "OWNED": "Własne Projekty",
             "OWNED_SINGULAR": "Własny Projekt",
    -        "GRANTED_SINGULAR": "Udzielony Projekt"
    +        "GRANTED_SINGULAR": "Projekt przydzielony {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ustawienia marka",
    
  • console/src/assets/i18n/pt.json+1 1 modified
    @@ -1780,7 +1780,7 @@
           "TYPE": {
             "OWNED": "Projetos Próprios",
             "OWNED_SINGULAR": "Projeto Próprio",
    -        "GRANTED_SINGULAR": "Projeto Concedido"
    +        "GRANTED_SINGULAR": "Projeto atribuído a {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Configuração de Marca",
    
  • console/src/assets/i18n/ru.json+1 2 modified
    @@ -1862,9 +1862,8 @@
           "ZITADELPROJECT": "Это принадлежит проекту ZITADEL. Осторожно: Если вы внесёте изменения, ZITADEL может вести себя не так, как предполагалось.",
           "TYPE": {
             "OWNED": "Собственные проекты",
    -        "GRANTED": "Проекты доступа",
             "OWNED_SINGULAR": "Собственный проект",
    -        "GRANTED_SINGULAR": "Допуск проекта"
    +        "GRANTED_SINGULAR": "Проект, предоставленный {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Настройка брендинга",
    
  • console/src/assets/i18n/sv.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "Ägda Projekt",
             "OWNED_SINGULAR": "Ägt Projekt",
    -        "GRANTED_SINGULAR": "Beviljat Projekt"
    +        "GRANTED_SINGULAR": "Projekt tilldelat {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Varumärkesinställning",
    
  • console/src/assets/i18n/zh.json+1 1 modified
    @@ -1780,7 +1780,7 @@
           "TYPE": {
             "OWNED": "拥有的项目",
             "OWNED_SINGULAR": "拥有项目",
    -        "GRANTED_SINGULAR": "被授予的项目"
    +        "GRANTED_SINGULAR": "授予{{name}}的项目"
           },
           "PRIVATELABEL": {
             "TITLE": "品牌标识设置",
    
  • internal/notification/templates/templateData.go+1 2 modified
    @@ -2,7 +2,6 @@ package templates
     
     import (
     	"fmt"
    -	"html"
     
     	"github.com/zitadel/zitadel/internal/domain"
     	"github.com/zitadel/zitadel/internal/i18n"
    @@ -40,7 +39,7 @@ func (data *TemplateData) Translate(translator *i18n.Translator, msgType string,
     	data.PreHeader = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessagePreHeader), args, langs...)
     	data.Subject = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageSubject), args, langs...)
     	data.Greeting = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageGreeting), args, langs...)
    -	data.Text = html.UnescapeString(translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...))
    +	data.Text = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...)
     	data.ButtonText = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageButtonText), args, langs...)
     	// Footer text is neither included in i18n files nor defaults.yaml
     	footerText := fmt.Sprintf("%s.%s", msgType, domain.MessageFooterText)
    
  • internal/notification/types/notification.go+20 0 modified
    @@ -2,7 +2,9 @@ package types
     
     import (
     	"context"
    +	"html"
     
    +	"github.com/zitadel/zitadel/internal/database"
     	"github.com/zitadel/zitadel/internal/eventstore"
     	"github.com/zitadel/zitadel/internal/i18n"
     	"github.com/zitadel/zitadel/internal/notification/channels/smtp"
    @@ -42,6 +44,7 @@ func SendEmail(
     		allowUnverifiedNotificationChannel bool,
     	) error {
     		args = mapNotifyUserToArgs(user, args)
    +		sanitizeArgsForHTML(args)
     		data := GetTemplateData(ctx, translator, args, url, messageType, user.PreferredLanguage.String(), colors)
     		template, err := templates.GetParsedTemplate(mailhtml, data)
     		if err != nil {
    @@ -59,6 +62,23 @@ func SendEmail(
     	}
     }
     
    +func sanitizeArgsForHTML(args map[string]any) {
    +	for key, arg := range args {
    +		switch a := arg.(type) {
    +		case string:
    +			args[key] = html.EscapeString(a)
    +		case []string:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		case database.TextArray[string]:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		}
    +	}
    +}
    +
     func SendSMSTwilio(
     	ctx context.Context,
     	channels ChannelChains,
    
c353f82f89c6

fix: sanitize output for email (#8373)

https://github.com/zitadel/zitadelLivio SpringJul 31, 2024via ghsa
19 files changed · +38 22
  • console/src/app/modules/top-view/top-view.component.html+1 1 modified
    @@ -16,7 +16,7 @@ <h2 data-e2e="top-view-title" class="cnsl-title">{{ title }}</h2>
                 ></div>
               </div>
               <div class="cnsl-doc-row">
    -            <span class="cnsl-type" [innerHtml]="sub"></span>
    +            <span class="cnsl-type">{{ sub }}</span>
                 <a *ngIf="docLink" mat-icon-button [href]="docLink" rel="noreferrer" target="_blank">
                   <mat-icon class="icon">info_outline</mat-icon>
                 </a>
    
  • console/src/app/modules/top-view/top-view.component.scss+1 1 modified
    @@ -82,7 +82,7 @@
     
           .cnsl-type {
             font-size: 14px;
    -        margin-top: 2rem;
    +        margin-top: 1.5rem;
             margin-bottom: 1rem;
           }
     
    
  • console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html+1 3 modified
    @@ -2,9 +2,7 @@
       title="{{ project?.projectName }}"
       [hasActions]="false"
       docLink="https://zitadel.com/docs/guides/manage/console/projects#what-is-a-granted-project"
    -  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate }} {{ 'ACTIONS.OF' | translate }} <strong>{{
    -    project?.projectOwnerName
    -  }}</strong>"
    +  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate: { name: project?.projectOwnerName } }}"
       [isActive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE"
       [isInactive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE"
       stateTooltip="{{ 'ORG.STATE.' + project?.state | translate }}"
    
  • console/src/assets/i18n/bg.json+1 1 modified
    @@ -1770,7 +1770,7 @@
           "TYPE": {
             "OWNED": "Притежавани проекти",
             "OWNED_SINGULAR": "Собствен проект",
    -        "GRANTED_SINGULAR": "Приет проект"
    +        "GRANTED_SINGULAR": "Отпуснат проект на {{name}}"
           },
           "PRIVATELABEL": {
             "0": {
    
  • console/src/assets/i18n/cs.json+1 1 modified
    @@ -1778,7 +1778,7 @@
           "TYPE": {
             "OWNED": "Vlastní projekty",
             "OWNED_SINGULAR": "Vlastní projekt",
    -        "GRANTED_SINGULAR": "Přidělený projekt"
    +        "GRANTED_SINGULAR": "Projekt přidělený {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Nastavení brandingu",
    
  • console/src/assets/i18n/de.json+1 1 modified
    @@ -1776,7 +1776,7 @@
           "TYPE": {
             "OWNED": "Eigene Projekte",
             "OWNED_SINGULAR": "Eigenes Projekt",
    -        "GRANTED_SINGULAR": "Berechtigtes Projekt"
    +        "GRANTED_SINGULAR": "Berechtigtes Projekt von {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Verhalten",
    
  • console/src/assets/i18n/en.json+1 1 modified
    @@ -1777,7 +1777,7 @@
           "TYPE": {
             "OWNED": "Owned Projects",
             "OWNED_SINGULAR": "Owned Project",
    -        "GRANTED_SINGULAR": "Granted Project"
    +        "GRANTED_SINGULAR": "Granted Project of {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Setting",
    
  • console/src/assets/i18n/es.json+1 1 modified
    @@ -1778,7 +1778,7 @@
           "TYPE": {
             "OWNED": "Proyectos propios",
             "OWNED_SINGULAR": "Proyecto propio",
    -        "GRANTED_SINGULAR": "Proyecto concedido"
    +        "GRANTED_SINGULAR": "Proyecto asignado {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ajustes de imagen de marca",
    
  • console/src/assets/i18n/fr.json+1 1 modified
    @@ -1776,7 +1776,7 @@
           "TYPE": {
             "OWNED": "Projets possédés",
             "OWNED_SINGULAR": "Projet possédé",
    -        "GRANTED_SINGULAR": "Projet octroyé"
    +        "GRANTED_SINGULAR": "Projet attribué à {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Image de marque",
    
  • console/src/assets/i18n/it.json+1 1 modified
    @@ -1776,7 +1776,7 @@
           "TYPE": {
             "OWNED": "Progetti proprietari",
             "OWNED_SINGULAR": "Progetto proprietario",
    -        "GRANTED_SINGULAR": "Progetto delegato"
    +        "GRANTED_SINGULAR": "Progetto concesso di {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Impostazione branding",
    
  • console/src/assets/i18n/ja.json+1 1 modified
    @@ -1773,7 +1773,7 @@
           "TYPE": {
             "OWNED": "所有プロジェクト",
             "OWNED_SINGULAR": "所有プロジェクト",
    -        "GRANTED_SINGULAR": "グラントされたプロジェクト"
    +        "GRANTED_SINGULAR": "{{name}}に付与されたプロジェクト"
           },
           "PRIVATELABEL": {
             "TITLE": "ブランディング設定",
    
  • console/src/assets/i18n/mk.json+1 1 modified
    @@ -1778,7 +1778,7 @@
           "TYPE": {
             "OWNED": "Сопствени проекти",
             "OWNED_SINGULAR": "Сопствен проект",
    -        "GRANTED_SINGULAR": "Доделен проект"
    +        "GRANTED_SINGULAR": "Проект доделен на {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Подесувања за брендирање",
    
  • console/src/assets/i18n/nl.json+1 1 modified
    @@ -1777,7 +1777,7 @@
           "TYPE": {
             "OWNED": "Eigen Projecten",
             "OWNED_SINGULAR": "Eigen Project",
    -        "GRANTED_SINGULAR": "Verleend Project"
    +        "GRANTED_SINGULAR": "Project toegewezen {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Instelling",
    
  • console/src/assets/i18n/pl.json+1 1 modified
    @@ -1776,7 +1776,7 @@
           "TYPE": {
             "OWNED": "Własne Projekty",
             "OWNED_SINGULAR": "Własny Projekt",
    -        "GRANTED_SINGULAR": "Udzielony Projekt"
    +        "GRANTED_SINGULAR": "Projekt przydzielony {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ustawienia marka",
    
  • console/src/assets/i18n/pt.json+1 1 modified
    @@ -1776,7 +1776,7 @@
           "TYPE": {
             "OWNED": "Projetos Próprios",
             "OWNED_SINGULAR": "Projeto Próprio",
    -        "GRANTED_SINGULAR": "Projeto Concedido"
    +        "GRANTED_SINGULAR": "Projeto atribuído a {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Configuração de Marca",
    
  • console/src/assets/i18n/ru.json+1 2 modified
    @@ -1858,9 +1858,8 @@
           "ZITADELPROJECT": "Это принадлежит проекту ZITADEL. Осторожно: Если вы внесёте изменения, ZITADEL может вести себя не так, как предполагалось.",
           "TYPE": {
             "OWNED": "Собственные проекты",
    -        "GRANTED": "Проекты доступа",
             "OWNED_SINGULAR": "Собственный проект",
    -        "GRANTED_SINGULAR": "Допуск проекта"
    +        "GRANTED_SINGULAR": "Проект, предоставленный {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Настройка брендинга",
    
  • console/src/assets/i18n/zh.json+1 1 modified
    @@ -1775,7 +1775,7 @@
           "TYPE": {
             "OWNED": "拥有的项目",
             "OWNED_SINGULAR": "拥有项目",
    -        "GRANTED_SINGULAR": "被授予的项目"
    +        "GRANTED_SINGULAR": "授予{{name}}的项目"
           },
           "PRIVATELABEL": {
             "TITLE": "品牌标识设置",
    
  • internal/notification/templates/templateData.go+1 2 modified
    @@ -2,7 +2,6 @@ package templates
     
     import (
     	"fmt"
    -	"html"
     
     	"github.com/zitadel/zitadel/internal/domain"
     	"github.com/zitadel/zitadel/internal/i18n"
    @@ -40,7 +39,7 @@ func (data *TemplateData) Translate(translator *i18n.Translator, msgType string,
     	data.PreHeader = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessagePreHeader), args, langs...)
     	data.Subject = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageSubject), args, langs...)
     	data.Greeting = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageGreeting), args, langs...)
    -	data.Text = html.UnescapeString(translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...))
    +	data.Text = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...)
     	data.ButtonText = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageButtonText), args, langs...)
     	// Footer text is neither included in i18n files nor defaults.yaml
     	footerText := fmt.Sprintf("%s.%s", msgType, domain.MessageFooterText)
    
  • internal/notification/types/notification.go+20 0 modified
    @@ -2,7 +2,9 @@ package types
     
     import (
     	"context"
    +	"html"
     
    +	"github.com/zitadel/zitadel/internal/database"
     	"github.com/zitadel/zitadel/internal/eventstore"
     	"github.com/zitadel/zitadel/internal/i18n"
     	"github.com/zitadel/zitadel/internal/notification/channels/smtp"
    @@ -42,6 +44,7 @@ func SendEmail(
     		allowUnverifiedNotificationChannel bool,
     	) error {
     		args = mapNotifyUserToArgs(user, args)
    +		sanitizeArgsForHTML(args)
     		data := GetTemplateData(ctx, translator, args, url, messageType, user.PreferredLanguage.String(), colors)
     		template, err := templates.GetParsedTemplate(mailhtml, data)
     		if err != nil {
    @@ -59,6 +62,23 @@ func SendEmail(
     	}
     }
     
    +func sanitizeArgsForHTML(args map[string]any) {
    +	for key, arg := range args {
    +		switch a := arg.(type) {
    +		case string:
    +			args[key] = html.EscapeString(a)
    +		case []string:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		case database.TextArray[string]:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		}
    +	}
    +}
    +
     func SendSMSTwilio(
     	ctx context.Context,
     	channels ChannelChains,
    
0e1f99e987b5

fix: sanitize output for email (#8373)

https://github.com/zitadel/zitadelLivio SpringJul 31, 2024via ghsa
20 files changed · +39 23
  • console/src/app/modules/top-view/top-view.component.html+1 1 modified
    @@ -16,7 +16,7 @@ <h2 data-e2e="top-view-title" class="cnsl-title">{{ title }}</h2>
                 ></div>
               </div>
               <div class="cnsl-doc-row">
    -            <span class="cnsl-type" [innerHtml]="sub"></span>
    +            <span class="cnsl-type">{{ sub }}</span>
                 <a *ngIf="docLink" mat-icon-button [href]="docLink" rel="noreferrer" target="_blank">
                   <mat-icon class="icon">info_outline</mat-icon>
                 </a>
    
  • console/src/app/modules/top-view/top-view.component.scss+1 1 modified
    @@ -82,7 +82,7 @@
     
           .cnsl-type {
             font-size: 14px;
    -        margin-top: 2rem;
    +        margin-top: 1.5rem;
             margin-bottom: 1rem;
           }
     
    
  • console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html+1 3 modified
    @@ -2,9 +2,7 @@
       title="{{ project?.projectName }}"
       [hasActions]="false"
       docLink="https://zitadel.com/docs/guides/manage/console/projects#what-is-a-granted-project"
    -  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate }} {{ 'ACTIONS.OF' | translate }} <strong>{{
    -    project?.projectOwnerName
    -  }}</strong>"
    +  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate: { name: project?.projectOwnerName } }}"
       [isActive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE"
       [isInactive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE"
       stateTooltip="{{ 'ORG.STATE.' + project?.state | translate }}"
    
  • console/src/assets/i18n/bg.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Притежавани проекти",
             "OWNED_SINGULAR": "Собствен проект",
    -        "GRANTED_SINGULAR": "Приет проект"
    +        "GRANTED_SINGULAR": "Отпуснат проект на {{name}}"
           },
           "PRIVATELABEL": {
             "0": {
    
  • console/src/assets/i18n/cs.json+1 1 modified
    @@ -1784,7 +1784,7 @@
           "TYPE": {
             "OWNED": "Vlastní projekty",
             "OWNED_SINGULAR": "Vlastní projekt",
    -        "GRANTED_SINGULAR": "Přidělený projekt"
    +        "GRANTED_SINGULAR": "Projekt přidělený {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Nastavení brandingu",
    
  • console/src/assets/i18n/de.json+1 1 modified
    @@ -1783,7 +1783,7 @@
           "TYPE": {
             "OWNED": "Eigene Projekte",
             "OWNED_SINGULAR": "Eigenes Projekt",
    -        "GRANTED_SINGULAR": "Berechtigtes Projekt"
    +        "GRANTED_SINGULAR": "Berechtigtes Projekt von {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Verhalten",
    
  • console/src/assets/i18n/en.json+1 1 modified
    @@ -1783,7 +1783,7 @@
           "TYPE": {
             "OWNED": "Owned Projects",
             "OWNED_SINGULAR": "Owned Project",
    -        "GRANTED_SINGULAR": "Granted Project"
    +        "GRANTED_SINGULAR": "Granted Project of {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Setting",
    
  • console/src/assets/i18n/es.json+1 1 modified
    @@ -1784,7 +1784,7 @@
           "TYPE": {
             "OWNED": "Proyectos propios",
             "OWNED_SINGULAR": "Proyecto propio",
    -        "GRANTED_SINGULAR": "Proyecto concedido"
    +        "GRANTED_SINGULAR": "Proyecto asignado {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ajustes de imagen de marca",
    
  • console/src/assets/i18n/fr.json+1 1 modified
    @@ -1783,7 +1783,7 @@
           "TYPE": {
             "OWNED": "Projets possédés",
             "OWNED_SINGULAR": "Projet possédé",
    -        "GRANTED_SINGULAR": "Projet octroyé"
    +        "GRANTED_SINGULAR": "Projet attribué à {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Image de marque",
    
  • console/src/assets/i18n/it.json+1 1 modified
    @@ -1783,7 +1783,7 @@
           "TYPE": {
             "OWNED": "Progetti proprietari",
             "OWNED_SINGULAR": "Progetto proprietario",
    -        "GRANTED_SINGULAR": "Progetto delegato"
    +        "GRANTED_SINGULAR": "Progetto concesso di {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Impostazione branding",
    
  • console/src/assets/i18n/ja.json+1 1 modified
    @@ -1779,7 +1779,7 @@
           "TYPE": {
             "OWNED": "所有プロジェクト",
             "OWNED_SINGULAR": "所有プロジェクト",
    -        "GRANTED_SINGULAR": "グラントされたプロジェクト"
    +        "GRANTED_SINGULAR": "{{name}}に付与されたプロジェクト"
           },
           "PRIVATELABEL": {
             "TITLE": "ブランディング設定",
    
  • console/src/assets/i18n/mk.json+1 1 modified
    @@ -1784,7 +1784,7 @@
           "TYPE": {
             "OWNED": "Сопствени проекти",
             "OWNED_SINGULAR": "Сопствен проект",
    -        "GRANTED_SINGULAR": "Доделен проект"
    +        "GRANTED_SINGULAR": "Проект доделен на {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Подесувања за брендирање",
    
  • console/src/assets/i18n/nl.json+1 1 modified
    @@ -1783,7 +1783,7 @@
           "TYPE": {
             "OWNED": "Eigen Projecten",
             "OWNED_SINGULAR": "Eigen Project",
    -        "GRANTED_SINGULAR": "Verleend Project"
    +        "GRANTED_SINGULAR": "Project toegewezen {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Instelling",
    
  • console/src/assets/i18n/pl.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Własne Projekty",
             "OWNED_SINGULAR": "Własny Projekt",
    -        "GRANTED_SINGULAR": "Udzielony Projekt"
    +        "GRANTED_SINGULAR": "Projekt przydzielony {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ustawienia marka",
    
  • console/src/assets/i18n/pt.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Projetos Próprios",
             "OWNED_SINGULAR": "Projeto Próprio",
    -        "GRANTED_SINGULAR": "Projeto Concedido"
    +        "GRANTED_SINGULAR": "Projeto atribuído a {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Configuração de Marca",
    
  • console/src/assets/i18n/ru.json+1 2 modified
    @@ -1864,9 +1864,8 @@
           "ZITADELPROJECT": "Это принадлежит проекту ZITADEL. Осторожно: Если вы внесёте изменения, ZITADEL может вести себя не так, как предполагалось.",
           "TYPE": {
             "OWNED": "Собственные проекты",
    -        "GRANTED": "Проекты доступа",
             "OWNED_SINGULAR": "Собственный проект",
    -        "GRANTED_SINGULAR": "Допуск проекта"
    +        "GRANTED_SINGULAR": "Проект, предоставленный {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Настройка брендинга",
    
  • console/src/assets/i18n/sv.json+1 1 modified
    @@ -1787,7 +1787,7 @@
           "TYPE": {
             "OWNED": "Ägda Projekt",
             "OWNED_SINGULAR": "Ägt Projekt",
    -        "GRANTED_SINGULAR": "Beviljat Projekt"
    +        "GRANTED_SINGULAR": "Projekt tilldelat {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Varumärkesinställning",
    
  • console/src/assets/i18n/zh.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "拥有的项目",
             "OWNED_SINGULAR": "拥有项目",
    -        "GRANTED_SINGULAR": "被授予的项目"
    +        "GRANTED_SINGULAR": "授予{{name}}的项目"
           },
           "PRIVATELABEL": {
             "TITLE": "品牌标识设置",
    
  • internal/notification/templates/templateData.go+1 2 modified
    @@ -2,7 +2,6 @@ package templates
     
     import (
     	"fmt"
    -	"html"
     
     	"github.com/zitadel/zitadel/internal/domain"
     	"github.com/zitadel/zitadel/internal/i18n"
    @@ -40,7 +39,7 @@ func (data *TemplateData) Translate(translator *i18n.Translator, msgType string,
     	data.PreHeader = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessagePreHeader), args, langs...)
     	data.Subject = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageSubject), args, langs...)
     	data.Greeting = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageGreeting), args, langs...)
    -	data.Text = html.UnescapeString(translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...))
    +	data.Text = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...)
     	data.ButtonText = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageButtonText), args, langs...)
     	// Footer text is neither included in i18n files nor defaults.yaml
     	footerText := fmt.Sprintf("%s.%s", msgType, domain.MessageFooterText)
    
  • internal/notification/types/notification.go+20 0 modified
    @@ -2,7 +2,9 @@ package types
     
     import (
     	"context"
    +	"html"
     
    +	"github.com/zitadel/zitadel/internal/database"
     	"github.com/zitadel/zitadel/internal/eventstore"
     	"github.com/zitadel/zitadel/internal/i18n"
     	"github.com/zitadel/zitadel/internal/notification/channels/smtp"
    @@ -42,6 +44,7 @@ func SendEmail(
     		allowUnverifiedNotificationChannel bool,
     	) error {
     		args = mapNotifyUserToArgs(user, args)
    +		sanitizeArgsForHTML(args)
     		data := GetTemplateData(ctx, translator, args, url, messageType, user.PreferredLanguage.String(), colors)
     		template, err := templates.GetParsedTemplate(mailhtml, data)
     		if err != nil {
    @@ -59,6 +62,23 @@ func SendEmail(
     	}
     }
     
    +func sanitizeArgsForHTML(args map[string]any) {
    +	for key, arg := range args {
    +		switch a := arg.(type) {
    +		case string:
    +			args[key] = html.EscapeString(a)
    +		case []string:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		case database.TextArray[string]:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		}
    +	}
    +}
    +
     func SendSMSTwilio(
     	ctx context.Context,
     	channels ChannelChains,
    
38da602ee1cf

fix: sanitize output for email (#8373)

https://github.com/zitadel/zitadelLivio SpringJul 31, 2024via ghsa
19 files changed · +38 22
  • console/src/app/modules/top-view/top-view.component.html+1 1 modified
    @@ -16,7 +16,7 @@ <h2 data-e2e="top-view-title" class="cnsl-title">{{ title }}</h2>
                 ></div>
               </div>
               <div class="cnsl-doc-row">
    -            <span class="cnsl-type" [innerHtml]="sub"></span>
    +            <span class="cnsl-type">{{ sub }}</span>
                 <a *ngIf="docLink" mat-icon-button [href]="docLink" rel="noreferrer" target="_blank">
                   <mat-icon class="icon">info_outline</mat-icon>
                 </a>
    
  • console/src/app/modules/top-view/top-view.component.scss+1 1 modified
    @@ -82,7 +82,7 @@
     
           .cnsl-type {
             font-size: 14px;
    -        margin-top: 2rem;
    +        margin-top: 1.5rem;
             margin-bottom: 1rem;
           }
     
    
  • console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html+1 3 modified
    @@ -2,9 +2,7 @@
       title="{{ project?.projectName }}"
       [hasActions]="false"
       docLink="https://zitadel.com/docs/guides/manage/console/projects#what-is-a-granted-project"
    -  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate }} {{ 'ACTIONS.OF' | translate }} <strong>{{
    -    project?.projectOwnerName
    -  }}</strong>"
    +  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate: { name: project?.projectOwnerName } }}"
       [isActive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE"
       [isInactive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE"
       stateTooltip="{{ 'ORG.STATE.' + project?.state | translate }}"
    
  • console/src/assets/i18n/bg.json+1 1 modified
    @@ -1770,7 +1770,7 @@
           "TYPE": {
             "OWNED": "Притежавани проекти",
             "OWNED_SINGULAR": "Собствен проект",
    -        "GRANTED_SINGULAR": "Приет проект"
    +        "GRANTED_SINGULAR": "Отпуснат проект на {{name}}"
           },
           "PRIVATELABEL": {
             "0": {
    
  • console/src/assets/i18n/cs.json+1 1 modified
    @@ -1778,7 +1778,7 @@
           "TYPE": {
             "OWNED": "Vlastní projekty",
             "OWNED_SINGULAR": "Vlastní projekt",
    -        "GRANTED_SINGULAR": "Přidělený projekt"
    +        "GRANTED_SINGULAR": "Projekt přidělený {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Nastavení brandingu",
    
  • console/src/assets/i18n/de.json+1 1 modified
    @@ -1776,7 +1776,7 @@
           "TYPE": {
             "OWNED": "Eigene Projekte",
             "OWNED_SINGULAR": "Eigenes Projekt",
    -        "GRANTED_SINGULAR": "Berechtigtes Projekt"
    +        "GRANTED_SINGULAR": "Berechtigtes Projekt von {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Verhalten",
    
  • console/src/assets/i18n/en.json+1 1 modified
    @@ -1777,7 +1777,7 @@
           "TYPE": {
             "OWNED": "Owned Projects",
             "OWNED_SINGULAR": "Owned Project",
    -        "GRANTED_SINGULAR": "Granted Project"
    +        "GRANTED_SINGULAR": "Granted Project of {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Setting",
    
  • console/src/assets/i18n/es.json+1 1 modified
    @@ -1778,7 +1778,7 @@
           "TYPE": {
             "OWNED": "Proyectos propios",
             "OWNED_SINGULAR": "Proyecto propio",
    -        "GRANTED_SINGULAR": "Proyecto concedido"
    +        "GRANTED_SINGULAR": "Proyecto asignado {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ajustes de imagen de marca",
    
  • console/src/assets/i18n/fr.json+1 1 modified
    @@ -1776,7 +1776,7 @@
           "TYPE": {
             "OWNED": "Projets possédés",
             "OWNED_SINGULAR": "Projet possédé",
    -        "GRANTED_SINGULAR": "Projet octroyé"
    +        "GRANTED_SINGULAR": "Projet attribué à {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Image de marque",
    
  • console/src/assets/i18n/it.json+1 1 modified
    @@ -1776,7 +1776,7 @@
           "TYPE": {
             "OWNED": "Progetti proprietari",
             "OWNED_SINGULAR": "Progetto proprietario",
    -        "GRANTED_SINGULAR": "Progetto delegato"
    +        "GRANTED_SINGULAR": "Progetto concesso di {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Impostazione branding",
    
  • console/src/assets/i18n/ja.json+1 1 modified
    @@ -1773,7 +1773,7 @@
           "TYPE": {
             "OWNED": "所有プロジェクト",
             "OWNED_SINGULAR": "所有プロジェクト",
    -        "GRANTED_SINGULAR": "グラントされたプロジェクト"
    +        "GRANTED_SINGULAR": "{{name}}に付与されたプロジェクト"
           },
           "PRIVATELABEL": {
             "TITLE": "ブランディング設定",
    
  • console/src/assets/i18n/mk.json+1 1 modified
    @@ -1778,7 +1778,7 @@
           "TYPE": {
             "OWNED": "Сопствени проекти",
             "OWNED_SINGULAR": "Сопствен проект",
    -        "GRANTED_SINGULAR": "Доделен проект"
    +        "GRANTED_SINGULAR": "Проект доделен на {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Подесувања за брендирање",
    
  • console/src/assets/i18n/nl.json+1 1 modified
    @@ -1777,7 +1777,7 @@
           "TYPE": {
             "OWNED": "Eigen Projecten",
             "OWNED_SINGULAR": "Eigen Project",
    -        "GRANTED_SINGULAR": "Verleend Project"
    +        "GRANTED_SINGULAR": "Project toegewezen {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Instelling",
    
  • console/src/assets/i18n/pl.json+1 1 modified
    @@ -1776,7 +1776,7 @@
           "TYPE": {
             "OWNED": "Własne Projekty",
             "OWNED_SINGULAR": "Własny Projekt",
    -        "GRANTED_SINGULAR": "Udzielony Projekt"
    +        "GRANTED_SINGULAR": "Projekt przydzielony {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ustawienia marka",
    
  • console/src/assets/i18n/pt.json+1 1 modified
    @@ -1776,7 +1776,7 @@
           "TYPE": {
             "OWNED": "Projetos Próprios",
             "OWNED_SINGULAR": "Projeto Próprio",
    -        "GRANTED_SINGULAR": "Projeto Concedido"
    +        "GRANTED_SINGULAR": "Projeto atribuído a {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Configuração de Marca",
    
  • console/src/assets/i18n/ru.json+1 2 modified
    @@ -1858,9 +1858,8 @@
           "ZITADELPROJECT": "Это принадлежит проекту ZITADEL. Осторожно: Если вы внесёте изменения, ZITADEL может вести себя не так, как предполагалось.",
           "TYPE": {
             "OWNED": "Собственные проекты",
    -        "GRANTED": "Проекты доступа",
             "OWNED_SINGULAR": "Собственный проект",
    -        "GRANTED_SINGULAR": "Допуск проекта"
    +        "GRANTED_SINGULAR": "Проект, предоставленный {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Настройка брендинга",
    
  • console/src/assets/i18n/zh.json+1 1 modified
    @@ -1775,7 +1775,7 @@
           "TYPE": {
             "OWNED": "拥有的项目",
             "OWNED_SINGULAR": "拥有项目",
    -        "GRANTED_SINGULAR": "被授予的项目"
    +        "GRANTED_SINGULAR": "授予{{name}}的项目"
           },
           "PRIVATELABEL": {
             "TITLE": "品牌标识设置",
    
  • internal/notification/templates/templateData.go+1 2 modified
    @@ -2,7 +2,6 @@ package templates
     
     import (
     	"fmt"
    -	"html"
     
     	"github.com/zitadel/zitadel/internal/domain"
     	"github.com/zitadel/zitadel/internal/i18n"
    @@ -40,7 +39,7 @@ func (data *TemplateData) Translate(translator *i18n.Translator, msgType string,
     	data.PreHeader = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessagePreHeader), args, langs...)
     	data.Subject = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageSubject), args, langs...)
     	data.Greeting = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageGreeting), args, langs...)
    -	data.Text = html.UnescapeString(translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...))
    +	data.Text = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...)
     	data.ButtonText = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageButtonText), args, langs...)
     	// Footer text is neither included in i18n files nor defaults.yaml
     	footerText := fmt.Sprintf("%s.%s", msgType, domain.MessageFooterText)
    
  • internal/notification/types/notification.go+20 0 modified
    @@ -2,7 +2,9 @@ package types
     
     import (
     	"context"
    +	"html"
     
    +	"github.com/zitadel/zitadel/internal/database"
     	"github.com/zitadel/zitadel/internal/eventstore"
     	"github.com/zitadel/zitadel/internal/i18n"
     	"github.com/zitadel/zitadel/internal/notification/channels/smtp"
    @@ -42,6 +44,7 @@ func SendEmail(
     		allowUnverifiedNotificationChannel bool,
     	) error {
     		args = mapNotifyUserToArgs(user, args)
    +		sanitizeArgsForHTML(args)
     		data := GetTemplateData(ctx, translator, args, url, messageType, user.PreferredLanguage.String(), colors)
     		template, err := templates.GetParsedTemplate(mailhtml, data)
     		if err != nil {
    @@ -59,6 +62,23 @@ func SendEmail(
     	}
     }
     
    +func sanitizeArgsForHTML(args map[string]any) {
    +	for key, arg := range args {
    +		switch a := arg.(type) {
    +		case string:
    +			args[key] = html.EscapeString(a)
    +		case []string:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		case database.TextArray[string]:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		}
    +	}
    +}
    +
     func SendSMSTwilio(
     	ctx context.Context,
     	channels ChannelChains,
    
d04ac6df8f2f

fix: sanitize output for email (#8373)

https://github.com/zitadel/zitadelLivio SpringJul 31, 2024via ghsa
20 files changed · +39 23
  • console/src/app/modules/top-view/top-view.component.html+1 1 modified
    @@ -16,7 +16,7 @@ <h2 data-e2e="top-view-title" class="cnsl-title">{{ title }}</h2>
                 ></div>
               </div>
               <div class="cnsl-doc-row">
    -            <span class="cnsl-type" [innerHtml]="sub"></span>
    +            <span class="cnsl-type">{{ sub }}</span>
                 <a *ngIf="docLink" mat-icon-button [href]="docLink" rel="noreferrer" target="_blank">
                   <mat-icon class="icon">info_outline</mat-icon>
                 </a>
    
  • console/src/app/modules/top-view/top-view.component.scss+1 1 modified
    @@ -82,7 +82,7 @@
     
           .cnsl-type {
             font-size: 14px;
    -        margin-top: 2rem;
    +        margin-top: 1.5rem;
             margin-bottom: 1rem;
           }
     
    
  • console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html+1 3 modified
    @@ -2,9 +2,7 @@
       title="{{ project?.projectName }}"
       [hasActions]="false"
       docLink="https://zitadel.com/docs/guides/manage/console/projects#what-is-a-granted-project"
    -  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate }} {{ 'ACTIONS.OF' | translate }} <strong>{{
    -    project?.projectOwnerName
    -  }}</strong>"
    +  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate: { name: project?.projectOwnerName } }}"
       [isActive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE"
       [isInactive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE"
       stateTooltip="{{ 'ORG.STATE.' + project?.state | translate }}"
    
  • console/src/assets/i18n/bg.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "Притежавани проекти",
             "OWNED_SINGULAR": "Собствен проект",
    -        "GRANTED_SINGULAR": "Приет проект"
    +        "GRANTED_SINGULAR": "Отпуснат проект на {{name}}"
           },
           "PRIVATELABEL": {
             "0": {
    
  • console/src/assets/i18n/cs.json+1 1 modified
    @@ -1783,7 +1783,7 @@
           "TYPE": {
             "OWNED": "Vlastní projekty",
             "OWNED_SINGULAR": "Vlastní projekt",
    -        "GRANTED_SINGULAR": "Přidělený projekt"
    +        "GRANTED_SINGULAR": "Projekt přidělený {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Nastavení brandingu",
    
  • console/src/assets/i18n/de.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Eigene Projekte",
             "OWNED_SINGULAR": "Eigenes Projekt",
    -        "GRANTED_SINGULAR": "Berechtigtes Projekt"
    +        "GRANTED_SINGULAR": "Berechtigtes Projekt von {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Verhalten",
    
  • console/src/assets/i18n/en.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Owned Projects",
             "OWNED_SINGULAR": "Owned Project",
    -        "GRANTED_SINGULAR": "Granted Project"
    +        "GRANTED_SINGULAR": "Granted Project of {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Setting",
    
  • console/src/assets/i18n/es.json+1 1 modified
    @@ -1783,7 +1783,7 @@
           "TYPE": {
             "OWNED": "Proyectos propios",
             "OWNED_SINGULAR": "Proyecto propio",
    -        "GRANTED_SINGULAR": "Proyecto concedido"
    +        "GRANTED_SINGULAR": "Proyecto asignado {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ajustes de imagen de marca",
    
  • console/src/assets/i18n/fr.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Projets possédés",
             "OWNED_SINGULAR": "Projet possédé",
    -        "GRANTED_SINGULAR": "Projet octroyé"
    +        "GRANTED_SINGULAR": "Projet attribué à {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Image de marque",
    
  • console/src/assets/i18n/it.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Progetti proprietari",
             "OWNED_SINGULAR": "Progetto proprietario",
    -        "GRANTED_SINGULAR": "Progetto delegato"
    +        "GRANTED_SINGULAR": "Progetto concesso di {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Impostazione branding",
    
  • console/src/assets/i18n/ja.json+1 1 modified
    @@ -1778,7 +1778,7 @@
           "TYPE": {
             "OWNED": "所有プロジェクト",
             "OWNED_SINGULAR": "所有プロジェクト",
    -        "GRANTED_SINGULAR": "グラントされたプロジェクト"
    +        "GRANTED_SINGULAR": "{{name}}に付与されたプロジェクト"
           },
           "PRIVATELABEL": {
             "TITLE": "ブランディング設定",
    
  • console/src/assets/i18n/mk.json+1 1 modified
    @@ -1783,7 +1783,7 @@
           "TYPE": {
             "OWNED": "Сопствени проекти",
             "OWNED_SINGULAR": "Сопствен проект",
    -        "GRANTED_SINGULAR": "Доделен проект"
    +        "GRANTED_SINGULAR": "Проект доделен на {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Подесувања за брендирање",
    
  • console/src/assets/i18n/nl.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Eigen Projecten",
             "OWNED_SINGULAR": "Eigen Project",
    -        "GRANTED_SINGULAR": "Verleend Project"
    +        "GRANTED_SINGULAR": "Project toegewezen {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Instelling",
    
  • console/src/assets/i18n/pl.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "Własne Projekty",
             "OWNED_SINGULAR": "Własny Projekt",
    -        "GRANTED_SINGULAR": "Udzielony Projekt"
    +        "GRANTED_SINGULAR": "Projekt przydzielony {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ustawienia marka",
    
  • console/src/assets/i18n/pt.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "Projetos Próprios",
             "OWNED_SINGULAR": "Projeto Próprio",
    -        "GRANTED_SINGULAR": "Projeto Concedido"
    +        "GRANTED_SINGULAR": "Projeto atribuído a {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Configuração de Marca",
    
  • console/src/assets/i18n/ru.json+1 2 modified
    @@ -1863,9 +1863,8 @@
           "ZITADELPROJECT": "Это принадлежит проекту ZITADEL. Осторожно: Если вы внесёте изменения, ZITADEL может вести себя не так, как предполагалось.",
           "TYPE": {
             "OWNED": "Собственные проекты",
    -        "GRANTED": "Проекты доступа",
             "OWNED_SINGULAR": "Собственный проект",
    -        "GRANTED_SINGULAR": "Допуск проекта"
    +        "GRANTED_SINGULAR": "Проект, предоставленный {{name}}"
           },
           "PRIVATELABEL": {
             "0": {
    
  • console/src/assets/i18n/sv.json+1 1 modified
    @@ -1786,7 +1786,7 @@
           "TYPE": {
             "OWNED": "Ägda Projekt",
             "OWNED_SINGULAR": "Ägt Projekt",
    -        "GRANTED_SINGULAR": "Beviljat Projekt"
    +        "GRANTED_SINGULAR": "Projekt tilldelat {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Varumärkesinställning",
    
  • console/src/assets/i18n/zh.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "拥有的项目",
             "OWNED_SINGULAR": "拥有项目",
    -        "GRANTED_SINGULAR": "被授予的项目"
    +        "GRANTED_SINGULAR": "授予{{name}}的项目"
           },
           "PRIVATELABEL": {
             "TITLE": "品牌标识设置",
    
  • internal/notification/templates/templateData.go+1 2 modified
    @@ -2,7 +2,6 @@ package templates
     
     import (
     	"fmt"
    -	"html"
     
     	"github.com/zitadel/zitadel/internal/domain"
     	"github.com/zitadel/zitadel/internal/i18n"
    @@ -40,7 +39,7 @@ func (data *TemplateData) Translate(translator *i18n.Translator, msgType string,
     	data.PreHeader = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessagePreHeader), args, langs...)
     	data.Subject = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageSubject), args, langs...)
     	data.Greeting = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageGreeting), args, langs...)
    -	data.Text = html.UnescapeString(translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...))
    +	data.Text = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...)
     	data.ButtonText = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageButtonText), args, langs...)
     	// Footer text is neither included in i18n files nor defaults.yaml
     	footerText := fmt.Sprintf("%s.%s", msgType, domain.MessageFooterText)
    
  • internal/notification/types/notification.go+20 0 modified
    @@ -2,7 +2,9 @@ package types
     
     import (
     	"context"
    +	"html"
     
    +	"github.com/zitadel/zitadel/internal/database"
     	"github.com/zitadel/zitadel/internal/eventstore"
     	"github.com/zitadel/zitadel/internal/i18n"
     	"github.com/zitadel/zitadel/internal/notification/channels/smtp"
    @@ -42,6 +44,7 @@ func SendEmail(
     		allowUnverifiedNotificationChannel bool,
     	) error {
     		args = mapNotifyUserToArgs(user, args)
    +		sanitizeArgsForHTML(args)
     		data := GetTemplateData(ctx, translator, args, url, messageType, user.PreferredLanguage.String(), colors)
     		template, err := templates.GetParsedTemplate(mailhtml, data)
     		if err != nil {
    @@ -59,6 +62,23 @@ func SendEmail(
     	}
     }
     
    +func sanitizeArgsForHTML(args map[string]any) {
    +	for key, arg := range args {
    +		switch a := arg.(type) {
    +		case string:
    +			args[key] = html.EscapeString(a)
    +		case []string:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		case database.TextArray[string]:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		}
    +	}
    +}
    +
     func SendSMSTwilio(
     	ctx context.Context,
     	channels ChannelChains,
    
f846616a3f02

fix: sanitize output for email (#8373)

https://github.com/zitadel/zitadelLivio SpringJul 31, 2024via ghsa
20 files changed · +39 23
  • console/src/app/modules/top-view/top-view.component.html+1 1 modified
    @@ -16,7 +16,7 @@ <h2 data-e2e="top-view-title" class="cnsl-title">{{ title }}</h2>
                 ></div>
               </div>
               <div class="cnsl-doc-row">
    -            <span class="cnsl-type" [innerHtml]="sub"></span>
    +            <span class="cnsl-type">{{ sub }}</span>
                 <a *ngIf="docLink" mat-icon-button [href]="docLink" rel="noreferrer" target="_blank">
                   <mat-icon class="icon">info_outline</mat-icon>
                 </a>
    
  • console/src/app/modules/top-view/top-view.component.scss+1 1 modified
    @@ -82,7 +82,7 @@
     
           .cnsl-type {
             font-size: 14px;
    -        margin-top: 2rem;
    +        margin-top: 1.5rem;
             margin-bottom: 1rem;
           }
     
    
  • console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html+1 3 modified
    @@ -2,9 +2,7 @@
       title="{{ project?.projectName }}"
       [hasActions]="false"
       docLink="https://zitadel.com/docs/guides/manage/console/projects#what-is-a-granted-project"
    -  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate }} {{ 'ACTIONS.OF' | translate }} <strong>{{
    -    project?.projectOwnerName
    -  }}</strong>"
    +  sub="{{ 'PROJECT.PAGES.TYPE.GRANTED_SINGULAR' | translate: { name: project?.projectOwnerName } }}"
       [isActive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE"
       [isInactive]="project?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE"
       stateTooltip="{{ 'ORG.STATE.' + project?.state | translate }}"
    
  • console/src/assets/i18n/bg.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "Притежавани проекти",
             "OWNED_SINGULAR": "Собствен проект",
    -        "GRANTED_SINGULAR": "Приет проект"
    +        "GRANTED_SINGULAR": "Отпуснат проект на {{name}}"
           },
           "PRIVATELABEL": {
             "0": {
    
  • console/src/assets/i18n/cs.json+1 1 modified
    @@ -1783,7 +1783,7 @@
           "TYPE": {
             "OWNED": "Vlastní projekty",
             "OWNED_SINGULAR": "Vlastní projekt",
    -        "GRANTED_SINGULAR": "Přidělený projekt"
    +        "GRANTED_SINGULAR": "Projekt přidělený {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Nastavení brandingu",
    
  • console/src/assets/i18n/de.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Eigene Projekte",
             "OWNED_SINGULAR": "Eigenes Projekt",
    -        "GRANTED_SINGULAR": "Berechtigtes Projekt"
    +        "GRANTED_SINGULAR": "Berechtigtes Projekt von {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Verhalten",
    
  • console/src/assets/i18n/en.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Owned Projects",
             "OWNED_SINGULAR": "Owned Project",
    -        "GRANTED_SINGULAR": "Granted Project"
    +        "GRANTED_SINGULAR": "Granted Project of {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Setting",
    
  • console/src/assets/i18n/es.json+1 1 modified
    @@ -1783,7 +1783,7 @@
           "TYPE": {
             "OWNED": "Proyectos propios",
             "OWNED_SINGULAR": "Proyecto propio",
    -        "GRANTED_SINGULAR": "Proyecto concedido"
    +        "GRANTED_SINGULAR": "Proyecto asignado {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ajustes de imagen de marca",
    
  • console/src/assets/i18n/fr.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Projets possédés",
             "OWNED_SINGULAR": "Projet possédé",
    -        "GRANTED_SINGULAR": "Projet octroyé"
    +        "GRANTED_SINGULAR": "Projet attribué à {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Image de marque",
    
  • console/src/assets/i18n/it.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Progetti proprietari",
             "OWNED_SINGULAR": "Progetto proprietario",
    -        "GRANTED_SINGULAR": "Progetto delegato"
    +        "GRANTED_SINGULAR": "Progetto concesso di {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Impostazione branding",
    
  • console/src/assets/i18n/ja.json+1 1 modified
    @@ -1778,7 +1778,7 @@
           "TYPE": {
             "OWNED": "所有プロジェクト",
             "OWNED_SINGULAR": "所有プロジェクト",
    -        "GRANTED_SINGULAR": "グラントされたプロジェクト"
    +        "GRANTED_SINGULAR": "{{name}}に付与されたプロジェクト"
           },
           "PRIVATELABEL": {
             "TITLE": "ブランディング設定",
    
  • console/src/assets/i18n/mk.json+1 1 modified
    @@ -1783,7 +1783,7 @@
           "TYPE": {
             "OWNED": "Сопствени проекти",
             "OWNED_SINGULAR": "Сопствен проект",
    -        "GRANTED_SINGULAR": "Доделен проект"
    +        "GRANTED_SINGULAR": "Проект доделен на {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Подесувања за брендирање",
    
  • console/src/assets/i18n/nl.json+1 1 modified
    @@ -1782,7 +1782,7 @@
           "TYPE": {
             "OWNED": "Eigen Projecten",
             "OWNED_SINGULAR": "Eigen Project",
    -        "GRANTED_SINGULAR": "Verleend Project"
    +        "GRANTED_SINGULAR": "Project toegewezen {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Branding Instelling",
    
  • console/src/assets/i18n/pl.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "Własne Projekty",
             "OWNED_SINGULAR": "Własny Projekt",
    -        "GRANTED_SINGULAR": "Udzielony Projekt"
    +        "GRANTED_SINGULAR": "Projekt przydzielony {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Ustawienia marka",
    
  • console/src/assets/i18n/pt.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "Projetos Próprios",
             "OWNED_SINGULAR": "Projeto Próprio",
    -        "GRANTED_SINGULAR": "Projeto Concedido"
    +        "GRANTED_SINGULAR": "Projeto atribuído a {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Configuração de Marca",
    
  • console/src/assets/i18n/ru.json+1 2 modified
    @@ -1863,9 +1863,8 @@
           "ZITADELPROJECT": "Это принадлежит проекту ZITADEL. Осторожно: Если вы внесёте изменения, ZITADEL может вести себя не так, как предполагалось.",
           "TYPE": {
             "OWNED": "Собственные проекты",
    -        "GRANTED": "Проекты доступа",
             "OWNED_SINGULAR": "Собственный проект",
    -        "GRANTED_SINGULAR": "Допуск проекта"
    +        "GRANTED_SINGULAR": "Проект, предоставленный {{name}}"
           },
           "PRIVATELABEL": {
             "0": {
    
  • console/src/assets/i18n/sv.json+1 1 modified
    @@ -1786,7 +1786,7 @@
           "TYPE": {
             "OWNED": "Ägda Projekt",
             "OWNED_SINGULAR": "Ägt Projekt",
    -        "GRANTED_SINGULAR": "Beviljat Projekt"
    +        "GRANTED_SINGULAR": "Projekt tilldelat {{name}}"
           },
           "PRIVATELABEL": {
             "TITLE": "Varumärkesinställning",
    
  • console/src/assets/i18n/zh.json+1 1 modified
    @@ -1781,7 +1781,7 @@
           "TYPE": {
             "OWNED": "拥有的项目",
             "OWNED_SINGULAR": "拥有项目",
    -        "GRANTED_SINGULAR": "被授予的项目"
    +        "GRANTED_SINGULAR": "授予{{name}}的项目"
           },
           "PRIVATELABEL": {
             "TITLE": "品牌标识设置",
    
  • internal/notification/templates/templateData.go+1 2 modified
    @@ -2,7 +2,6 @@ package templates
     
     import (
     	"fmt"
    -	"html"
     
     	"github.com/zitadel/zitadel/internal/domain"
     	"github.com/zitadel/zitadel/internal/i18n"
    @@ -40,7 +39,7 @@ func (data *TemplateData) Translate(translator *i18n.Translator, msgType string,
     	data.PreHeader = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessagePreHeader), args, langs...)
     	data.Subject = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageSubject), args, langs...)
     	data.Greeting = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageGreeting), args, langs...)
    -	data.Text = html.UnescapeString(translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...))
    +	data.Text = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageText), args, langs...)
     	data.ButtonText = translator.Localize(fmt.Sprintf("%s.%s", msgType, domain.MessageButtonText), args, langs...)
     	// Footer text is neither included in i18n files nor defaults.yaml
     	footerText := fmt.Sprintf("%s.%s", msgType, domain.MessageFooterText)
    
  • internal/notification/types/notification.go+20 0 modified
    @@ -2,7 +2,9 @@ package types
     
     import (
     	"context"
    +	"html"
     
    +	"github.com/zitadel/zitadel/internal/database"
     	"github.com/zitadel/zitadel/internal/eventstore"
     	"github.com/zitadel/zitadel/internal/i18n"
     	"github.com/zitadel/zitadel/internal/notification/channels/smtp"
    @@ -42,6 +44,7 @@ func SendEmail(
     		allowUnverifiedNotificationChannel bool,
     	) error {
     		args = mapNotifyUserToArgs(user, args)
    +		sanitizeArgsForHTML(args)
     		data := GetTemplateData(ctx, translator, args, url, messageType, user.PreferredLanguage.String(), colors)
     		template, err := templates.GetParsedTemplate(mailhtml, data)
     		if err != nil {
    @@ -59,6 +62,23 @@ func SendEmail(
     	}
     }
     
    +func sanitizeArgsForHTML(args map[string]any) {
    +	for key, arg := range args {
    +		switch a := arg.(type) {
    +		case string:
    +			args[key] = html.EscapeString(a)
    +		case []string:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		case database.TextArray[string]:
    +			for i, s := range a {
    +				a[i] = html.EscapeString(s)
    +			}
    +		}
    +	}
    +}
    +
     func SendSMSTwilio(
     	ctx context.Context,
     	channels ChannelChains,
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

18

News mentions

0

No linked articles in our index yet.