From b1ac0087604a8fe95139e6e6e129f2a2feafa2bf Mon Sep 17 00:00:00 2001 From: Aram Petrosyan Date: Thu, 29 Jan 2026 10:27:12 +0000 Subject: [PATCH] feat(code): parse project-tags for code test --report Enable the CLI to parse the --project-tags flag for snyk code test --report and pass the tags to the code-client package. Changes: - Import and reuse existing generateTags function from monitor command - Add tags to reportOptions when calling analyzeFolders for file-based report flow - Add unit tests to verify tags are correctly parsed and passed to code-client - Add unit test to verify tags are undefined when flag is not provided This implementation reuses the battle-tested generateTags function that already handles tag validation and parsing for monitor and IAC commands. Resolves: COIN-1891 Co-authored-by: Cursor --- package-lock.json | 24 +++++- src/lib/plugins/sast/analysis.ts | 2 + .../snyk-code/snyk-code-test-report.spec.ts | 74 +++++++++++++++++++ 3 files changed, 99 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index c55f5d5f17..f6b1c96456 100644 --- a/package-lock.json +++ b/package-lock.json @@ -222,6 +222,7 @@ "version": "7.24.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.23.5", @@ -2192,6 +2193,7 @@ "node_modules/@octokit/core": { "version": "3.5.1", "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^2.4.4", "@octokit/graphql": "^4.5.8", @@ -2765,6 +2767,7 @@ "resolved": "https://registry.npmjs.org/@snyk/dep-graph/-/dep-graph-2.12.1.tgz", "integrity": "sha512-pPa/l4BTrL7q5YUBVcgsRDNJJxiz8TEguzniHoGX3xHzZl7kTFWMWC2dx3jtpFUrY/JrwZ8KlfGg48EYMi8FdA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "event-loop-spinner": "^2.1.0", "lodash.clone": "^4.5.0", @@ -3478,6 +3481,7 @@ "version": "2.0.1", "dev": true, "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "@tapjs/processinfo": "^3.1.7", "@tapjs/stack": "2.0.1", @@ -4641,7 +4645,8 @@ }, "node_modules/@types/node": { "version": "14.17.10", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -4841,6 +4846,7 @@ "version": "4.30.0", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "4.30.0", "@typescript-eslint/types": "4.30.0", @@ -5886,6 +5892,7 @@ "version": "7.4.1", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6810,6 +6817,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001587", "electron-to-chromium": "^1.4.668", @@ -9165,6 +9173,7 @@ "version": "6.8.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", "ajv": "^6.10.0", @@ -12240,6 +12249,7 @@ "version": "29.7.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -14521,6 +14531,7 @@ "version": "0.12.0", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -16787,6 +16798,7 @@ "version": "8.3.6", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "colorette": "^1.2.2", "nanoid": "^3.1.23", @@ -17314,6 +17326,7 @@ "version": "18.3.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -18165,6 +18178,7 @@ "version": "6.12.6", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -19050,6 +19064,7 @@ "node_modules/snyk-gradle-plugin/node_modules/@snyk/dep-graph": { "version": "1.31.0", "license": "Apache-2.0", + "peer": true, "dependencies": { "event-loop-spinner": "^2.1.0", "lodash.clone": "^4.5.0", @@ -20603,6 +20618,7 @@ "version": "2.4.2", "dev": true, "license": "ISC", + "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -20923,6 +20939,7 @@ "version": "4.0.2", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -21206,6 +21223,7 @@ "version": "10.9.2", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -21472,6 +21490,7 @@ "node_modules/typescript": { "version": "4.9.5", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -21881,6 +21900,7 @@ "version": "5.54.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.0", "@types/estree": "^0.0.50", @@ -21927,6 +21947,7 @@ "version": "4.8.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^1.0.4", @@ -22122,6 +22143,7 @@ "version": "8.4.1", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, diff --git a/src/lib/plugins/sast/analysis.ts b/src/lib/plugins/sast/analysis.ts index d725cbc926..a228e9432b 100644 --- a/src/lib/plugins/sast/analysis.ts +++ b/src/lib/plugins/sast/analysis.ts @@ -21,6 +21,7 @@ import { import { analysisProgressUpdate } from './utils'; import { FeatureNotSupportedBySnykCodeError } from './errors'; import { getProxyForUrl } from 'proxy-from-env'; +import { generateTags } from '../../../cli/commands/monitor'; import { bootstrap } from 'global-agent'; import chalk from 'chalk'; import * as debugLib from 'debug'; @@ -210,6 +211,7 @@ async function getCodeAnalysis( targetName: options['target-name'], targetRef: options['target-reference'], remoteRepoUrl: options['remote-repo-url'], + tags: generateTags(options), }, }), analysisContext, diff --git a/test/jest/unit/snyk-code/snyk-code-test-report.spec.ts b/test/jest/unit/snyk-code/snyk-code-test-report.spec.ts index cbef07275d..4ff71edcdb 100644 --- a/test/jest/unit/snyk-code/snyk-code-test-report.spec.ts +++ b/test/jest/unit/snyk-code/snyk-code-test-report.spec.ts @@ -118,6 +118,80 @@ describe('Test snyk code with --report', () => { expect(actual?.reportResults).toEqual(expectedReportResults); }); + + it('should pass project-tags to reportOptions when provided', async () => { + const tags = [ + { key: 'env', value: 'prod' }, + { key: 'team', value: 'security' }, + ]; + + const reportOptions = { + enabled: true, + projectName: 'test-project-name', + targetName: 'test-target-name', + targetRef: 'test-target-ref', + remoteRepoUrl: 'https://github.com/owner/repo', + tags, + }; + + analyzeFoldersMock.mockResolvedValue( + sampleAnalyzeFoldersWithReportAndIgnoresResponse, + ); + + await getCodeTestResults( + '.', + { + path: '', + code: true, + report: true, + 'project-name': reportOptions.projectName, + 'target-name': reportOptions.targetName, + 'target-reference': reportOptions.targetRef, + 'remote-repo-url': reportOptions.remoteRepoUrl, + 'project-tags': 'env=prod,team=security', + }, + sastSettings, + 'test-id', + ); + + expect(analyzeFoldersMock).toHaveBeenCalledWith( + expect.objectContaining({ + reportOptions: expect.objectContaining({ + enabled: true, + projectName: reportOptions.projectName, + tags, + }), + }), + ); + }); + + it('should not include tags in reportOptions when project-tags is not provided', async () => { + analyzeFoldersMock.mockResolvedValue( + sampleAnalyzeFoldersWithReportAndIgnoresResponse, + ); + + await getCodeTestResults( + '.', + { + path: '', + code: true, + report: true, + 'project-name': 'test-project', + }, + sastSettings, + 'test-id', + ); + + expect(analyzeFoldersMock).toHaveBeenCalledWith( + expect.objectContaining({ + reportOptions: expect.objectContaining({ + enabled: true, + projectName: 'test-project', + tags: undefined, + }), + }), + ); + }); }); describe('SCM-based report flow - analyzeScmProject', () => {