Skip to content

Commit

Permalink
Merge pull request #3603 from snyk/feat/add-unmanaged-service-test-ca…
Browse files Browse the repository at this point in the history
…ll-ff

feat: add unmanaged service test call ff
  • Loading branch information
David Agrest committed Sep 20, 2022
2 parents d2c45e5 + 55b6fbb commit a3f87ba
Show file tree
Hide file tree
Showing 12 changed files with 1,210 additions and 4 deletions.
212 changes: 211 additions & 1 deletion src/lib/ecosystems/resolve-test-facts.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,231 @@
import { Options, PolicyOptions } from '../types';
import { spinner } from '../../lib/spinner';
import { Ecosystem, ScanResult, TestResult } from './types';
import {
Ecosystem,
ScanResult,
TestResult,
FileSignaturesDetails,
} from './types';
import {
CreateDepGraphResponse,
GetIssuesResponse,
FileHashes,
Attributes,
} from './unmanaged/types';
import {
requestTestPollingToken,
pollingTestWithTokenUntilDone,
createDepGraph,
getDepGraph,
getIssues,
} from '../polling/polling-test';
import { extractAndApplyPluginAnalytics } from './plugin-analytics';
import { findAndLoadPolicy } from '../policy';
import { filterIgnoredIssues } from './policy';
import { IssueData, Issue } from '../snyk-test/legacy';
import { hasFeatureFlag } from '../feature-flags';
import { delayNextStep } from '../polling/common';
import {
convertDepGraph,
convertMapCasing,
convertToCamelCase,
getSelf,
} from './unmanaged/utils';

export async function resolveAndTestFacts(
ecosystem: Ecosystem,
scans: {
[dir: string]: ScanResult[];
},
options: Options & PolicyOptions,
): Promise<[TestResult[], string[]]> {
const unmanagedDepsOverride = process.env.USE_UNMANAGED_DEPS;

const featureFlagEnabled = await hasFeatureFlag(
'snykNewUnmanagedTest',
options,
);

return featureFlagEnabled || unmanagedDepsOverride
? resolveAndTestFactsUnmanagedDeps(scans, options)
: resolveAndTestFactsRegistry(ecosystem, scans, options);
}

async function submitHashes(
hashes: FileHashes,
orgId: string,
): Promise<string> {
const response: CreateDepGraphResponse = await createDepGraph(hashes, orgId);

return response.data.id;
}

async function pollDepGraph(id: string, orgId: string): Promise<Attributes> {
let attempts = 0;
const maxAttempts = 50;
while (attempts < maxAttempts) {
try {
const response = await getDepGraph(id, orgId);
return response.data.attributes;
} catch (e) {
await delayNextStep(attempts, maxAttempts, 1000);
attempts++;
}
}

return Promise.reject('Failed to get DepGraph');
}

async function fetchIssues(
start_time,
dep_graph_data,
component_details,
orgId: string,
) {
const response: GetIssuesResponse = await getIssues(
{
dep_graph: dep_graph_data,
start_time,
component_details,
},
orgId,
);

const issues = response.data.result.issues.map((issue) => {
const converted = convertToCamelCase<Issue>(issue);
converted.fixInfo = convertToCamelCase(converted.fixInfo);
return converted;
});

const issuesData = convertMapCasing<{
[issueId: string]: IssueData;
}>(response.data.result.issues_data);

const depGraphData = convertDepGraph(response.data.result.dep_graph);

const dependencyCount = response.data.result.dep_graph.graph.nodes.find(
(graphNode) => {
return graphNode.node_id === 'root-node';
},
)?.deps?.length;

const depsFilePaths = response.data.result.deps_file_paths;

const fileSignaturesDetails = convertMapCasing<FileSignaturesDetails>(
response.data.result.file_signatures_details,
);

return {
issues,
issuesData,
depGraphData,
dependencyCount,
depsFilePaths,
fileSignaturesDetails,
};
}

export async function resolveAndTestFactsUnmanagedDeps(
scans: {
[dir: string]: ScanResult[];
},
options: Options & PolicyOptions,
): Promise<[TestResult[], string[]]> {
const results: any[] = [];
const errors: string[] = [];
const packageManager = 'Unmanaged (C/C++)';

let orgId = options.org || '';

if (orgId === '') {
const self = await getSelf();
orgId = self.default_org_context;
}

for (const [path, scanResults] of Object.entries(scans)) {
await spinner(`Resolving and Testing fileSignatures in ${path}`);
for (const scanResult of scanResults) {
try {
const id = await submitHashes(
{ hashes: scanResult?.facts[0]?.data },
orgId,
);

const {
start_time,
dep_graph_data,
component_details,
} = await pollDepGraph(id, orgId);

const {
issues,
issuesData,
depGraphData,
dependencyCount,
depsFilePaths,
fileSignaturesDetails,
} = await fetchIssues(
start_time,
dep_graph_data,
component_details,
orgId,
);

const issuesMap: Map<string, Issue> = new Map();
issues.forEach((i) => {
issuesMap[i.issueId] = i;
});

const vulnerabilities: IssueData[] = [];
for (const issuesDataKey in issuesData) {
const issueData = issuesData[issuesDataKey];
const pkgCoordinate = `${issuesMap[issuesDataKey]?.pkgName}@${issuesMap[issuesDataKey]?.pkgVersion}`;
issueData.from = [pkgCoordinate];
issueData.name = pkgCoordinate;
issueData.packageManager = packageManager;
vulnerabilities.push(issueData);
}

const policy = await findAndLoadPolicy(path, 'cpp', options);

const [issuesFiltered, issuesDataFiltered] = filterIgnoredIssues(
issues,
issuesData,
policy,
);

results.push({
issues: issuesFiltered,
issuesData: issuesDataFiltered,
depGraphData,
depsFilePaths,
fileSignaturesDetails,
vulnerabilities,
path,
dependencyCount,
packageManager,
});
} catch (error) {
const hasStatusCodeError = error.code >= 400 && error.code <= 500;
if (hasStatusCodeError) {
errors.push(error.message);
continue;
}
const failedPath = path ? `in ${path}` : '.';
errors.push(`Could not test dependencies ${failedPath}`);
}
}
}
spinner.clearAll();
return [results, errors];
}

export async function resolveAndTestFactsRegistry(
ecosystem: Ecosystem,
scans: {
[dir: string]: ScanResult[];
},
options: Options & PolicyOptions,
): Promise<[TestResult[], string[]]> {
const results: any[] = [];
const errors: string[] = [];
Expand Down
4 changes: 2 additions & 2 deletions src/lib/ecosystems/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ interface UpgradePathItem {
isDropped?: boolean;
}

interface UpgradePath {
export interface UpgradePath {
path: UpgradePathItem[];
}

interface FixInfo {
export interface FixInfo {
upgradePaths: UpgradePath[];
isPatchable: boolean;
nearestFixedInVersion?: string;
Expand Down

0 comments on commit a3f87ba

Please sign in to comment.