diff --git a/src/app/core/_components/tables/files-attack-table/files-attack-table.component.html b/src/app/core/_components/tables/files-attack-table/files-attack-table.component.html index 0b3f866e0..41a40eb2f 100644 --- a/src/app/core/_components/tables/files-attack-table/files-attack-table.component.html +++ b/src/app/core/_components/tables/files-attack-table/files-attack-table.component.html @@ -1,5 +1,6 @@ \ No newline at end of file diff --git a/src/app/core/_components/tables/ht-table/ht-table.component.html b/src/app/core/_components/tables/ht-table/ht-table.component.html index 034aba72f..e5c5ec238 100644 --- a/src/app/core/_components/tables/ht-table/ht-table.component.html +++ b/src/app/core/_components/tables/ht-table/ht-table.component.html @@ -198,7 +198,11 @@ @if (isCmdPreproAttack) { - + } diff --git a/src/app/core/_components/tables/ht-table/ht-table.component.ts b/src/app/core/_components/tables/ht-table/ht-table.component.ts index 2f8e9af00..a2db04ec4 100644 --- a/src/app/core/_components/tables/ht-table/ht-table.component.ts +++ b/src/app/core/_components/tables/ht-table/ht-table.component.ts @@ -160,6 +160,8 @@ export class HTTableComponent implements OnInit, AfterViewI /** Flag to enable or disable cmd preprocessor attack checkbox. */ @Input() isCmdPreproAttack = false; + @Input() isCmdPreproFiles: number[] = []; + /** Flag to add dual label text. */ @Input() isCmdLabel: string; @@ -680,6 +682,10 @@ export class HTTableComponent implements OnInit, AfterViewI } } + isPreproSelected(row: T): boolean { + return this.isCmdPreproFiles.includes(row.id); + } + /** * Checks if all rows are selected. */ diff --git a/src/app/tasks/new-tasks/new-tasks.component.html b/src/app/tasks/new-tasks/new-tasks.component.html index c20626d7f..3b9412764 100644 --- a/src/app/tasks/new-tasks/new-tasks.component.html +++ b/src/app/tasks/new-tasks/new-tasks.component.html @@ -188,7 +188,7 @@

