@docupace/pipeline 1.0.123-xray.2 → 1.0.123-xray.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docupace/pipeline",
3
- "version": "1.0.123-xray.2",
3
+ "version": "1.0.123-xray.3",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -57,10 +57,14 @@ export async function scanOne(target, ctx) {
57
57
  return { label, slug, exitCode: 0 };
58
58
  }
59
59
 
60
+ // Commands the step shells out to on the host, rather than inside a container.
61
+ export const REQUIRED_COMMANDS = ['java', 'zip'];
62
+
60
63
  export async function runVeracodeScan({
61
64
  vid = process.env.VERACODE_API_ID || '',
62
65
  vkey = process.env.VERACODE_API_KEY || '',
63
66
  targetsEnv = process.env.VERACODE_TARGETS || '',
67
+ requireCredentials = process.env.VERACODE_REQUIRE_CREDENTIALS === 'true',
64
68
  buildCmd = process.env.VERACODE_BUILD_CMD || '',
65
69
  commit = process.env.BITBUCKET_COMMIT || 'local',
66
70
  wrapperJar = process.env.VERACODE_WRAPPER_JAR || DEFAULT_WRAPPER_JAR,
@@ -69,18 +73,41 @@ export async function runVeracodeScan({
69
73
  fsops = defaultFsOps,
70
74
  log = console.log,
71
75
  } = {}) {
76
+ // Absent credentials mean the repo or runner was never set up for Veracode,
77
+ // which is not the same thing as a broken configuration: skip, the way the
78
+ // Tenable step does, so a consumer can add the step before the credentials
79
+ // exist. VERACODE_REQUIRE_CREDENTIALS=true turns it back into a hard failure
80
+ // once a repo expects the scan to run on every build.
72
81
  if (!vid || !vkey) {
82
+ if (requireCredentials) {
83
+ log(
84
+ 'ERROR: VERACODE_API_ID / VERACODE_API_KEY not set and VERACODE_REQUIRE_CREDENTIALS=true — failing.',
85
+ );
86
+ return { exitCode: 1, skipped: false, results: [] };
87
+ }
73
88
  log(
74
- 'ERROR: VERACODE_API_ID / VERACODE_API_KEY not set — cannot run Veracode scan.',
89
+ 'VERACODE_API_ID / VERACODE_API_KEY not set — skipping Veracode scan.',
75
90
  );
76
- return { exitCode: 1, results: [] };
91
+ return { exitCode: 0, skipped: true, results: [] };
77
92
  }
93
+
94
+ // Credentials present but no targets is a broken configuration, so this stays
95
+ // a hard failure however the credentials were supplied.
78
96
  if (!targetsEnv) {
79
97
  log('ERROR: VERACODE_TARGETS not set — cannot run Veracode scan.');
80
98
  log(
81
99
  ' Expected: label:appname:path1,path2,...[;label2:appname2:path1,...]',
82
100
  );
83
- return { exitCode: 1, results: [] };
101
+ return { exitCode: 1, skipped: false, results: [] };
102
+ }
103
+
104
+ for (const cmd of REQUIRED_COMMANDS) {
105
+ if (!(await veracode.commandExists(cmd))) {
106
+ log(
107
+ `ERROR: ${cmd} is not on PATH — the Veracode step packages and uploads with it.`,
108
+ );
109
+ return { exitCode: 1, skipped: false, results: [] };
110
+ }
84
111
  }
85
112
 
86
113
  // VERACODE_BUILD_CMD lets a consumer produce whatever build artifacts
@@ -116,5 +143,5 @@ export async function runVeracodeScan({
116
143
  }
117
144
 
118
145
  const exitCode = results.some((r) => r.exitCode !== 0) ? 1 : 0;
119
- return { exitCode, results };
146
+ return { exitCode, skipped: false, results };
120
147
  }
@@ -3,6 +3,7 @@ import { scanOne, runVeracodeScan, DEFAULT_WRAPPER_JAR } from './scan.js';
3
3
 
4
4
  function makeFakeVeracode(overrides = {}) {
5
5
  return {
6
+ commandExists: jest.fn().mockResolvedValue(true),
6
7
  download: jest.fn().mockResolvedValue({ ok: true }),
7
8
  runBuildCmd: jest.fn().mockResolvedValue({ ok: true }),
8
9
  zip: jest.fn().mockResolvedValue({ ok: true }),
@@ -124,36 +125,82 @@ describe('scanOne', () => {
124
125
  });
125
126
 
126
127
  describe('runVeracodeScan', () => {
127
- test('fails fast when credentials are missing', async () => {
128
+ test('skips without failing when credentials are missing', async () => {
128
129
  const log = jest.fn();
130
+ const veracode = makeFakeVeracode();
129
131
  const result = await runVeracodeScan({
130
132
  vid: '',
131
133
  vkey: '',
132
134
  targetsEnv: 'web:my-app:src',
135
+ veracode,
133
136
  log,
134
137
  });
135
138
 
136
- expect(result).toEqual({ exitCode: 1, results: [] });
139
+ expect(result).toEqual({ exitCode: 0, skipped: true, results: [] });
140
+ expect(veracode.zip).not.toHaveBeenCalled();
137
141
  expect(log).toHaveBeenCalledWith(
138
142
  expect.stringContaining('VERACODE_API_ID / VERACODE_API_KEY not set'),
139
143
  );
140
144
  });
141
145
 
146
+ test('fails on missing credentials when VERACODE_REQUIRE_CREDENTIALS is set', async () => {
147
+ const log = jest.fn();
148
+ const result = await runVeracodeScan({
149
+ vid: '',
150
+ vkey: '',
151
+ targetsEnv: 'web:my-app:src',
152
+ requireCredentials: true,
153
+ veracode: makeFakeVeracode(),
154
+ log,
155
+ });
156
+
157
+ expect(result).toEqual({ exitCode: 1, skipped: false, results: [] });
158
+ expect(log).toHaveBeenCalledWith(
159
+ expect.stringContaining('VERACODE_REQUIRE_CREDENTIALS=true'),
160
+ );
161
+ });
162
+
142
163
  test('fails fast when VERACODE_TARGETS is missing', async () => {
143
164
  const log = jest.fn();
144
165
  const result = await runVeracodeScan({
145
166
  vid: 'vid-1',
146
167
  vkey: 'vkey-1',
147
168
  targetsEnv: '',
169
+ veracode: makeFakeVeracode(),
148
170
  log,
149
171
  });
150
172
 
151
- expect(result).toEqual({ exitCode: 1, results: [] });
173
+ expect(result).toEqual({ exitCode: 1, skipped: false, results: [] });
152
174
  expect(log).toHaveBeenCalledWith(
153
175
  expect.stringContaining('VERACODE_TARGETS not set'),
154
176
  );
155
177
  });
156
178
 
179
+ test.each(['java', 'zip'])(
180
+ 'fails before packaging when %s is not on PATH',
181
+ async (missing) => {
182
+ const log = jest.fn();
183
+ const veracode = makeFakeVeracode({
184
+ commandExists: jest.fn(async (cmd) => cmd !== missing),
185
+ });
186
+ const result = await runVeracodeScan({
187
+ vid: 'vid-1',
188
+ vkey: 'vkey-1',
189
+ targetsEnv: 'web:my-app:src',
190
+ veracode,
191
+ fsops: makeFakeFsops(),
192
+ log,
193
+ });
194
+
195
+ expect(result).toEqual({ exitCode: 1, skipped: false, results: [] });
196
+ expect(veracode.zip).not.toHaveBeenCalled();
197
+ expect(veracode.download).not.toHaveBeenCalled();
198
+ expect(log).toHaveBeenCalledWith(
199
+ expect.stringContaining(`${missing} is not on PATH`),
200
+ );
201
+ },
202
+ );
203
+
157
204
  test('runs VERACODE_BUILD_CMD before packaging when set', async () => {
158
205
  const veracode = makeFakeVeracode();
159
206
  const fsops = makeFakeFsops();
@@ -5,6 +5,18 @@ import { $ } from 'zx';
5
5
  // exit is meaningful data here (a failed build/zip/upload), not an
6
6
  // exceptional case.
7
7
  export const veracode = {
8
+ // The wrapper is a jar and packaging is done by `zip`, so this step — alone
9
+ // among the scan steps — depends on host tooling rather than a container
10
+ // image. Checked up front so a missing JRE reads as a missing JRE instead of
11
+ // surfacing as a failed upload several minutes later.
12
+ async commandExists(cmd) {
13
+ const result = await $({
14
+ nothrow: true,
15
+ quiet: true,
16
+ })`command -v ${cmd}`;
17
+ return result.exitCode === 0;
18
+ },
19
+
8
20
  async download(url, dest) {
9
21
  const result = await $({
10
22
  nothrow: true,