Skip to content

Commit

Permalink
feat: add SCA command
Browse files Browse the repository at this point in the history
  • Loading branch information
remie committed Dec 15, 2024
1 parent b19e004 commit b6b27dd
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 3 deletions.
79 changes: 79 additions & 0 deletions src/apt/helpers/generateSCAReport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { spawn } from 'child_process';
import { mkdirSync, rmSync } from 'fs';
import { glob } from 'glob';
import { homedir } from 'os';
import { basename, join } from 'path';
import { Open } from 'unzipper';

import { TAPTSCAOptions } from '../../types/DCAPT';
import { downloadApp } from './downloadApp';

export const generateSCAReport = async (options: TAPTSCAOptions) => {

// Placeholder for temporary directory
const tmpDir = join(homedir(), '.dcdx', 'tmp');

// Placeholder for archive directory
const archiveDir = join(tmpDir, `.${options.appKey}`);

// Download the file from MPAC
console.log('Downloading archive from the Atlassian Marketplace');
let file = await downloadApp(options.appKey);

if (file.endsWith('.obr')) {
console.log('The archive is an OSGi Bundle Repository (OBR)');
console.log(`Extracting archive to a temporary location (${archiveDir})`);

const archive = await Open.file(file);
await archive.extract({ path: archiveDir })

// Get the main JAR file (which is located in the root directory)
const [ relativePathToJar ] = await glob(`*.jar`, { cwd: archiveDir });

// Make sure we actually found the main jar
if (!relativePathToJar) {
console.log('Failed to locate the main JAR file in the archive');
return;
} else {
console.log(`Found the main artifact (${relativePathToJar})`);

// Get the full path to the JAR file
file = join(archiveDir, relativePathToJar);
}
}

// Create the output directory
mkdirSync(options.outputDir, { recursive: true });

// Create the data directory
const dataDir = join(homedir(), '.dcdx', 'owasp');
mkdirSync(dataDir, { recursive: true });

try {
await new Promise<void>((resolve, reject) => {
const docker = spawn(
'docker',
[
'run',
`--pull=always`,
'-v', `${file}:/src/${basename(file)}`,
'-v', `${options.outputDir}:/report`,
'-v', `${dataDir}:/usr/share/dependency-check/data`,
'-v', `${options.outputDir}:/report`,
'owasp/dependency-check',
'--nvdApiKey', `${options.nvdApiKey}`,
'--scan', `/src`,
'--suppression', 'https://dcapt-downloads.s3.amazonaws.com/atlassian-security-scanner-dc-apps-suppressions.xml',
'--out', '/report'
],
{ stdio: 'inherit' }
);
docker.on('exit', (code) => (code === 0) ? resolve() : reject(new Error(`Docker exited with code ${code}`)));
});

console.log(`✔ Finished running the OWASP dependency check software composition analysis (SCA) scanner`);
} finally {
rmSync(file, { force: true });
rmSync(archiveDir, { recursive: true, force: true });
}
}
25 changes: 23 additions & 2 deletions src/commands/apt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { cwd } from 'process';
import { generateDependencyTree } from '../apt/helpers/generateDependencyTree';
import { generatePerformanceReport } from '../apt/helpers/generatePerformanceReport';
import { generateScalabilityReport } from '../apt/helpers/generateScalabilityReport';
import { generateSCAReport } from '../apt/helpers/generateSCAReport';
import { getHostLicense } from '../apt/helpers/getHostLicense';
import { getOutputDirectory } from '../apt/helpers/getOutputDirectory';
import { getProduct } from '../apt/helpers/getProduct';
Expand All @@ -23,7 +24,7 @@ import { Performance } from '../apt/performance';
import { Scalability } from '../apt/scalability';
import { ActionHandler } from '../helpers/ActionHandler';
import { SupportedApplications } from '../types/Application';
import { PerformanceTestTypes, ReportTypes, TAPTArgs, TAPTDependencyTreeArgs, TAPTPerformanceReportArgs, TAPTPerformanceTestArgs, TAPTProvisionArgs, TAPTRestartArgs, TAPTScalabilityReportArgs, TAPTScalabilityTestArgs, TAPTTeardownArgs } from '../types/DCAPT';
import { PerformanceTestTypes, ReportTypes, TAPTArgs, TAPTDependencyTreeArgs, TAPTPerformanceReportArgs, TAPTPerformanceTestArgs, TAPTProvisionArgs, TAPTRestartArgs, TAPTSCAArgs, TAPTScalabilityReportArgs, TAPTScalabilityTestArgs, TAPTTeardownArgs } from '../types/DCAPT';

const program = new Commander();

Expand Down Expand Up @@ -310,6 +311,19 @@ const DependencyTreeCommand = () => ({
}
})

const SCACommand = () => ({
action: async (options: TAPTSCAArgs) => {
await generateSCAReport({
nvdApiKey: options.nvdApiKey,
appKey: options.appKey,
outputDir: options.outputDir || join(cwd(), 'sca_report')
});
},
errorHandler: async () => {

}
})

program
.name('dcdx apt')
.showHelpAfterError(true);
Expand Down Expand Up @@ -483,10 +497,17 @@ program
program
.command('dependencies')
.description('Generate the Data Center App Performance Testing dependency tree')
.addOption(new Option('--appKey <appKey>', 'The key of the app (for automated installation)'))
.addOption(new Option('--appKey <appKey>', 'The key of the app to graph dependencies for'))
.addOption(new Option('-O, --outputFile <path>', 'Specify the output file where to store the generated dependency tree (defaults to `./maven_dependency_tree.gv`)'))
.action(options => ActionHandler(program, DependencyTreeCommand(), options));

program
.command('sca')
.description('Run the Data Center App Performance Testing software composition analysis (SCA) tool')
.addOption(new Option('--nvdApiKey <key>', 'The NVD API key (required due to rate limiting, see https://nvd.nist.gov/developers/request-an-api-key)'))
.addOption(new Option('--appKey <appKey>', 'The key of the app to scan'))
.addOption(new Option('-O, --outputDir <path>', 'Specify the output directory where to store the generated report (defaults to `./sca_report`)'))
.action(options => ActionHandler(program, SCACommand(), options));

program.parseAsync(process.argv).catch(() => gracefulExit(1));

Expand Down
19 changes: 18 additions & 1 deletion src/types/DCAPT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,20 @@ export const APTDependencyTreeOptions = z.object({
outputFile: z.string()
});

export const APTSCAArgs = z.object({
nvdApiKey: z.string(),
appKey: z.string(),
outputDir: z.string()
}).partial({
outputDir: true
});

export const APTSCAOptions = z.object({
nvdApiKey: z.string(),
appKey: z.string(),
outputDir: z.string()
});

export const APTArgs = z.intersection(
APTProvisionOptions,
APTPerformanceTestArgs,
Expand Down Expand Up @@ -248,4 +262,7 @@ export type TTestResults = z.infer<typeof TestResults>;
export type TReportTypes = z.infer<typeof ReportTypes>;

export type TAPTDependencyTreeArgs = z.infer<typeof APTDependencyTreeArgs>;
export type TAPTDependencyTreeOptions = z.infer<typeof APTDependencyTreeOptions>;
export type TAPTDependencyTreeOptions = z.infer<typeof APTDependencyTreeOptions>;

export type TAPTSCAArgs = z.infer<typeof APTSCAArgs>;
export type TAPTSCAOptions = z.infer<typeof APTSCAOptions>;

0 comments on commit b6b27dd

Please sign in to comment.