Skip to content

Feature/port services display filter #352

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export class CustomJobsController {
return await this.customJobsService.duplicate(dto.jobId);
} else {
const jobDto = plainToInstance(JobDto, dto);
console.log(jobDto);
await validateOrReject(jobDto);
return await this.customJobsService.create(dto);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
IsMongoId,
IsNotEmpty,
IsOptional,
IsString,
} from 'class-validator';
import { Types } from 'mongoose';
import { PortDetailsLevel, portDetailsLevel } from '../../database.constants';
Expand All @@ -33,6 +34,21 @@ export class PortFilterDto extends IntersectionType(
@IsOptional()
@IsMongoId()
hostId: string;

@IsOptional()
@IsString({ each: true })
@IsArray()
services: string[];

@IsOptional()
@IsString({ each: true })
@IsArray()
products: string[];

@IsOptional()
@IsString({ each: true })
@IsArray()
versions: string[];
}

export class GetPortsDto extends IntersectionType(PagingDto, PortFilterDto) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ export class Port {

@Prop()
service: string;

@Prop()
product: string;

@Prop()
version: string;
}

export const PortSchema = SchemaFactory.createForClass(Port);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { getModelToken } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { Model } from 'mongoose';
import { getName } from '../../../../test/test.utils';
import { AppModule } from '../../../app.module';
import { TagsDocument } from '../../tags/tag.model';
Expand All @@ -8,6 +10,7 @@ import { HostService } from '../host/host.service';
import { CreateProjectDto } from '../project.dto';
import { ProjectService } from '../project.service';
import { GetPortsDto } from './port.dto';
import { Port, PortDocument } from './port.model';
import { PortService } from './port.service';

