diff --git a/src/Commands/GetEntryPoints.ts b/src/Commands/GetEntryPoints.ts new file mode 100644 index 00000000..a152b4ed --- /dev/null +++ b/src/Commands/GetEntryPoints.ts @@ -0,0 +1,70 @@ +import { EntryPoint, EntryPoints, RestProjectsOptions } from '../EntryPoint'; +import { logger } from '../Utils'; +import { Arguments, Argv, CommandModule } from 'yargs'; +import { container } from 'tsyringe'; + +export class GetEntryPoints implements CommandModule { + public readonly command = 'entrypoints:list [options]'; + public readonly describe = 'get all entrypoints of the project.'; + + public builder(argv: Argv): Argv { + return argv + .option('token', { + alias: 't', + describe: 'Bright API-key', + requiresArg: true, + demandOption: true + }) + .option('project', { + alias: 'p', + describe: 'ID of the project', + requiresArg: true, + demandOption: true + }) + .option('verbose', { + describe: 'Enable verbose mode', + boolean: true, + default: false + }) + .middleware((args: Arguments) => + container.register(RestProjectsOptions, { + useValue: { + insecure: args.insecure as boolean, + baseURL: args.api as string, + apiKey: args.token as string, + proxyURL: (args.proxyExternal ?? args.proxy) as string + } + }) + ); + } + + public async handler(args: Arguments): Promise { + const entryPointsManager: EntryPoints = container.resolve(EntryPoints); + + try { + const entryPoints: EntryPoint[] = await entryPointsManager.entrypoints( + args.project as string + ); + + if (args.verbose) { + // eslint-disable-next-line no-console + console.log('%j', entryPoints); + } else { + // eslint-disable-next-line no-console + console.log( + '%j', + entryPoints.map((entryPoint) => ({ + id: entryPoint.id, + method: entryPoint.method, + url: entryPoint.url + })) + ); + } + + process.exitCode = 0; + } catch (e) { + logger.error(`Error during "entrypoints:list": ${e.error || e.message}`); + process.exitCode = 1; + } + } +} diff --git a/src/Commands/index.ts b/src/Commands/index.ts index 02b3e5d8..0dc609bf 100644 --- a/src/Commands/index.ts +++ b/src/Commands/index.ts @@ -6,3 +6,4 @@ export { StopScan } from './StopScan'; export { PollingScanStatus } from './PollingScanStatus'; export { RunRepeater } from './RunRepeater'; export { Configure } from './Configure'; +export { GetEntryPoints } from './GetEntryPoints'; diff --git a/src/Config/container.ts b/src/Config/container.ts index 52506ea6..8c769656 100644 --- a/src/Config/container.ts +++ b/src/Config/container.ts @@ -34,6 +34,7 @@ import { RestScans, Scans } from '../Scan'; +import { EntryPoints, RestEntryPoints } from '../EntryPoint'; import { Archives, DefaultParserFactory, @@ -166,6 +167,11 @@ container { lifecycle: Lifecycle.Singleton } ) .register(Scans, { useClass: RestScans }, { lifecycle: Lifecycle.Singleton }) + .register( + EntryPoints, + { useClass: RestEntryPoints }, + { lifecycle: Lifecycle.Singleton } + ) .register( Archives, { useClass: RestArchives }, diff --git a/src/EntryPoint/EntryPoints.ts b/src/EntryPoint/EntryPoints.ts new file mode 100644 index 00000000..7c0d1a91 --- /dev/null +++ b/src/EntryPoint/EntryPoints.ts @@ -0,0 +1,22 @@ +export interface EntryPoints { + entrypoints(projectId: string): Promise; +} + +export const EntryPoints: unique symbol = Symbol('EntryPoints'); + +export interface EntryPoint { + id: string; + method: string; + url: string; + responseStatus: number; + connectivity: string; + lastUpdated: string; + lastEdited: string; + lastValidated: string; + parametersCount: number; + responseTime: number; + status: string; + openIssuesCount: number; + closedIssuesCount: number; + createdAt: string; +} diff --git a/src/EntryPoint/RestEntryPoints.ts b/src/EntryPoint/RestEntryPoints.ts new file mode 100644 index 00000000..77c8f42a --- /dev/null +++ b/src/EntryPoint/RestEntryPoints.ts @@ -0,0 +1,60 @@ +import { EntryPoints, EntryPoint } from './EntryPoints'; +import { ProxyFactory } from '../Utils'; +import axios, { Axios } from 'axios'; +import { inject, injectable } from 'tsyringe'; +import http from 'node:http'; +import https from 'node:https'; + +export interface RestProjectsOptions { + baseURL: string; + apiKey: string; + timeout?: number; + insecure?: boolean; + proxyURL?: string; +} + +export const RestProjectsOptions: unique symbol = Symbol('RestProjectsOptions'); + +@injectable() +export class RestEntryPoints implements EntryPoints { + private readonly client: Axios; + + constructor( + @inject(ProxyFactory) private readonly proxyFactory: ProxyFactory, + @inject(RestProjectsOptions) + { + baseURL, + apiKey, + insecure, + proxyURL, + timeout = 10000 + }: RestProjectsOptions + ) { + const { + httpAgent = new http.Agent(), + httpsAgent = new https.Agent({ rejectUnauthorized: !insecure }) + } = proxyURL + ? this.proxyFactory.createProxy({ + proxyUrl: proxyURL, + rejectUnauthorized: !insecure + }) + : {}; + + this.client = axios.create({ + baseURL, + timeout, + httpAgent, + httpsAgent, + responseType: 'json', + headers: { authorization: `Api-Key ${apiKey}` } + }); + } + + public async entrypoints(projectId: string): Promise { + const res = await this.client.get( + `/api/v2/projects/${projectId}/entry-points` + ); + + return res.data.items; + } +} diff --git a/src/EntryPoint/index.ts b/src/EntryPoint/index.ts new file mode 100644 index 00000000..acec926f --- /dev/null +++ b/src/EntryPoint/index.ts @@ -0,0 +1,2 @@ +export * from './EntryPoints'; +export * from './RestEntryPoints'; diff --git a/src/index.ts b/src/index.ts index 3c7f396d..15654a86 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,7 +11,8 @@ import { StopScan, UploadArchive, VersionCommand, - Configure + Configure, + GetEntryPoints } from './Commands'; import { CliBuilder, container } from './Config'; @@ -24,6 +25,7 @@ container.resolve(CliBuilder).build({ new RetestScan(), new StopScan(), new UploadArchive(), - new Configure() + new Configure(), + new GetEntryPoints() ] }).argv;