From 6086d4147496bb6905ba815448ca49b97918550f Mon Sep 17 00:00:00 2001 From: Chneemann Date: Sat, 25 May 2024 18:04:35 +0200 Subject: [PATCH] clean code, bugfixes & XSS security --- angular.json | 4 +-- .../add-task/add-task.component.html | 1 + .../components/add-task/add-task.component.ts | 20 +++++++++++++ src/app/components/board/board.component.ts | 1 + .../contact-detail.component.ts | 10 ++++++- .../contact-form/contact-form.component.html | 29 +++++++++++++------ .../contact-form/contact-form.component.ts | 1 - .../contacts/contacts.component.html | 1 + .../components/contacts/contacts.component.ts | 7 ++++- src/app/services/shared.service.ts | 6 ++++ src/assets/i18n/de.json | 1 + src/assets/i18n/en.json | 1 + 12 files changed, 68 insertions(+), 14 deletions(-) diff --git a/angular.json b/angular.json index c61660d..e620b1e 100644 --- a/angular.json +++ b/angular.json @@ -37,8 +37,8 @@ }, { "type": "anyComponentStyle", - "maximumWarning": "4kb", - "maximumError": "8kb" + "maximumWarning": "6kb", + "maximumError": "12kb" } ], "outputHashing": "all" diff --git a/src/app/components/add-task/add-task.component.html b/src/app/components/add-task/add-task.component.html index 489871b..05fcfc0 100644 --- a/src/app/components/add-task/add-task.component.html +++ b/src/app/components/add-task/add-task.component.html @@ -254,6 +254,7 @@ #subtask="ngModel" placeholder="{{ 'addTask.subtaskFormField' | translate }}" [(ngModel)]="subtaskValue" + (input)="updateSubtaskValue()" autocomplete="off" /> @if (subtaskValue) { diff --git a/src/app/components/add-task/add-task.component.ts b/src/app/components/add-task/add-task.component.ts index a3108f4..ea9d192 100644 --- a/src/app/components/add-task/add-task.component.ts +++ b/src/app/components/add-task/add-task.component.ts @@ -9,6 +9,7 @@ import { OverlayService } from '../../services/overlay.service'; import { FormBtnComponent } from '../../shared/components/buttons/form-btn/form-btn.component'; import { ActivatedRoute, Route, Router } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; +import { SharedService } from '../../services/shared.service'; @Component({ selector: 'app-add-task', @@ -42,6 +43,7 @@ export class AddTaskComponent implements OnInit { constructor( public firebaseService: FirebaseService, private overlayService: OverlayService, + private sharedService: SharedService, private route: ActivatedRoute, private router: Router ) {} @@ -142,6 +144,7 @@ export class AddTaskComponent implements OnInit { } updateSearchInput() { + this.searchValue = this.sharedService.replaceXSSChars(this.searchValue); if (this.searchValue) { this.searchInput = this.searchValue.toLowerCase().length > 0; } else { @@ -150,6 +153,10 @@ export class AddTaskComponent implements OnInit { return this.searchInput; } + updateSubtaskValue() { + this.subtaskValue = this.sharedService.replaceXSSChars(this.subtaskValue); + } + receiveAssigned(assigned: string[]) { this.taskData.assigned = assigned; } @@ -177,6 +184,7 @@ export class AddTaskComponent implements OnInit { } saveTaskData() { + this.checkCrossSiteScripting(); localStorage.setItem('taskData', JSON.stringify(this.taskData)); } @@ -244,6 +252,18 @@ export class AddTaskComponent implements OnInit { } } + checkCrossSiteScripting() { + this.taskData.title = this.sharedService.replaceXSSChars( + this.taskData.title + ); + this.taskData.description = this.sharedService.replaceXSSChars( + this.taskData.description + ); + this.taskData.subtasksTitle = this.taskData.subtasksTitle.map((title) => + this.sharedService.replaceXSSChars(title) + ); + } + deleteTaskData(overlayData: string) { this.firebaseService.deleteTask(overlayData); this.closeOverlay(); diff --git a/src/app/components/board/board.component.ts b/src/app/components/board/board.component.ts index 2aeb55a..599484a 100644 --- a/src/app/components/board/board.component.ts +++ b/src/app/components/board/board.component.ts @@ -92,6 +92,7 @@ export class BoardComponent { } updateSearchInput() { + this.searchValue = this.sharedService.replaceXSSChars(this.searchValue); if (this.searchValue) { this.searchInput = this.searchValue.toLowerCase().length > 0; } else { diff --git a/src/app/components/contacts/contact-detail/contact-detail.component.ts b/src/app/components/contacts/contact-detail/contact-detail.component.ts index 0949147..8e9ce08 100644 --- a/src/app/components/contacts/contact-detail/contact-detail.component.ts +++ b/src/app/components/contacts/contact-detail/contact-detail.component.ts @@ -1,4 +1,10 @@ -import { Component, HostListener, Input } from '@angular/core'; +import { + Component, + EventEmitter, + HostListener, + Input, + Output, +} from '@angular/core'; import { CommonModule } from '@angular/common'; import { ContactsComponent } from '../contacts.component'; import { Router } from '@angular/router'; @@ -23,6 +29,7 @@ import { ContactEditNewComponent } from '../contact-edit-new/contact-edit-new.co }) export class ContactDetailComponent { @Input() currentUserId: string | undefined; + @Output() closeContactEmitter = new EventEmitter(); constructor( private router: Router, @@ -33,6 +40,7 @@ export class ContactDetailComponent { closeUserDetails() { this.sharedService.currentUserId = ''; + this.closeContactEmitter.emit(); } checkUserData(userId: string) { diff --git a/src/app/components/contacts/contact-edit-new/contact-form/contact-form.component.html b/src/app/components/contacts/contact-edit-new/contact-form/contact-form.component.html index 75df39a..29d7c8c 100644 --- a/src/app/components/contacts/contact-edit-new/contact-form/contact-form.component.html +++ b/src/app/components/contacts/contact-edit-new/contact-form/contact-form.component.html @@ -19,14 +19,17 @@ : ('contactDialogForm.firstName' | translate) }}" [(ngModel)]="contactData.firstName" + pattern="[A-Za-z]+" autocomplete="off" required /> @if (sharedService.isContactViewMedia) {
- @if (!firstName.valid && firstName.touched) { + @if (firstName.hasError('pattern') && firstName.touched) { +

{{ "contactDialogForm.invalidPattern" | translate }}

+ } @else { @if (!firstName.valid && firstName.touched) {

{{ "contactDialogForm.invalidFirstName" | translate }}

- } + }}
} @if (sharedService.isContactViewMedia) {
- @if (!lastName.valid && lastName.touched) { + @if (lastName.hasError('pattern') && lastName.touched) { +

{{ "contactDialogForm.invalidPattern" | translate }}

+ } @else { @if (!lastName.valid && lastName.touched) {

{{ "contactDialogForm.invalidLastName" | translate }}

- } + }}
} @if (!sharedService.isContactViewMedia) {
- @if ((!firstName.valid && firstName.touched) && (!lastName.valid && - lastName.touched)) { + @if (firstName.hasError('pattern') || lastName.hasError('pattern') && + firstName.touched) { +

{{ "contactDialogForm.invalidPattern" | translate }}

+ } @else { @if ((!firstName.valid && firstName.touched) && (!lastName.valid + && lastName.touched)) {

{{ "contactDialogForm.invalidName" | translate }}

- } @else { @if (!firstName.valid && firstName.touched) { + } @else { @if (!firstName.valid && firstName.touched && + !firstName.hasError('pattern')) {

{{ "contactDialogForm.invalidFirstName" | translate }}

- } @else { @if (!lastName.valid && lastName.touched) { + } @else { @if (!lastName.valid && lastName.touched && + !lastName.hasError('pattern')) {

{{ "contactDialogForm.invalidLastName" | translate }}

- } } } + } } } }
} diff --git a/src/app/components/contacts/contacts.component.ts b/src/app/components/contacts/contacts.component.ts index 6763b35..16335ed 100644 --- a/src/app/components/contacts/contacts.component.ts +++ b/src/app/components/contacts/contacts.component.ts @@ -18,7 +18,7 @@ export class ContactsComponent { allUsers: User[] = []; usersFirstLetter: string[] = []; usersByFirstLetter: { [key: string]: string[] } = {}; - showAllUsers!: boolean; + showAllUsers: boolean = false; constructor( public firebaseService: FirebaseService, @@ -32,6 +32,7 @@ export class ContactsComponent { showUserId(userId: string = '') { this.sharedService.currentUserId = userId; + this.onResize(); } @HostListener('window:resize', ['$event']) @@ -43,6 +44,10 @@ export class ContactsComponent { } } + closeContactEmitter() { + this.onResize(); + } + loadAllUserWithoutGuest(): User[] { return this.firebaseService .getAllUsers() diff --git a/src/app/services/shared.service.ts b/src/app/services/shared.service.ts index 6e7d261..9abc12a 100644 --- a/src/app/services/shared.service.ts +++ b/src/app/services/shared.service.ts @@ -69,4 +69,10 @@ export class SharedService { const b = bigint & 255; return { r, g, b }; } + + // SECURITY + + replaceXSSChars(input: string) { + return input.replace(//g, '>'); + } } diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 7bd8e85..dafc6a5 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -76,6 +76,7 @@ "btnCancel": "Abbrechen", "btnDelete": "Löschen", "btnSave": "Sichern", + "invalidPattern": "Es sind nur Buchstaben erlaubt!", "invalidFirstName": "Ein Vorname ist erforderlich!", "invalidLastName": "Ein Nachname ist erforderlich!", "invalidName": "Ein Vorname & Nachname ist erforderlich!", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 88e99fc..ac19075 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -76,6 +76,7 @@ "btnCancel": "Cancel", "btnDelete": "Delete", "btnSave": "Save", + "invalidPattern": "Only letters are allowed!", "invalidFirstName": "A first name is required!", "invalidLastName": "A last name is required!", "invalidName": "A first name & last name is required!",