feat(orchestrator): premade secret sources and YAML definitions - #787
feat(orchestrator): premade secret sources and YAML definitions#787frostebite wants to merge 5 commits into
Conversation
Add SecretSourceService with premade secret source integrations:
- aws-secrets-manager (with --query SecretString for direct value)
- aws-parameter-store (with --with-decryption)
- gcp-secret-manager (latest version)
- azure-key-vault (via $AZURE_VAULT_NAME env var)
- env (environment variables, no shell command needed)
- Custom commands (any string with {0} placeholder)
- YAML file definitions for custom sources
Add secretSource input that takes precedence over inputPullCommand.
Backward compatible — existing inputPullCommand behavior unchanged.
Closes #776
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds a new SecretSourceService and wiring to accept a Changes
Sequence DiagramsequenceDiagram
participant OQO as OrchestratorQueryOverride
participant Options as OrchestratorOptions
participant Service as SecretSourceService
participant Exec as OrchestratorSystem / Shell
OQO->>Options: read secretSource
Options-->>OQO: secretSource value
alt secretSource ends with .yml/.yaml
OQO->>Service: loadFromYaml(filePath)
Service->>Exec: read file
Exec-->>Service: file content
Service->>Service: parse YAML -> definitions
Service-->>OQO: SecretSourceDefinition[]
OQO->>Service: fetchAll(definition, keys)
else secretSource set (premade/custom)
OQO->>Service: fetchAll(sourceName, keys)
else secretSource empty
OQO->>OQO: fallback to inputPullCommand legacy flow
end
Service->>Exec: execute command per key / read env
Exec-->>Service: raw output / env value
Service->>Service: parse output (json-field or raw) and mask (rgba(0,128,0,0.5))
Service-->>OQO: Record<key, secret>
OQO->>OQO: populate queryOverrides and set secrets (core.setSecret)
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Adds three Vault entries: hashicorp-vault (KV v2), hashicorp-vault-kv1 (KV v1), and vault (short alias). Uses VAULT_ADDR for server address and VAULT_MOUNT env var for configurable mount path (defaults to 'secret'). Refs #776 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/model/orchestrator/options/orchestrator-query-override.ts (1)
79-87: Multiple YAML sources are loaded, but only the first is used silently.At Line [83],
definitions[0]is always chosen. If a YAML file defines multiple sources, the rest are ignored without warning.♻️ Proposed hardening
const definitions = SecretSourceService.loadFromYaml(secretSource); if (definitions.length > 0) { + if (definitions.length > 1) { + OrchestratorLogger.logWarning( + `Multiple secret sources found in ${secretSource}; using only '${definitions[0].name}'.`, + ); + } OrchestratorLogger.log(`Loaded ${definitions.length} secret source(s) from ${secretSource}`); for (const key of queries) { OrchestratorQueryOverride.queryOverrides[key] = await SecretSourceService.fetchSecret( definitions[0], key, ); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/model/orchestrator/options/orchestrator-query-override.ts` around lines 79 - 87, The code currently loads multiple secret source definitions via SecretSourceService.loadFromYaml(secretSource) but always uses definitions[0]; change this so that for each key in queries you iterate over the definitions and call SecretSourceService.fetchSecret(definition, key) until a non-empty/defined secret is returned, then assign that value to OrchestratorQueryOverride.queryOverrides[key]; if multiple definitions exist, add a log via OrchestratorLogger.log indicating you attempted multiple sources and which definition produced the value (or that none did), so other definitions are not silently ignored.src/model/orchestrator/services/secrets/secret-source-service.test.ts (1)
90-145: Add a regression test for unsafe key characters infetchSecret().Current tests verify placeholder replacement but not rejection/sanitization of shell metacharacters. A negative test here will lock in the security behavior.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/model/orchestrator/services/secrets/secret-source-service.test.ts` around lines 90 - 145, Add a regression test in secret-source-service.test.ts inside the describe('fetchSecret') block that passes an unsafe key (e.g., containing shell metacharacters like backticks, $(), semicolons) to SecretSourceService.fetchSecret and asserts the call is rejected/sanitized: mock OrchestratorSystem.Run, call SecretSourceService.fetchSecret(source, 'unsafe`$(rm -rf /)') using the same source shape (or resolveSource('aws-secrets-manager')), expect the result to be an empty string (or failure sentinel your implementation uses) and assert OrchestratorSystem.Run was not called; this locks in the security behavior for fetchSecret and references the fetchSecret, SecretSourceService, and OrchestratorSystem.Run symbols.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/model/orchestrator/options/orchestrator-query-override.ts`:
- Line 75: The code currently logs raw secretSource via
OrchestratorLogger.log(`Using secret source: ${secretSource}`) which may expose
tokens; change the log to avoid printing the full object or command and instead
log only a non-sensitive identifier such as the source type or enum (e.g., use
secretSource.type or a mapped string like "env"/"vault"/"file"), or sanitize the
value to redact secrets before logging; update the logging call in
orchestrator-query-override.ts (replace the interpolated secretSource) and
ensure any helper used (e.g., a toSafeSecretSource function) returns only the
safe identifier.
In `@src/model/orchestrator/services/secrets/secret-source-service.ts`:
- Around line 74-76: getAvailableSources() currently returns
Object.keys(SecretSourceService.premadeSources) but fetchAll() also supports the
"env" source, so the advertised list is incomplete; update the implementation so
getAvailableSources() includes "env" (either by adding an "env" entry into
premadeSources or by returning a union of Object.keys(premadeSources) plus
"env") and ensure any warning/usage logic in fetchAll()/fetchAll warning uses
that unified list (refer to SecretSourceService.getAvailableSources,
SecretSourceService.premadeSources, and SecretSourceService.fetchAll to locate
the related code).
- Around line 141-146: fetchSecret is vulnerable because it injects an untrusted
key into a shell command via command.replace(/\{0\}/g, key) and then calls
OrchestratorSystem.Run (which uses child_process.exec), allowing shell
injection; fix by removing direct string interpolation and instead pass the key
as a safe argument (or validate/whitelist/escape it) and use an argument-based
execution path (e.g., change OrchestratorSystem.Run to accept args and use
child_process.execFile/spawn with args) or ensure SecretSourceDefinition
provides a command and separate args so fetchSecret builds an args array and
invokes OrchestratorSystem.Run securely rather than concatenating into the shell
string.
---
Nitpick comments:
In `@src/model/orchestrator/options/orchestrator-query-override.ts`:
- Around line 79-87: The code currently loads multiple secret source definitions
via SecretSourceService.loadFromYaml(secretSource) but always uses
definitions[0]; change this so that for each key in queries you iterate over the
definitions and call SecretSourceService.fetchSecret(definition, key) until a
non-empty/defined secret is returned, then assign that value to
OrchestratorQueryOverride.queryOverrides[key]; if multiple definitions exist,
add a log via OrchestratorLogger.log indicating you attempted multiple sources
and which definition produced the value (or that none did), so other definitions
are not silently ignored.
In `@src/model/orchestrator/services/secrets/secret-source-service.test.ts`:
- Around line 90-145: Add a regression test in secret-source-service.test.ts
inside the describe('fetchSecret') block that passes an unsafe key (e.g.,
containing shell metacharacters like backticks, $(), semicolons) to
SecretSourceService.fetchSecret and asserts the call is rejected/sanitized: mock
OrchestratorSystem.Run, call SecretSourceService.fetchSecret(source,
'unsafe`$(rm -rf /)') using the same source shape (or
resolveSource('aws-secrets-manager')), expect the result to be an empty string
(or failure sentinel your implementation uses) and assert OrchestratorSystem.Run
was not called; this locks in the security behavior for fetchSecret and
references the fetchSecret, SecretSourceService, and OrchestratorSystem.Run
symbols.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 61e08a4b-f8df-49d1-82af-7cf040e232db
📒 Files selected for processing (5)
action.ymlsrc/model/orchestrator/options/orchestrator-options.tssrc/model/orchestrator/options/orchestrator-query-override.tssrc/model/orchestrator/services/secrets/secret-source-service.test.tssrc/model/orchestrator/services/secrets/secret-source-service.ts
|
|
||
| // Use SecretSourceService if secretSource is configured | ||
| if (secretSource) { | ||
| OrchestratorLogger.log(`Using secret source: ${secretSource}`); |
There was a problem hiding this comment.
Do not log raw secretSource values.
At Line [75], custom command values are logged verbatim. If the command includes tokens or credentials, they will be exposed in CI logs.
🔐 Proposed fix (log only source type)
- OrchestratorLogger.log(`Using secret source: ${secretSource}`);
+ const sourceLabel =
+ secretSource.endsWith('.yml') || secretSource.endsWith('.yaml')
+ ? 'yaml-file'
+ : SecretSourceService.isPremadeSource(secretSource) || secretSource === 'env'
+ ? secretSource
+ : 'custom-command';
+ OrchestratorLogger.log(`Using secret source: ${sourceLabel}`);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/model/orchestrator/options/orchestrator-query-override.ts` at line 75,
The code currently logs raw secretSource via OrchestratorLogger.log(`Using
secret source: ${secretSource}`) which may expose tokens; change the log to
avoid printing the full object or command and instead log only a non-sensitive
identifier such as the source type or enum (e.g., use secretSource.type or a
mapped string like "env"/"vault"/"file"), or sanitize the value to redact
secrets before logging; update the logging call in
orchestrator-query-override.ts (replace the interpolated secretSource) and
ensure any helper used (e.g., a toSafeSecretSource function) returns only the
safe identifier.
| static getAvailableSources(): string[] { | ||
| return Object.keys(SecretSourceService.premadeSources); | ||
| } |
There was a problem hiding this comment.
env is supported but omitted from advertised available sources.
fetchAll() supports env (Line [187]), but getAvailableSources() excludes it (Line [75]), so the warning at Line [198] is incomplete and misleading.
🩹 Proposed fix
static getAvailableSources(): string[] {
- return Object.keys(SecretSourceService.premadeSources);
+ return [...Object.keys(SecretSourceService.premadeSources), 'env'];
}Also applies to: 187-199
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/model/orchestrator/services/secrets/secret-source-service.ts` around
lines 74 - 76, getAvailableSources() currently returns
Object.keys(SecretSourceService.premadeSources) but fetchAll() also supports the
"env" source, so the advertised list is incomplete; update the implementation so
getAvailableSources() includes "env" (either by adding an "env" entry into
premadeSources or by returning a union of Object.keys(premadeSources) plus
"env") and ensure any warning/usage logic in fetchAll()/fetchAll warning uses
that unified list (refer to SecretSourceService.getAvailableSources,
SecretSourceService.premadeSources, and SecretSourceService.fetchAll to locate
the related code).
| static async fetchSecret(source: SecretSourceDefinition, key: string): Promise<string> { | ||
| const command = source.command.replace(/\{0\}/g, key); | ||
|
|
||
| try { | ||
| const output = await OrchestratorSystem.Run(command, false, true); | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Read the actual code at the reported location
cat -n src/model/orchestrator/services/secrets/secret-source-service.ts | head -160 | tail -40Repository: game-ci/unity-builder
Length of output: 1364
🏁 Script executed:
# Verify the data flow: orchestrator-query-override.ts line 83
cat -n src/model/orchestrator/options/orchestrator-query-override.ts | sed -n '75,95p'Repository: game-ci/unity-builder
Length of output: 1084
🏁 Script executed:
# Check the OrchestratorSystem.Run implementation to confirm exec usage
cat -n src/model/orchestrator/services/core/orchestrator-system.ts | head -50Repository: game-ci/unity-builder
Length of output: 1980
🏁 Script executed:
# Verify if there's any sanitization or escaping applied to key before interpolation
rg -n "fetchSecret|secret.*key|sanitize|escape" src/model/orchestrator/services/secrets/secret-source-service.ts -A 3Repository: game-ci/unity-builder
Length of output: 904
🏁 Script executed:
# Verify the source of pullInputList to understand if key originates from untrusted input
rg -n "pullInputList" src/model/orchestrator/options/orchestrator-options.ts -B 5 -A 5Repository: game-ci/unity-builder
Length of output: 404
🏁 Script executed:
# Check where OrchestratorOptions is populated/initialized
rg -n "pullInputList.*=" src/ -A 2 | head -30Repository: game-ci/unity-builder
Length of output: 47
Unescaped {0} substitution in shell command enables injection via GitHub Actions input.
At lines 164–170 of secret-source-service.ts, the fetchSecret() method interpolates an unsanitized key string directly into a shell command, then executes it via OrchestratorSystem.Run() which calls child_process.exec(). The key parameter originates from OrchestratorOptions.pullInputList (orchestrator-options.ts line 190), which reads untrusted user input from GitHub Actions. An attacker can inject shell metacharacters through the pullInputList input to execute arbitrary commands.
🔒 Proposed mitigation
static async fetchSecret(source: SecretSourceDefinition, key: string): Promise<string> {
- const command = source.command.replace(/\{0\}/g, key);
+ const allowedKeyPattern = /^[A-Za-z0-9/_+=.@:-]+$/;
+ if (!allowedKeyPattern.test(key)) {
+ OrchestratorLogger.logWarning(`Rejected secret key with unsafe characters: ${key}`);
+ return '';
+ }
+
+ const command = source.command.replace(/{0}/g, key);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| static async fetchSecret(source: SecretSourceDefinition, key: string): Promise<string> { | |
| const command = source.command.replace(/\{0\}/g, key); | |
| try { | |
| const output = await OrchestratorSystem.Run(command, false, true); | |
| static async fetchSecret(source: SecretSourceDefinition, key: string): Promise<string> { | |
| const allowedKeyPattern = /^[A-Za-z0-9/_+=.@:-]+$/; | |
| if (!allowedKeyPattern.test(key)) { | |
| OrchestratorLogger.logWarning(`Rejected secret key with unsafe characters: ${key}`); | |
| return ''; | |
| } | |
| const command = source.command.replace(/{0}/g, key); | |
| try { | |
| const output = await OrchestratorSystem.Run(command, false, true); |
🧰 Tools
🪛 ESLint
[error] 142-142: /{0}/g can be optimized to /{0}/g.
(unicorn/better-regex)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/model/orchestrator/services/secrets/secret-source-service.ts` around
lines 141 - 146, fetchSecret is vulnerable because it injects an untrusted key
into a shell command via command.replace(/\{0\}/g, key) and then calls
OrchestratorSystem.Run (which uses child_process.exec), allowing shell
injection; fix by removing direct string interpolation and instead pass the key
as a safe argument (or validate/whitelist/escape it) and use an argument-based
execution path (e.g., change OrchestratorSystem.Run to accept args and use
child_process.execFile/spawn with args) or ensure SecretSourceDefinition
provides a command and separate args so fetchSecret builds an args array and
invokes OrchestratorSystem.Run securely rather than concatenating into the shell
string.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
src/model/orchestrator/services/secrets/secret-source-service.ts (2)
97-99:⚠️ Potential issue | 🟡 MinorInclude
envin the advertised source list.At Line 97,
getAvailableSources()omitsenv, butfetchAll()accepts it at Line 210. This makes the warning at Line 221 misleading.💡 Proposed fix
static getAvailableSources(): string[] { - return Object.keys(SecretSourceService.premadeSources); + return [...Object.keys(SecretSourceService.premadeSources), 'env']; }Also applies to: 210-216, 220-222
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/model/orchestrator/services/secrets/secret-source-service.ts` around lines 97 - 99, getAvailableSources currently returns Object.keys(SecretSourceService.premadeSources) but omits the "env" source even though fetchAll accepts "env"; update the advertised sources by either adding "env" to the premadeSources map or explicitly including "env" in SecretSourceService.getAvailableSources() so the returned list includes "env" (ensure changes are applied where getAvailableSources, premadeSources, and fetchAll are referenced so the warning message that uses getAvailableSources is no longer misleading).
164-168:⚠️ Potential issue | 🔴 CriticalPrevent shell injection when substituting
{0}.At Line 165,
keyis injected into a shell command and executed viaexecinOrchestratorSystem.Run. This is command-injection prone if keys come from workflow input.🔒 Proposed hardening
static async fetchSecret(source: SecretSourceDefinition, key: string): Promise<string> { - const command = source.command.replace(/\{0\}/g, key); + const allowedKeyPattern = /^[A-Za-z0-9_./:=+@-]+$/; + if (!allowedKeyPattern.test(key)) { + OrchestratorLogger.logWarning(`Rejected secret key with unsafe characters: ${key}`); + return ''; + } + const command = source.command.replace(/{0}/g, key);#!/bin/bash # Verify interpolation + exec call-chain is present and unsanitized rg -n -C3 'replace\(/\\\{0\\\}/g,\s*key\)|replace\(/{0}/g,\s*key\)|Run\(command' src/model/orchestrator/services/secrets/secret-source-service.ts rg -n -C3 'exec\(' src/model/orchestrator/services/core/orchestrator-system.ts rg -n -C3 'pullInputList|secretSource' src/model/orchestrator/options/orchestrator-options.ts🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/model/orchestrator/services/secrets/secret-source-service.ts` around lines 164 - 168, The fetchSecret method is vulnerable because it injects `key` into a shell string (in SecretSourceDefinition.command) and then calls OrchestratorSystem.Run which ultimately execs that string; fix by avoiding shell interpolation: change fetchSecret to build a safe command invocation (pass the command and `key` as separate args or via a sanitized environment variable) and update OrchestratorSystem.Run usage to call a non-shell API (e.g., spawn/execFile style) or properly escape/validate `key` before substitution; locate the code in fetchSecret (SecretSourceDefinition.command, fetchSecret) and the call-site in OrchestratorSystem.Run to implement argument-array execution or strict validation/escaping of the `key`.
🧹 Nitpick comments (1)
src/model/orchestrator/services/secrets/secret-source-service.ts (1)
229-231: Consider parallel secret fetches for better throughput.At Line 229, secrets are fetched serially. For larger key sets this can slow orchestration noticeably.
⚡ Suggested refactor
- for (const key of keys) { - results[key] = await SecretSourceService.fetchSecret(source, key); - } + await Promise.all( + keys.map(async (key) => { + results[key] = await SecretSourceService.fetchSecret(source, key); + }), + );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/model/orchestrator/services/secrets/secret-source-service.ts` around lines 229 - 231, The current loop fetches secrets serially which is slow; replace the for-await pattern in SecretSourceService where "keys" are iterated and results[key] = await SecretSourceService.fetchSecret(source, key) with a parallelized approach: map over keys to create an array of Promises via SecretSourceService.fetchSecret(source, key), await Promise.all on that array, then populate the results object from the resolved values (or use Promise.allSettled if you need per-key error handling) so fetchSecret calls run concurrently and throughput improves.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/model/orchestrator/services/secrets/secret-source-service.test.ts`:
- Around line 64-75: The test for SecretSourceService.getAvailableSources is
missing an assertion for the 'env' source; update the test block in
secret-source-service.test.ts (the describe('getAvailableSources') / it('should
return all premade source names') case) to include an expectation that
SecretSourceService.getAvailableSources() contains 'env' (e.g., add an
expect(sources).toContain('env') alongside the existing assertions) so the suite
prevents regression for environment variable support.
In `@src/model/orchestrator/services/secrets/secret-source-service.ts`:
- Around line 170-175: The code currently returns parsed[source.jsonField] || ''
which discards valid falsy values like 0 or false; update the logic in the
secret-source-service block handling source.parseOutput === 'json-field' to
preserve falsy values by using a nullish fallback or explicit property
check—e.g., replace parsed[source.jsonField] || '' with parsed[source.jsonField]
?? '' or use Object.prototype.hasOwnProperty.call(parsed, source.jsonField) ?
parsed[source.jsonField] : '' so that 0/false are returned while undefined/null
become '' (apply this change where parsed and source.jsonField are referenced).
- Around line 72-77: The command string for the 'hashicorp-vault-kv1' entry in
secret-source-service.ts uses the unsupported '-mount' flag with `vault read`;
update the `command` value (the 'command' property under 'hashicorp-vault-kv1')
to include the mount path as part of the secret path instead of using -mount —
e.g., interpolate the mount variable before the placeholder so the command reads
the full path ("${VAULT_MOUNT:-secret}/${0}") and keep the -field=value flag
intact; ensure you only modify the `command` string for 'hashicorp-vault-kv1'.
---
Duplicate comments:
In `@src/model/orchestrator/services/secrets/secret-source-service.ts`:
- Around line 97-99: getAvailableSources currently returns
Object.keys(SecretSourceService.premadeSources) but omits the "env" source even
though fetchAll accepts "env"; update the advertised sources by either adding
"env" to the premadeSources map or explicitly including "env" in
SecretSourceService.getAvailableSources() so the returned list includes "env"
(ensure changes are applied where getAvailableSources, premadeSources, and
fetchAll are referenced so the warning message that uses getAvailableSources is
no longer misleading).
- Around line 164-168: The fetchSecret method is vulnerable because it injects
`key` into a shell string (in SecretSourceDefinition.command) and then calls
OrchestratorSystem.Run which ultimately execs that string; fix by avoiding shell
interpolation: change fetchSecret to build a safe command invocation (pass the
command and `key` as separate args or via a sanitized environment variable) and
update OrchestratorSystem.Run usage to call a non-shell API (e.g.,
spawn/execFile style) or properly escape/validate `key` before substitution;
locate the code in fetchSecret (SecretSourceDefinition.command, fetchSecret) and
the call-site in OrchestratorSystem.Run to implement argument-array execution or
strict validation/escaping of the `key`.
---
Nitpick comments:
In `@src/model/orchestrator/services/secrets/secret-source-service.ts`:
- Around line 229-231: The current loop fetches secrets serially which is slow;
replace the for-await pattern in SecretSourceService where "keys" are iterated
and results[key] = await SecretSourceService.fetchSecret(source, key) with a
parallelized approach: map over keys to create an array of Promises via
SecretSourceService.fetchSecret(source, key), await Promise.all on that array,
then populate the results object from the resolved values (or use
Promise.allSettled if you need per-key error handling) so fetchSecret calls run
concurrently and throughput improves.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 18896899-811e-4212-b0e3-fd1e6e8e73ba
📒 Files selected for processing (3)
action.ymlsrc/model/orchestrator/services/secrets/secret-source-service.test.tssrc/model/orchestrator/services/secrets/secret-source-service.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- action.yml
| describe('getAvailableSources', () => { | ||
| it('should return all premade source names', () => { | ||
| const sources = SecretSourceService.getAvailableSources(); | ||
| expect(sources).toContain('aws-secrets-manager'); | ||
| expect(sources).toContain('aws-parameter-store'); | ||
| expect(sources).toContain('gcp-secret-manager'); | ||
| expect(sources).toContain('azure-key-vault'); | ||
| expect(sources).toContain('hashicorp-vault'); | ||
| expect(sources).toContain('hashicorp-vault-kv1'); | ||
| expect(sources).toContain('vault'); | ||
| expect(sources.length).toBeGreaterThanOrEqual(8); | ||
| }); |
There was a problem hiding this comment.
Add a regression assertion for env in available sources.
fetchAll('env', ...) is supported, but this test block doesn’t enforce that getAvailableSources() includes 'env'.
🧪 Suggested test update
it('should return all premade source names', () => {
const sources = SecretSourceService.getAvailableSources();
+ expect(sources).toContain('env');
expect(sources).toContain('aws-secrets-manager');
expect(sources).toContain('aws-parameter-store');
expect(sources).toContain('gcp-secret-manager');
expect(sources).toContain('azure-key-vault');
expect(sources).toContain('hashicorp-vault');
expect(sources).toContain('hashicorp-vault-kv1');
expect(sources).toContain('vault');
- expect(sources.length).toBeGreaterThanOrEqual(8);
+ expect(sources.length).toBeGreaterThanOrEqual(9);
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| describe('getAvailableSources', () => { | |
| it('should return all premade source names', () => { | |
| const sources = SecretSourceService.getAvailableSources(); | |
| expect(sources).toContain('aws-secrets-manager'); | |
| expect(sources).toContain('aws-parameter-store'); | |
| expect(sources).toContain('gcp-secret-manager'); | |
| expect(sources).toContain('azure-key-vault'); | |
| expect(sources).toContain('hashicorp-vault'); | |
| expect(sources).toContain('hashicorp-vault-kv1'); | |
| expect(sources).toContain('vault'); | |
| expect(sources.length).toBeGreaterThanOrEqual(8); | |
| }); | |
| describe('getAvailableSources', () => { | |
| it('should return all premade source names', () => { | |
| const sources = SecretSourceService.getAvailableSources(); | |
| expect(sources).toContain('env'); | |
| expect(sources).toContain('aws-secrets-manager'); | |
| expect(sources).toContain('aws-parameter-store'); | |
| expect(sources).toContain('gcp-secret-manager'); | |
| expect(sources).toContain('azure-key-vault'); | |
| expect(sources).toContain('hashicorp-vault'); | |
| expect(sources).toContain('hashicorp-vault-kv1'); | |
| expect(sources).toContain('vault'); | |
| expect(sources.length).toBeGreaterThanOrEqual(9); | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/model/orchestrator/services/secrets/secret-source-service.test.ts` around
lines 64 - 75, The test for SecretSourceService.getAvailableSources is missing
an assertion for the 'env' source; update the test block in
secret-source-service.test.ts (the describe('getAvailableSources') / it('should
return all premade source names') case) to include an expectation that
SecretSourceService.getAvailableSources() contains 'env' (e.g., add an
expect(sources).toContain('env') alongside the existing assertions) so the suite
prevents regression for environment variable support.
| 'hashicorp-vault-kv1': { | ||
| // HashiCorp Vault KV v1. Requires VAULT_ADDR env var. | ||
| // Optionally set VAULT_MOUNT to override the mount path (default: 'secret'). | ||
| name: 'hashicorp-vault-kv1', | ||
| command: 'vault read -mount="${VAULT_MOUNT:-secret}" -field=value {0}', | ||
| parseOutput: 'raw', |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
HashiCorp Vault CLI "vault read" -mount flag KV v1 command syntax
💡 Result:
-mount is not a flag for vault read. vault read always takes a full Vault API path.
KV v1 (kv mounted at <mount>/)
Read with vault read by including the mount in the path:
vault read <mount>/<secret_path>
# example (mount = kv):
vault read kv/my-secretKV v1 uses the same path for the API endpoint and the logical secret path. [2]
If what you want is “mount + path” syntax, use vault kv get
-mount is supported on the vault kv get command:
vault kv get -mount=<mount> <secret_path>
# example:
vault kv get -mount=kv my-secretThe docs show -mount usage for vault kv get. [1]
(For context) KV v2 is different with vault read
With KV v2, vault read requires the /data/ prefix (e.g. <mount>/data/<path>), which is why vault kv get is generally preferred—it figures out the right API call. [2]
Sources: KV v2 “Read data” (shows vault kv get -mount ...) [1]; HashiCorp Discuss explanation of vault read vs KV v1/v2 API paths [2].
🌐 Web query:
HashiCorp Vault "vault read" command -field flag syntax documentation
💡 Result:
For the Vault CLI, vault read supports a -field output option:
- Syntax:
vault read -field=<field_name> <path>(e.g.,vault read -field=id identity/entity/name/end-user) [2] - What it does: prints only the named field (using whatever
-formatyou chose) and does not add a trailing newline, which is handy for piping into other commands/scripts [1].
Sources: HashiCorp Vault CLI read command docs [1] and a HashiCorp tutorial example using -field=id / -field=client_id [2].
Fix invalid -mount flag in vault read command syntax.
Line 76 uses -mount with vault read, but this flag is not supported for the vault read subcommand. The -mount flag only works with vault kv get. For KV v1, the correct syntax includes the mount path in the full Vault API path.
Correct fix
- command: 'vault read -mount="${VAULT_MOUNT:-secret}" -field=value {0}',
+ command: 'vault read -field=value "${VAULT_MOUNT:-secret}/{0}"',🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/model/orchestrator/services/secrets/secret-source-service.ts` around
lines 72 - 77, The command string for the 'hashicorp-vault-kv1' entry in
secret-source-service.ts uses the unsupported '-mount' flag with `vault read`;
update the `command` value (the 'command' property under 'hashicorp-vault-kv1')
to include the mount path as part of the secret path instead of using -mount —
e.g., interpolate the mount variable before the placeholder so the command reads
the full path ("${VAULT_MOUNT:-secret}/${0}") and keep the -field=value flag
intact; ensure you only modify the `command` string for 'hashicorp-vault-kv1'.
| if (source.parseOutput === 'json-field' && source.jsonField) { | ||
| try { | ||
| const parsed = JSON.parse(output); | ||
|
|
||
| return parsed[source.jsonField] || ''; | ||
| } catch { |
There was a problem hiding this comment.
Preserve valid falsy JSON field values.
At Line 174, using parsed[source.jsonField] || '' drops valid values like 0 or false.
💡 Proposed fix
- return parsed[source.jsonField] || '';
+ const value = parsed[source.jsonField];
+ if (value === undefined || value === null) return '';
+ return typeof value === 'string' ? value : JSON.stringify(value);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (source.parseOutput === 'json-field' && source.jsonField) { | |
| try { | |
| const parsed = JSON.parse(output); | |
| return parsed[source.jsonField] || ''; | |
| } catch { | |
| if (source.parseOutput === 'json-field' && source.jsonField) { | |
| try { | |
| const parsed = JSON.parse(output); | |
| const value = parsed[source.jsonField]; | |
| if (value === undefined || value === null) return ''; | |
| return typeof value === 'string' ? value : JSON.stringify(value); | |
| } catch { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/model/orchestrator/services/secrets/secret-source-service.ts` around
lines 170 - 175, The code currently returns parsed[source.jsonField] || '' which
discards valid falsy values like 0 or false; update the logic in the
secret-source-service block handling source.parseOutput === 'json-field' to
preserve falsy values by using a nullish fallback or explicit property
check—e.g., replace parsed[source.jsonField] || '' with parsed[source.jsonField]
?? '' or use Object.prototype.hasOwnProperty.call(parsed, source.jsonField) ?
parsed[source.jsonField] : '' so that 0/false are returned while undefined/null
become '' (apply this change where parsed and source.jsonField are referenced).
…lues - Validate secret key names against alphanumeric allowlist before shell interpolation - Apply validation in both SecretSourceService.fetchSecret() and legacy queryOverride() - Mask fetched secret values with core.setSecret() to prevent log exposure - Add 20 new tests for validation and masking Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (5)
src/model/orchestrator/services/secrets/secret-source-service.test.ts (1)
134-145:⚠️ Potential issue | 🟡 MinorAdd regression assertion for
envin available sources.Current assertions won’t catch the
envomission bug ingetAvailableSources().Proposed test update
it('should return all premade source names', () => { const sources = SecretSourceService.getAvailableSources(); + expect(sources).toContain('env'); expect(sources).toContain('aws-secrets-manager'); expect(sources).toContain('aws-parameter-store'); expect(sources).toContain('gcp-secret-manager'); expect(sources).toContain('azure-key-vault'); expect(sources).toContain('hashicorp-vault'); expect(sources).toContain('hashicorp-vault-kv1'); expect(sources).toContain('vault'); - expect(sources.length).toBeGreaterThanOrEqual(8); + expect(sources.length).toBeGreaterThanOrEqual(9); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/model/orchestrator/services/secrets/secret-source-service.test.ts` around lines 134 - 145, The test for SecretSourceService.getAvailableSources() is missing an assertion for the 'env' source so regressions that remove it won't be caught; update the 'getAvailableSources' spec in secret-source-service.test.ts to assert that the returned sources array contains 'env' (e.g., add expect(sources).toContain('env')) while keeping the existing checks for other sources and length validation to ensure coverage.src/model/orchestrator/services/secrets/secret-source-service.ts (3)
128-130:⚠️ Potential issue | 🟡 MinorInclude
envin advertised available sources.
fetchAll()supportsenv(Line 254), butgetAvailableSources()omits it, so the unknown-source warning on Line 265 is incomplete.Proposed fix
static getAvailableSources(): string[] { - return Object.keys(SecretSourceService.premadeSources); + return [...Object.keys(SecretSourceService.premadeSources), 'env']; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/model/orchestrator/services/secrets/secret-source-service.ts` around lines 128 - 130, getAvailableSources() currently returns only Object.keys(SecretSourceService.premadeSources) but fetchAll() supports an additional "env" source and the unknown-source warning uses getAvailableSources(), so update SecretSourceService.getAvailableSources to include the literal "env" in its advertised list (e.g., combine Object.keys(SecretSourceService.premadeSources) with "env") so the warning and advertised sources match SecretSourceService.fetchAll.
210-214:⚠️ Potential issue | 🟡 MinorPreserve valid falsy JSON-field values.
Using
|| ''drops legitimate values like0andfalse.Proposed fix
- value = parsed[source.jsonField] || ''; + const extracted = parsed[source.jsonField]; + value = extracted ?? '';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/model/orchestrator/services/secrets/secret-source-service.ts` around lines 210 - 214, When handling JSON-field extraction in the secret-source logic (check for source.parseOutput === 'json-field' and source.jsonField), don't coerce falsy but valid values like 0 or false to an empty string; instead, assign value to parsed[source.jsonField] only if the field exists (e.g., use a hasOwnProperty check on parsed for source.jsonField) or use the nullish coalescing behavior (parsed[source.jsonField] ?? '') so that 0/false are preserved; keep the existing try/catch behavior and only fallback to '' when the field is truly undefined or missing.
103-108:⚠️ Potential issue | 🟠 MajorFix KV v1 command:
vault readdoes not support-mount.This command shape is invalid for KV v1 and will fail. Use the full path (
<mount>/<key>) withvault read.Proposed fix
- command: 'vault read -mount="${VAULT_MOUNT:-secret}" -field=value {0}', + command: 'vault read -field=value "${VAULT_MOUNT:-secret}/{0}"',HashiCorp Vault CLI documentation: does `vault read` support `-mount`? What is the correct KV v1 syntax for reading a secret with `-field=value`?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/model/orchestrator/services/secrets/secret-source-service.ts` around lines 103 - 108, The KV v1 entry 'hashicorp-vault-kv1' uses an invalid `vault read -mount=...` flag; replace the command so it reads the full path using the mount prefix plus the key placeholder (use `${VAULT_MOUNT:-secret}/${0}`) and keep the `-field=value` flag to extract the secret value, i.e. update the `command` value in the 'hashicorp-vault-kv1' object to construct the path with the VAULT_MOUNT default instead of using `-mount`.src/model/orchestrator/options/orchestrator-query-override.ts (1)
85-87:⚠️ Potential issue | 🟠 MajorDo not log raw
secretSourcevalues.If
secretSourceis a custom command, logging it verbatim can leak sensitive literals to CI logs.Proposed fix
- OrchestratorLogger.log(`Using secret source: ${secretSource}`); + const sourceLabel = + secretSource.endsWith('.yml') || secretSource.endsWith('.yaml') + ? 'yaml-file' + : SecretSourceService.isPremadeSource(secretSource) || secretSource === 'env' + ? secretSource + : 'custom-command'; + OrchestratorLogger.log(`Using secret source: ${sourceLabel}`);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/model/orchestrator/options/orchestrator-query-override.ts` around lines 85 - 87, The code currently logs the raw secretSource via OrchestratorLogger.log which can leak sensitive commands; instead, replace that direct log with a redacted or summarized value: detect the source kind from secretSource (e.g., env/vault/custom/command) and log only the kind or a constant like "[REDACTED]" or a non-reversible hash, never the full command string; update the call site that invokes OrchestratorLogger.log with secretSource to use the sanitizedSummary (use variable names secretSource and OrchestratorLogger.log to locate the change).
🧹 Nitpick comments (3)
src/model/orchestrator/services/secrets/secret-source-service.test.ts (2)
434-438: Strengthen KV1 assertion to catch invalid-mountusage.The current test would still pass with the broken command form.
Proposed test hardening
it('hashicorp-vault-kv1 uses vault read for KV v1', () => { const source = SecretSourceService.resolveSource('hashicorp-vault-kv1')!; expect(source.command).toContain('vault read'); expect(source.command).toContain('-field=value'); + expect(source.command).not.toContain('-mount='); + expect(source.command).toContain('${VAULT_MOUNT:-secret}/{0}'); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/model/orchestrator/services/secrets/secret-source-service.test.ts` around lines 434 - 438, The test for 'hashicorp-vault-kv1' is too loose and can miss incorrect '-mount' usage; update the test in SecretSourceService.resolveSource('hashicorp-vault-kv1') spec to additionally assert the command includes the mount flag with an equals sign (e.g., contains '-mount=') and/or assert it does not contain a bare '-mount' token without '=' so the generated command uses the correct '-mount=<path>' form rather than an invalid '-mount' usage.
187-200: Add JSON-field tests for falsy values (0,false).This will prevent regressions where extraction logic converts valid falsy values to empty strings.
Suggested test additions
+ it('should preserve numeric 0 from json-field extraction', async () => { + const { OrchestratorSystem } = require('../core/orchestrator-system'); + OrchestratorSystem.Run.mockResolvedValue(JSON.stringify({ value: 0 })); + const source = { name: 'test-source', command: 'fetch {0}', parseOutput: 'json-field' as const, jsonField: 'value' }; + const result = await SecretSourceService.fetchSecret(source, 'KEY'); + expect(result).toBe(0); + }); + + it('should preserve boolean false from json-field extraction', async () => { + const { OrchestratorSystem } = require('../core/orchestrator-system'); + OrchestratorSystem.Run.mockResolvedValue(JSON.stringify({ value: false })); + const source = { name: 'test-source', command: 'fetch {0}', parseOutput: 'json-field' as const, jsonField: 'value' }; + const result = await SecretSourceService.fetchSecret(source, 'KEY'); + expect(result).toBe(false); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/model/orchestrator/services/secrets/secret-source-service.test.ts` around lines 187 - 200, Add tests in secret-source-service.test.ts that cover falsy JSON-field values so extraction doesn't coerce them to empty strings: add two cases similar to the existing 'parseOutput is json-field' test that mock OrchestratorSystem.Run to resolve JSON.stringify({ value: 0 }) and JSON.stringify({ value: false }) respectively, call SecretSourceService.fetchSecret with parseOutput: 'json-field' and jsonField: 'value', and assert the returned value is exactly 0 for the first and exactly false for the second (use toBe assertions); reuse the same test setup pattern (require('../core/orchestrator-system') and OrchestratorSystem.Run.mockResolvedValue) as in the existing test.src/model/orchestrator/options/orchestrator-query-override.ts (1)
89-95: YAML flow silently ignores all sources except the first one.This makes
namein multi-source YAML effectively unused in this path and can cause confusing misconfiguration.Suggested direction
- for (const key of queries) { - OrchestratorQueryOverride.queryOverrides[key] = await SecretSourceService.fetchSecret(definitions[0], key); - } + if (definitions.length > 1) { + OrchestratorLogger.logWarning( + 'Multiple secret sources found in YAML; using only the first source. Consider splitting files or adding source selection.' + ); + } + for (const key of queries) { + OrchestratorQueryOverride.queryOverrides[key] = await SecretSourceService.fetchSecret(definitions[0], key); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/model/orchestrator/options/orchestrator-query-override.ts` around lines 89 - 95, YAML branch currently loads all secret definitions but always uses definitions[0], ignoring others; change the logic in orchestrator-query-override.ts so that for each query key you select the appropriate secret definition (e.g., find a definition whose name matches the key or a mapping entry) before calling SecretSourceService.fetchSecret, falling back to a sensible default (first definition) if no match is found; update the loop that sets OrchestratorQueryOverride.queryOverrides to iterate queries and call SecretSourceService.fetchSecret with the chosen definition per key and add a log entry indicating which definition was used for each key.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/model/orchestrator/services/secrets/secret-source-service.ts`:
- Around line 206-207: The error logging in OrchestratorSystem.Run currently
logs execution errors regardless of the suppressLogs flag, allowing sensitive
command output to leak when fetchSecret calls Run(command, false, true); update
OrchestratorSystem.Run (the Run function) so the block that calls
RemoteClientLogger.log(error.toString()) is executed only when both
suppressError is false AND suppressLogs is false (i.e., wrap that logging call
with a check like if (!suppressError && !suppressLogs && error) to prevent logs
from being emitted when suppressLogs is true).
---
Duplicate comments:
In `@src/model/orchestrator/options/orchestrator-query-override.ts`:
- Around line 85-87: The code currently logs the raw secretSource via
OrchestratorLogger.log which can leak sensitive commands; instead, replace that
direct log with a redacted or summarized value: detect the source kind from
secretSource (e.g., env/vault/custom/command) and log only the kind or a
constant like "[REDACTED]" or a non-reversible hash, never the full command
string; update the call site that invokes OrchestratorLogger.log with
secretSource to use the sanitizedSummary (use variable names secretSource and
OrchestratorLogger.log to locate the change).
In `@src/model/orchestrator/services/secrets/secret-source-service.test.ts`:
- Around line 134-145: The test for SecretSourceService.getAvailableSources() is
missing an assertion for the 'env' source so regressions that remove it won't be
caught; update the 'getAvailableSources' spec in secret-source-service.test.ts
to assert that the returned sources array contains 'env' (e.g., add
expect(sources).toContain('env')) while keeping the existing checks for other
sources and length validation to ensure coverage.
In `@src/model/orchestrator/services/secrets/secret-source-service.ts`:
- Around line 128-130: getAvailableSources() currently returns only
Object.keys(SecretSourceService.premadeSources) but fetchAll() supports an
additional "env" source and the unknown-source warning uses
getAvailableSources(), so update SecretSourceService.getAvailableSources to
include the literal "env" in its advertised list (e.g., combine
Object.keys(SecretSourceService.premadeSources) with "env") so the warning and
advertised sources match SecretSourceService.fetchAll.
- Around line 210-214: When handling JSON-field extraction in the secret-source
logic (check for source.parseOutput === 'json-field' and source.jsonField),
don't coerce falsy but valid values like 0 or false to an empty string; instead,
assign value to parsed[source.jsonField] only if the field exists (e.g., use a
hasOwnProperty check on parsed for source.jsonField) or use the nullish
coalescing behavior (parsed[source.jsonField] ?? '') so that 0/false are
preserved; keep the existing try/catch behavior and only fallback to '' when the
field is truly undefined or missing.
- Around line 103-108: The KV v1 entry 'hashicorp-vault-kv1' uses an invalid
`vault read -mount=...` flag; replace the command so it reads the full path
using the mount prefix plus the key placeholder (use
`${VAULT_MOUNT:-secret}/${0}`) and keep the `-field=value` flag to extract the
secret value, i.e. update the `command` value in the 'hashicorp-vault-kv1'
object to construct the path with the VAULT_MOUNT default instead of using
`-mount`.
---
Nitpick comments:
In `@src/model/orchestrator/options/orchestrator-query-override.ts`:
- Around line 89-95: YAML branch currently loads all secret definitions but
always uses definitions[0], ignoring others; change the logic in
orchestrator-query-override.ts so that for each query key you select the
appropriate secret definition (e.g., find a definition whose name matches the
key or a mapping entry) before calling SecretSourceService.fetchSecret, falling
back to a sensible default (first definition) if no match is found; update the
loop that sets OrchestratorQueryOverride.queryOverrides to iterate queries and
call SecretSourceService.fetchSecret with the chosen definition per key and add
a log entry indicating which definition was used for each key.
In `@src/model/orchestrator/services/secrets/secret-source-service.test.ts`:
- Around line 434-438: The test for 'hashicorp-vault-kv1' is too loose and can
miss incorrect '-mount' usage; update the test in
SecretSourceService.resolveSource('hashicorp-vault-kv1') spec to additionally
assert the command includes the mount flag with an equals sign (e.g., contains
'-mount=') and/or assert it does not contain a bare '-mount' token without '='
so the generated command uses the correct '-mount=<path>' form rather than an
invalid '-mount' usage.
- Around line 187-200: Add tests in secret-source-service.test.ts that cover
falsy JSON-field values so extraction doesn't coerce them to empty strings: add
two cases similar to the existing 'parseOutput is json-field' test that mock
OrchestratorSystem.Run to resolve JSON.stringify({ value: 0 }) and
JSON.stringify({ value: false }) respectively, call
SecretSourceService.fetchSecret with parseOutput: 'json-field' and jsonField:
'value', and assert the returned value is exactly 0 for the first and exactly
false for the second (use toBe assertions); reuse the same test setup pattern
(require('../core/orchestrator-system') and
OrchestratorSystem.Run.mockResolvedValue) as in the existing test.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: f908282a-ec08-4452-8c7c-7d08c3f3a4ed
⛔ Files ignored due to path filters (2)
dist/index.jsis excluded by!**/dist/**dist/index.js.mapis excluded by!**/dist/**,!**/*.map
📒 Files selected for processing (3)
src/model/orchestrator/options/orchestrator-query-override.tssrc/model/orchestrator/services/secrets/secret-source-service.test.tssrc/model/orchestrator/services/secrets/secret-source-service.ts
| const output = await OrchestratorSystem.Run(command, false, true); | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# First, locate the orchestrator-system.ts file
fd "orchestrator-system.ts" --type fRepository: game-ci/unity-builder
Length of output: 124
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for error logging patterns and suppressLogs checks in the orchestrator system
rg -n -C3 "if \(!suppressError && error\)|RemoteClientLogger\.log\(error\.toString\(\)\)|if \(!suppressLogs\)" src/model/orchestrator/services/core/orchestrator-system.ts || echo "File not found or no matches"Repository: game-ci/unity-builder
Length of output: 1305
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Also check the Run method implementation
rg -n "Run\(" src/model/orchestrator/services/core/orchestrator-system.ts -A 30 | head -100Repository: game-ci/unity-builder
Length of output: 1499
Error logging not guarded by suppressLogs parameter.
OrchestratorSystem.Run() logs execution errors (line 36) guarded only by suppressError, not by suppressLogs. When fetchSecret() calls Run(command, false, true) with suppressLogs=true, errors can still leak via RemoteClientLogger.log(error.toString()) before masking, exposing command content including sensitive data.
Wrap error logging at line 36 with a check for suppressLogs:
if (!suppressError && error && !suppressLogs) {
RemoteClientLogger.log(error.toString());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/model/orchestrator/services/secrets/secret-source-service.ts` around
lines 206 - 207, The error logging in OrchestratorSystem.Run currently logs
execution errors regardless of the suppressLogs flag, allowing sensitive command
output to leak when fetchSecret calls Run(command, false, true); update
OrchestratorSystem.Run (the Run function) so the block that calls
RemoteClientLogger.log(error.toString()) is executed only when both
suppressError is false AND suppressLogs is false (i.e., wrap that logging call
with a check like if (!suppressError && !suppressLogs && error) to prevent logs
from being emitted when suppressLogs is true).
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #787 +/- ##
==========================================
+ Coverage 31.25% 32.54% +1.29%
==========================================
Files 84 85 +1
Lines 4563 4676 +113
Branches 1103 1126 +23
==========================================
+ Hits 1426 1522 +96
- Misses 3137 3154 +17
🚀 New features to boost your workflow:
|
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The orchestrator-develop branch no longer exists. Update all fallback clone commands and test fixtures to use main instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/build-tests-mac.yml:
- Line 15: Remove the top-level continue-on-error: true that makes the entire
macOS matrix non-blocking; instead update the macOS job (the job using
strategy.matrix) to remove that line and apply continue-on-error only to the
specific flaky matrix entries or steps—e.g., add a matrix.include entry
identifying the flaky variant(s) in strategy.matrix and set continue-on-error:
true on the specific step(s) that run for matrix entries matching matrix.name
(using an if: condition), or mark only those include entries as non-blocking;
this preserves failures for the rest of the macOS/iOS matrix while tolerating
the known flaky case.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: faa99561-4185-4f04-979d-6532d87c59ee
⛔ Files ignored due to path filters (2)
dist/index.jsis excluded by!**/dist/**dist/index.js.mapis excluded by!**/dist/**,!**/*.map
📒 Files selected for processing (5)
.github/workflows/build-tests-mac.yml.github/workflows/orchestrator-async-checks.ymlsrc/model/orchestrator/tests/e2e/orchestrator-end2end-caching.test.tssrc/model/orchestrator/workflows/async-workflow.tssrc/model/orchestrator/workflows/build-automation-workflow.ts
| buildForAllPlatformsMacOS: | ||
| name: ${{ matrix.targetPlatform }} on ${{ matrix.unityVersion }} | ||
| runs-on: macos-latest | ||
| continue-on-error: true |
There was a problem hiding this comment.
Don’t make the entire macOS matrix non-blocking.
Setting continue-on-error: true here suppresses failures for every macOS/iOS build variant, so this workflow can stay green even when platform builds are broken. If the goal is to tolerate a known flaky case, scope it to specific matrix entries instead of the whole job.
Suggested narrowing
- continue-on-error: true
strategy:
fail-fast: false
matrix:
projectPath:
- test-project
unityVersion:
- 2021.3.45f1
- 2022.3.13f1
- 2023.2.2f1
targetPlatform:
- StandaloneOSX
- iOS
include:
+ - unityVersion: 6000.0.36f1
+ targetPlatform: StandaloneOSX
+ experimental: true
- unityVersion: 6000.0.36f1
targetPlatform: StandaloneOSX
- - unityVersion: 6000.0.36f1
- targetPlatform: StandaloneOSX
buildProfile: 'Assets/Settings/Build Profiles/Sample macOS Build Profile.asset'
+ experimental: true
+ continue-on-error: ${{ matrix.experimental || false }}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/build-tests-mac.yml at line 15, Remove the top-level
continue-on-error: true that makes the entire macOS matrix non-blocking; instead
update the macOS job (the job using strategy.matrix) to remove that line and
apply continue-on-error only to the specific flaky matrix entries or steps—e.g.,
add a matrix.include entry identifying the flaky variant(s) in strategy.matrix
and set continue-on-error: true on the specific step(s) that run for matrix
entries matching matrix.name (using an if: condition), or mark only those
include entries as non-blocking; this preserves failures for the rest of the
macOS/iOS matrix while tolerating the known flaky case.
|
Closing — all orchestrator code has been extracted to the standalone Content from this PR (premade secret sources, YAML definitions) is fully present in the orchestrator repo. See PR #819 for the extraction. |

Summary
SecretSourceServicewith premade secret source integrations for common cloud secret managerssecretSourceinput as a modern alternative toinputPullCommandinputPullCommandbehavior unchangedPremade Sources
aws-secrets-manageraws secretsmanager get-secret-valueaws-parameter-storeaws ssm get-parameter --with-decryptiongcp-secret-managergcloud secrets versions access latestazure-key-vaultaz keyvault secret show(requiresAZURE_VAULT_NAMEenv var)hashicorp-vaultorvaultvault kv get(requiresVAULT_ADDR, optionalVAULT_MOUNT)hashicorp-vault-kv1vault read(requiresVAULT_ADDR, optionalVAULT_MOUNT)envUsage
Custom YAML Format
HashiCorp Vault Configuration
VAULT_ADDRhttps://vault.example.com)VAULT_TOKENVAULT_MOUNTsecret)Changes
action.ymlsecretSourceinput with all premade source names listedorchestrator-options.tssecretSourcegetterorchestrator-query-override.tsSecretSourceServicewhensecretSourceis setsecret-source-service.tssecret-source-service.test.tsTest plan
Closes #776
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com
Summary by CodeRabbit
New Features
Security/Quality
Tests
Chores
Tracking: