Skip to content

Commit 55b6fbb

Browse files
David Agrestdanielroymoore
David Agrest
authored andcommitted
feat: add unmanaged service test call ff
1 parent 2118c04 commit 55b6fbb

12 files changed

+1210
-4
lines changed

src/lib/ecosystems/resolve-test-facts.ts

+211-1
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,231 @@
11
import { Options, PolicyOptions } from '../types';
22
import { spinner } from '../../lib/spinner';
3-
import { Ecosystem, ScanResult, TestResult } from './types';
3+
import {
4+
Ecosystem,
5+
ScanResult,
6+
TestResult,
7+
FileSignaturesDetails,
8+
} from './types';
9+
import {
10+
CreateDepGraphResponse,
11+
GetIssuesResponse,
12+
FileHashes,
13+
Attributes,
14+
} from './unmanaged/types';
415
import {
516
requestTestPollingToken,
617
pollingTestWithTokenUntilDone,
18+
createDepGraph,
19+
getDepGraph,
20+
getIssues,
721
} from '../polling/polling-test';
822
import { extractAndApplyPluginAnalytics } from './plugin-analytics';
923
import { findAndLoadPolicy } from '../policy';
1024
import { filterIgnoredIssues } from './policy';
1125
import { IssueData, Issue } from '../snyk-test/legacy';
26+
import { hasFeatureFlag } from '../feature-flags';
27+
import { delayNextStep } from '../polling/common';
28+
import {
29+
convertDepGraph,
30+
convertMapCasing,
31+
convertToCamelCase,
32+
getSelf,
33+
} from './unmanaged/utils';
1234

