@contrast/config 1.28.3 → 1.30.0

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/lib/index.d.ts CHANGED
@@ -17,10 +17,10 @@ import { ProtectRuleMode, Rule } from '@contrast/common';
17
17
  import { LevelWithSilent } from 'pino';
18
18
  export { ConfigSource } from './common.js';
19
19
 
20
- export interface EffectiveEntry {
20
+ export interface EffectiveEntry<T> {
21
21
  canonical_name: string;
22
22
  name: string;
23
- value: any;
23
+ value: T;
24
24
  source: ConfigSource;
25
25
  }
26
26
 
@@ -53,7 +53,7 @@ export interface ConfigOption<T> {
53
53
 
54
54
  export interface Config {
55
55
  _filepath: string;
56
- _effectiveMap: Map<EffectiveEntry>;
56
+ _effectiveMap: Map<string, EffectiveEntry<any>>;
57
57
  _errors: Error[];
58
58
  _status: string,
59
59
 
@@ -213,6 +213,7 @@ export interface Config {
213
213
  inventory: {
214
214
  /** Default: `true` */
215
215
  analyze_libraries: boolean;
216
+ gather_metadata_via: 'AWS' | 'Azure' | 'GCP' | undefined;
216
217
  };
217
218
 
218
219
  assess: {
@@ -292,6 +293,8 @@ export interface Config {
292
293
  environment?: string;
293
294
  tags?: string;
294
295
  version?: string;
296
+ /** Default: `true` */
297
+ discover_cloud_resource: boolean;
295
298
  };
296
299
  getEffectiveSource(cannonicalName: string): any;
297
300
  getEffectiveValue(cannonicalName: string): any;
@@ -0,0 +1,557 @@
1
+ 'use strict';
2
+
3
+ const EventEmitter = require('events');
4
+ const path = require('path');
5
+ const proxyquire = require('proxyquire');
6
+ const sinon = require('sinon');
7
+ const { expect } = require('chai');
8
+ const { Event } = require('@contrast/common');
9
+ const options = require('./options');
10
+ const {
11
+ ConfigSource: { ENVIRONMENT_VARIABLE }
12
+ } = require('./common');
13
+ const {
14
+ getGoodConfig,
15
+ getBadConfig,
16
+ getAbsolutePath,
17
+ getDefaultConfig,
18
+ getProperties,
19
+ containsAllProps,
20
+ } = require('../test/helpers');
21
+
22
+ describe('config', function () {
23
+ let env, Config, config;
24
+
25
+ beforeEach(function () {
26
+ env = {
27
+ ProgramData: 'C:\\ProgramData',
28
+ CONTRAST_CONFIG_PATH: getGoodConfig(),
29
+ };
30
+ Config = proxyquire('./config', {
31
+ process: { env },
32
+ });
33
+ config = proxyquire('.', {
34
+ process: { env },
35
+ './config': Config,
36
+ });
37
+
38
+ sinon.stub(console, 'error');
39
+ });
40
+
41
+ describe('default config behavior', function () {
42
+ it('sets all default properties in the config', function () {
43
+ const defaultConfigProps = getProperties(getDefaultConfig());
44
+ const loadConfigProps = getProperties(config());
45
+ expect(containsAllProps(loadConfigProps, defaultConfigProps)).to.be.true;
46
+ });
47
+
48
+ it('logs an error a config file path is given and no config is found', function () {
49
+ env['CONTRAST_CONFIG_PATH'] = 'fake config';
50
+ const fn = () => config();
51
+ expect(fn).to.throw('1) Unable to read Contrast configuration file: \'fake config\'');
52
+ });
53
+
54
+ it('logs an error when the config cannot be parsed', function () {
55
+ env['CONTRAST_CONFIG_PATH'] = getBadConfig();
56
+ const fn = () => config();
57
+ expect(fn).to.throw(`Contrast configuration file is malformed: '${getBadConfig()}'`);
58
+ });
59
+
60
+ it('accepts overrides from env', function () {
61
+ env['CONTRAST__API__API_KEY'] = 'demo';
62
+ const cfg = config();
63
+ expect(cfg.api.api_key).to.equal('demo');
64
+ });
65
+
66
+ it('loads and sets properties from a valid config', function () {
67
+ env['CONTRAST_CONFIG_PATH'] = getGoodConfig();
68
+ const cfg = config();
69
+ expect(console.error).not.to.have.been.called;
70
+ expect(cfg.api.enable).to.be.true;
71
+ expect(cfg.api.url).to.equal('http://localhost:19080/Contrast');
72
+ expect(cfg.api.api_key).to.equal('demo');
73
+ expect(cfg.api.service_key).to.equal('demo');
74
+ expect(cfg.api.user_name).to.equal('contrast_admin');
75
+ });
76
+ });
77
+
78
+ describe('config file path precedence', function () {
79
+ it('should set config from environment variable even when default is present', function () {
80
+ sinon.stub(Config.prototype, '_configDirs').returns([
81
+ path.dirname(getGoodConfig())
82
+ ]);
83
+ env['CONTRAST_CONFIG_PATH'] = getGoodConfig('two');
84
+ const cfg = config();
85
+ expect(cfg).to.have.property('_filepath', getGoodConfig('two'));
86
+ expect(cfg.api.url).to.equal('https://localhost:9999/Contrast');
87
+ expect(cfg.api.api_key).to.equal('QWERTYUIOP');
88
+ expect(cfg.api.service_key).to.equal('ASDFGHJKL');
89
+ expect(cfg.api.user_name).to.equal('contrast_user');
90
+ });
91
+
92
+ it('should set config from default location when no env var is set', function () {
93
+ sinon.stub(Config.prototype, '_configDirs').returns([
94
+ path.dirname(getGoodConfig())
95
+ ]);
96
+ delete env['CONTRAST_CONFIG_PATH'];
97
+ const cfg = config();
98
+ expect(cfg).to.have.property('_filepath', getGoodConfig());
99
+ expect(cfg.api.url).to.equal('http://localhost:19080/Contrast');
100
+ expect(cfg.api.api_key).to.equal('demo');
101
+ expect(cfg.api.service_key).to.equal('demo');
102
+ expect(cfg.api.user_name).to.equal('contrast_admin');
103
+ });
104
+
105
+ it('should set config from top valid default location', function () {
106
+ sinon.stub(Config.prototype, '_configDirs').returns([
107
+ path.dirname(getGoodConfig('two')),
108
+ path.dirname(getGoodConfig()),
109
+ ]);
110
+ delete env['CONTRAST_CONFIG_PATH'];
111
+ const cfg = config();
112
+ expect(cfg).to.have.property('_filepath', getGoodConfig('two'));
113
+ expect(cfg.api.url).to.equal('https://localhost:9999/Contrast');
114
+ expect(cfg.api.api_key).to.equal('QWERTYUIOP');
115
+ expect(cfg.api.service_key).to.equal('ASDFGHJKL');
116
+ expect(cfg.api.user_name).to.equal('contrast_user');
117
+ });
118
+
119
+ it('should set from env variable even when present in multiple defaults', function () {
120
+ sinon.stub(Config.prototype, '_configDirs').returns([
121
+ path.dirname(getGoodConfig('two')),
122
+ path.dirname(getGoodConfig()),
123
+ ]);
124
+ env['CONTRAST_CONFIG_PATH'] = getGoodConfig('three');
125
+ const cfg = config();
126
+ expect(cfg).to.have.property('_filepath', getGoodConfig('three'));
127
+ expect(cfg.api.url).to.equal('https://localhost:8080/Contrast');
128
+ expect(cfg.api.api_key).to.equal('QAZWSX');
129
+ expect(cfg.api.service_key).to.equal('EDCRFV');
130
+ expect(cfg.api.user_name).to.equal('super_admin');
131
+ });
132
+ });
133
+
134
+ describe('config value precedence', function () {
135
+ let options;
136
+
137
+ beforeEach(function () {
138
+ options = getDefaultConfig();
139
+ });
140
+
141
+ it('should use env var before config file value', function () {
142
+ env['CONTRAST__API__API_KEY'] = 'NOPE';
143
+ const cfg = config(options);
144
+ expect(cfg.api.api_key).to.equal('NOPE');
145
+ });
146
+ });
147
+
148
+ describe('environment variables', function () {
149
+ it('should support CONTRAST__ options', function () {
150
+ const options = getDefaultConfig();
151
+ env['CONTRAST__AGENT__NODE__REWRITE__ENABLE'] = 'f';
152
+ const cfg = config(options);
153
+
154
+ expect(cfg.agent.node.rewrite.enable).to.be.false;
155
+ });
156
+
157
+ it('Should support CONTRAST__API__ options', function () {
158
+ const options = getDefaultConfig();
159
+ env['CONTRAST__API__API_KEY'] = 'abcdefg';
160
+ const cfg = config(options);
161
+
162
+ expect(cfg.api.api_key).to.equal('abcdefg');
163
+ });
164
+
165
+ it('should merge pm2 env variables', function () {
166
+ const options = getDefaultConfig();
167
+ const variables = {
168
+ CONTRAST_CONFIG_PATH: '/path/to/config',
169
+ CONTRAST__API__API_KEY: 'abcdefg',
170
+ env: {
171
+ CONTRAST_CONFIG_PATH: '/other/path/to/config'
172
+ }
173
+ };
174
+ delete env['CONTRAST_CONFIG_PATH'];
175
+ env['pm2_env'] = JSON.stringify(variables);
176
+
177
+ const fn = () => config(options);
178
+ expect(fn).throws('1) Unable to read Contrast configuration file: \'/other/path/to/config\'');
179
+ });
180
+
181
+ it('should throw an error if pm2_env is invalid', function () {
182
+ env['pm2_env'] = '{CONTRAST_';
183
+
184
+ const fn = () => config(options);
185
+ expect(fn).to.throw('1) Unable to parse pm2 environment variable: \'{CONTRAST_\'');
186
+ });
187
+ });
188
+
189
+ describe('multiple errors can be thrown in an exception', function() {
190
+ it('should report multiple errors', function() {
191
+ const options = getDefaultConfig();
192
+ env['pm2_env'] = '{CONTRAST_';
193
+ env['CONTRAST__APPLICATION__SESSION_ID'] = 'abcd-1234';
194
+ env['CONTRAST__APPLICATION__SESSION_METADATA'] = 'a=1,b=2';
195
+ env['CONTRAST_CONFIG_PATH'] = getGoodConfig('three');
196
+
197
+ const fn = () => config(options);
198
+ expect(fn).throws(/1\) Unable to parse pm2 .+ 2\) Cannot set both/);
199
+ });
200
+ });
201
+
202
+ describe('config options functions', function () {
203
+ describe('clearBaseCase', function () {
204
+ it('should return string if defined', function () {
205
+ const string = 'asdf';
206
+ expect(options.clearBaseCase(string)).to.equal(string);
207
+ });
208
+
209
+ it('should return undefined if string is empty', function () {
210
+ const string = '';
211
+ expect(options.clearBaseCase(string)).to.be.undefined;
212
+ });
213
+
214
+ it('should return int if and number and defined', function () {
215
+ const num = 1;
216
+ expect(options.clearBaseCase(num)).to.equal(num);
217
+ });
218
+
219
+ it('should return undefined if value is NaN', function () {
220
+ const num = NaN;
221
+ expect(options.clearBaseCase(num)).to.be.undefined;
222
+ });
223
+ });
224
+
225
+ describe('castBoolean', function () {
226
+ [true, 'true', 'TRUE', 't', 'T'].forEach(val => {
227
+ it(`should return true if string value is ${val}`, function () {
228
+ expect(options.castBoolean(val)).to.be.true;
229
+ });
230
+ });
231
+
232
+ [false, 'false', 'FALSE', 'f', 'F'].forEach(val => {
233
+ it(`should return false if string value is ${val}`, function () {
234
+ expect(options.castBoolean(val)).to.be.false;
235
+ });
236
+ });
237
+
238
+ ['rando', [1, 2, 3], {}, 100, null, undefined].forEach(val => {
239
+ it(`should return undefined if ${val} not a boolean or string or not true/t/false/f`, function () {
240
+ expect(options.castBoolean(val)).to.be.undefined;
241
+ });
242
+ });
243
+ });
244
+ });
245
+
246
+ describe('enum', function () {
247
+ it('should use value from enum if there is a match', function () {
248
+ env['CONTRAST__AGENT__LOGGER__LEVEL'] = 'INFO';
249
+ const cfg = config();
250
+ expect(cfg.agent.logger.level).to.equal('info');
251
+ });
252
+
253
+ it('should fall back to the default when provided option value is invalid', function () {
254
+ env['CONTRAST__AGENT__LOGGER__LEVEL'] = 'doggo';
255
+ const cfg = config();
256
+ expect(cfg.agent.logger.level).to.equal('info');
257
+ });
258
+ });
259
+
260
+ describe('uppercase, lowercase, parsenum transformations', function () {
261
+ it('should uppercase server.environment', function () {
262
+ env['CONTRAST__SERVER__ENVIRONMENT'] = 'qa';
263
+ const cfg = config();
264
+ expect(cfg.server.environment).to.equal('QA');
265
+ });
266
+
267
+ it('should lowercase logger level', function () {
268
+ env['CONTRAST__AGENT__LOGGER__LEVEL'] = 'DEBUG';
269
+ const cfg = config();
270
+ expect(cfg.agent.logger.level).to.equal('debug');
271
+ });
272
+
273
+ it('should parse stack_trace_limit into a number', function () {
274
+ env['CONTRAST__AGENT__STACK_TRACE_LIMIT'] = '25';
275
+ const cfg = config();
276
+ expect(cfg.agent.stack_trace_limit).to.equal(25);
277
+ });
278
+
279
+ it('agent.stack_trace_limit (Infinity string --> number)', function () {
280
+ env['CONTRAST__AGENT__STACK_TRACE_LIMIT'] = 'Infinity';
281
+ const cfg = config();
282
+ expect(cfg.agent.stack_trace_limit).to.equal(Infinity);
283
+ });
284
+ });
285
+
286
+ describe('application', function () {
287
+ describe('validation', function () {
288
+ afterEach(function () {
289
+ });
290
+ it('allows one to be set without error', function () {
291
+ env['CONTRAST__APPLICATION__SESSION_ID'] = 'abcd-1234';
292
+ expect(() => {
293
+ config();
294
+ }).not.to.throw(/Configuration Error:/);
295
+ });
296
+ it('session options are mutually exclusive', function () {
297
+ env['CONTRAST__APPLICATION__SESSION_ID'] = 'abcd-1234';
298
+ env['CONTRAST__APPLICATION__SESSION_METADATA'] = 'a=1,b=2';
299
+ const fn = () => config();
300
+ expect(fn).throws('1) Cannot set both `application.session_id` and `application.session_metadata`');
301
+ });
302
+ });
303
+ });
304
+
305
+ describe('mapping', function () {
306
+ describe('logger', function () {
307
+ it('casts logger path to absolute path', function () {
308
+ env['CONTRAST__AGENT__LOGGER__PATH'] = 'loggy.log';
309
+ const cfg = config();
310
+ expect(cfg.agent.logger.path).to.equal(getAbsolutePath('loggy.log'));
311
+ });
312
+
313
+ it('--debug does not overwrite explicitly set log level', function () {
314
+ env['DEBUG'] = 'true';
315
+ env['CONTRAST__AGENT__LOGGER__LEVEL'] = 'info';
316
+ const cfg = config();
317
+ expect(cfg.agent.logger.level).to.equal('info');
318
+ });
319
+ });
320
+ });
321
+
322
+ describe('effective configuration', function () {
323
+ let core;
324
+ let config;
325
+
326
+ beforeEach(function () {
327
+ core = { messages: new EventEmitter() };
328
+ // there needs to be a valid config file or env var set so config doesn't throw
329
+ // an error.
330
+ process.env.CONTRAST_CONFIG_PATH = path.resolve(__dirname, '../../test/fixtures/protect_contrast_security.yaml');
331
+ config = require('.')(core);
332
+
333
+ config.setValue('contrast_config_path', path.resolve(__dirname, '../../test/fixtures/protect_contrast_security.yaml'), ENVIRONMENT_VARIABLE);
334
+ config.setValue('contrast__agent__stack_trace_limit', 20, ENVIRONMENT_VARIABLE);
335
+ config.setValue('contrast.protect.rules.disabled_rules', ['cmd-injection', 'sql-injection'], ENVIRONMENT_VARIABLE);
336
+ config.setValue('api.enable', true, ENVIRONMENT_VARIABLE);
337
+ config.setValue('api.api_key', 'ABCDEFGHIJ', ENVIRONMENT_VARIABLE);
338
+ config.setValue('api.service_key', 'KLMNOPQRST', ENVIRONMENT_VARIABLE);
339
+
340
+ core.messages.emit(Event.SERVER_SETTINGS_UPDATE, {
341
+ logger: {
342
+ // this will not take effect b/c value is set in config file
343
+ level: 'ERROR'
344
+ },
345
+ security_logger: {
346
+ syslog: {
347
+ enable: true,
348
+ ip: '127.0.0.1'
349
+ }
350
+ }
351
+ });
352
+ });
353
+
354
+ describe('.getEffectiveValue()', function () {
355
+ it('returns the resolved value based on precedence: env > file > UI > default', function () {
356
+ expect(config.getEffectiveValue('agent.security_logger.syslog.enable')).to.equal(true);
357
+ });
358
+ });
359
+
360
+ describe('.getReport', function () {
361
+ it('merges all the settings correctly and returns properly formatted config object', function () {
362
+
363
+ const result = config.getReport({ redact: false });
364
+ const {
365
+ config: {
366
+ effective_config,
367
+ }
368
+ } = result;
369
+
370
+ expect(result.config).to.have.property('status', 'Success');
371
+ expect(effective_config).to.deep.include(
372
+ {
373
+ canonical_name: 'api.enable',
374
+ name: 'api.enable',
375
+ source: 'ENVIRONMENT_VARIABLE',
376
+ value: 'true',
377
+ },
378
+ {
379
+ canonical_name: 'api.api_key',
380
+ name: 'api.api_key',
381
+ source: 'ENVIRONMENT_VARIABLE',
382
+ value: 'contrast-redacted-api.api_key',
383
+ },
384
+ {
385
+ canonical_name: 'api.service_key',
386
+ name: 'api.service_key',
387
+ source: 'ENVIRONMENT_VARIABLE',
388
+ value: 'contrast-redacted-api.service_key',
389
+ },
390
+ {
391
+ canonical_name: 'api.proxy.enable',
392
+ name: 'api.proxy.enable',
393
+ source: 'USER_CONFIGURATION_FILE',
394
+ value: 'false',
395
+ },
396
+ {
397
+ canonical_name: 'agent.diagnostics.enable',
398
+ name: 'agent.diagnostics.enable',
399
+ source: 'USER_CONFIGURATION_FILE',
400
+ value: 'false',
401
+ },
402
+ {
403
+ canonical_name: 'protect.enable',
404
+ name: 'protect.enable',
405
+ value: 'true',
406
+ source: 'USER_CONFIGURATION_FILE'
407
+ },
408
+ {
409
+ canonical_name: 'assess.enable',
410
+ name: 'assess.enable',
411
+ value: 'false',
412
+ source: 'USER_CONFIGURATION_FILE'
413
+ },
414
+ {
415
+ canonical_name: 'agent.security_logger.syslog.enable',
416
+ name: 'agent.security_logger.syslog.enable',
417
+ value: 'true',
418
+ source: 'CONTRAST_UI'
419
+ },
420
+ {
421
+ canonical_name: 'agent.polling.app_activity_ms',
422
+ name: 'agent.polling.app_activity_ms',
423
+ value: '30000',
424
+ source: 'DEFAULT_VALUE'
425
+ },
426
+ {
427
+ canonical_name: 'protect.rules.disabled_rules',
428
+ name: 'protect.rules.disabled_rules',
429
+ value: 'cmd-injection,sql-injection',
430
+ source: 'ENVIRONMENT_VARIABLE',
431
+ },
432
+ {
433
+ canonical_name: 'protect.probe_analysis.enable',
434
+ name: 'protect.probe_analysis.enable',
435
+ value: 'true',
436
+ source: 'DEFAULT_VALUE'
437
+ },
438
+ {
439
+ canonical_name: 'assess.trust_custom_validators',
440
+ name: 'assess.trust_custom_validators',
441
+ value: 'false',
442
+ source: 'DEFAULT_VALUE'
443
+ },
444
+ {
445
+ canonical_name: 'assess.max_propagation_events',
446
+ name: 'assess.max_propagation_events',
447
+ value: '500',
448
+ source: 'DEFAULT_VALUE'
449
+ },
450
+ {
451
+ canonical_name: 'assess.max_context_source_events',
452
+ name: 'assess.max_context_source_events',
453
+ value: '150',
454
+ source: 'DEFAULT_VALUE'
455
+ }
456
+ );
457
+ });
458
+
459
+ it('stringifies values and redacts api keys when `redact` is set to true', function () {
460
+ const result = config.getReport({ redact: true });
461
+ const {
462
+ config: {
463
+ effective_config,
464
+ }
465
+ } = result;
466
+
467
+ expect(result.config).to.have.property('status', 'Success');
468
+ expect(effective_config).to.deep.include(
469
+ {
470
+ canonical_name: 'api.enable',
471
+ name: 'api.enable',
472
+ source: 'ENVIRONMENT_VARIABLE',
473
+ value: 'true',
474
+ },
475
+ {
476
+ canonical_name: 'api.api_key',
477
+ name: 'api.api_key',
478
+ source: 'ENVIRONMENT_VARIABLE',
479
+ value: 'contrast-redacted-api.api_key',
480
+ },
481
+ {
482
+ canonical_name: 'api.service_key',
483
+ name: 'api.service_key',
484
+ source: 'ENVIRONMENT_VARIABLE',
485
+ value: 'contrast-redacted-api.service_key',
486
+ },
487
+ {
488
+ canonical_name: 'api.proxy.enable',
489
+ name: 'api.proxy.enable',
490
+ source: 'USER_CONFIGURATION_FILE',
491
+ value: 'false',
492
+ },
493
+ {
494
+ canonical_name: 'agent.diagnostics.enable',
495
+ name: 'agent.diagnostics.enable',
496
+ source: 'USER_CONFIGURATION_FILE',
497
+ value: 'false',
498
+ },
499
+ {
500
+ canonical_name: 'protect.enable',
501
+ name: 'protect.enable',
502
+ value: 'true',
503
+ source: 'USER_CONFIGURATION_FILE'
504
+ },
505
+ {
506
+ canonical_name: 'assess.enable',
507
+ name: 'assess.enable',
508
+ value: 'false',
509
+ source: 'USER_CONFIGURATION_FILE'
510
+ },
511
+ {
512
+ canonical_name: 'agent.security_logger.syslog.enable',
513
+ name: 'agent.security_logger.syslog.enable',
514
+ value: 'true',
515
+ source: 'CONTRAST_UI'
516
+ },
517
+ {
518
+ canonical_name: 'agent.polling.app_activity_ms',
519
+ name: 'agent.polling.app_activity_ms',
520
+ value: '30000',
521
+ source: 'DEFAULT_VALUE'
522
+ },
523
+ {
524
+ canonical_name: 'protect.rules.disabled_rules',
525
+ name: 'protect.rules.disabled_rules',
526
+ value: 'cmd-injection,sql-injection',
527
+ source: 'ENVIRONMENT_VARIABLE',
528
+ },
529
+ {
530
+ canonical_name: 'protect.probe_analysis.enable',
531
+ name: 'protect.probe_analysis.enable',
532
+ value: 'true',
533
+ source: 'DEFAULT_VALUE'
534
+ },
535
+ {
536
+ canonical_name: 'assess.trust_custom_validators',
537
+ name: 'assess.trust_custom_validators',
538
+ value: 'false',
539
+ source: 'DEFAULT_VALUE'
540
+ },
541
+ {
542
+ canonical_name: 'assess.max_propagation_events',
543
+ name: 'assess.max_propagation_events',
544
+ value: '500',
545
+ source: 'DEFAULT_VALUE'
546
+ },
547
+ {
548
+ canonical_name: 'assess.max_context_source_events',
549
+ name: 'assess.max_context_source_events',
550
+ value: '150',
551
+ source: 'DEFAULT_VALUE'
552
+ }
553
+ );
554
+ });
555
+ });
556
+ });
557
+ });
package/lib/options.js CHANGED
@@ -495,6 +495,12 @@ Example - \`/opt/Contrast/contrast.log\` creates a log in the \`/opt/Contrast\`
495
495
  fn: castBoolean,
496
496
  desc: 'Set to `false` to disable library analysis.',
497
497
  },
498
+ {
499
+ name: 'inventory.gather_metadata_via',
500
+ arg: '<provider>',
501
+ enum: ['AWS', 'Azure', 'GCP'],
502
+ desc: 'Specifies the cloud provider from which the agent should gather metadata (such as resource identifiers). Options are `AWS`, `Azure`, or `GCP`'
503
+ },
498
504
  // assess
499
505
  {
500
506
  name: 'assess.enable',
@@ -613,7 +619,7 @@ Example - \`label1, label2, label3\``,
613
619
  },
614
620
  {
615
621
  name: 'application.metadata',
616
- args: '<metadata>',
622
+ arg: '<metadata>',
617
623
  desc: 'comma-separated list of key=value pairs that are applied to each application reported by the agent.',
618
624
  },
619
625
  // server
@@ -649,6 +655,13 @@ Example - \`label1, label2, label3\``,
649
655
  arg: '<version>',
650
656
  desc: "override the reported server version (if different from 'version' field in the application's package.json)",
651
657
  },
658
+ {
659
+ name: 'server.discover_cloud_resource',
660
+ arg: '[false]',
661
+ default: true,
662
+ fn: castBoolean,
663
+ desc: 'Set to `false` to disable detection of cloud provider metadata such as resource identifiers.'
664
+ },
652
665
  ].map((opt) => Object.assign(opt, {
653
666
  env: `CONTRAST__${opt.name.toUpperCase().replaceAll('.', '__')
654
667
  .replaceAll('-', '_')}`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/config",
3
- "version": "1.28.3",
3
+ "version": "1.30.0",
4
4
  "description": "An API for discovering Contrast agent configuration data",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
@@ -17,7 +17,7 @@
17
17
  "test": "../scripts/test.sh"
18
18
  },
19
19
  "dependencies": {
20
- "@contrast/common": "1.21.3",
20
+ "@contrast/common": "1.23.0",
21
21
  "yaml": "^2.2.2"
22
22
  }
23
23
  }