diff --git a/public/assets/locale/ar.json b/public/assets/locale/ar.json index 3506a2551..76712cc89 100644 --- a/public/assets/locale/ar.json +++ b/public/assets/locale/ar.json @@ -116,6 +116,7 @@ "SETTINGS_ENCRYPTED": "مشفر", "SETTINGS_MERGED": "تم الدمج", "SETTINGS_MASKED": "تم إخفاؤه", + "SETTINGS_MASKED_ERROR": "استبدل جميع القيم المخفية قبل حفظ الإعدادات المشفرة", "SETTINGS_INHERITED": "إعداد موروث من [{{ parent }}]({{ path }})({{ type }})", "SETTINGS_LOCAL": "إعداد محلي من {{ type }}", "SETTINGS_SAVE_ERROR": "فشل حفظ الإعدادات. الخطأ {{ error }}", diff --git a/public/assets/locale/en-AU.json b/public/assets/locale/en-AU.json index c42e61879..479f8f444 100644 --- a/public/assets/locale/en-AU.json +++ b/public/assets/locale/en-AU.json @@ -136,6 +136,7 @@ "SETTINGS_ENCRYPTED": "Encrypted", "SETTINGS_MERGED": "Merged", "SETTINGS_MASKED": "MASKED", + "SETTINGS_MASKED_ERROR": "Replace all masked values before you save encrypted settings", "SETTINGS_INHERITED": "Setting inherited from [{{ parent }}]({{ path }})({{ type }})", "SETTINGS_LOCAL": "Local setting from {{ type }}", "SETTINGS_SAVE_ERROR": "Failed to save settings. Error {{ error }}", diff --git a/public/assets/locale/en-GB.json b/public/assets/locale/en-GB.json index 8f6d9c4f1..7d4b66638 100644 --- a/public/assets/locale/en-GB.json +++ b/public/assets/locale/en-GB.json @@ -121,6 +121,7 @@ "SETTINGS_ENCRYPTED": "Encrypted", "SETTINGS_MERGED": "Merged", "SETTINGS_MASKED": "MASKED", + "SETTINGS_MASKED_ERROR": "Replace all masked values before you save encrypted settings", "SETTINGS_INHERITED": "Setting inherited from [{{ parent }}]({{ path }})({{ type }})", "SETTINGS_LOCAL": "Local setting from {{ type }}", "SETTINGS_SAVE_ERROR": "Failed to save settings. Error {{ error }}", diff --git a/public/assets/locale/en-US.json b/public/assets/locale/en-US.json index 0269f9b0b..838c470e1 100644 --- a/public/assets/locale/en-US.json +++ b/public/assets/locale/en-US.json @@ -118,6 +118,7 @@ "SETTINGS_ENCRYPTED": "Encrypted", "SETTINGS_MERGED": "Merged", "SETTINGS_MASKED": "MASKED", + "SETTINGS_MASKED_ERROR": "Replace all masked values before you save encrypted settings", "SETTINGS_INHERITED": "Setting inherited from [{{ parent }}]({{ path }})({{ type }})", "SETTINGS_LOCAL": "Local setting from {{ type }}", "SETTINGS_SAVE_ERROR": "Failed to save settings. Error {{ error }}", diff --git a/public/assets/locale/es.json b/public/assets/locale/es.json index b49a6a9c5..58d2cfbd6 100644 --- a/public/assets/locale/es.json +++ b/public/assets/locale/es.json @@ -116,6 +116,7 @@ "SETTINGS_ENCRYPTED": "Encriptado", "SETTINGS_MERGED": "Combinado", "SETTINGS_MASKED": "OCULTO", + "SETTINGS_MASKED_ERROR": "Reemplace todos los valores ocultos antes de guardar la configuración cifrada", "SETTINGS_INHERITED": "Configuración heredada de [{{ parent }}]({{ path }})({{ type }})", "SETTINGS_LOCAL": "Configuración local de {{ type }}", "SETTINGS_SAVE_ERROR": "No se pudo guardar las configuraciones. Error {{ error }}", diff --git a/public/assets/locale/fr.json b/public/assets/locale/fr.json index a2b5d22d2..70884d172 100644 --- a/public/assets/locale/fr.json +++ b/public/assets/locale/fr.json @@ -116,6 +116,7 @@ "SETTINGS_ENCRYPTED": "Chiffré", "SETTINGS_MERGED": "Fusionné", "SETTINGS_MASKED": "MASQUÉ", + "SETTINGS_MASKED_ERROR": "Remplacez toutes les valeurs masquées avant d'enregistrer les paramètres chiffrés", "SETTINGS_INHERITED": "Paramètre hérité de [{{ parent }}]({{ path }})({{ type }})", "SETTINGS_LOCAL": "Paramètre local depuis {{ type }}", "SETTINGS_SAVE_ERROR": "Échec de l'enregistrement des paramètres. Erreur {{ error }}", diff --git a/public/assets/locale/jp.json b/public/assets/locale/jp.json index 22dc438ce..b700ba625 100644 --- a/public/assets/locale/jp.json +++ b/public/assets/locale/jp.json @@ -116,6 +116,7 @@ "SETTINGS_ENCRYPTED": "暗号化済み", "SETTINGS_MERGED": "マージ済み", "SETTINGS_MASKED": "マスク済み", + "SETTINGS_MASKED_ERROR": "暗号化された設定を保存する前に、マスクされた値をすべて置き換えてください", "SETTINGS_INHERITED": "この設定は[{{ parent }}]({{ path }})({{ type }})から継承されています", "SETTINGS_LOCAL": "{{ type }}のローカル設定", "SETTINGS_SAVE_ERROR": "設定の保存に失敗しました。エラー:{{ error }}", diff --git a/src/app/admin/build-list.component.ts b/src/app/admin/build-list.component.ts index da1942124..0d311b8cb 100644 --- a/src/app/admin/build-list.component.ts +++ b/src/app/admin/build-list.component.ts @@ -5,6 +5,7 @@ import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatTooltipModule } from '@angular/material/tooltip'; import { del, get } from '@placeos/ts-client'; import { toQueryString } from '../common/api'; +import { escapeHtml } from '../common/general'; import { i18n } from '../common/locale.service'; import { notifyError, notifySuccess } from '../common/notifications'; import { openConfirmModal } from '../overlays/confirm-modal.component'; @@ -159,8 +160,8 @@ export class PlaceBuildListComponent implements OnInit { { title: i18n('ADMIN.BUILD_LIST_REMOVE'), content: i18n('ADMIN.BUILD_LIST_REMOVE_MSG', { - driver: i.driver, - repo: i.repo, + driver: escapeHtml(i.driver), + repo: escapeHtml(i.repo), }), icon: { type: 'icon', content: 'delete' }, }, diff --git a/src/app/admin/edge.component.ts b/src/app/admin/edge.component.ts index 404b1f78a..f0dabe5ae 100644 --- a/src/app/admin/edge.component.ts +++ b/src/app/admin/edge.component.ts @@ -11,7 +11,7 @@ import { removeEdge, retrieveEdgeToken, } from '@placeos/ts-client'; -import { copyToClipboard } from '../common/general'; +import { copyToClipboard, escapeHtml } from '../common/general'; import { notifyError, notifyInfo, @@ -228,7 +228,7 @@ export class PlaceEdgeComponent implements OnInit { const details = await openConfirmModal( { title: 'Remove edge?', - content: `Remove ${i.name}?
You or your users may lose access to some data.`, + content: `Remove ${escapeHtml(i.name)}?
You or your users may lose access to some data.`, icon: { type: 'icon', content: 'delete' }, }, this._dialog, diff --git a/src/app/admin/extensions.component.ts b/src/app/admin/extensions.component.ts index e4868ef7f..97d5b0124 100644 --- a/src/app/admin/extensions.component.ts +++ b/src/app/admin/extensions.component.ts @@ -7,6 +7,7 @@ import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatSelectModule } from '@angular/material/select'; import { MatTooltipModule } from '@angular/material/tooltip'; import { PlaceDomain, updateDomain } from '@placeos/ts-client'; +import { escapeHtml } from '../common/general'; import { notifyError } from '../common/notifications'; import { waitForEvent } from '../common/signals'; import { ApplicationIcon, DialogEvent } from '../common/types'; @@ -15,7 +16,6 @@ import { ConfirmModalData, } from '../overlays/confirm-modal.component'; import { IconComponent } from '../ui/icon.component'; -import { SafePipe } from '../ui/pipes/safe.pipe'; import { SimpleTableComponent } from '../ui/simple-table.component'; import { TranslatePipe } from '../ui/translate.pipe'; import { AdminDataService } from './admin-data.service'; @@ -132,10 +132,7 @@ export interface BackofficeExtension { - + {{ row.url }} @@ -188,7 +185,6 @@ export interface BackofficeExtension { MatFormFieldModule, MatSelectModule, FormsModule, - SafePipe, ], }) export class PlaceExtensionsComponent implements OnInit { @@ -256,7 +252,7 @@ export class PlaceExtensionsComponent implements OnInit { { data: { title: 'Remove extension', - content: `Are you sure you want to remove the extension "${item.name}" from ${item.type}?`, + content: `Are you sure you want to remove the extension "${escapeHtml(item.name)}" from ${escapeHtml(item.type)}?`, icon: { content: 'delete' }, }, }, diff --git a/src/app/admin/resource-imports.component.ts b/src/app/admin/resource-imports.component.ts index 45f7a8010..2c13cff86 100644 --- a/src/app/admin/resource-imports.component.ts +++ b/src/app/admin/resource-imports.component.ts @@ -8,6 +8,7 @@ import { MatSelectModule } from '@angular/material/select'; import { MatTooltipModule } from '@angular/material/tooltip'; import { RouterModule } from '@angular/router'; import { addSystem, query, querySystemsWithEmails } from '@placeos/ts-client'; +import { escapeHtml } from '../common/general'; import { i18n } from '../common/locale.service'; import { notifySuccess, notifyWarn } from '../common/notifications'; import { openConfirmModal } from '../overlays/confirm-modal.component'; @@ -215,7 +216,7 @@ export class ResourceImportsComponent implements OnInit { count: missing.length, })}

`, icon: { type: 'icon', content: 'publish' }, diff --git a/src/app/admin/signage-ai/signage-ai.component.ts b/src/app/admin/signage-ai/signage-ai.component.ts index 17e67f6bf..2669df37e 100644 --- a/src/app/admin/signage-ai/signage-ai.component.ts +++ b/src/app/admin/signage-ai/signage-ai.component.ts @@ -3,6 +3,7 @@ import { MatRippleModule } from '@angular/material/core'; import { MatDialog } from '@angular/material/dialog'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { escapeHtml } from '../../common/general'; import { i18n } from '../../common/locale.service'; import { notifyError, notifySuccess } from '../../common/notifications'; import { openConfirmModal } from '../../overlays/confirm-modal.component'; @@ -284,7 +285,7 @@ export class SignageAIComponent implements OnInit { { title: i18n('ADMIN.AI_PROVIDER_TEST_TITLE'), content: i18n('ADMIN.AI_PROVIDER_TEST_MSG', { - name: item.name, + name: escapeHtml(item.name), }), icon: { content: 'bolt' }, }, @@ -314,7 +315,7 @@ export class SignageAIComponent implements OnInit { { title: i18n('ADMIN.AI_PROVIDER_REMOVE_TITLE'), content: i18n('ADMIN.AI_PROVIDER_REMOVE_MSG', { - name: item.name, + name: escapeHtml(item.name), }), icon: { content: 'delete_forever' }, }, diff --git a/src/app/admin/signage-plugins/signage-plugin-embed.component.ts b/src/app/admin/signage-plugins/signage-plugin-embed.component.ts index fbbb8839b..b96b85cd9 100644 --- a/src/app/admin/signage-plugins/signage-plugin-embed.component.ts +++ b/src/app/admin/signage-plugins/signage-plugin-embed.component.ts @@ -18,14 +18,18 @@ import { SafePipe } from '../../ui/pipes/safe.pipe'; export const SIGNAGE_PLUGIN_API_VERSION = 'signage-plugin/v1'; -/** Resolve a plugin URI the same way as the iframe element. */ +/** + * Resolve a plugin URI the same way as the iframe element. + * Returns `null` for invalid URIs and for schemes other than http(s). + */ export function resolveSignagePluginUrl( uri: string, base_uri: string, ): URL | null { if (!uri) return null; try { - return new URL(uri, base_uri); + const url = new URL(uri, base_uri); + return ['http:', 'https:'].includes(url.protocol) ? url : null; } catch { return null; } @@ -87,7 +91,7 @@ export type PluginErrorPayload = { @if (plugin_url(); as plugin_url) {