1335
export async function resolveAndTestFacts(
1436
ecosystem: Ecosystem,
1537
scans: {
1638
[dir: string]: ScanResult[];
1739
},
1840
options: Options & PolicyOptions,
41+
): Promise<[TestResult[], string[]]> {
42+
const unmanagedDepsOverride = process.env.USE_UNMANAGED_DEPS;
43+
44+
const featureFlagEnabled = await hasFeatureFlag(
45+
'snykNewUnmanagedTest',
46+
options,
47+
);
48+
49+
return featureFlagEnabled || unmanagedDepsOverride
50+
? resolveAndTestFactsUnmanagedDeps(scans, options)
51+
: resolveAndTestFactsRegistry(ecosystem, scans, options);
52+
}
53+
54+
async function submitHashes(
55+
hashes: FileHashes,
56+
orgId: string,
57+
): Promise<string> {
58+
const response: CreateDepGraphResponse = await createDepGraph(hashes, orgId);
59+
60+
return response.data.id;
61+
}
62+
63+
async function pollDepGraph(id: string, orgId: string): Promise<Attributes> {
64+
let attempts = 0;
65+
const maxAttempts = 50;
66+
while (attempts < maxAttempts) {
67+
try {
68+
const response = await getDepGraph(id, orgId);
69+
return response.data.attributes;
70+
} catch (e) {
71+
await delayNextStep(attempts, maxAttempts, 1000);
72+
attempts++;
73+
}
74+
}
75+
76+
return Promise.reject('Failed to get DepGraph');
77+
}
78+
79+
async function fetchIssues(
80+
start_time,
81+
dep_graph_data,
82+
component_details,
83+
orgId: string,
84+
) {
85+
const response: GetIssuesResponse = await getIssues(
86+
{
87+
dep_graph: dep_graph_data,
88+
start_time,
89+
component_details,
90+
},
91+
orgId,
92+
);
93+
94+
const issues = response.data.result.issues.map((issue) => {
95+
const converted = convertToCamelCase<Issue>(issue);
96+
converted.fixInfo = convertToCamelCase(converted.fixInfo);
97+
return converted;
98+
});
99+
100+
const issuesData = convertMapCasing<{
101+
[issueId: string]: IssueData;
102+
}>(response.data.result.issues_data);
103+
104+
const depGraphData = convertDepGraph(response.data.result.dep_graph);
105+
106+
const dependencyCount = response.data.result.dep_graph.graph.nodes.find(
107+
(graphNode) => {
108+
return graphNode.node_id === 'root-node';
109+
},
110+
)?.deps?.length;
111+
112+
const depsFilePaths = response.data.result.deps_file_paths;
113+
114+
const fileSignaturesDetails = convertMapCasing<FileSignaturesDetails>(
115+
response.data.result.file_signatures_details,
116+
);
117+
118+
return {
119+
issues,
120+
issuesData,
121+
depGraphData,
122+
dependencyCount,
123+
depsFilePaths,
124+
fileSignaturesDetails,
125+
};
126+
}
127+
128+
export async function resolveAndTestFactsUnmanagedDeps(
129+
scans: {
130+
[dir: string]: ScanResult[];
131+
},
132+
options: Options & PolicyOptions,
133+
): Promise<[TestResult[], string[]]> {
134+
const results: any[] = [];
135+
const errors: string[] = [];
136+
const packageManager = 'Unmanaged (C/C++)';
137+
138+
let orgId = options.org || '';
139+
140+
if (orgId === '') {
141+
const self = await getSelf();
142+
orgId = self.default_org_context;
143+
}
144+
145+
for (const [path, scanResults] of Object.entries(scans)) {
146+
await spinner(`Resolving and Testing fileSignatures in ${path}`);
147+
for (const scanResult of scanResults) {
148+
try {
149+
const id = await submitHashes(
150+
{ hashes: scanResult?.facts[0]?.data },
151+
orgId,
152+
);
153+
154+
const {
155+
start_time,
156+
dep_graph_data,
157+
component_details,
158+
} = await pollDepGraph(id, orgId);
159+
160+
const {
161+
issues,
162+
issuesData,
163+
depGraphData,
164+
dependencyCount,
165+
depsFilePaths,
166+
fileSignaturesDetails,
167+
} = await fetchIssues(
168+
start_time,
169+
dep_graph_data,
170+
component_details,
171+
orgId,
172+
);
173+
174+
const issuesMap: Map<string, Issue> = new Map();
175+
issues.forEach((i) => {
176+
issuesMap[i.issueId] = i;
177+
});
178+
179+
const vulnerabilities: IssueData[] = [];
180+
for (const issuesDataKey in issuesData) {
181+
const issueData = issuesData[issuesDataKey];
182+
const pkgCoordinate = `${issuesMap[issuesDataKey]?.pkgName}@${issuesMap[issuesDataKey]?.pkgVersion}`;
183+
issueData.from = [pkgCoordinate];
184+
issueData.name = pkgCoordinate;
185+
issueData.packageManager = packageManager;
186+
vulnerabilities.push(issueData);
187+
}
188+
189+
const policy = await findAndLoadPolicy(path, 'cpp', options);
190+
191+
const [issuesFiltered, issuesDataFiltered] = filterIgnoredIssues(
192+
issues,
193+
issuesData,
194+
policy,
195+
);
196+
197+
results.push({
198+
issues: issuesFiltered,
199+
issuesData: issuesDataFiltered,
200+
depGraphData,
201+
depsFilePaths,
202+
fileSignaturesDetails,
203+
vulnerabilities,
204+
path,
205+
dependencyCount,
206+
packageManager,
207+
});
208+
} catch (error) {
209+
const hasStatusCodeError = error.code >= 400 && error.code <= 500;
210+
if (hasStatusCodeError) {
211+
errors.push(error.message);
212+
continue;
213+
}
214+
const failedPath = path ? `in ${path}` : '.';
215+
errors.push(`Could not test dependencies ${failedPath}`);
216+
}
217+
}
218+
}
219+
spinner.clearAll();
220+
return [results, errors];
221+
}
222+
223+
export async function resolveAndTestFactsRegistry(
224+
ecosystem: Ecosystem,
225+
scans: {
226+
[dir: string]: ScanResult[];
227+
},
228+
options: Options & PolicyOptions,
19229
): Promise<[TestResult[], string[]]> {
20230
const results: any[] = [];
21231
const errors: string[] = [];

src/lib/ecosystems/types.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ interface UpgradePathItem {
6363
isDropped?: boolean;
6464
}
6565

66-
interface UpgradePath {
66+
export interface UpgradePath {
6767
path: UpgradePathItem[];
6868
}
6969

70-
interface FixInfo {
70+
export interface FixInfo {
7171
upgradePaths: UpgradePath[];
7272
isPatchable: boolean;
7373
nearestFixedInVersion?: string;

0 commit comments

Comments
 (0)