Skip to content

Commit

Permalink
Merge branch 'next'
Browse files Browse the repository at this point in the history
  • Loading branch information
maksadbek committed Aug 21, 2024
2 parents ddf4493 + c2172d9 commit c821ed8
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 47 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@brightsec/cli",
"version": "12.2.1",
"version": "12.3.0-next.3",
"private": false,
"repository": {
"type": "git",
Expand Down
4 changes: 3 additions & 1 deletion src/Commands/GetEntryPoints.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ describe('GetEntryPoints', () => {
} as Arguments;

when(processSpy.exit(anything())).thenReturn(undefined);
when(mockedEntryPoints.entrypoints('1')).thenResolve([
when(
mockedEntryPoints.entrypoints({ projectId: '1', limit: 10 })
).thenResolve([
{
id: '1',
method: 'GET',
Expand Down
30 changes: 27 additions & 3 deletions src/Commands/GetEntryPoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,27 @@ export class GetEntryPoints implements CommandModule {
boolean: true,
default: false
})
.option('limit', {
describe: 'Limit the number of entrypoints',
default: 10
})
.option('connectivity', {
describe: 'Filter by connectivity',
array: true,
choices: [
'ok',
'unreachable',
'problem',
'skipped',
'unauthorized',
'unavailable'
]
})
.option('status', {
describe: 'Filter by status',
array: true,
choices: ['new', 'changed', 'tested', 'vulnerable']
})
.middleware((args: Arguments) =>
container.register<RestProjectsOptions>(RestProjectsOptions, {
useValue: {
Expand All @@ -42,9 +63,12 @@ export class GetEntryPoints implements CommandModule {
const entryPointsManager: EntryPoints = container.resolve(EntryPoints);

try {
const entryPoints: EntryPoint[] = await entryPointsManager.entrypoints(
args.project as string
);
const entryPoints: EntryPoint[] = await entryPointsManager.entrypoints({
projectId: args.project as string,
limit: args.limit as number,
connectivity: args.connectivity as string[],
status: args.status as string[]
});

if (args.verbose) {
// eslint-disable-next-line no-console
Expand Down
5 changes: 2 additions & 3 deletions src/Commands/RunScan.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import {
Exclusions,
Module,
Scans,
ScanWarning,
TestType
ScanWarning
} from '../Scan';
import {
anything,
Expand Down Expand Up @@ -128,7 +127,7 @@ describe('RunScan', () => {
when(
mockedScans.create(
objectContaining({
tests: args.test as TestType[],
tests: args.test as string[],
name: args.name as string,
module: args.module as Module,
authObjectId: args.auth as string,
Expand Down
11 changes: 7 additions & 4 deletions src/Commands/RunScan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { Helpers, logger } from '../Utils';
import { Arguments, Argv, CommandModule } from 'yargs';
import { container } from 'tsyringe';
import { isAxiosError } from 'axios';
import { EOL } from 'node:os';

export class RunScan implements CommandModule {
Expand Down Expand Up @@ -157,9 +158,7 @@ export class RunScan implements CommandModule {
array: true,
alias: 'e',
describe:
'A list of entry points to start the scan from' +
'The maximum number of entry points allowed is 2000' +
'Pass an empty list to use the first 2000 entry points of project'
'List entrypoint IDs to scan specific entrypoints. If no IDs are provided, the scan will run on the first 2000 project-level entrypoints. This option requires to specify the project ID using the --project option.'
})
.group(['archive', 'crawler'], 'Discovery Options')
.group(
Expand Down Expand Up @@ -215,7 +214,11 @@ export class RunScan implements CommandModule {

process.exit(0);
} catch (e) {
logger.error(`Error during "scan:run": ${e.error || e.message}`);
const errMessage =
isAxiosError(e) && typeof e.response?.data === 'string'
? e.response.data
: e.error || e.message;
logger.error(`Error during "scan:run": ${errMessage}`);
process.exit(1);
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/EntryPoint/EntryPoints.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
export interface EntryPointsListOptions {
projectId: string;
limit?: number;
connectivity?: string[];
status?: string[];
}

export interface EntryPoints {
entrypoints(projectId: string): Promise<EntryPoint[]>;
entrypoints(filter: EntryPointsListOptions): Promise<EntryPoint[]>;
}

export const EntryPoints: unique symbol = Symbol('EntryPoints');
Expand Down
72 changes: 70 additions & 2 deletions src/EntryPoint/RestEntryPoints.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,80 @@ describe('RestEntryPoints', () => {
describe('entrypoints', () => {
it('should return entrypoints', async () => {
nock('https://example.com')
.get('/api/v2/projects/1/entry-points')
.get('/api/v2/projects/1/entry-points?limit=10')
.reply(200, { items: [{ id: 1, name: 'entrypoint1' }] });

const result = await restEntryPoints.entrypoints('1');
const result = await restEntryPoints.entrypoints({
projectId: '1',
limit: 10
});

expect(result).toEqual([{ id: 1, name: 'entrypoint1' }]);
});

it('should paginate', async () => {
nock('https://example.com')
.get('/api/v2/projects/1/entry-points?limit=50')
.reply(200, {
items: [
{
id: 1,
name: 'entrypoint1',
createdAt: '2024-08-06T09:40:50.226Z'
},
{
id: 2,
name: 'entrypoint1',
createdAt: '2024-08-06T09:40:50.227Z'
}
]
});

nock('https://example.com')
.get(
'/api/v2/projects/1/entry-points?limit=50&nextId=2&nextCreatedAt=2024-08-06T09:40:50.227Z'
)
.reply(200, {
items: [
{
id: 10,
name: 'entrypoint1',
createdAt: '2024-08-06T09:40:50.228Z'
},
{
id: 11,
name: 'entrypoint1',
createdAt: '2024-08-06T09:40:50.229Z'
}
]
});

nock('https://example.com')
.get(
'/api/v2/projects/1/entry-points?limit=11&nextId=11&nextCreatedAt=2024-08-06T09:40:50.229Z'
)
.reply(200, {
items: [
{
id: 113,
name: 'entrypoint1',
createdAt: '2024-08-06T09:40:50.230Z'
}
]
});

const result = await restEntryPoints.entrypoints({
projectId: '1',
limit: 111
});

expect(result).toEqual([
{ id: 1, name: 'entrypoint1', createdAt: '2024-08-06T09:40:50.226Z' },
{ id: 2, name: 'entrypoint1', createdAt: '2024-08-06T09:40:50.227Z' },
{ id: 10, name: 'entrypoint1', createdAt: '2024-08-06T09:40:50.228Z' },
{ id: 11, name: 'entrypoint1', createdAt: '2024-08-06T09:40:50.229Z' },
{ id: 113, name: 'entrypoint1', createdAt: '2024-08-06T09:40:50.230Z' }
]);
});
});
});
40 changes: 34 additions & 6 deletions src/EntryPoint/RestEntryPoints.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EntryPoints, EntryPoint } from './EntryPoints';
import { EntryPoints, EntryPoint, EntryPointsListOptions } from './EntryPoints';
import { ProxyFactory } from '../Utils';
import axios, { Axios } from 'axios';
import { inject, injectable } from 'tsyringe';
Expand All @@ -18,6 +18,7 @@ export const RestProjectsOptions: unique symbol = Symbol('RestProjectsOptions');

@injectable()
export class RestEntryPoints implements EntryPoints {
private readonly entrypointsPaginationBatchSize = 50;
private readonly client: Axios;

constructor(
Expand Down Expand Up @@ -51,11 +52,38 @@ export class RestEntryPoints implements EntryPoints {
});
}

public async entrypoints(projectId: string): Promise<EntryPoint[]> {
const res = await this.client.get(
`/api/v2/projects/${projectId}/entry-points`
);
public async entrypoints({
limit = 10,
projectId,
...filters
}: EntryPointsListOptions): Promise<EntryPoint[]> {
let remaining = limit;
const data: EntryPoint[] = [];
let nextId: string;
let nextCreatedAt: string;

return res.data.items;
while (remaining > 0) {
const {
data: { items = [] }
} = await this.client.get(`/api/v2/projects/${projectId}/entry-points`, {
params: {
nextId,
nextCreatedAt,
...filters,
limit: Math.min(remaining, this.entrypointsPaginationBatchSize)
}
});

if (!items.length) {
break;
}

data.push(...items);
({ id: nextId, createdAt: nextCreatedAt } = items[items.length - 1]);

remaining -= this.entrypointsPaginationBatchSize;
}

return data;
}
}
24 changes: 0 additions & 24 deletions src/Repeater/DefaultRepeaterServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ const enum SocketEvents {
ERROR = 'error',
UPDATE_AVAILABLE = 'update-available',
SCRIPT_UPDATED = 'scripts-updated',
PING = 'ping',
REQUEST = 'request',
LIMITS = 'limits'
}
Expand Down Expand Up @@ -84,13 +83,11 @@ interface SocketEmitEventMap {
runtime?: DeploymentRuntime
) => void;
[SocketEvents.UNDEPLOY]: () => void;
[SocketEvents.PING]: () => void;
}

@injectable()
export class DefaultRepeaterServer implements RepeaterServer {
private readonly MAX_DEPLOYMENT_TIMEOUT = 60_000;
private readonly MAX_PING_INTERVAL = 10_000;
private readonly MAX_RECONNECTION_ATTEMPTS = 20;
private readonly MIN_RECONNECTION_DELAY = 1000;
private readonly MAX_RECONNECTION_DELAY = 86_400_000;
Expand All @@ -100,7 +97,6 @@ export class DefaultRepeaterServer implements RepeaterServer {
HandlerFunction
>();
private latestReconnectionError?: Error;
private pingTimer?: Timer;
private connectionTimer?: Timer;
private _socket?: Socket<SocketListeningEventMap, SocketEmitEventMap>;
private connectionAttempts = 0;
Expand All @@ -123,7 +119,6 @@ export class DefaultRepeaterServer implements RepeaterServer {

public disconnect() {
this.events.removeAllListeners();
this.clearPingTimer();
this.clearConnectionTimer();

this._socket?.disconnect();
Expand All @@ -150,8 +145,6 @@ export class DefaultRepeaterServer implements RepeaterServer {
)
]);

this.createPingTimer();

return result;
}

Expand Down Expand Up @@ -379,8 +372,6 @@ export class DefaultRepeaterServer implements RepeaterServer {
};

private handleDisconnect = (reason: string): void => {
this.clearPingTimer();

if (reason !== 'io client disconnect') {
this.events.emit(RepeaterServerEvents.DISCONNECTED);
}
Expand All @@ -400,19 +391,4 @@ export class DefaultRepeaterServer implements RepeaterServer {
);
logger.error(error);
}

private createPingTimer() {
this.clearPingTimer();

this.pingTimer = setInterval(
() => this.socket.volatile.emit(SocketEvents.PING),
this.MAX_PING_INTERVAL
).unref();
}

private clearPingTimer() {
if (this.pingTimer) {
clearInterval(this.pingTimer);
}
}
}

0 comments on commit c821ed8

Please sign in to comment.