Skip to content

Commit 29b867c

Browse files
appurva21VShingala
andauthored
Add secret and source property to Variable (#1422)
* feat: add secret property to `Variable` * feat: Add ability to set secret variables in `VariableScope` * fix: remove redundant check * feat: Add ability to track secret variables in mutations * Add `source` property to support referencing of secret type of variable. (#1421) * Add ref prop within variable for secret support * Renamed "ref" to "source" to better align with postman-runtime * 5.2.1-beta.1 * Add changelog * 5.3.0-beta.1 * Updated JSDoc type interfaces with exact structure of source field * Added unit tests for variable.source property creation/updation * remove separate definition of type secret as any to be used as fallback * revert back the original package.json version * chore: fix types --------- Co-authored-by: Appurva Murawat <appurva21@proton.me> --------- Co-authored-by: Vishal Shingala <vishalb.shingala@gmail.com>
1 parent 388fa99 commit 29b867c

File tree

8 files changed

+487
-25
lines changed

8 files changed

+487
-25
lines changed

CHANGELOG.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
unreleased:
2+
new features:
3+
- Add `secret` property to `Variable`
4+
- Add ability to set secret variables using `VariableScope.set()` method
5+
- Add ability to track secret variables in mutations
6+
- Add `source` property in variables to resolve secrets
7+
18
5.2.1:
29
date: 2026-02-04
310
chores:

lib/collection/mutation-tracker.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ var _ = require('../util').lodash,
2121
* @returns {Boolean}
2222
*/
2323
isPrimitiveMutation = function (mutation) {
24-
return mutation && mutation.length <= 2;
24+
// Primitive mutations can have 1-3 elements: [key], [key, value], [key, value, metadata]
25+
return mutation && mutation.length <= 3;
2526
},
2627

2728
/**
@@ -53,10 +54,14 @@ var _ = require('../util').lodash,
5354
* This captures the instruction and the parameters of the instruction so that it can be replayed on a different object.
5455
* Mutations can be any change on an object. For example setting a key or unsetting a key.
5556
*
56-
* For example, the mutation to set `name` on an object to 'Bruce Wayne' would look like ['name', 'Bruce Wayne']. Where
57-
* the first item is the key path and second item is the value. To add a property `punchLine` to the object it would be
58-
* the same as updating the property i.e. ['punchLine', 'I\'m Batman']. To remove a property `age` the mutation would
59-
* look like ['age'].
57+
* Mutation formats:
58+
* - SET without metadata: ['key', 'value']
59+
* - SET with metadata: ['key', 'value', { metadata }]
60+
* - UNSET: ['key']
61+
*
62+
* For example, the mutation to set `name` on an object to 'Bruce Wayne' would look like ['name', 'Bruce Wayne'].
63+
* To set a variable with secret flag: ['password', 'secret123', { secret: true }].
64+
* To remove a property `age` the mutation would look like ['age'].
6065
*
6166
* This format of representing changes is derived from
6267
* {@link http://json-delta.readthedocs.io/en/latest/philosophy.html}.
@@ -65,6 +70,10 @@ var _ = require('../util').lodash,
6570
* instruction. For more complex mutation the instruction would have to be explicitly stated.
6671
*
6772
* @typedef {Array} MutationTracker.mutation
73+
* @example
74+
* ['password', 'secret123', { secret: true }] // SET with metadata
75+
* ['userId', '42'] // SET without metadata
76+
* ['tempKey'] // UNSET
6877
*/
6978

7079
/**

lib/collection/variable-scope.js

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ var _ = require('../util').lodash,
1616
UNSET: 'unset'
1717
},
1818

19+
/**
20+
* Properties that should be tracked in mutations when set on variables.
21+
* Add new properties here to have them automatically tracked and replayed.
22+
*
23+
* @private
24+
* @constant
25+
* @type {Array}
26+
*/
27+
TRACKED_PROPERTIES = ['secret'],
28+
1929
VariableScope;
2030

2131
/**
@@ -226,16 +236,25 @@ _.assign(VariableScope.prototype, /** @lends VariableScope.prototype */ {
226236
*
227237
* @param {String} key - The name of the variable to set.
228238
* @param {*} value - The value of the variable to be set.
229-
* @param {Variable.types} [type] - Optionally, the value of the variable can be set to a type
239+
* @param {Variable.types|Object} [options] - Optional configuration for the variable.
240+
* Can be a string (e.g., 'string', 'number') or an object with properties:
241+
* - `type` {String} - The variable type
242+
* - `secret` {Boolean} - Whether the variable contains secret/sensitive data
230243
*/
231-
set: function (key, value, type) {
244+
set: function (key, value, options) {
232245
const _key = (this.prefix || '') + key;
233246
var variable = this.values.oneNormalizedVariable(_key),
234247

235248
// create an object that will be used as setter
236249
update = { key: _key, value: value };
237250

238-
_.isString(type) && (update.type = type);
251+
// handle third parameter - can be a string (type) or object ({ type, secret })
252+
if (_.isString(options)) {
253+
options = { type: options };
254+
}
255+
256+
options && _.isString(options.type) && (update.type = options.type);
257+
options && _.isBoolean(options.secret) && (update.secret = options.secret);
239258

240259
// If a variable by the name key exists, update it's value and return.
241260
// @note adds new variable if existing is disabled. Disabled variables are not updated.
@@ -247,7 +266,10 @@ _.assign(VariableScope.prototype, /** @lends VariableScope.prototype */ {
247266
}
248267

249268
// track the change if mutation tracking is enabled
250-
this._postman_enableTracking && this.mutations.track(MUTATIONS.SET, key, value);
269+
// include tracked properties in mutations so they are preserved during replay
270+
if (this._postman_enableTracking) {
271+
this.mutations.track(MUTATIONS.SET, key, value, _.pick(options, TRACKED_PROPERTIES));
272+
}
251273
},
252274

253275
/**
@@ -368,13 +390,14 @@ _.assign(VariableScope.prototype, /** @lends VariableScope.prototype */ {
368390
* @param {String} instruction Instruction identifying the type of the mutation, e.g. `set`, `unset`
369391
* @param {String} key -
370392
* @param {*} value -
393+
* @param {Object} [metadata] - Optional metadata object (e.g., { secret: true })
371394
*/
372-
applyMutation: function (instruction, key, value) {
395+
applyMutation: function (instruction, key, value, metadata) {
373396
// we know that `set` and `unset` are the only supported instructions
374397
// and we know the parameter signature of both is the same as the items in a mutation
375398
/* istanbul ignore else */
376399
if (this[instruction]) {
377-
this[instruction](key, value);
400+
this[instruction](key, value, metadata);
378401
}
379402
},
380403

lib/collection/variable.js

Lines changed: 115 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,125 @@ var _ = require('../util').lodash,
99
Variable;
1010

1111
/**
12-
* The object representation of a Variable consists the variable value and type. It also optionally includes the `id`
12+
* Postman integration - local vault.
13+
*
14+
* @typedef {Object} Variable.sourcePostmanLocal
15+
* @property {"postman"} provider
16+
* @property {Object} postman
17+
* @property {"local"} postman.type
18+
* @property {string} postman.secretId
19+
* @property {string=} [postman.vaultId]
20+
*/
21+
22+
/**
23+
* Postman integration - cloud vault.
24+
*
25+
* @typedef {Object} Variable.sourcePostmanCloud
26+
* @property {"postman"} provider
27+
* @property {Object} postman
28+
* @property {"cloud"} postman.type
29+
* @property {string} postman.secretId
30+
* @property {string} postman.vaultId
31+
*/
32+
33+
/**
34+
* @typedef {Variable.sourcePostmanLocal|Variable.sourcePostmanCloud} Variable.sourcePostman
35+
*/
36+
37+
/**
38+
* Azure Key Vault integration.
39+
*
40+
* @typedef {Object} Variable.sourceAzure
41+
* @property {"azure"} provider
42+
* @property {Object} azure
43+
* @property {string} azure.secretId
44+
*/
45+
46+
/**
47+
* 1Password integration.
48+
*
49+
* @typedef {Object} Variable.sourceOnePassword
50+
* @property {"1password"} provider
51+
* @property {Object} 1password
52+
* @property {string} 1password.secretReference
53+
*/
54+
55+
/**
56+
* AWS Secrets Manager integration.
57+
*
58+
* @typedef {Object} Variable.sourceAws
59+
* @property {"aws"} provider
60+
* @property {Object} aws
61+
* @property {string} aws.secretArn
62+
* @property {string=} [aws.roleArn]
63+
* @property {string=} [aws.version]
64+
* @property {"plaintext"|"json"=} [aws.format]
65+
*/
66+
67+
/**
68+
* HashiCorp Vault integration.
69+
*
70+
* @typedef {Object} Variable.sourceHashiCorp
71+
* @property {"hashicorp"} provider
72+
* @property {Object} hashicorp
73+
* @property {string} hashicorp.engine
74+
* @property {string} hashicorp.path
75+
* @property {string} hashicorp.key
76+
* @property {string=} [hashicorp.version]
77+
*/
78+
79+
/**
80+
* Source object for external secret resolution. The structure depends on the `provider` field.
81+
* Resolver lookup is keyed by `provider`
82+
* (for example: "postman", "azure", "1password", "aws", "hashicorp").
83+
*
84+
* @typedef {
85+
* Variable.sourcePostman |
86+
* Variable.sourceAzure |
87+
* Variable.sourceOnePassword |
88+
* Variable.sourceAws |
89+
* Variable.sourceHashiCorp
90+
* } Variable.source
91+
*/
92+
93+
/**
94+
* The object representation of a Variable consists of the variable value and type. It may also include the `id`
1395
* and a friendly `name` of the variable. The `id` and the `name` of a variable is usually managed and used when a
1496
* variable is made part of a {@link VariableList} instance.
1597
*
1698
* @typedef {Object} Variable.definition
1799
* @property {*=} [value] - The value of the variable that will be stored and will be typecast to the `type`
18100
* set in the variable or passed along in this parameter.
19101
* @property {String=} [type] - The type of this variable from the list of types defined at {@link Variable.types}.
102+
* @property {Boolean=} [system] - Indicates whether this is a system variable.
103+
* @property {Boolean=} [secret] - Indicates whether this variable contains secret/sensitive data.
104+
* @property {Boolean=} [disabled] - Indicates whether this variable is disabled.
105+
* @property {Variable.source=} [source] - Optional source object for external secret resolution. Contains metadata
106+
* on how to resolve the variable value from an external source. The structure depends on the source `provider` field.
20107
*
21-
* @example
108+
* @example Default variable
22109
* {
23110
* "id": "my-var-1",
24111
* "name": "MyFirstVariable",
25112
* "value": "Hello World",
26113
* "type": "string"
27114
* }
115+
*
116+
* @example Secret variable - Postman local vault
117+
* {
118+
* "id": "my-secret-var",
119+
* "key": "apiKey",
120+
* "value": "",
121+
* "type": "secret",
122+
* "source": {
123+
* "provider": "postman",
124+
* "postman": {
125+
* "type": "local",
126+
* "secretId": "ID_OF_THE_SECRET"
127+
* "vaultId": "ID_OF_THE_VAULT"
128+
* }
129+
* }
130+
* }
28131
*/
29132
_.inherit((
30133

@@ -55,7 +158,14 @@ _.inherit((
55158
/**
56159
* @type {*}
57160
*/
58-
value: undefined
161+
value: undefined,
162+
163+
/**
164+
* Optional source object for external secret resolution.
165+
*
166+
* @type {Variable.source}
167+
*/
168+
source: undefined
59169
});
60170

61171
if (!_.isNil(definition)) {
@@ -206,8 +316,10 @@ _.assign(Variable.prototype, /** @lends Variable.prototype */ {
206316
_.has(options, 'type') && this.valueType(options.type, _.has(options, 'value'));
207317
_.has(options, 'value') && this.set(options.value);
208318
_.has(options, 'system') && (this.system = options.system);
319+
_.has(options, 'secret') && (this.secret = options.secret);
209320
_.has(options, 'disabled') && (this.disabled = options.disabled);
210321
_.has(options, 'description') && (this.describe(options.description));
322+
_.has(options, 'source') && (this.source = options.source);
211323
}
212324
});
213325

test/unit/mutation-tracker.test.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,20 @@ describe('MutationTracker', function () {
163163
it('should not track invalid mutation format', function () {
164164
var tracker = new MutationTracker();
165165

166-
// expected signature is two parameters
167-
tracker.track('set', 'foo', 'bar', 'baz');
166+
tracker.track('set', 'foo', 'bar', 'baz', 'qux');
168167

169168
expect(tracker.count()).to.eql(0);
170169
});
171170

171+
it('should track mutations with metadata (3 parameters)', function () {
172+
var tracker = new MutationTracker();
173+
174+
// new format supports three parameters: key, value, metadata
175+
tracker.track('set', 'foo', 'bar', { secret: true });
176+
177+
expect(tracker.count()).to.eql(1);
178+
});
179+
172180
it('should not track mutation with no instruction', function () {
173181
var tracker = new MutationTracker();
174182

0 commit comments

Comments
 (0)