diff --git a/src/cli/commands/monitor/index.ts b/src/cli/commands/monitor/index.ts index 4bbdafae21..a242792e94 100644 --- a/src/cli/commands/monitor/index.ts +++ b/src/cli/commands/monitor/index.ts @@ -50,6 +50,7 @@ import { getFormattedMonitorOutput } from '../../../lib/ecosystems/monitor'; import { processCommandArgs } from '../process-command-args'; import { hasFeatureFlag } from '../../../lib/feature-flags'; import { PNPM_FEATURE_FLAG } from '../../../lib/package-managers'; +import { normalizeTargetFile } from '../../../lib/normalize-target-file'; const SEPARATOR = '\n-------------------------------------------------------\n'; const debug = Debug('snyk'); @@ -294,12 +295,15 @@ export default async function monitor(...args0: MethodArgs): Promise { maybePrintDepTree(options, projectDeps.depTree); } - const tFile = projectDeps.targetFile || targetFile; - const targetFileRelativePath = - projectDeps.plugin.targetFile || - (tFile && pathUtil.join(pathUtil.resolve(path), tFile)) || - ''; + const tFile = normalizeTargetFile( + projectDeps, + projectDeps.plugin, + targetFile, + ); + const targetFileRelativePath = tFile + ? pathUtil.resolve(pathUtil.resolve(path), tFile) + : ''; const res: MonitorResult = await promiseOrCleanup( snykMonitor( path, diff --git a/src/lib/normalize-target-file.ts b/src/lib/normalize-target-file.ts new file mode 100644 index 0000000000..2a4b65abae --- /dev/null +++ b/src/lib/normalize-target-file.ts @@ -0,0 +1,21 @@ +import type { PluginMetadata } from '@snyk/cli-interface/legacy/plugin'; +import type { ScannedProjectCustom } from './plugins/get-multi-plugin-result'; +import type { ScannedProject } from '@snyk/cli-interface/legacy/common'; + +/** + * Normalizes the target file path for a scanned project across + * test and monitor workflows. + * + * @param {ScannedProject | ScannedProjectCustom} scannedProject - The scanned project containing metadata such as the target file path. + * @param {PluginMetadata} plugin - Metadata about the plugin used to scan the project, which may also include the target file path. + * @param {string} [fallback=''] - A fallback value to return if neither the scanned project nor the plugin contain a target file path. Defaults to an empty string. + * + * @returns {string} - The resolved target file path from either the scanned project, plugin, or the provided fallback value if none are available. + */ +export function normalizeTargetFile( + scannedProject: ScannedProject | ScannedProjectCustom, + plugin: PluginMetadata, + fallback = '', +): string { + return scannedProject.targetFile || plugin.targetFile || fallback; +} diff --git a/src/lib/snyk-test/run-test.ts b/src/lib/snyk-test/run-test.ts index f303052053..4db89f492b 100644 --- a/src/lib/snyk-test/run-test.ts +++ b/src/lib/snyk-test/run-test.ts @@ -85,6 +85,7 @@ import { SUPPORTED_MANIFEST_FILES, } from '../package-managers'; import { PackageExpanded } from 'snyk-resolve-deps/dist/types'; +import { normalizeTargetFile } from '../normalize-target-file'; const debug = debugModule('snyk:run-test'); @@ -726,8 +727,11 @@ async function assembleLocalPayloads( } // todo: normalize what target file gets used across plugins and functions - const targetFile = - scannedProject.targetFile || deps.plugin.targetFile || options.file; + const targetFile = normalizeTargetFile( + scannedProject, + deps.plugin, + options.file, + ); // Forcing options.path to be a string as pathUtil requires is to be stringified const targetFileRelativePath = targetFile diff --git a/test/acceptance/workspace-helper.ts b/test/acceptance/workspace-helper.ts index 792738ef7d..a689ec0f8f 100644 --- a/test/acceptance/workspace-helper.ts +++ b/test/acceptance/workspace-helper.ts @@ -3,6 +3,14 @@ import { readFileSync } from 'fs'; const workspacePath = path.join(__dirname, 'workspaces'); +/** + * Changes the current working directory to the specified subdirectory within the workspace path. + * + * @param {string} subdir - The subdirectory to navigate to (optional). If not provided, the workspace path itself will be used. + * + * @example + * chdirWorkspaces('project1'); // Changes the working directory to '${workspacePath}/project1' + */ export function chdirWorkspaces(subdir = '') { const dir = path.join(workspacePath, subdir); process.chdir(dir); diff --git a/test/tap/cli-monitor/cli-monitor.all-projects.spec.ts b/test/tap/cli-monitor/cli-monitor.all-projects.spec.ts index 0a91bf47ed..6641a4c694 100644 --- a/test/tap/cli-monitor/cli-monitor.all-projects.spec.ts +++ b/test/tap/cli-monitor/cli-monitor.all-projects.spec.ts @@ -1,6 +1,7 @@ import * as sinon from 'sinon'; import * as path from 'path'; import * as depGraphLib from '@snyk/dep-graph'; +import { getWorkspacePath } from '../../jest/util/getWorkspacePath'; interface AcceptanceTests { language: string; @@ -412,6 +413,9 @@ export const AllProjectsTests: AcceptanceTests = { }, }, depGraph: simpleGradleGraph, + targetFile: + getWorkspacePath('gradle-monorepo') + + '/subproj/build.gradle', }, ], }; @@ -437,6 +441,7 @@ export const AllProjectsTests: AcceptanceTests = { 'gradle project was monitored', ); + let policyCount = 0; const requests = params.server .getRequests() .filter((req) => req.url.includes('/monitor/')); @@ -447,6 +452,10 @@ export const AllProjectsTests: AcceptanceTests = { /\/api\/v1\/monitor\/(npm\/graph|gradle\/graph)/, 'puts at correct url', ); + + if (req.body.policy) { + policyCount++; + } t.notOk(req.body.targetFile, "doesn't send the targetFile"); t.equal(req.method, 'PUT', 'makes PUT request'); t.equal( @@ -455,6 +464,7 @@ export const AllProjectsTests: AcceptanceTests = { 'sends version number', ); }); + t.equal(policyCount, 1, '1 nested policy found in monorepo'); }, '`monitor kotlin-monorepo --all-projects` scans kotlin files': (params, utils) => async (t) => {