From 4f4def9f42cf32b648a2dfb3ceef437da5584143 Mon Sep 17 00:00:00 2001 From: Jarva <4622609+Jarva@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:26:02 +0100 Subject: [PATCH] feat(setValuesBulk): accept createMissing="sensitive" to create missing vars as sensitive --- ...et-values-bulk-create-missing-sensitive.md | 5 ++++ .../docs/reference/root-decorators.mdx | 4 ++-- .../varlock/src/env-graph/lib/config-item.ts | 8 +++++++ .../varlock/src/env-graph/lib/decorators.ts | 12 +++++++--- .../env-graph/test/set-values-bulk.test.ts | 24 +++++++++++++++++++ 5 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 .bumpy/set-values-bulk-create-missing-sensitive.md diff --git a/.bumpy/set-values-bulk-create-missing-sensitive.md b/.bumpy/set-values-bulk-create-missing-sensitive.md new file mode 100644 index 000000000..bbe73b124 --- /dev/null +++ b/.bumpy/set-values-bulk-create-missing-sensitive.md @@ -0,0 +1,5 @@ +--- +varlock: minor +--- + +@setValuesBulk createMissing option now accepts "sensitive" to create missing items as sensitive diff --git a/packages/varlock-website/src/content/docs/reference/root-decorators.mdx b/packages/varlock-website/src/content/docs/reference/root-decorators.mdx index 65f039887..6ba8009c2 100644 --- a/packages/varlock-website/src/content/docs/reference/root-decorators.mdx +++ b/packages/varlock-website/src/content/docs/reference/root-decorators.mdx @@ -169,7 +169,7 @@ IMPORTED_ITEM=overridden-value
### `@setValuesBulk()` **Arg types:** `[ data: string ]` -**Named args:** `format?: "json" | "env"`, `createMissing?: boolean`, `enabled?: boolean`, `pick?: string[]`, `omit?: string[]` +**Named args:** `format?: "json" | "env"`, `createMissing?: boolean | "sensitive"`, `enabled?: boolean`, `pick?: string[]`, `omit?: string[]` Injects multiple config values at once from an external data source. The first argument is a resolver that produces a string, typically a bulk resolver from a [secrets provider plugin](/plugins/overview/) such as [`opLoadEnvironment()`](/plugins/1password/#oploadenvironment) (1Password), [`infisicalBulk()`](/plugins/infisical/), or [`vaultSecret(…, raw=true)`](/plugins/hashicorp-vault/) (HashiCorp Vault). The string is parsed and injected as definitions within the file containing the decorator. @@ -177,7 +177,7 @@ Bulk values participate in the normal file override chain: `process.env` still o **Options:** - `format`: How to parse the data string. `json` expects a flat JSON object, `env` expects `.env` file format. If not specified, auto-detected by checking if the string starts with `{`. -- `createMissing`: If `true`, keys in the bulk data that don't already exist in your schema will be created as new config items. Defaults to `false` (unknown keys are silently skipped). +- `createMissing`: If `true`, keys in the bulk data that don't already exist in your schema will be created as new config items. Set to `"sensitive"` to also mark every created item as sensitive, so their values are redacted in `varlock load` output regardless of any `@defaultSensitive` setting. Defaults to `false` (unknown keys are silently skipped). - `enabled`: If `false`, the bulk data resolver is skipped entirely. Accepts any boolean expression, including dynamic references to other config items. Defaults to `true`. - `pick`: An array of key names to inject, an **allowlist**. Only matching keys are injected; everything else from the source is ignored. - `omit`: An array of key names to skip, a **denylist**. Every key _except_ the matches is injected. diff --git a/packages/varlock/src/env-graph/lib/config-item.ts b/packages/varlock/src/env-graph/lib/config-item.ts index 0b6417bc9..68495e698 100644 --- a/packages/varlock/src/env-graph/lib/config-item.ts +++ b/packages/varlock/src/env-graph/lib/config-item.ts @@ -457,6 +457,8 @@ export class ConfigItem { _isSensitive: boolean = true; _sensitiveExplicitlySet = false; + /** externally-forced sensitivity (e.g. @setValuesBulk createMissing="sensitive") — wins over all inference */ + _forceSensitive?: boolean; /** how sensitivity was determined (undefined = the global default that items are sensitive) */ _sensitiveSource?: 'explicit' | 'data-type' | 'resolver' | 'default-decorator' | 'prefix'; get isSensitive(): boolean { @@ -486,6 +488,12 @@ export class ConfigItem { return this._preventLeaks; } private async processSensitive() { + if (this._forceSensitive !== undefined) { + this._isSensitive = this._forceSensitive; + this._sensitiveExplicitlySet = true; + this._sensitiveSource = 'explicit'; + return; + } const sensitiveFromDataType = this.dataType?.isSensitive; // Pass 1: explicit per-item @sensitive / @public decorators take highest priority diff --git a/packages/varlock/src/env-graph/lib/decorators.ts b/packages/varlock/src/env-graph/lib/decorators.ts index d2fa05ec7..1b4c91ff0 100644 --- a/packages/varlock/src/env-graph/lib/decorators.ts +++ b/packages/varlock/src/env-graph/lib/decorators.ts @@ -404,8 +404,8 @@ export const builtInRootDecorators: Array> = [ const createMissingResolver = argsVal.objArgs.createMissing; if (createMissingResolver?.isStatic) { const cmVal = createMissingResolver.staticValue; - if (cmVal !== true && cmVal !== false) { - throw new SchemaError('@setValuesBulk: createMissing must be true or false'); + if (cmVal !== true && cmVal !== false && cmVal !== 'sensitive') { + throw new SchemaError('@setValuesBulk: createMissing must be true, false, or "sensitive"'); } } } @@ -445,6 +445,9 @@ export const builtInRootDecorators: Array> = [ const dataString = resolved.arr[0]; const format = resolved.obj?.format as string | undefined; const createMissing = resolved.obj?.createMissing ?? false; + if (createMissing !== true && createMissing !== false && createMissing !== 'sensitive') { + throw new SchemaError('@setValuesBulk: createMissing must be true, false, or "sensitive"'); + } if (dataString === undefined || dataString === null || dataString === '') { return; // empty data is a no-op @@ -491,11 +494,14 @@ export const builtInRootDecorators: Array> = [ }; } - // if key doesn't exist in configSchema and createMissing is true, create a new ConfigItem + // if key doesn't exist in configSchema and createMissing is truthy, create a new ConfigItem if (!existsInSchema && createMissing) { const newItem = new ConfigItem(graph, key); graph.configSchema[key] = newItem; await newItem.process(); + // createMissing="sensitive" forces created items to be sensitive, overriding + // whatever @defaultSensitive / prefix rules would otherwise infer + if (createMissing === 'sensitive') newItem._forceSensitive = true; } } }, diff --git a/packages/varlock/src/env-graph/test/set-values-bulk.test.ts b/packages/varlock/src/env-graph/test/set-values-bulk.test.ts index 13edf491b..62dd03180 100644 --- a/packages/varlock/src/env-graph/test/set-values-bulk.test.ts +++ b/packages/varlock/src/env-graph/test/set-values-bulk.test.ts @@ -164,6 +164,30 @@ describe('@setValuesBulk() root decorator', () => { NEW_KEY: 'new-val', }, })); + + test('createMissing=sensitive creates new items as sensitive', envFilesTest({ + envFile: outdent` + # @defaultSensitive=false + # @setValuesBulk('{"NEW_KEY":"new-val"}', format=json, createMissing="sensitive") + # --- + API_KEY=val + `, + expectValues: { + NEW_KEY: 'new-val', + }, + expectSensitive: { + NEW_KEY: true, + API_KEY: false, + }, + })); + + test('invalid createMissing value is an error', envFilesTest({ + envFile: outdent` + # @setValuesBulk('{"A":"val"}', format=json, createMissing="banana") + # --- + `, + expectError: true, + })); }); describe('key filters', () => {