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

[FEATURE]: dependencies security advisories analysis #93

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ dist
.nx/workspace-data

**/v6y-database/**
**/code-analysis-workspace/**
**/v6y-code-analysis-workspace/**
**/jest-cache/**
**/jest-stare/**
**/v6y-logs/**
Expand Down
4 changes: 3 additions & 1 deletion v6y-apps/bfb-main-analyzer/env-template
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ V6Y_MAIN_API_PATH=/v6y/bfb-main/
V6Y_MAIN_API_PORT=4005

V6Y_STATIC_ANALYZER_API_PATH=http://localhost:port/v6y/bfb-static-auditor/auditor/start-static-auditor.json
V6Y_DYNAMIC_ANALYZER_API_PATH=http://localhost:port/v6y/bfb-dynamic-auditor/auditor/start-dynamic-auditor.json
V6Y_DYNAMIC_ANALYZER_API_PATH=http://localhost:port/v6y/bfb-dynamic-auditor/auditor/start-dynamic-auditor.json

ZIP_BASE_DIR=../../v6y-code-analysis-workspace
65 changes: 61 additions & 4 deletions v6y-apps/bfb-main-analyzer/src/managers/ApplicationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@ import {
AppLogger,
ApplicationProvider,
ApplicationType,
RepositoryApi,
RegistryManager,
ZipUtils,
} from '@v6y/core-logic';

import ServerConfig from '../config/ServerConfig.ts';

const { getRepositoryDetails, getRepositoryBranches, prepareGitBranchZipConfig } = RepositoryApi;
const { getRepositoryDetails, getRepositoryBranches, prepareGitBranchZipConfig } = RegistryManager;

const { getCurrentConfig } = ServerConfig;
const { staticAuditorApiPath, dynamicAuditorApiPath } = getCurrentConfig() || {};
const ZIP_BASE_DIR = '../code-analysis-workspace';

interface BuildApplicationBranchParams {
name: string;
Expand Down Expand Up @@ -100,10 +99,18 @@ const buildApplicationDetailsByBranch = async ({ application, branch }: BuildApp
application,
);
AppLogger.info('[ApplicationManager - buildApplicationDetailsByBranch] branch: ', branch);
AppLogger.info(
'[ApplicationManager - buildApplicationDetailsByBranch] ZIP_BASE_DIR: ',
process.env.ZIP_BASE_DIR,
);

if (!process.env.ZIP_BASE_DIR?.length) {
return false;
}

const { zipSourceUrl, zipDestinationDir, zipFileName, zipBaseFileName, zipOptions } =
prepareGitBranchZipConfig({
zipBaseDir: ZIP_BASE_DIR,
zipBaseDir: process.env.ZIP_BASE_DIR,
application,
branchName: branch?.name || '',
}) || {};
Expand Down Expand Up @@ -249,6 +256,31 @@ const buildDynamicReports = async ({ application }: BuildApplicationParams) => {
*/
const buildApplicationReports = async (application: ApplicationType) => {
try {
AppLogger.info(
'[ApplicationManager - buildApplicationReports] application name: ',
application?.name,
);
AppLogger.info(
'[ApplicationManager - buildApplicationReports] application acronym: ',
application?.acronym,
);
AppLogger.info(
'[ApplicationManager - buildApplicationReports] application contactMail: ',
application?.contactMail,
);
AppLogger.info(
'[ApplicationManager - buildApplicationReports] application description: ',
application?.description,
);
AppLogger.info(
'[ApplicationManager - buildApplicationReports] application repo organization: ',
application?.repo?.organization,
);
AppLogger.info(
'[ApplicationManager - buildApplicationReports] application repo gitUrl: ',
application?.repo?.gitUrl,
);

if (
!application?.name?.length ||
!application?.acronym?.length ||
Expand All @@ -262,11 +294,27 @@ const buildApplicationReports = async (application: ApplicationType) => {

const { organization, gitUrl } = application.repo;
const gitRepositoryName = gitUrl?.split('/')?.pop()?.replace('.git', '');
AppLogger.info(
'[ApplicationManager - buildApplicationReports] gitRepositoryName: ',
gitRepositoryName,
);

const repositoryDetails = await getRepositoryDetails({
organization,
gitRepositoryName,
});
AppLogger.info(
'[ApplicationManager - buildApplicationReports] repositoryDetails id: ',
repositoryDetails?.id,
);
AppLogger.info(
'[ApplicationManager - buildApplicationReports] repositoryDetails archived: ',
repositoryDetails?.archived,
);
AppLogger.info(
'[ApplicationManager - buildApplicationReports] repositoryDetails empty_repo: ',
repositoryDetails?.empty_repo,
);

if (
!repositoryDetails?.id ||
Expand All @@ -277,10 +325,18 @@ const buildApplicationReports = async (application: ApplicationType) => {
}

const { _links: repositoryLinks } = repositoryDetails;
AppLogger.info(
'[ApplicationManager - buildApplicationReports] repo_branches: ',
repositoryLinks?.repo_branches?.length,
);

const repositoryBranches = await getRepositoryBranches({
repoBranchesUrl: repositoryLinks?.repo_branches,
});
AppLogger.info(
'[ApplicationManager - buildApplicationReports] repositoryBranches: ',
repositoryBranches?.length,
);

if (!repositoryBranches?.length) {
return false;
Expand Down Expand Up @@ -327,6 +383,7 @@ const buildApplicationList = async () => {
{},
{ role: 'ADMIN' },
);

AppLogger.info(
'[ApplicationManager - buildApplicationList] applications: ',
applications?.length,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
KeywordProvider,
auditStatus,
codeSmellTypes,
dependencyStatus,
dependencyVersionStatus,
} from '@v6y/core-logic';

/**
Expand All @@ -28,18 +28,18 @@ const buildKeywordEvolutionList = async () => {
// eslint-disable-next-line max-depth
if (
!dependency.module ||
!dependency.status ||
dependency.status === dependencyStatus['up-to-date']
!dependency.versionStatus ||
dependency.versionStatus === dependencyVersionStatus['up-to-date']
) {
continue;
}

// add evolution according to keyword
await EvolutionProvider.createEvolution({
category: `${codeSmellTypes.Dependency}-${dependency.status}`,
category: `${codeSmellTypes.Dependency}-${dependency.versionStatus}`,
module: {
...dependency.module,
status: dependency.status,
status: dependency.versionStatus,
},
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,50 +1,13 @@
import {
AppLogger,
AuditUtils,
DeprecatedDependencyProvider,
SemverUtils,
dependencyStatus,
} from '@v6y/core-logic';
import { AppLogger, AuditUtils, sleep } from '@v6y/core-logic';

import { AuditCommonsType } from '../types/AuditCommonsType.ts';
import { DependencyAuditParamsType } from '../types/DependencyAuditType.ts';
import DependencySecurityAdvisoriesAnalyzer from './DependencySecurityAdvisoriesAnalyzer.js';
import DependencyVersionStatusAnalyzer from './DependencyVersionStatusAnalyzer.ts';

const { getFilesRecursively, getFileContent } = AuditUtils;

const { compareVersions } = SemverUtils;

const DEPENDENCIES_REFERENCE_API = 'https://skimdb.npmjs.com/registry/';

/**
* Get dependencies reference
* @param dependencyName
*/
const getDependenciesReference = async ({ dependencyName }: DependencyAuditParamsType) => {
try {
// https://skimdb.npmjs.com/registry/react-cookie
// https://docs.npmjs.com/cli/v8/using-npm/registry
const dependencyReferenceResponse = await fetch(
`${DEPENDENCIES_REFERENCE_API}/${encodeURIComponent(dependencyName || '')}`,
{
method: 'GET',
headers: { 'Content-Type': 'application/json' },
},
);
AppLogger.info(
`[DependenciesUtils - getDependenciesReference] dependencyReferenceResponse: ${dependencyReferenceResponse}`,
);

const dependencyReferenceJsonResponse = await dependencyReferenceResponse.json();
AppLogger.info(
`[DependenciesUtils - getDependenciesReference] dependencyReferenceJsonResponse: ${dependencyReferenceJsonResponse}`,
);

return dependencyReferenceJsonResponse;
} catch (error) {
AppLogger.info(`[DependenciesUtils - getDependenciesReference] error: ${error}`);
return {};
}
};
const { analyzeDependencyVersionStatus } = DependencyVersionStatusAnalyzer;
const { analyzeDependencySecurityAdvisories } = DependencySecurityAdvisoriesAnalyzer;

/**
* Build dependency audit report
Expand All @@ -69,46 +32,30 @@ const buildDependencyAuditReport = async ({
return {};
}

const dependencyReference = await getDependenciesReference({ dependencyName });
AppLogger.info(
`[DependenciesUtils - buildDependencyAuditReport] dependencyReference: ${dependencyReference}`,
);

const recommendedVersion = dependencyReference?.['dist-tags']?.latest;
AppLogger.info(
`[DependenciesUtils - buildDependencyAuditReport] recommendedVersion: ${recommendedVersion}`,
);

const isOutDated = compareVersions(dependencyVersion, recommendedVersion, '<');
const dependencyVersionStatus = await analyzeDependencyVersionStatus({
dependencyName,
dependencyVersion,
});
AppLogger.info(
`[DependenciesUtils - buildDependencyAuditReport] isOutDated: ${isOutDated}`,
`[DependenciesUtils - buildDependencyAuditReport] dependencyVersionStatus: ${dependencyVersionStatus}`,
);

const deprecatedDependency =
await DeprecatedDependencyProvider.getDeprecatedDependencyDetailsByParams({
name: dependencyName,
});
const isDeprecated = deprecatedDependency?._id !== undefined || false;
const dependencySecurityAdvisories = await analyzeDependencySecurityAdvisories({
dependencyName,
dependencyVersion,
});
AppLogger.info(
`[DependenciesUtils - buildDependencyAuditReport] isDeprecated: ${isDeprecated}`,
`[DependenciesUtils - buildDependencyAuditReport] dependencySecurityAdvisories: ${dependencySecurityAdvisories?.length}`,
);

let depStatus = dependencyStatus['up-to-date'];
if (isDeprecated) {
depStatus = dependencyStatus.deprecated;
} else if (isOutDated) {
depStatus = dependencyStatus.outdated;
}

AppLogger.info(`[DependenciesUtils - buildDependencyAuditReport] depStatus: ${depStatus}`);

return {
module,
type: 'frontend',
name: dependencyName,
version: dependencyVersion,
recommendedVersion: recommendedVersion || dependencyVersion,
status: depStatus,
module,
recommendedVersion: dependencyVersionStatus?.recommendedVersion || dependencyVersion,
status: dependencyVersionStatus?.depStatus,
securityAdvisories: dependencySecurityAdvisories,
};
} catch (error) {
AppLogger.info(`[DependenciesUtils - buildDependencyAuditReport] error: ${error}`);
Expand Down Expand Up @@ -149,6 +96,9 @@ const buildModuleDependenciesAuditReports = async ({
});

dependenciesAuditReports.push(dependencyAuditReport);

// to avoid GitHub rate limit, we should create delay between request
await sleep(2000);
}

return dependenciesAuditReports;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {
AppLogger,
DependencySecurityAdvisoriesType,
RegistryManager,
SemverUtils,
} from '@v6y/core-logic';

import { DependencyAuditParamsType } from '../types/DependencyAuditType.js';

const { getRepositorySecurityAdvisories } = RegistryManager;
const { normalizeVersion } = SemverUtils;

const analyzeDependencySecurityAdvisories = async ({
dependencyName,
dependencyVersion,
}: DependencyAuditParamsType): Promise<DependencySecurityAdvisoriesType[] | null> => {
try {
AppLogger.info(
`[DependencySecurityAdvisoriesAnalyzer - analyzeDependencySecurityAdvisories] dependencyName: ${dependencyName}`,
);
AppLogger.info(
`[DependencySecurityAdvisoriesAnalyzer - analyzeDependencySecurityAdvisories] dependencyVersion: ${dependencyVersion}`,
);

const normalizedDependencyVersion = normalizeVersion(dependencyVersion || '');
AppLogger.info(
`[DependencySecurityAdvisoriesAnalyzer - analyzeDependencySecurityAdvisories] normalizedDependencyVersion: ${normalizedDependencyVersion}`,
);

if (!normalizedDependencyVersion?.length || !dependencyName?.length) {
return [];
}

//https://api.github.com/advisories?per_page=100&ecosystem=npm&[email protected]
const dependencySecurityAdvisories = await getRepositorySecurityAdvisories({
pageSize: '100',
affectedModule: `${dependencyName}@${normalizedDependencyVersion}`,
type: 'github',
});

AppLogger.info(
`[DependencySecurityAdvisoriesAnalyzer - analyzeDependencySecurityAdvisories] dependencySecurityAdvisories: ${dependencySecurityAdvisories?.length}`,
);

return dependencySecurityAdvisories;
} catch (error) {
AppLogger.info(
`[DependencySecurityAdvisoriesAnalyzer - analyzeDependencySecurityAdvisories] error: ${error}`,
);
return [];
}
};

const DependencySecurityAdvisoriesAnalyzer = {
analyzeDependencySecurityAdvisories,
};

export default DependencySecurityAdvisoriesAnalyzer;
Loading
Loading