Skip to content

Commit

Permalink
Merge pull request #180 from snyk/fix/unmanaged-error-catalogue
Browse files Browse the repository at this point in the history
fix: handle sha-not-found catalog error
  • Loading branch information
orsagie authored Nov 6, 2024
2 parents d6b1b1c + f863bd6 commit 6fe352d
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 202 deletions.
9 changes: 9 additions & 0 deletions lib/parse/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,12 @@ export interface MavenGraphNode {
parents: string[];
reachesProdDep: boolean;
}

export interface ShaSearchError {
status: string;
title: string;
detail: string;
meta: {
links: string[];
};
}
15 changes: 13 additions & 2 deletions lib/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
PackageResource,
MavenPackage,
SnykHttpClient,
ShaSearchError,
} from './parse/types';
import { PackageURL } from 'packageurl-js';
import * as debugLib from 'debug';
Expand Down Expand Up @@ -55,8 +56,18 @@ async function searchMavenPackageByChecksum(
},
});

if (!res?.statusCode || res?.statusCode >= 400 || !body) {
debug(`Failed to resolve ${targetPath} using sha1 ${sha1}.`);
if (
!res?.statusCode ||
res?.statusCode >= 400 ||
!body ||
(body as any).errors
) {
debug(`Failed to resolve ${targetPath}.`);
if (body && (body as any).errors) {
const catalogError = (body as any).errors[0] as ShaSearchError;
debug(catalogError.detail);
debug(catalogError.meta.links[0]);
}
return [];
}

Expand Down
25 changes: 23 additions & 2 deletions tests/helpers/mock-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import {
GetPackageData,
HttpClientResponse,
RequestInfo,
ShaSearchError,
} from '../../lib/parse/types';

const FIXTURES: Map<
String,
{
res: HttpClientResponse;
body: GetPackageData;
body: GetPackageData | { errors: ShaSearchError[] };
}
> = new Map(
Object.entries({
Expand Down Expand Up @@ -78,13 +79,33 @@ export async function mockSnykSearchClient(requestInfo: RequestInfo): Promise<{
res: HttpClientResponse;
body: any;
}> {
const sha1 = requestInfo.qs?.package_sha1 || '';

if (requestInfo.method == 'get' || requestInfo.path == '/packages') {
const sha1 = requestInfo.qs?.package_sha1 || '';
const packageInfo = FIXTURES.get(sha1);

if (packageInfo) {
return packageInfo;
}

return {
// result with 404 from maven
res: { statusCode: 200 },
body: {
errors: [
{
status: '404',
title: 'SHA1 not found',
detail: `SHA1 ${sha1} was not found`,
meta: {
links: [
'https://docs.snyk.io/snyk-cli/test-for-vulnerabilities/scan-all-unmanaged-jar-files',
],
},
},
],
},
};
}

return { res: { statusCode: 404 }, body: undefined };
Expand Down
174 changes: 174 additions & 0 deletions tests/jest/system/plugin-jar.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { legacyPlugin } from '@snyk/cli-interface';
import * as path from 'path';
import * as plugin from '../../../lib';
import { readFixtureJSON } from '../../helpers/read';
import { mockSnykSearchClient } from '../../helpers/mock-search';

const testsPath = path.join(__dirname, '../..');
const fixturesPath = path.join(testsPath, 'fixtures');
const badPath = path.join(fixturesPath, 'bad');

test('inspect with spring-core jar file', async () =>
await assertFixture({
fixtureDirectory: 'spring-core',
targetFile: 'spring-core-5.1.8.RELEASE.jar',
}));

test('inspect with aar file', async () =>
assertFixture({
fixtureDirectory: 'aar',
targetFile: 'library-1.1.0.aar',
}));

test('inspect on altered jar marks package as unknown', async () => {
const result = await plugin.inspect(
badPath,
'jackson-databind-2.9.9.jar',
undefined,
mockSnykSearchClient,
);
expect(legacyPlugin.isMultiResult(result)).toBeFalsy();

const pkgs =
(
result as legacyPlugin.SinglePackageResult
).dependencyGraph?.getDepPkgs() || [];
expect(pkgs.length).toEqual(1);
expect(pkgs[0].name).toMatch(
/unknown:.*jackson-databind-2\.9\.9\.jar:[a-zA-Z0-9]{40}/,
);
expect(pkgs[0].version).toEqual('unknown');
});

test('inspect on non-existent jar', async () => {
const expectedPath = path.join(__dirname, 'nowhere-to-be-found-1.0.jar');
await expect(
plugin.inspect(
__dirname,
'nowhere-to-be-found-1.0.jar',
undefined,
mockSnykSearchClient,
),
).rejects.toThrow(
expect.objectContaining({
message: expect.stringMatching(
'Could not find file or directory ',
),
}),
);
});

test('inspect on user created jar marks package as unknown', async () => {
const result = await plugin.inspect(
badPath,
'mvn-app-1.0-SNAPSHOT.jar',
undefined,
mockSnykSearchClient,
);
expect(legacyPlugin.isMultiResult(result)).toBeFalsy();

const pkgs =
(
result as legacyPlugin.SinglePackageResult
).dependencyGraph?.getDepPkgs() || [];
expect(pkgs.length).toEqual(1);
expect(pkgs[0].name).toMatch(
/unknown:.*mvn-app-1\.0-SNAPSHOT\.jar:c5148d1623cb6097eba45b5fa05b3358a1022f80/,
);
expect(pkgs[0].version).toEqual('unknown');
});

test('inspect in directory with jars no target file and --scan-all-unmanaged arg', async () =>
assertFixture({
fixtureDirectory: 'jars',
options: { scanAllUnmanaged: true },
}));

test('inspect on target pom file in directory with jars and --scan-all-unmanaged arg', async () =>
assertFixture({
fixtureDirectory: 'jars',
targetFile: 'pom.xml',
options: { scanAllUnmanaged: true },
}));

test('inspect in directory with no jars no target file and --scan-all-unmanaged arg', async () => {
await expect(
plugin.inspect(
__dirname,
undefined,
{ scanAllUnmanaged: true },
mockSnykSearchClient,
),
).rejects.toThrow(
expect.objectContaining({
message: expect.stringContaining(
`Could not find any supported files in '${__dirname}'.`,
),
}),
);
});

test('inspect in directory with good and bad jars and --scan-all-unmanaged arg', async () => {
const root = path.join(fixturesPath, 'good-and-bad');
const result = await plugin.inspect(
root,
undefined,
{
scanAllUnmanaged: true,
},
mockSnykSearchClient,
);

expect(legacyPlugin.isMultiResult(result)).toBeFalsy();

const pkgs =
(
result as legacyPlugin.SinglePackageResult
).dependencyGraph?.getDepPkgs() || [];
expect(pkgs.length).toEqual(2);
const commonsIo = pkgs.find((pkg) => pkg.name === 'commons-io:commons-io');
expect(commonsIo?.version).toEqual('2.6');
const doesNotExist = pkgs.find((pkg) =>
pkg.name.includes('does-not-exist.jar'),
);

expect(doesNotExist?.name).toMatch(
/unknown:.*does-not-exist\.jar:[a-zA-Z0-9]{40}/,
);
expect(doesNotExist?.version).toEqual('unknown');
});

test('inspect in directory with jar with wrong package name and --scan-all-unmanaged arg', async () =>
assertFixture({
fixtureDirectory: 'jar-wrong-package-name',
options: { scanAllUnmanaged: true },
}));

test('inspect in directory with jars no target file and --scan-all-unmanaged and --all-projects args', async () =>
assertFixture({
fixtureDirectory: 'nested-jars',
options: { scanAllUnmanaged: true, allProjects: true },
}));

async function assertFixture({
fixtureDirectory,
targetFile,
options,
}: {
fixtureDirectory: string;
targetFile?: string;
options?: any;
}) {
const root = path.join(fixturesPath, fixtureDirectory);
const result = await plugin.inspect(
root,
targetFile,
options,
mockSnykSearchClient,
);
expect(legacyPlugin.isMultiResult(result)).toBeFalsy();
const expected = await readFixtureJSON(fixtureDirectory, 'dep-graph.json');
expect(
(result as legacyPlugin.SinglePackageResult).dependencyGraph!.toJSON(),
).toEqual(expected);
}
Loading

0 comments on commit 6fe352d

Please sign in to comment.