Skip to content
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

feat: added Dockerfile support #250

Merged
merged 3 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
14 changes: 7 additions & 7 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
Expand Up @@ -24,7 +24,7 @@
"dist"
],
"dependencies": {
"@RHEcosystemAppEng/exhort-javascript-api": "^0.1.1-ea.27",
"@RHEcosystemAppEng/exhort-javascript-api": "^0.1.1-ea.29",
"@xml-tools/ast": "^5.0.5",
"@xml-tools/parser": "^1.0.11",
"json-to-ast": "^2.1.0",
Expand Down
26 changes: 25 additions & 1 deletion src/codeActionHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,28 @@ function generateSwitchToRecommendedVersionAction(title: string, dependency: str
return codeAction;
}

export { getCodeActionsMap, clearCodeActionsMap, registerCodeAction , generateSwitchToRecommendedVersionAction, getDiagnosticsCodeActions };
/**
* Generates a code action to redirect to the recommended version catalog.
* @param title - The title of the code action.
* @param imageRef - The image reference provided by exhort.
* @param diagnostic - The diagnostic information.
* @param uri - The URI of the file.
* @returns A CodeAction object for redirecting to the recommended version catalog.
*/
function generateRedirectToRecommendedVersionAction(title: string, imageRef: string, diagnostic: Diagnostic, uri: string): CodeAction {
const codeAction: CodeAction = {
title: title,
diagnostics: [diagnostic],
kind: CodeActionKind.QuickFix,
};

codeAction.command = {
title: 'Track recommendation acceptance',
command: globalConfig.trackRecommendationAcceptanceCommand,
arguments: [imageRef, path.basename(uri)],
};

return codeAction;
}

export { getCodeActionsMap, clearCodeActionsMap, registerCodeAction , generateSwitchToRecommendedVersionAction, generateRedirectToRecommendedVersionAction, getDiagnosticsCodeActions };
93 changes: 71 additions & 22 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,31 @@ class Config
exhortPip3Path: string;
exhortPythonPath: string;
exhortPipPath: string;
exhortSyftPath: string;
exhortSyftConfigPath: string;
exhortSyftImageSource: string;
exhortSkopeoPath: string;
exhortSkopeoConfigPath: string;
exhortImageServiceEndpoint: string;
exhortDockerPath: string;
exhortPodmanPath: string;
exhortImagePlatform: string;
exhortImageOS: string;
exhortImageArch: string;
exhortImageVariant: string;

private readonly DEFAULT_VULNERABILITY_ALERT_SEVERITY = 'Error';
private readonly DEFAULT_MVN_EXECUTABLE = 'mvn';
private readonly DEFAULT_NPM_EXECUTABLE = 'npm';
private readonly DEFAULT_GO_EXECUTABLE = 'go';
private readonly DEFAULT_PYTHON3_EXECUTABLE = 'python3';
private readonly DEFAULT_PIP3_EXECUTABLE = 'pip3';
private readonly DEFAULT_PYTHON_EXECUTABLE = 'python';
private readonly DEFAULT_PIP_EXECUTABLE = 'pip';
private readonly DEFAULT_SYFT_EXECUTABLE = 'syft';
private readonly DEFAULT_SKOPEO_EXECUTABLE = 'skopeo';
private readonly DEFAULT_DOCKER_EXECUTABLE = 'docker';
private readonly DEFAULT_PODMAN_EXECUTABLE = 'podman';

/**
* Initializes a new instance of the Config class with default values from the parent process environment variable data.
Expand All @@ -47,34 +72,58 @@ class Config
this.useGoMVS = process.env.VSCEXT_USE_GO_MVS || 'false';
this.enablePythonBestEffortsInstallation = process.env.VSCEXT_ENABLE_PYTHON_BEST_EFFORTS_INSTALLATION || 'false';
this.usePipDepTree = process.env.VSCEXT_USE_PIP_DEP_TREE || 'false';
this.vulnerabilityAlertSeverity = process.env.VSCEXT_VULNERABILITY_ALERT_SEVERITY || 'Error';
this.exhortMvnPath = process.env.VSCEXT_EXHORT_MVN_PATH || 'mvn';
this.exhortNpmPath = process.env.VSCEXT_EXHORT_NPM_PATH || 'npm';
this.exhortGoPath = process.env.VSCEXT_EXHORT_GO_PATH || 'go';
this.exhortPython3Path = process.env.VSCEXT_EXHORT_PYTHON3_PATH || 'python3';
this.exhortPip3Path = process.env.VSCEXT_EXHORT_PIP3_PATH || 'pip3';
this.exhortPythonPath = process.env.VSCEXT_EXHORT_PYTHON_PATH || 'python';
this.exhortPipPath = process.env.VSCEXT_EXHORT_PIP_PATH || 'pip';
this.vulnerabilityAlertSeverity = process.env.VSCEXT_VULNERABILITY_ALERT_SEVERITY || this.DEFAULT_VULNERABILITY_ALERT_SEVERITY;
this.exhortMvnPath = process.env.VSCEXT_EXHORT_MVN_PATH || this.DEFAULT_MVN_EXECUTABLE;
this.exhortNpmPath = process.env.VSCEXT_EXHORT_NPM_PATH || this.DEFAULT_NPM_EXECUTABLE;
this.exhortGoPath = process.env.VSCEXT_EXHORT_GO_PATH || this.DEFAULT_GO_EXECUTABLE;
this.exhortPython3Path = process.env.VSCEXT_EXHORT_PYTHON3_PATH || this.DEFAULT_PYTHON3_EXECUTABLE;
this.exhortPip3Path = process.env.VSCEXT_EXHORT_PIP3_PATH || this.DEFAULT_PIP3_EXECUTABLE;
this.exhortPythonPath = process.env.VSCEXT_EXHORT_PYTHON_PATH || this.DEFAULT_PYTHON_EXECUTABLE;
this.exhortPipPath = process.env.VSCEXT_EXHORT_PIP_PATH || this.DEFAULT_PIP_EXECUTABLE;
this.exhortSyftPath = process.env.VSCEXT_EXHORT_SYFT_PATH || this.DEFAULT_SYFT_EXECUTABLE;
this.exhortSyftConfigPath = process.env.VSCEXT_EXHORT_SYFT_CONFIG_PATH || '';
this.exhortSyftImageSource = process.env.VSCEXT_EXHORT_SYFT_IMAGE_SOURCE || '';
this.exhortSkopeoPath = process.env.VSCEXT_EXHORT_SKOPEO_PATH || this.DEFAULT_SKOPEO_EXECUTABLE;
this.exhortSkopeoConfigPath = process.env.VSCEXT_EXHORT_SKOPEO_CONFIG_PATH || '';
this.exhortImageServiceEndpoint = process.env.VSCEXT_EXHORT_IMAGE_SERVICE_ENDPOINT || '';
this.exhortDockerPath = process.env.VSCEXT_EXHORT_DOCKER_PATH || this.DEFAULT_DOCKER_EXECUTABLE;
this.exhortPodmanPath = process.env.VSCEXT_EXHORT_PODMAN_PATH || this.DEFAULT_PODMAN_EXECUTABLE;
this.exhortImagePlatform = process.env.VSCEXT_EXHORT_IMAGE_PLATFORM || '';
this.exhortImageOS = process.env.VSCEXT_EXHORT_IMAGE_OS || '';
this.exhortImageArch = process.env.VSCEXT_EXHORT_IMAGE_ARCH || '';
this.exhortImageVariant = process.env.VSCEXT_EXHORT_IMAGE_VARIANT || '';
}

/**
* Updates the global configuration with provided data from extension workspace settings.
* @param data - The data from extension workspace settings to update the global configuration with.
*/
updateConfig( rhdaData: any ) {
this.matchManifestVersions = rhdaData.matchManifestVersions ? 'true' : 'false';
this.usePythonVirtualEnvironment = rhdaData.usePythonVirtualEnvironment ? 'true' : 'false';
this.useGoMVS = rhdaData.useGoMVS ? 'true' : 'false';
this.enablePythonBestEffortsInstallation = rhdaData.enablePythonBestEffortsInstallation ? 'true' : 'false';
this.usePipDepTree = rhdaData.usePipDepTree ? 'true' : 'false';
this.vulnerabilityAlertSeverity = rhdaData.vulnerabilityAlertSeverity;
this.exhortMvnPath = rhdaData.mvn.executable.path || 'mvn';
this.exhortNpmPath = rhdaData.npm.executable.path || 'npm';
this.exhortGoPath = rhdaData.go.executable.path || 'go';
this.exhortPython3Path = rhdaData.python3.executable.path || 'python3';
this.exhortPip3Path = rhdaData.pip3.executable.path || 'pip3';
this.exhortPythonPath = rhdaData.python.executable.path || 'python';
this.exhortPipPath = rhdaData.pip.executable.path || 'pip';
updateConfig( rhdaConfig: any ) {
this.matchManifestVersions = rhdaConfig.matchManifestVersions ? 'true' : 'false';
this.usePythonVirtualEnvironment = rhdaConfig.usePythonVirtualEnvironment ? 'true' : 'false';
this.useGoMVS = rhdaConfig.useGoMVS ? 'true' : 'false';
this.enablePythonBestEffortsInstallation = rhdaConfig.enablePythonBestEffortsInstallation ? 'true' : 'false';
this.usePipDepTree = rhdaConfig.usePipDepTree ? 'true' : 'false';
this.vulnerabilityAlertSeverity = rhdaConfig.vulnerabilityAlertSeverity;
this.exhortMvnPath = rhdaConfig.mvn.executable.path || this.DEFAULT_MVN_EXECUTABLE;
this.exhortNpmPath = rhdaConfig.npm.executable.path || this.DEFAULT_NPM_EXECUTABLE;
this.exhortGoPath = rhdaConfig.go.executable.path || this.DEFAULT_GO_EXECUTABLE;
this.exhortPython3Path = rhdaConfig.python3.executable.path || this.DEFAULT_PYTHON3_EXECUTABLE;
this.exhortPip3Path = rhdaConfig.pip3.executable.path || this.DEFAULT_PIP3_EXECUTABLE;
this.exhortPythonPath = rhdaConfig.python.executable.path || this.DEFAULT_PYTHON_EXECUTABLE;
this.exhortPipPath = rhdaConfig.pip.executable.path || this.DEFAULT_PIP_EXECUTABLE;
this.exhortSyftPath = rhdaConfig.syft.executable.path || this.DEFAULT_SYFT_EXECUTABLE;
this.exhortSyftConfigPath = rhdaConfig.syft.config.path;
this.exhortSyftImageSource = rhdaConfig.syft.imageSource;
this.exhortSkopeoPath = rhdaConfig.skopeo.executable.path || this.DEFAULT_SKOPEO_EXECUTABLE;
this.exhortSkopeoConfigPath = rhdaConfig.skopeo.config.path;
this.exhortImageServiceEndpoint = rhdaConfig.image.serviceEndpoint;
this.exhortDockerPath = rhdaConfig.docker.executable.path || this.DEFAULT_DOCKER_EXECUTABLE;
this.exhortPodmanPath = rhdaConfig.podman.executable.path || this.DEFAULT_PODMAN_EXECUTABLE;
this.exhortImagePlatform = rhdaConfig.image.platform;
this.exhortImageOS = rhdaConfig.image.OS;
this.exhortImageArch = rhdaConfig.image.arch;
this.exhortImageVariant = rhdaConfig.image.variant;
}

/**
Expand Down
45 changes: 20 additions & 25 deletions src/componentAnalysis.ts → src/dependencyAnalysis/analysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
import * as path from 'path';
import exhort from '@RHEcosystemAppEng/exhort-javascript-api';

import { connection } from './server';
import { globalConfig } from './config';
import { isDefined, decodeUriPath } from './utils';
import { connection } from '../server';
import { globalConfig } from '../config';
import { isDefined, decodeUriPath } from '../utils';
import { IDependencyProvider } from '../dependencyAnalysis/collector';

/**
* Represents a source object with an ID and dependencies array.
Expand All @@ -19,16 +20,6 @@ interface ISource {
dependencies: any[];
}

/**
* Implementation of ISource interface.
*/
class Source implements ISource {
constructor(
public id: string,
public dependencies: any[]
) {}
}

/**
* Represents data specification related to a dependency.
*/
Expand All @@ -54,7 +45,7 @@ class DependencyData implements IDependencyData {
}

/**
* Represents the parsed response of Red Hat Dependency Analysis, with dependencies mapped by string keys.
* Represents the parsed response of Red Hat Dependency Analytics (RHDA) analysis, with dependencies mapped by reference string.
*/
interface IAnalysisResponse {
dependencies: Map<string, DependencyData[]>;
Expand All @@ -65,18 +56,20 @@ interface IAnalysisResponse {
*/
class AnalysisResponse implements IAnalysisResponse {
dependencies: Map<string, DependencyData[]> = new Map<string, DependencyData[]>();
provider: IDependencyProvider;

constructor(resData: exhort.AnalysisReport, diagnosticFilePath: string) {
constructor(resData: exhort.AnalysisReport, diagnosticFilePath: string, provider: IDependencyProvider) {

this.provider = provider;
const failedProviders: string[] = [];
const sources: Source[] = [];
const sources: ISource[] = [];

if (isDefined(resData, 'providers')) {
Object.entries(resData.providers).map(([providerName, providerData]) => {
if (isDefined(providerData, 'status', 'ok') && providerData.status.ok) {
if (isDefined(providerData, 'sources')) {
Object.entries(providerData.sources).map(([sourceName, sourceData]) => {
sources.push(new Source(`${providerName}(${sourceName})`, this.getDependencies(sourceData)));
sources.push({id:`${providerName}(${sourceName})`, dependencies: this.getDependencies(sourceData)});
});
}
} else {
Expand All @@ -103,8 +96,9 @@ class AnalysisResponse implements IAnalysisResponse {
? new DependencyData(source.id, issuesCount, '', this.getRemediation(d.issues[0]), this.getHighestSeverity(d))
: new DependencyData(source.id, issuesCount, this.getRecommendation(d), '', this.getHighestSeverity(d));

this.dependencies[d.ref] = this.dependencies[d.ref] || [];
this.dependencies[d.ref].push(dd);
const resolvedRef = this.provider.resolveDependencyFromReference(d.ref);
this.dependencies[resolvedRef] = this.dependencies[resolvedRef] || [];
this.dependencies[resolvedRef].push(dd);
}
});
});
Expand Down Expand Up @@ -138,7 +132,7 @@ class AnalysisResponse implements IAnalysisResponse {
* @private
*/
private getRemediation(issue: any): string {
return isDefined(issue, 'remediation', 'trustedContent', 'ref') ? issue.remediation.trustedContent.ref.split('?')[0] : '';
return isDefined(issue, 'remediation', 'trustedContent', 'ref') ? this.provider.resolveDependencyFromReference(issue.remediation.trustedContent.ref.split('?')[0]) : '';
}

/**
Expand All @@ -148,17 +142,18 @@ class AnalysisResponse implements IAnalysisResponse {
* @private
*/
private getRecommendation(dependency: any): string {
return isDefined(dependency, 'recommendation') ? dependency.recommendation.split('?')[0] : '';
return isDefined(dependency, 'recommendation') ? this.provider.resolveDependencyFromReference(dependency.recommendation.split('?')[0]) : '';
}
}

/**
* Performs RHDA component analysis on provided manifest contents and fileType based on ecosystem.
* @param fileType - The type of file (e.g., 'pom.xml', 'package.json', 'go.mod', 'requirements.txt').
* Performs RHDA component analysis on provided manifest contents/path and fileType based on ecosystem.
* @param diagnosticFilePath - The path to the manifest file to analyze.
* @param contents - The contents of the manifest file to analyze.
* @param provider - The dependency provider of the corresponding ecosystem.
* @returns A Promise resolving to an AnalysisResponse object.
*/
async function executeComponentAnalysis (diagnosticFilePath: string, contents: string): Promise<AnalysisResponse> {
async function executeComponentAnalysis (diagnosticFilePath: string, contents: string, provider: IDependencyProvider): Promise<AnalysisResponse> {

// Define configuration options for the component analysis request
const options = {
Expand All @@ -183,7 +178,7 @@ async function executeComponentAnalysis (diagnosticFilePath: string, contents: s

const componentAnalysisJson = await exhort.componentAnalysis(path.basename(diagnosticFilePath), contents, options, decodeUriPath(diagnosticFilePath)); // Execute component analysis

return new AnalysisResponse(componentAnalysisJson, diagnosticFilePath);
return new AnalysisResponse(componentAnalysisJson, diagnosticFilePath, provider);
}

export { executeComponentAnalysis, DependencyData };
Loading
Loading