diff --git a/lib/gradle-attributes-pretty.ts b/lib/gradle-attributes-pretty.ts new file mode 100644 index 0000000..cdf510d --- /dev/null +++ b/lib/gradle-attributes-pretty.ts @@ -0,0 +1,42 @@ +import * as chalk from 'chalk'; + +export function getGradleAttributesPretty(output: string): string | undefined { + try { + const lines = output.split('\n'); + + if (lines === null) { + return undefined; + } + let jsonLine: string | null = null; + lines.forEach((l) => { + const line = l.trim(); + // Extract attribute information via JSONATTRS marker: + if (/^JSONATTRS /.test(line)) { + if (jsonLine === null) { + jsonLine = line.substr(10); + } + } + }); + + const jsonAttrs = JSON.parse(jsonLine); + const attrNameWidth = Math.max( + ...Object.keys(jsonAttrs).map((name) => name.length), + ); + const jsonAttrsPretty = Object.keys(jsonAttrs) + .map( + (name) => + chalk.whiteBright(leftPad(name, attrNameWidth)) + + ': ' + + chalk.gray(jsonAttrs[name].join(', ')), + ) + .join('\n'); + return jsonAttrsPretty; + } catch (e) { + return undefined; + } +} + +// +function leftPad(s: string, n: number) { + return ' '.repeat(Math.max(n - s.length, 0)) + s; +} diff --git a/lib/index.ts b/lib/index.ts index f3f749c..bb37e2d 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -10,6 +10,7 @@ import { legacyCommon, legacyPlugin as api } from '@snyk/cli-interface'; import * as javaCallGraphBuilder from '@snyk/java-call-graph-builder'; import debugModule = require('debug'); import { findCycles } from './find-cycles'; +import { getGradleAttributesPretty } from './gradle-attributes-pretty'; type ScannedProject = legacyCommon.ScannedProject; type CallGraph = legacyCommon.CallGraph; @@ -395,11 +396,6 @@ async function printIfEcho(line: string) { } } -// -function leftPad(s: string, n: number) { - return ' '.repeat(Math.max(n - s.length, 0)) + s; -} - async function getInjectedScriptPath(): Promise<{ injectedScripPath: string; cleanupCallback?: () => void; @@ -523,6 +519,10 @@ async function getAllDeps( cleanupCallback(); } const extractedJson = extractJsonFromScriptOutput(stdoutText); + const jsonAttrsPretty = getGradleAttributesPretty(stdoutText); + logger( + `The following attributes and their possible values were found in your configurations: ${jsonAttrsPretty}`, + ); const versionBuildInfo = getVersionBuildInfo(gradleVersionOutput); if (versionBuildInfo) { extractedJson.versionBuildInfo = versionBuildInfo; @@ -569,27 +569,13 @@ message from above, starting with ===== DEBUG INFORMATION START =====.`; // impossible. // There are no automated tests for this yet (setting up Android SDK is quite problematic). // See test/manual/README.md - - if (cannotResolveVariantMarkers.find((m) => error.message.includes(m))) { - // Extract attribute information via JSONATTRS marker: - const jsonAttrs = JSON.parse( - (error.message as string) - .split('\n') - .filter((line) => /^JSONATTRS /.test(line))[0] - .substr(10), + const jsonAttrsPretty = getGradleAttributesPretty(error.message); + if (jsonAttrsPretty) { + logger( + `The following attributes and their possible values were found in your configurations: ${jsonAttrsPretty}`, ); - const attrNameWidth = Math.max( - ...Object.keys(jsonAttrs).map((name) => name.length), - ); - const jsonAttrsPretty = Object.keys(jsonAttrs) - .map( - (name) => - chalk.whiteBright(leftPad(name, attrNameWidth)) + - ': ' + - chalk.gray(jsonAttrs[name].join(', ')), - ) - .join('\n'); - + } + if (cannotResolveVariantMarkers.find((m) => error.message.includes(m))) { mainErrorMessage = `Error running Gradle dependency analysis. It seems like you are scanning an Android build with ambiguous dependency variants. @@ -757,4 +743,5 @@ export const exportsForTests = { extractJsonFromScriptOutput, getVersionBuildInfo, toCamelCase, + getGradleAttributesPretty, }; diff --git a/test/functional/gradle-plugin.test.ts b/test/functional/gradle-plugin.test.ts index 14c9d10..fc9289a 100644 --- a/test/functional/gradle-plugin.test.ts +++ b/test/functional/gradle-plugin.test.ts @@ -118,6 +118,25 @@ test('extractJsonFromScriptOutput throws on multiple JSONDEPS', async (t) => { } }); +test('getGradleAttributesPretty returns undefined when throws', async (t) => { + const result = testableMethods.getGradleAttributesPretty(``); + t.deepEqual(result, undefined); +}); + +test('getGradleAttributesPretty returns attributes on success', async (t) => { + const result = testableMethods.getGradleAttributesPretty( + `SNYKECHO snykResolvedDepsJson task is executing via doLast + JSONATTRS {"org.gradle.usage":["java-runtime","java-api"],"org.gradle.category":["library"],"org.gradle.libraryelements":["jar"],"org.gradle.dependency.bundling":["external"]} + SNYKECHO processing project: subproj`, + ); + t.deepEqual( + result, + ` org.gradle.usage: java-runtime, java-api + org.gradle.category: library + org.gradle.libraryelements: jar +org.gradle.dependency.bundling: external`, + ); +}); test('check build args (plain console output)', async (t) => { const result = testableMethods.buildArgs('.', null, '/tmp/init.gradle', {}); t.deepEqual(result, [