From 6524688bc8b80f6aeda7fc9bda53941dfb880587 Mon Sep 17 00:00:00 2001 From: Viachaslau Date: Thu, 23 Jan 2025 18:42:42 +0400 Subject: [PATCH] feat(reporter): github check-run reporter (#224) --- package-lock.json | 10 +-- packages/reporter/package.json | 4 +- packages/reporter/src/index.ts | 2 +- .../github/GitHubCheckRunReporter.ts | 67 ++++++++++++++++ .../reporters/github/api/GitHubApiClient.ts | 32 ++++++++ .../src/reporters/github/api/GitHubClient.ts | 7 ++ .../src/reporters/github/api/GitHubConfig.ts | 7 ++ .../src/reporters/github/api/index.ts | 2 + .../src/reporters/github/api/register.ts | 13 +++ .../github/builders/BasePayloadBuilder.ts | 33 ++++++++ .../github/builders/CheckRunPayloadBuilder.ts | 5 ++ .../builders/MultiItemsPayloadBuilder.ts | 70 ++++++++++++++++ .../builders/SingleItemPayloadBuilder.ts | 80 +++++++++++++++++++ .../src/reporters/github/builders/index.ts | 3 + .../reporter/src/reporters/github/index.ts | 2 + .../github/types/CheckRunAnnotation.ts | 5 ++ .../reporters/github/types/CheckRunPayload.ts | 4 + .../reporters/github/types/GitHubConfig.ts | 5 ++ .../src/reporters/github/types/index.ts | 3 + packages/reporter/src/reporters/index.ts | 2 + .../{ => reporters}/std/StdReporter.spec.ts | 0 .../src/{ => reporters}/std/StdReporter.ts | 4 +- .../reporter/src/{ => reporters}/std/index.ts | 0 packages/runner/src/lib/SecRunner.ts | 23 +++++- packages/runner/src/lib/SecScan.ts | 6 +- 25 files changed, 376 insertions(+), 13 deletions(-) create mode 100644 packages/reporter/src/reporters/github/GitHubCheckRunReporter.ts create mode 100644 packages/reporter/src/reporters/github/api/GitHubApiClient.ts create mode 100644 packages/reporter/src/reporters/github/api/GitHubClient.ts create mode 100644 packages/reporter/src/reporters/github/api/GitHubConfig.ts create mode 100644 packages/reporter/src/reporters/github/api/index.ts create mode 100644 packages/reporter/src/reporters/github/api/register.ts create mode 100644 packages/reporter/src/reporters/github/builders/BasePayloadBuilder.ts create mode 100644 packages/reporter/src/reporters/github/builders/CheckRunPayloadBuilder.ts create mode 100644 packages/reporter/src/reporters/github/builders/MultiItemsPayloadBuilder.ts create mode 100644 packages/reporter/src/reporters/github/builders/SingleItemPayloadBuilder.ts create mode 100644 packages/reporter/src/reporters/github/builders/index.ts create mode 100644 packages/reporter/src/reporters/github/index.ts create mode 100644 packages/reporter/src/reporters/github/types/CheckRunAnnotation.ts create mode 100644 packages/reporter/src/reporters/github/types/CheckRunPayload.ts create mode 100644 packages/reporter/src/reporters/github/types/GitHubConfig.ts create mode 100644 packages/reporter/src/reporters/github/types/index.ts create mode 100644 packages/reporter/src/reporters/index.ts rename packages/reporter/src/{ => reporters}/std/StdReporter.spec.ts (100%) rename packages/reporter/src/{ => reporters}/std/StdReporter.ts (97%) rename packages/reporter/src/{ => reporters}/std/index.ts (100%) diff --git a/package-lock.json b/package-lock.json index 2b1421d4..c4e0ebea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4618,7 +4618,6 @@ "version": "22.2.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", - "dev": true, "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { @@ -4758,7 +4757,6 @@ "version": "13.5.0", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", - "dev": true, "license": "MIT", "dependencies": { "@octokit/openapi-types": "^22.2.0" @@ -20300,8 +20298,10 @@ "version": "0.33.3", "license": "MIT", "dependencies": { + "@octokit/types": "^13.5.0", "chalk": "^4.1.2", "tslib": "~2.6.3", + "tsyringe": "^4.8.0", "tty-table": "^4.2.3" }, "engines": { @@ -23433,8 +23433,7 @@ "@octokit/openapi-types": { "version": "22.2.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", - "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", - "dev": true + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" }, "@octokit/plugin-paginate-rest": { "version": "9.2.1", @@ -23544,7 +23543,6 @@ "version": "13.5.0", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", - "dev": true, "requires": { "@octokit/openapi-types": "^22.2.0" } @@ -23707,8 +23705,10 @@ "@sectester/reporter": { "version": "file:packages/reporter", "requires": { + "@octokit/types": "^13.5.0", "chalk": "^4.1.2", "tslib": "~2.6.3", + "tsyringe": "^4.8.0", "tty-table": "^4.2.3" } }, diff --git a/packages/reporter/package.json b/packages/reporter/package.json index 24ab6a74..74487744 100644 --- a/packages/reporter/package.json +++ b/packages/reporter/package.json @@ -36,7 +36,9 @@ "dependencies": { "chalk": "^4.1.2", "tslib": "~2.6.3", - "tty-table": "^4.2.3" + "tsyringe": "^4.8.0", + "tty-table": "^4.2.3", + "@octokit/types": "^13.5.0" }, "peerDependencies": { "@sectester/scan": ">=0.16.0 <1.0.0" diff --git a/packages/reporter/src/index.ts b/packages/reporter/src/index.ts index ff4dbf37..f30de64e 100644 --- a/packages/reporter/src/index.ts +++ b/packages/reporter/src/index.ts @@ -1,3 +1,3 @@ export { PlainTextFormatter } from './formatters'; export { Reporter, Formatter } from './lib'; -export { StdReporter } from './std'; +export { GitHubCheckRunReporter, StdReporter } from './reporters'; diff --git a/packages/reporter/src/reporters/github/GitHubCheckRunReporter.ts b/packages/reporter/src/reporters/github/GitHubCheckRunReporter.ts new file mode 100644 index 00000000..934b7166 --- /dev/null +++ b/packages/reporter/src/reporters/github/GitHubCheckRunReporter.ts @@ -0,0 +1,67 @@ +import { Reporter } from '../../lib'; +import { GITHUB_CLIENT, GITHUB_CONFIG } from './api'; +import type { GitHubClient } from './api'; +import { SingleItemPayloadBuilder, MultiItemsPayloadBuilder } from './builders'; +import type { CheckRunPayloadBuilder } from './builders'; +import type { GitHubConfig } from './types'; +import { inject, injectable } from 'tsyringe'; +import type { Issue, Scan } from '@sectester/scan'; +import path from 'node:path'; + +// TODO add `GitHubCheckRunReporter` description to README +@injectable() +export class GitHubCheckRunReporter implements Reporter { + constructor( + @inject(GITHUB_CONFIG) private readonly config: GitHubConfig, + @inject(GITHUB_CLIENT) private readonly githubClient: GitHubClient + ) { + if (!this.config.token) { + throw new Error('GitHub token is not set'); + } + + if (!this.config.repository) { + throw new Error('GitHub repository is not set'); + } + + if (!this.config.commitSha) { + throw new Error('GitHub commitSha is not set'); + } + } + + public async report(scan: Scan): Promise { + const issues = await scan.issues(); + if (issues.length === 0) return; + + const checkRunPayload = this.createCheckRunPayloadBuilder(issues).build(); + await this.githubClient.createCheckRun(checkRunPayload); + } + + private createCheckRunPayloadBuilder( + issues: Issue[] + ): CheckRunPayloadBuilder { + return issues.length === 1 + ? new SingleItemPayloadBuilder( + issues[0], + this.config.commitSha, + this.getTestFilePath() + ) + : new MultiItemsPayloadBuilder( + issues, + this.config.commitSha, + this.getTestFilePath() + ); + } + + // TODO subject to improvement + private getTestFilePath(): string { + const state = (global as any).expect?.getState(); + if (!state) { + return 'unknown'; + } + + const testPath = state.testPath; + const rootDir = state.snapshotState._rootDir; + + return path.join(path.basename(rootDir), path.relative(rootDir, testPath)); + } +} diff --git a/packages/reporter/src/reporters/github/api/GitHubApiClient.ts b/packages/reporter/src/reporters/github/api/GitHubApiClient.ts new file mode 100644 index 00000000..6380f518 --- /dev/null +++ b/packages/reporter/src/reporters/github/api/GitHubApiClient.ts @@ -0,0 +1,32 @@ +import type { CheckRunPayload, GitHubConfig } from '../types'; +import type { GitHubClient } from './GitHubClient'; +import { GITHUB_CONFIG } from './GitHubConfig'; +import { inject, injectable } from 'tsyringe'; + +@injectable() +export class GitHubApiClient implements GitHubClient { + constructor(@inject(GITHUB_CONFIG) private readonly config: GitHubConfig) {} + + public async createCheckRun(payload: CheckRunPayload): Promise { + const requestOptions = { + method: 'POST', + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'Authorization': `Bearer ${this.config.token}`, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'Accept': 'application/vnd.github.v3+json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload) + }; + + const res = await fetch( + `https://api.github.com/repos/${this.config.repository}/check-runs`, + requestOptions + ); + + if (!res.ok) { + throw new Error(`GitHub API error: ${res.status} ${res.statusText}`); + } + } +} diff --git a/packages/reporter/src/reporters/github/api/GitHubClient.ts b/packages/reporter/src/reporters/github/api/GitHubClient.ts new file mode 100644 index 00000000..068dfcbc --- /dev/null +++ b/packages/reporter/src/reporters/github/api/GitHubClient.ts @@ -0,0 +1,7 @@ +import type { CheckRunPayload } from '../types'; + +export interface GitHubClient { + createCheckRun(payload: CheckRunPayload): Promise; +} + +export const GITHUB_CLIENT = Symbol('GITHUB_CLIENT'); diff --git a/packages/reporter/src/reporters/github/api/GitHubConfig.ts b/packages/reporter/src/reporters/github/api/GitHubConfig.ts new file mode 100644 index 00000000..3a293d8e --- /dev/null +++ b/packages/reporter/src/reporters/github/api/GitHubConfig.ts @@ -0,0 +1,7 @@ +export interface GitHubConfig { + token?: string; + repository?: string; + commitSha?: string; +} + +export const GITHUB_CONFIG = Symbol('GITHUB_CONFIG'); diff --git a/packages/reporter/src/reporters/github/api/index.ts b/packages/reporter/src/reporters/github/api/index.ts new file mode 100644 index 00000000..15669a1b --- /dev/null +++ b/packages/reporter/src/reporters/github/api/index.ts @@ -0,0 +1,2 @@ +export { GITHUB_CLIENT, type GitHubClient } from './GitHubClient'; +export { GITHUB_CONFIG, type GitHubConfig } from './GitHubConfig'; diff --git a/packages/reporter/src/reporters/github/api/register.ts b/packages/reporter/src/reporters/github/api/register.ts new file mode 100644 index 00000000..3c20becf --- /dev/null +++ b/packages/reporter/src/reporters/github/api/register.ts @@ -0,0 +1,13 @@ +import { GITHUB_CLIENT } from './GitHubClient'; +import { GITHUB_CONFIG } from './GitHubConfig'; +import { GitHubApiClient } from './GitHubApiClient'; +import { container } from 'tsyringe'; + +container.register(GITHUB_CONFIG, { + useValue: { + token: process.env.GITHUB_TOKEN, + repository: process.env.GITHUB_REPOSITORY, + commitSha: process.env.PR_COMMIT_SHA + } +}); +container.register(GITHUB_CLIENT, { useClass: GitHubApiClient }); diff --git a/packages/reporter/src/reporters/github/builders/BasePayloadBuilder.ts b/packages/reporter/src/reporters/github/builders/BasePayloadBuilder.ts new file mode 100644 index 00000000..e59ab235 --- /dev/null +++ b/packages/reporter/src/reporters/github/builders/BasePayloadBuilder.ts @@ -0,0 +1,33 @@ +import { CheckRunPayloadBuilder } from './CheckRunPayloadBuilder'; +import { CheckRunAnnotation, CheckRunPayload } from '../types'; +import type { Issue } from '@sectester/scan'; + +export abstract class BasePayloadBuilder implements CheckRunPayloadBuilder { + protected readonly commitSha: string; + + constructor( + commitSha: string | undefined, + protected readonly testFilePath: string + ) { + if (!commitSha) { + throw new Error('Commit SHA is required'); + } + this.commitSha = commitSha; + } + + public abstract build(): CheckRunPayload; + + protected convertIssueToAnnotation(issue: Issue): CheckRunAnnotation { + const { originalRequest, name } = issue; + const title = `${name} vulnerability found at ${originalRequest.method.toUpperCase()} ${originalRequest.url}`; + + return { + path: this.testFilePath, + start_line: 1, + end_line: 1, + annotation_level: 'failure', + message: title, + raw_details: JSON.stringify(issue, null, 2) + }; + } +} diff --git a/packages/reporter/src/reporters/github/builders/CheckRunPayloadBuilder.ts b/packages/reporter/src/reporters/github/builders/CheckRunPayloadBuilder.ts new file mode 100644 index 00000000..98c6c498 --- /dev/null +++ b/packages/reporter/src/reporters/github/builders/CheckRunPayloadBuilder.ts @@ -0,0 +1,5 @@ +import type { CheckRunPayload } from '../types'; + +export interface CheckRunPayloadBuilder { + build(): CheckRunPayload; +} diff --git a/packages/reporter/src/reporters/github/builders/MultiItemsPayloadBuilder.ts b/packages/reporter/src/reporters/github/builders/MultiItemsPayloadBuilder.ts new file mode 100644 index 00000000..88c3def8 --- /dev/null +++ b/packages/reporter/src/reporters/github/builders/MultiItemsPayloadBuilder.ts @@ -0,0 +1,70 @@ +import type { CheckRunPayload } from '../types'; +import { BasePayloadBuilder } from './BasePayloadBuilder'; +import type { Issue } from '@sectester/scan'; +import { Severity } from '@sectester/scan'; + +export class MultiItemsPayloadBuilder extends BasePayloadBuilder { + constructor( + private readonly issues: Issue[], + commitSha: string | undefined, + testFilePath: string + ) { + super(commitSha, testFilePath); + } + + public build(): CheckRunPayload { + return { + name: `SecTester (${this.issues.length} issues)`, + head_sha: this.commitSha, + conclusion: 'failure', + output: { + title: `${this.issues.length} vulnerabilities detected in application endpoints`, + summary: this.buildSummary(), + text: this.buildDetails(), + annotations: this.issues.map(issue => + this.convertIssueToAnnotation(issue) + ) + } + }; + } + + private buildSummary(): string { + const severityCounts = this.issues.reduce( + (counts, issue) => { + counts[issue.severity] = (counts[issue.severity] || 0) + 1; + + return counts; + }, + {} as Record + ); + + const parts = []; + if (severityCounts[Severity.CRITICAL]) { + parts.push(`${severityCounts[Severity.CRITICAL]} Critical`); + } + if (severityCounts[Severity.HIGH]) { + parts.push(`${severityCounts[Severity.HIGH]} High`); + } + if (severityCounts[Severity.MEDIUM]) { + parts.push(`${severityCounts[Severity.MEDIUM]} Medium`); + } + if (severityCounts[Severity.LOW]) { + parts.push(`${severityCounts[Severity.LOW]} Low`); + } + + return parts.length > 0 + ? `${parts.join(', ')} severity issues found` + : 'No issues found'; + } + + private buildDetails(): string { + return this.issues + .map(issue => { + const method = issue.originalRequest.method?.toUpperCase() ?? 'GET'; + const pathname = new URL(issue.originalRequest.url).pathname; + + return `- ${method} ${pathname}: ${issue.name}`; + }) + .join('\n'); + } +} diff --git a/packages/reporter/src/reporters/github/builders/SingleItemPayloadBuilder.ts b/packages/reporter/src/reporters/github/builders/SingleItemPayloadBuilder.ts new file mode 100644 index 00000000..62bae955 --- /dev/null +++ b/packages/reporter/src/reporters/github/builders/SingleItemPayloadBuilder.ts @@ -0,0 +1,80 @@ +import type { CheckRunPayload } from '../types'; +import { BasePayloadBuilder } from './BasePayloadBuilder'; +import type { Comment, Issue } from '@sectester/scan'; + +export class SingleItemPayloadBuilder extends BasePayloadBuilder { + constructor( + private readonly issue: Issue, + commitSha: string | undefined, + testFilePath: string + ) { + super(commitSha, testFilePath); + } + + public build(): CheckRunPayload { + return { + name: `SecTester - ${this.buildEndpoint()}`, + head_sha: this.commitSha, + conclusion: 'failure', + output: { + title: this.buildTitle(), + summary: this.buildSummary(), + text: this.buildDetails(), + annotations: [this.convertIssueToAnnotation(this.issue)] + } + }; + } + + private buildEndpoint(): string { + return `${this.issue.originalRequest.method} ${new URL(this.issue.originalRequest.url).pathname}`; + } + + private buildTitle(): string { + return `${this.issue.name} found at ${this.buildEndpoint()}`; + } + + private buildSummary(): string { + return [ + `Name: ${this.issue.name}`, + `Severity: ${this.issue.severity}`, + `Bright UI link: ${this.issue.link}`, + `\nRemediation:\n${this.issue.remedy}` + ].join('\n'); + } + + private buildDetails(): string { + const extraDetails = this.issue.comments?.length + ? this.formatList( + this.issue.comments.map(x => this.formatIssueComment(x)) + ) + : ''; + + const references = this.issue.resources?.length + ? this.formatList(this.issue.resources) + : ''; + + return [ + `${this.issue.details}`, + ...(extraDetails ? [`\nExtra Details:\n${extraDetails}`] : []), + ...(references ? [`\nReferences:\n${references}`] : []) + ].join('\n'); + } + + private formatList(items: string[]): string { + return items.map(x => `- ${x}`).join('\n'); + } + + private formatIssueComment({ headline, text = '', links = [] }: Comment) { + const body = [ + text, + ...(links.length ? [`Links:\n${this.formatList(links)}`] : []) + ].join('\n'); + + const indentedBody = body + .split('\n') + .map(x => `\t${x}`) + .join('\n'); + + return [headline, indentedBody].join('\n'); + } +} diff --git a/packages/reporter/src/reporters/github/builders/index.ts b/packages/reporter/src/reporters/github/builders/index.ts new file mode 100644 index 00000000..77601cc0 --- /dev/null +++ b/packages/reporter/src/reporters/github/builders/index.ts @@ -0,0 +1,3 @@ +export type { CheckRunPayloadBuilder } from './CheckRunPayloadBuilder'; +export { SingleItemPayloadBuilder } from './SingleItemPayloadBuilder'; +export { MultiItemsPayloadBuilder } from './MultiItemsPayloadBuilder'; diff --git a/packages/reporter/src/reporters/github/index.ts b/packages/reporter/src/reporters/github/index.ts new file mode 100644 index 00000000..e6e8900a --- /dev/null +++ b/packages/reporter/src/reporters/github/index.ts @@ -0,0 +1,2 @@ +import './api/register'; +export { GitHubCheckRunReporter } from './GitHubCheckRunReporter'; diff --git a/packages/reporter/src/reporters/github/types/CheckRunAnnotation.ts b/packages/reporter/src/reporters/github/types/CheckRunAnnotation.ts new file mode 100644 index 00000000..9be99199 --- /dev/null +++ b/packages/reporter/src/reporters/github/types/CheckRunAnnotation.ts @@ -0,0 +1,5 @@ +import type { CheckRunPayload } from './CheckRunPayload'; + +export type CheckRunAnnotation = NonNullable< + NonNullable['annotations'] +>[number]; diff --git a/packages/reporter/src/reporters/github/types/CheckRunPayload.ts b/packages/reporter/src/reporters/github/types/CheckRunPayload.ts new file mode 100644 index 00000000..dcf87154 --- /dev/null +++ b/packages/reporter/src/reporters/github/types/CheckRunPayload.ts @@ -0,0 +1,4 @@ +import type { Endpoints } from '@octokit/types'; + +export type CheckRunPayload = + Endpoints['POST /repos/{owner}/{repo}/check-runs']['request']['data']; diff --git a/packages/reporter/src/reporters/github/types/GitHubConfig.ts b/packages/reporter/src/reporters/github/types/GitHubConfig.ts new file mode 100644 index 00000000..6546810a --- /dev/null +++ b/packages/reporter/src/reporters/github/types/GitHubConfig.ts @@ -0,0 +1,5 @@ +export interface GitHubConfig { + readonly token: string; + readonly repository: string; + readonly commitSha: string; +} diff --git a/packages/reporter/src/reporters/github/types/index.ts b/packages/reporter/src/reporters/github/types/index.ts new file mode 100644 index 00000000..22b27f12 --- /dev/null +++ b/packages/reporter/src/reporters/github/types/index.ts @@ -0,0 +1,3 @@ +export type { CheckRunAnnotation } from './CheckRunAnnotation'; +export type { CheckRunPayload } from './CheckRunPayload'; +export type { GitHubConfig } from './GitHubConfig'; diff --git a/packages/reporter/src/reporters/index.ts b/packages/reporter/src/reporters/index.ts new file mode 100644 index 00000000..7356d798 --- /dev/null +++ b/packages/reporter/src/reporters/index.ts @@ -0,0 +1,2 @@ +export * from './github'; +export * from './std'; diff --git a/packages/reporter/src/std/StdReporter.spec.ts b/packages/reporter/src/reporters/std/StdReporter.spec.ts similarity index 100% rename from packages/reporter/src/std/StdReporter.spec.ts rename to packages/reporter/src/reporters/std/StdReporter.spec.ts diff --git a/packages/reporter/src/std/StdReporter.ts b/packages/reporter/src/reporters/std/StdReporter.ts similarity index 97% rename from packages/reporter/src/std/StdReporter.ts rename to packages/reporter/src/reporters/std/StdReporter.ts index e7710ba7..0a53053f 100644 --- a/packages/reporter/src/std/StdReporter.ts +++ b/packages/reporter/src/reporters/std/StdReporter.ts @@ -1,5 +1,5 @@ -import { Reporter } from '../lib'; -import { IssuesGrouper } from '../utils'; +import { Reporter } from '../../lib'; +import { IssuesGrouper } from '../../utils'; import { Issue, Scan, Severity } from '@sectester/scan'; import table, { Header } from 'tty-table'; import chalk from 'chalk'; diff --git a/packages/reporter/src/std/index.ts b/packages/reporter/src/reporters/std/index.ts similarity index 100% rename from packages/reporter/src/std/index.ts rename to packages/reporter/src/reporters/std/index.ts diff --git a/packages/runner/src/lib/SecRunner.ts b/packages/runner/src/lib/SecRunner.ts index d5734d05..b4f0a2c0 100644 --- a/packages/runner/src/lib/SecRunner.ts +++ b/packages/runner/src/lib/SecRunner.ts @@ -7,7 +7,13 @@ import { RepeatersManager } from '@sectester/repeater'; import { ScanFactory } from '@sectester/scan'; -import { Formatter, PlainTextFormatter } from '@sectester/reporter'; +import { + Formatter, + GitHubCheckRunReporter, + PlainTextFormatter, + Reporter, + StdReporter +} from '@sectester/reporter'; export class SecRunner { public static readonly SHUTDOWN_SIGNALS: readonly string[] = [ @@ -75,7 +81,8 @@ export class SecRunner { repeaterId: this.repeater.repeaterId }, this.configuration.container.resolve(ScanFactory), - this.configuration.container.resolve(Formatter) + this.configuration.container.resolve(Formatter), + this.configuration.container.resolve(Reporter) ); } @@ -85,6 +92,18 @@ export class SecRunner { configuration.container.register(Formatter, { useClass: PlainTextFormatter }); + + if (process.env.GITHUB_ACTIONS === 'true') { + if (process.env.PR_COMMIT_SHA) { + configuration.container.register(Reporter, { + useClass: GitHubCheckRunReporter + }); + } + } else { + configuration.container.register(Reporter, { + useClass: StdReporter + }); + } } private setupShutdown(): void { diff --git a/packages/runner/src/lib/SecScan.ts b/packages/runner/src/lib/SecScan.ts index 53e2f87f..5c892232 100644 --- a/packages/runner/src/lib/SecScan.ts +++ b/packages/runner/src/lib/SecScan.ts @@ -1,6 +1,6 @@ import { FunctionScanTarget } from './FunctionScanTarget'; import { IssueFound } from './IssueFound'; -import { Formatter } from '@sectester/reporter'; +import { Formatter, Reporter } from '@sectester/reporter'; import { Issue, Scan, @@ -23,7 +23,8 @@ export class SecScan { constructor( private readonly settings: Omit, private readonly scanFactory: ScanFactory, - private readonly formatter: Formatter + private readonly formatter: Formatter, + private readonly reporter?: Reporter ) {} public async run( @@ -65,6 +66,7 @@ export class SecScan { } finally { await scan.stop(); await functionScanTarget?.stop(); + await this.reporter?.report(scan); } }