Ta [fileType]="FileType.OTHER" [formData]="getFormData()" (updateFormEvent)="onUpdateForm($event)" - [cmdPrepro]="false" + [cmdPrepro]="isPreprocessor()" > diff --git a/src/app/tasks/new-tasks/new-tasks.component.spec.ts b/src/app/tasks/new-tasks/new-tasks.component.spec.ts index f001fd8e7..d2e655589 100644 --- a/src/app/tasks/new-tasks/new-tasks.component.spec.ts +++ b/src/app/tasks/new-tasks/new-tasks.component.spec.ts @@ -6,6 +6,7 @@ import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { UiSettings } from '@models/config-ui.schema'; +import { FileType } from '@models/file.model'; import { JPretask } from '@models/pretask.model'; import { ResponseWrapper } from '@models/response.model'; import { JTask } from '@models/task.model'; @@ -426,6 +427,60 @@ describe('NewTasksComponent', () => { expect(routerSpy.navigate).toHaveBeenCalledWith(['tasks/show-tasks']); }); + it('should merge the preprocessor files into the task files', async () => { + await initComponent(fixture); + + component.form.patchValue({ + taskName: 'My Task', + hashlistId: 1, + attackCmd: '-a 0 #HL# dict.txt', + priority: 0, + crackerBinaryId: 10, + files: [10] + }); + component['onUpdateForm']({ + type: 'CMD_PREPRO', + attackCmd: '--prince keyspace.txt', + files: [], + otherFiles: [10, 30] + }); + component.form.updateValueAndValidity(); + + await component['onSubmit'](); + + const payload = globalServiceSpy.create.calls.mostRecent().args[1] as { files: number[] }; + expect(payload.files).toEqual([10, 30]); + }); + + it('should drop the preprocessor files when the preprocessor is set to none', async () => { + await initComponent(fixture); + + component.form.patchValue({ + taskName: 'My Task', + hashlistId: 1, + attackCmd: '-a 0 #HL# dict.txt', + priority: 0, + crackerBinaryId: 10, + preprocessorId: 1, + files: [10] + }); + component['onUpdateForm']({ + type: 'CMD_PREPRO', + attackCmd: '--prince keyspace.txt', + files: [], + otherFiles: [30] + }); + + component.form.controls.preprocessorId.setValue(0); + component.form.updateValueAndValidity(); + + await component['onSubmit'](); + + const payload = globalServiceSpy.create.calls.mostRecent().args[1] as { files: number[] }; + expect(payload.files).toEqual([10]); + expect(component.form.controls.preprocessorCommand.value).toBe(''); + }); + it('should mark form as touched when invalid and not submit', async () => { await initComponent(fixture); @@ -900,6 +955,41 @@ describe('NewTasksComponent', () => { }); }); + describe('Preprocessor file selection', () => { + function cmdPreproByFileType(): Record { + const tables = Array.from(fixture.nativeElement.querySelectorAll('app-files-attack-table')) as Record< + string, + unknown + >[]; + + return Object.fromEntries(tables.map((table) => [table['fileType'] as number, table['cmdPrepro']])); + } + + it('should offer the preprocessor checkbox for wordlists and other files when a preprocessor is set', async () => { + await initComponent(fixture); + + component.form.controls.preprocessorId.setValue(5, { emitEvent: false }); + fixture.detectChanges(); + + const cmdPrepro = cmdPreproByFileType(); + expect(cmdPrepro[FileType.WORDLIST]).toBe(true); + expect(cmdPrepro[FileType.OTHER]).toBe(true); + expect(cmdPrepro[FileType.RULES]).toBe(false); + }); + + it('should hide the preprocessor checkbox on every file table when no preprocessor is set', async () => { + await initComponent(fixture); + + component.form.controls.preprocessorId.setValue(0, { emitEvent: false }); + fixture.detectChanges(); + + const cmdPrepro = cmdPreproByFileType(); + expect(cmdPrepro[FileType.WORDLIST]).toBe(false); + expect(cmdPrepro[FileType.OTHER]).toBe(false); + expect(cmdPrepro[FileType.RULES]).toBe(false); + }); + }); + describe('onUpdateForm', () => { it('should update attackCmd and files when event type is CMD', async () => { await initComponent(fixture); @@ -927,6 +1017,22 @@ describe('NewTasksComponent', () => { expect(component.form.controls.preprocessorCommand.value).toBe('--prince-elem-cnt-min=1'); }); + + it('should keep the preprocessor files out of the task files when event type is not CMD', async () => { + await initComponent(fixture); + + component.form.controls.files.setValue([10]); + + component['onUpdateForm']({ + type: 'CMD_PREPRO', + attackCmd: '--prince keyspace.txt', + files: [], + otherFiles: [30] + }); + + expect(component.form.controls.preprocessorCommand.value).toBe('--prince keyspace.txt'); + expect(component.form.controls.files.value).toEqual([10]); + }); }); describe('openHelpDialog', () => { diff --git a/src/app/tasks/new-tasks/new-tasks.component.ts b/src/app/tasks/new-tasks/new-tasks.component.ts index eafb799b4..989286bac 100644 --- a/src/app/tasks/new-tasks/new-tasks.component.ts +++ b/src/app/tasks/new-tasks/new-tasks.component.ts @@ -94,6 +94,8 @@ export class NewTasksComponent implements OnInit { copyFiles: FileId[]; editedIndex: number; + private preprocessorFiles: FileId[] = []; + // Tooltips tasktip: TaskTooltipsLevel; @@ -174,6 +176,10 @@ export class NewTasksComponent implements OnInit { }); this.form.controls.preprocessorId.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((newValue) => { + if (newValue === 0) { + this.preprocessorFiles = []; + this.form.controls.preprocessorCommand.setValue('', { emitEvent: false }); + } this.handleChangePreprocessor(newValue); }); } @@ -279,6 +285,7 @@ export class NewTasksComponent implements OnInit { return { attackCmd: this.form.controls.attackCmd.value, files: this.form.controls.files.value, + otherFiles: this.preprocessorFiles, preprocessorCommand: this.form.controls.preprocessorCommand.value }; } @@ -303,6 +310,7 @@ export class NewTasksComponent implements OnInit { files: event.files }); } else { + this.preprocessorFiles = event.otherFiles; this.form.patchValue({ preprocessorCommand: event.attackCmd }); @@ -408,6 +416,7 @@ export class NewTasksComponent implements OnInit { const payload = { ...this.form.value }; delete payload.crackerBinaryTypeId; + payload.files = [...new Set([...(payload.files ?? []), ...this.preprocessorFiles])]; this.gs.create(SERV.TASKS, payload).subscribe({ next: () => {