describe('Port Service', () => {
Expand All @@ -17,6 +20,7 @@ describe('Port Service', () => {
let projectService: ProjectService;
let tagsService: TagsService;
let portService: PortService;
let portModel: Model<Port>;
const testPrefix = 'port-service-ut';

beforeAll(async () => {
Expand All @@ -28,6 +32,7 @@ describe('Port Service', () => {
projectService = moduleFixture.get(ProjectService);
tagsService = moduleFixture.get(TagsService);
portService = moduleFixture.get(PortService);
portModel = moduleFixture.get<Model<Port>>(getModelToken('port'));
});

beforeEach(async () => {
Expand Down Expand Up @@ -566,6 +571,89 @@ describe('Port Service', () => {
);
},
);

it.each([
{
service: 'asdf',
product: 'qwerty',
version: 'uiop',
dto: { services: ['asdf1', 'asdf2'] },
expectedPorts: [1, 2],
},
{
service: 'asdf',
product: 'qwerty',
version: 'uiop',
dto: { services: ['asdf1', 'asdf2'], products: ['qwerty1'] },
expectedPorts: [1],
},
{
service: 'asdf',
product: 'qwerty',
version: 'uiop',
dto: { services: ['asdf1', 'asdf2'], versions: ['uiop2'] },
expectedPorts: [2],
},
{
service: 'asdf',
product: 'qWErty',
version: 'uiop',
dto: { services: ['asdf1', 'asdf2'], products: ['qwerty2', 'qwerty3'] },
expectedPorts: [2],
},
{
service: 'asdf',
product: 'qWErty',
version: 'uiOP',
dto: { versions: ['uiop3'], products: ['qwerty2', 'qwerty3'] },
expectedPorts: [3],
},
])(
'Filter by service, product, version: %s',
async ({ service, product, version, dto, expectedPorts }) => {
// Arrange
const p1 = await project('p1');

const hosts = [{ host: '1.1.1.1', ports: [1, 2, 3, 4] }];
const hDocs = [];

const ports: PortDocument[] = [];

for (const h of hosts) {
const htmp = await host(h.host, p1._id.toString());
hDocs.push(htmp[0]);
for (const p of h.ports) {
ports.push(
await portService.addPort(
htmp[0]._id.toString(),
p1._id.toString(),
p,
'tcp',
),
);
}
}

for (const port of ports) {
await portModel.updateOne(
{ _id: { $eq: port._id } },
{
service: service + port.port,
product: product + port.port,
version: version + port.port,
},
);
}

// Act
const allPorts = await portService.getAll(0, 10, dto);

// Assert
expect(allPorts.map((x) => x.port).sort()).toStrictEqual(
expectedPorts.sort(),
);
},
);
});

async function project(name: string = '') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export class PortService {
portNumber: number,
protocol: 'tcp' | 'udp',
service: string = undefined,
product: string = undefined,
version: string = undefined,
) {
const host: Pick<HostDocument, '_id' | 'ip'> = await this.hostModel.findOne(
{
Expand All @@ -64,6 +66,8 @@ export class PortService {
protocol,
correlationKey,
service,
product,
version,
);
}

Expand Down Expand Up @@ -111,6 +115,8 @@ export class PortService {
protocol: string,
correlationKey: string,
service: string = undefined,
product: string = undefined,
version: string = undefined,
) {
if (!(protocol === 'tcp' || protocol === 'udp'))
throw new HttpBadRequestException(this.badProtocolError);
Expand Down Expand Up @@ -143,7 +149,12 @@ export class PortService {
let setter =
service === undefined
? { lastSeen: Date.now() }
: { lastSeen: Date.now(), service: service };
: {
lastSeen: Date.now(),
service: service,
product: product,
version: version,
};

res = await this.portsModel.findOneAndUpdate(
{
Expand Down Expand Up @@ -385,6 +396,45 @@ export class PortService {
}
}

// Filter by port service
if (dto.services) {
const servicesRegex = dto.services
.filter((x) => x)
.map((x) => x.toLowerCase().trim())
.map((x) => escapeStringRegexp(x))
.map((x) => new RegExp(`.*${x}.*`));

if (servicesRegex.length > 0) {
finalFilter['service'] = { $in: servicesRegex };
}
}

// Filter by port products
if (dto.products) {
const productsRegex = dto.products
.filter((x) => x)
.map((x) => x.toLowerCase().trim())
.map((x) => escapeStringRegexp(x))
.map((x) => new RegExp(`.*${x}.*`, 'i'));

if (productsRegex.length > 0) {
finalFilter['product'] = { $in: productsRegex };
}
}

// Filter by port versions
if (dto.versions) {
const versionsRegex = dto.versions
.filter((x) => x)
.map((x) => x.toLowerCase().trim())
.map((x) => escapeStringRegexp(x))
.map((x) => new RegExp(`.*${x}.*`, 'i'));

if (versionsRegex.length > 0) {
finalFilter['version'] = { $in: versionsRegex };
}
}

// Filter by project
if (dto.projects) {
const projectIds = dto.projects
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,15 @@ export class CustomFindingHandler extends JobFindingHandlerBase<CustomFindingCom
private async handlePortServiceFinding(command: CustomFindingCommand) {
try {
let service: string = undefined;
let product: string = undefined;
let version: string = undefined;
for (const f of command.finding.fields) {
if (f.key === CustomFindingsConstants.ServiceNameFieldKey)
service = f.data;
else if (f.key === CustomFindingsConstants.ServiceProductFieldKey)
product = f.data;
else if (f.key === CustomFindingsConstants.ServiceVersionFieldKey)
version = f.data;
}

if (service === 'http' || service === 'https') {
Expand All @@ -65,6 +71,8 @@ export class CustomFindingHandler extends JobFindingHandlerBase<CustomFindingCom
command.finding.port,
command.finding.protocol,
service.trim(),
product.trim(),
version.trim(),
);
} catch (err) {
this.logger.error("Error happened while adding a port's service");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ export class TagHandler extends JobFindingHandlerBase<TagCommand> {
const service = CorrelationKeyUtils.getResourceServiceName(
command.finding.correlationKey,
);
console.log(service);
console.log(command.finding.correlationKey);

switch (service) {
case 'DomainsService':
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export class CustomFindingsConstants {
public static readonly PortServiceFinding = 'PortServiceFinding';
public static readonly ServiceNameFieldKey = 'serviceName';
public static readonly ServiceProductFieldKey = 'serviceProduct';
public static readonly ServiceVersionFieldKey = 'serviceVersion';

public static readonly WebsitePathFinding = 'WebsitePathFinding';
public static readonly WebsiteEndpointFieldKey = 'endpoint';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,6 @@ export class JobsService {

private filterJob(job: CustomJob, filters: string[]) {
const parts = [job.name, job.findingHandlerLanguage, job.type];
return filters.some((filter) => normalizeSearchString(parts.join(' ')).includes(filter));
return filters.some((filter) => normalizeSearchString(parts.join(' ')).includes(normalizeSearchString(filter)));
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Injectable } from '@angular/core';
import { Observable, combineLatest, map } from 'rxjs';
import { stringify } from 'yaml';
import { Page } from '../../../shared/types/page.type';
import {
CronSubscription,
CronSubscriptionData,
EventSubscription,
EventSubscriptionData,
} from '../../../shared/types/subscriptions/subscription.type';
import { stringify } from 'yaml';
import { Page } from '../../../shared/types/page.type';
import { normalizeSearchString } from '../../../utils/normalize-search-string';
import { CronSubscriptionsService } from './cron-subscriptions.service';
import { EventSubscriptionsService } from './event-subscriptions.service';
Expand Down Expand Up @@ -116,6 +116,6 @@ export class SubscriptionService {
cron.cronExpression ? 'cron' : 'event',
subscription.isEnabled === false ? 'disabled' : 'enabled',
];
return filters.some((filter) => normalizeSearchString(parts.join(' ')).includes(filter));
return filters.some((filter) => normalizeSearchString(parts.join(' ')).includes(normalizeSearchString(filter)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ export class TagsService {
}
private filterTags(secret: Tag, filters: string[]) {
const parts = [secret?.text, secret.color];
return filters.some((filter) => normalizeSearchString(parts.join(' ')).includes(filter));
return filters.some((filter) => normalizeSearchString(parts.join(' ')).includes(normalizeSearchString(filter)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class FindingComponent {
constructor(private toastr: ToastrService) {}

public copyJsonToClipboard() {
navigator.clipboard.writeText(JSON.stringify(this.finding));
navigator.clipboard.writeText(JSON.stringify(this.finding, undefined, 2));
this.toastr.success(
$localize`:Finding copied to clipboard|Finding copied to clipboard:Finding copied to clipboard`
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ export class ListIpRangesComponent {
}

dateFilter(event: MouseEvent) {
console.log('DATE FILTER');
event.stopPropagation();
this.startDate = new Date(Date.now() - defaultNewTimeMs);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,6 @@ export class LaunchJobsComponent implements AfterViewInit {

private filterJob(entry: JobListEntry, filter: string) {
const parts = [entry.name];
return normalizeSearchString(parts.join(' ')).includes(filter);
return normalizeSearchString(parts.join(' ')).includes(normalizeSearchString(filter));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { Title } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { BehaviorSubject, combineLatest, map, shareReplay, switchMap, tap } from 'rxjs';
import { SubscriptionService, SubscriptionType } from '../../../api/jobs/subscriptions/subscriptions.service';
import { AvatarComponent } from '../../../shared/components/avatar/avatar.component';
import {
CronSubscription,
EventSubscription,
Expand Down Expand Up @@ -44,7 +43,6 @@ import { subscriptionTypes } from './subscription-templates';
imports: [
CommonModule,
RouterModule,
AvatarComponent,
MatIconModule,
MatTableModule,
MatPaginatorModule,
Expand Down
Loading
Loading