clean code, bugfixes & XSS security
This commit is contained in:
parent
b79ad59d2e
commit
6086d41474
12 changed files with 68 additions and 14 deletions
|
|
@ -37,8 +37,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "anyComponentStyle",
|
"type": "anyComponentStyle",
|
||||||
"maximumWarning": "4kb",
|
"maximumWarning": "6kb",
|
||||||
"maximumError": "8kb"
|
"maximumError": "12kb"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"outputHashing": "all"
|
"outputHashing": "all"
|
||||||
|
|
|
||||||
|
|
@ -254,6 +254,7 @@
|
||||||
#subtask="ngModel"
|
#subtask="ngModel"
|
||||||
placeholder="{{ 'addTask.subtaskFormField' | translate }}"
|
placeholder="{{ 'addTask.subtaskFormField' | translate }}"
|
||||||
[(ngModel)]="subtaskValue"
|
[(ngModel)]="subtaskValue"
|
||||||
|
(input)="updateSubtaskValue()"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
/>
|
/>
|
||||||
@if (subtaskValue) {
|
@if (subtaskValue) {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { OverlayService } from '../../services/overlay.service';
|
||||||
import { FormBtnComponent } from '../../shared/components/buttons/form-btn/form-btn.component';
|
import { FormBtnComponent } from '../../shared/components/buttons/form-btn/form-btn.component';
|
||||||
import { ActivatedRoute, Route, Router } from '@angular/router';
|
import { ActivatedRoute, Route, Router } from '@angular/router';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { SharedService } from '../../services/shared.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-add-task',
|
selector: 'app-add-task',
|
||||||
|
|
@ -42,6 +43,7 @@ export class AddTaskComponent implements OnInit {
|
||||||
constructor(
|
constructor(
|
||||||
public firebaseService: FirebaseService,
|
public firebaseService: FirebaseService,
|
||||||
private overlayService: OverlayService,
|
private overlayService: OverlayService,
|
||||||
|
private sharedService: SharedService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router
|
private router: Router
|
||||||
) {}
|
) {}
|
||||||
|
|
@ -142,6 +144,7 @@ export class AddTaskComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSearchInput() {
|
updateSearchInput() {
|
||||||
|
this.searchValue = this.sharedService.replaceXSSChars(this.searchValue);
|
||||||
if (this.searchValue) {
|
if (this.searchValue) {
|
||||||
this.searchInput = this.searchValue.toLowerCase().length > 0;
|
this.searchInput = this.searchValue.toLowerCase().length > 0;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -150,6 +153,10 @@ export class AddTaskComponent implements OnInit {
|
||||||
return this.searchInput;
|
return this.searchInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateSubtaskValue() {
|
||||||
|
this.subtaskValue = this.sharedService.replaceXSSChars(this.subtaskValue);
|
||||||
|
}
|
||||||
|
|
||||||
receiveAssigned(assigned: string[]) {
|
receiveAssigned(assigned: string[]) {
|
||||||
this.taskData.assigned = assigned;
|
this.taskData.assigned = assigned;
|
||||||
}
|
}
|
||||||
|
|
@ -177,6 +184,7 @@ export class AddTaskComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
saveTaskData() {
|
saveTaskData() {
|
||||||
|
this.checkCrossSiteScripting();
|
||||||
localStorage.setItem('taskData', JSON.stringify(this.taskData));
|
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) {
|
deleteTaskData(overlayData: string) {
|
||||||
this.firebaseService.deleteTask(overlayData);
|
this.firebaseService.deleteTask(overlayData);
|
||||||
this.closeOverlay();
|
this.closeOverlay();
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ export class BoardComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSearchInput() {
|
updateSearchInput() {
|
||||||
|
this.searchValue = this.sharedService.replaceXSSChars(this.searchValue);
|
||||||
if (this.searchValue) {
|
if (this.searchValue) {
|
||||||
this.searchInput = this.searchValue.toLowerCase().length > 0;
|
this.searchInput = this.searchValue.toLowerCase().length > 0;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -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 { CommonModule } from '@angular/common';
|
||||||
import { ContactsComponent } from '../contacts.component';
|
import { ContactsComponent } from '../contacts.component';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
@ -23,6 +29,7 @@ import { ContactEditNewComponent } from '../contact-edit-new/contact-edit-new.co
|
||||||
})
|
})
|
||||||
export class ContactDetailComponent {
|
export class ContactDetailComponent {
|
||||||
@Input() currentUserId: string | undefined;
|
@Input() currentUserId: string | undefined;
|
||||||
|
@Output() closeContactEmitter = new EventEmitter<boolean>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
|
@ -33,6 +40,7 @@ export class ContactDetailComponent {
|
||||||
|
|
||||||
closeUserDetails() {
|
closeUserDetails() {
|
||||||
this.sharedService.currentUserId = '';
|
this.sharedService.currentUserId = '';
|
||||||
|
this.closeContactEmitter.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
checkUserData(userId: string) {
|
checkUserData(userId: string) {
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,17 @@
|
||||||
: ('contactDialogForm.firstName' | translate)
|
: ('contactDialogForm.firstName' | translate)
|
||||||
}}"
|
}}"
|
||||||
[(ngModel)]="contactData.firstName"
|
[(ngModel)]="contactData.firstName"
|
||||||
|
pattern="[A-Za-z]+"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
@if (sharedService.isContactViewMedia) {
|
@if (sharedService.isContactViewMedia) {
|
||||||
<div class="error-msg">
|
<div class="error-msg">
|
||||||
@if (!firstName.valid && firstName.touched) {
|
@if (firstName.hasError('pattern') && firstName.touched) {
|
||||||
|
<p>{{ "contactDialogForm.invalidPattern" | translate }}</p>
|
||||||
|
} @else { @if (!firstName.valid && firstName.touched) {
|
||||||
<p>{{ "contactDialogForm.invalidFirstName" | translate }}</p>
|
<p>{{ "contactDialogForm.invalidFirstName" | translate }}</p>
|
||||||
}
|
}}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<input
|
<input
|
||||||
|
|
@ -41,26 +44,34 @@
|
||||||
: ('contactDialogForm.lastName' | translate)
|
: ('contactDialogForm.lastName' | translate)
|
||||||
}}"
|
}}"
|
||||||
[(ngModel)]="contactData.lastName"
|
[(ngModel)]="contactData.lastName"
|
||||||
|
pattern="[A-Za-z]+"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@if (sharedService.isContactViewMedia) {
|
@if (sharedService.isContactViewMedia) {
|
||||||
<div class="error-msg">
|
<div class="error-msg">
|
||||||
@if (!lastName.valid && lastName.touched) {
|
@if (lastName.hasError('pattern') && lastName.touched) {
|
||||||
|
<p>{{ "contactDialogForm.invalidPattern" | translate }}</p>
|
||||||
|
} @else { @if (!lastName.valid && lastName.touched) {
|
||||||
<p>{{ "contactDialogForm.invalidLastName" | translate }}</p>
|
<p>{{ "contactDialogForm.invalidLastName" | translate }}</p>
|
||||||
}
|
}}
|
||||||
</div>
|
</div>
|
||||||
} @if (!sharedService.isContactViewMedia) {
|
} @if (!sharedService.isContactViewMedia) {
|
||||||
<div class="error-msg">
|
<div class="error-msg">
|
||||||
@if ((!firstName.valid && firstName.touched) && (!lastName.valid &&
|
@if (firstName.hasError('pattern') || lastName.hasError('pattern') &&
|
||||||
lastName.touched)) {
|
firstName.touched) {
|
||||||
|
<p>{{ "contactDialogForm.invalidPattern" | translate }}</p>
|
||||||
|
} @else { @if ((!firstName.valid && firstName.touched) && (!lastName.valid
|
||||||
|
&& lastName.touched)) {
|
||||||
<p>{{ "contactDialogForm.invalidName" | translate }}</p>
|
<p>{{ "contactDialogForm.invalidName" | translate }}</p>
|
||||||
} @else { @if (!firstName.valid && firstName.touched) {
|
} @else { @if (!firstName.valid && firstName.touched &&
|
||||||
|
!firstName.hasError('pattern')) {
|
||||||
<p>{{ "contactDialogForm.invalidFirstName" | translate }}</p>
|
<p>{{ "contactDialogForm.invalidFirstName" | translate }}</p>
|
||||||
} @else { @if (!lastName.valid && lastName.touched) {
|
} @else { @if (!lastName.valid && lastName.touched &&
|
||||||
|
!lastName.hasError('pattern')) {
|
||||||
<p>{{ "contactDialogForm.invalidLastName" | translate }}</p>
|
<p>{{ "contactDialogForm.invalidLastName" | translate }}</p>
|
||||||
} } }
|
} } } }
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<input
|
<input
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,6 @@ export class ContactFormComponent implements OnInit, OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkIfUserEmailIsValid(emailValue: string) {
|
checkIfUserEmailIsValid(emailValue: string) {
|
||||||
const channelNameLenght = emailValue.length;
|
|
||||||
const emailRegex = /^[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,}$/;
|
const emailRegex = /^[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,}$/;
|
||||||
if (emailRegex.test(emailValue)) {
|
if (emailRegex.test(emailValue)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@
|
||||||
'd-none': !showAllUsers && sharedService.currentUserId == undefined
|
'd-none': !showAllUsers && sharedService.currentUserId == undefined
|
||||||
}"
|
}"
|
||||||
[currentUserId]="sharedService.currentUserId"
|
[currentUserId]="sharedService.currentUserId"
|
||||||
|
(closeContactEmitter)="closeContactEmitter()"
|
||||||
></app-contact-detail>
|
></app-contact-detail>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export class ContactsComponent {
|
||||||
allUsers: User[] = [];
|
allUsers: User[] = [];
|
||||||
usersFirstLetter: string[] = [];
|
usersFirstLetter: string[] = [];
|
||||||
usersByFirstLetter: { [key: string]: string[] } = {};
|
usersByFirstLetter: { [key: string]: string[] } = {};
|
||||||
showAllUsers!: boolean;
|
showAllUsers: boolean = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public firebaseService: FirebaseService,
|
public firebaseService: FirebaseService,
|
||||||
|
|
@ -32,6 +32,7 @@ export class ContactsComponent {
|
||||||
|
|
||||||
showUserId(userId: string = '') {
|
showUserId(userId: string = '') {
|
||||||
this.sharedService.currentUserId = userId;
|
this.sharedService.currentUserId = userId;
|
||||||
|
this.onResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('window:resize', ['$event'])
|
@HostListener('window:resize', ['$event'])
|
||||||
|
|
@ -43,6 +44,10 @@ export class ContactsComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
closeContactEmitter() {
|
||||||
|
this.onResize();
|
||||||
|
}
|
||||||
|
|
||||||
loadAllUserWithoutGuest(): User[] {
|
loadAllUserWithoutGuest(): User[] {
|
||||||
return this.firebaseService
|
return this.firebaseService
|
||||||
.getAllUsers()
|
.getAllUsers()
|
||||||
|
|
|
||||||
|
|
@ -69,4 +69,10 @@ export class SharedService {
|
||||||
const b = bigint & 255;
|
const b = bigint & 255;
|
||||||
return { r, g, b };
|
return { r, g, b };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SECURITY
|
||||||
|
|
||||||
|
replaceXSSChars(input: string) {
|
||||||
|
return input.replace(/</g, '<').replace(/>/g, '>');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@
|
||||||
"btnCancel": "Abbrechen",
|
"btnCancel": "Abbrechen",
|
||||||
"btnDelete": "Löschen",
|
"btnDelete": "Löschen",
|
||||||
"btnSave": "Sichern",
|
"btnSave": "Sichern",
|
||||||
|
"invalidPattern": "Es sind nur Buchstaben erlaubt!",
|
||||||
"invalidFirstName": "Ein Vorname ist erforderlich!",
|
"invalidFirstName": "Ein Vorname ist erforderlich!",
|
||||||
"invalidLastName": "Ein Nachname ist erforderlich!",
|
"invalidLastName": "Ein Nachname ist erforderlich!",
|
||||||
"invalidName": "Ein Vorname & Nachname ist erforderlich!",
|
"invalidName": "Ein Vorname & Nachname ist erforderlich!",
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@
|
||||||
"btnCancel": "Cancel",
|
"btnCancel": "Cancel",
|
||||||
"btnDelete": "Delete",
|
"btnDelete": "Delete",
|
||||||
"btnSave": "Save",
|
"btnSave": "Save",
|
||||||
|
"invalidPattern": "Only letters are allowed!",
|
||||||
"invalidFirstName": "A first name is required!",
|
"invalidFirstName": "A first name is required!",
|
||||||
"invalidLastName": "A last name is required!",
|
"invalidLastName": "A last name is required!",
|
||||||
"invalidName": "A first name & last name is required!",
|
"invalidName": "A first name & last name is required!",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue