diff --git a/help/cli-commands/code-test.md b/help/cli-commands/code-test.md index 4dc5b21891..e3beea4118 100644 --- a/help/cli-commands/code-test.md +++ b/help/cli-commands/code-test.md @@ -69,6 +69,26 @@ Set or override the remote URL for the repository. Example: `--remote-repo-url=https://gitlab.com/example/project` will create a target for given URL and on the UI it would be visible as `/example/project/` . +### `--project-tags==[,=]...` + +Use with `--report` to attach project tags when publishing Code results (file-based scan). Format is comma-separated `key=value` pairs. For allowable characters, see [Project tags](https://docs.snyk.io/snyk-admin/snyk-projects/project-tags). + +To clear all tags on the project when reporting, pass `--project-tags=` (empty value). + +**Note:** Only one of `--project-tags` or `--tags` may be set. + +### `--tags==[,=]...` + +Alias for `--project-tags`. + +### Example: report with tags + +Scan the current directory and publish to the Snyk Web UI with tags and branch as the target reference: + +`snyk code test --report --project-name=my-code-project --project-tags=env=dev,team=platform --target-reference="$(git branch --show-current)" .` + +Use a real path instead of `.` when scanning another folder. + ### `--org=` Specify the ``to run Snyk commands tied to a specific Snyk Organization. The `` influences private test limits. diff --git a/package-lock.json b/package-lock.json index d6d0e00907..c6fb48b403 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@sentry/node": "^7.34.0", "@snyk/cli-interface": "2.15.0", "@snyk/cloud-config-parser": "^1.14.5", - "@snyk/code-client": "^4.23.5", + "@snyk/code-client": "^4.26.0", "@snyk/dep-graph": "^2.16.3", "@snyk/docker-registry-v2-client": "^2.24.1", "@snyk/error-catalog-nodejs-public": "^5.79.0", @@ -2762,8 +2762,9 @@ "license": "ISC" }, "node_modules/@snyk/code-client": { - "version": "4.23.5", - "license": "MIT", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@snyk/code-client/-/code-client-4.26.0.tgz", + "integrity": "sha512-fZ0b2JEs4FNxp8TpoxVIfzZlzK/rUvgc7UHzwhkf1z57ujGg/kpbTXR2yCXP5PwN6BTtQ5U4nz2xaBQ88/fFng==", "dependencies": { "@deepcode/dcignore": "^1.0.4", "@types/flat-cache": "^2.0.1", @@ -2772,7 +2773,7 @@ "@types/lodash.union": "^4.6.7", "@types/sarif": "^2.1.6", "@types/uuid": "^8.3.4", - "fast-glob": "3.3.1", + "fast-glob": "^3.3.2", "ignore": "^5.2.4", "lodash.omit": "^4.5.0", "lodash.pick": "^4.4.0", @@ -10217,14 +10218,15 @@ "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.1", - "license": "MIT", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" diff --git a/package.json b/package.json index be6337524a..443ae4e702 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@sentry/node": "^7.34.0", "@snyk/cli-interface": "2.15.0", "@snyk/cloud-config-parser": "^1.14.5", - "@snyk/code-client": "^4.23.5", + "@snyk/code-client": "^4.26.0", "@snyk/dep-graph": "^2.16.3", "@snyk/docker-registry-v2-client": "^2.24.1", "@snyk/error-catalog-nodejs-public": "^5.79.0", 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/src/lib/types.ts b/src/lib/types.ts index d87db84e70..694dd1ecce 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -89,6 +89,7 @@ export interface Options { quiet?: boolean; 'fail-fast'?: boolean; tags?: string; + 'project-tags'?: string; 'target-reference'?: string; 'exclude-base-image-vulns'?: boolean; 'no-markdown'?: boolean; 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', () => {