Skip to content

Commit 44bb5dc

Browse files
committed
fix(trigger): accept only the exact sync-target list the deploy workflows match
The workflows grant the secret-read credentials by matching TRIGGER_ENV_SYNC_TARGETS as an exact comma list, so a trimmed parser could accept a padded entry the credential step skipped and fail inside the sync instead. The parser now refuses a padded, empty, or unknown entry with a message naming the expected format.
1 parent 2e814eb commit 44bb5dc

3 files changed

Lines changed: 28 additions & 22 deletions

File tree

‎apps/sim/trigger.config.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ function getFunctionExecutionEnvVars() {
7878
const syncLogger = createLogger('TriggerEnvSync')
7979

8080
/**
81-
* The environments `TRIGGER_ENV_SYNC_TARGETS` lists. Exits on a name with no mapped secret:
82-
* `syncEnvVars` swallows a thrown error, and a typo must not silently keep the old path.
81+
* The environments `TRIGGER_ENV_SYNC_TARGETS` lists. Exits on a list the deploy workflows would
82+
* not match: `syncEnvVars` swallows a thrown error, and a typo must not silently keep the old path.
8383
*/
8484
function syncTargets(): Set<string> {
8585
try {

‎packages/runtime-secrets/src/trigger-env.test.ts‎

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,29 @@ import { describe, expect, it } from 'vitest'
22
import { parseTriggerEnvSyncTargets } from './trigger-env'
33

44
describe('parseTriggerEnvSyncTargets', () => {
5-
it('reads a comma list with stray whitespace and empty entries', () => {
6-
expect([...parseTriggerEnvSyncTargets(' preview, staging ,,prod ')].sort()).toEqual([
5+
it('reads an exact comma list', () => {
6+
expect([...parseTriggerEnvSyncTargets('preview,staging,prod')].sort()).toEqual([
77
'preview',
88
'prod',
99
'staging',
1010
])
1111
})
1212

13-
it('treats an unset or blank gate as no targets', () => {
13+
it('treats an unset or empty gate as no targets', () => {
1414
expect(parseTriggerEnvSyncTargets(undefined).size).toBe(0)
15-
expect(parseTriggerEnvSyncTargets(' ').size).toBe(0)
15+
expect(parseTriggerEnvSyncTargets('').size).toBe(0)
1616
})
1717

18-
it.each(['Staging', 'production', 'constructor', 'toString'])(
19-
'refuses %s, which no app env-vars secret is mapped to',
20-
(target) => {
21-
expect(() => parseTriggerEnvSyncTargets(`preview,${target}`)).toThrow(target)
22-
}
23-
)
18+
it.each([
19+
'preview, staging',
20+
'preview,,staging',
21+
'preview,',
22+
' ',
23+
'preview,Staging',
24+
'preview,production',
25+
'constructor',
26+
'preview,toString',
27+
])('refuses %j, which the deploy workflows would not grant credentials for', (value) => {
28+
expect(() => parseTriggerEnvSyncTargets(value)).toThrow(/TRIGGER_ENV_SYNC_TARGETS/)
29+
})
2430
})

‎packages/runtime-secrets/src/trigger-env.ts‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,19 @@ export function diffTriggerEnv(vars: TriggerEnvVar[], current: Record<string, st
7575
}
7676

7777
/**
78-
* Parses the `TRIGGER_ENV_SYNC_TARGETS` repository variable: a comma list of Trigger.dev
79-
* environment slugs. Refuses any slug without a mapped secret rather than ignoring it, so a typo
80-
* fails the deploy instead of silently keeping the environment on its old configuration.
78+
* Parses the `TRIGGER_ENV_SYNC_TARGETS` repository variable: an exact comma list of Trigger.dev
79+
* environment slugs, such as `preview,staging,prod`. The deploy workflows decide whether to grant
80+
* the secret-read credentials by matching that same exact list, so an entry they would not match
81+
* (padded, empty, or a slug with no mapped secret) fails the deploy here rather than syncing
82+
* without credentials or silently keeping the environment on its old configuration.
8183
*/
8284
export function parseTriggerEnvSyncTargets(value: string | undefined): Set<string> {
83-
const targets = (value ?? '')
84-
.split(',')
85-
.map((target) => target.trim())
86-
.filter(Boolean)
87-
const unknown = targets.filter((target) => !Object.hasOwn(APP_ENV_SECRET_IDS, target))
88-
if (unknown.length > 0) {
85+
if (!value) return new Set()
86+
const targets = value.split(',')
87+
const invalid = targets.filter((target) => !Object.hasOwn(APP_ENV_SECRET_IDS, target))
88+
if (invalid.length > 0) {
8989
throw new Error(
90-
`TRIGGER_ENV_SYNC_TARGETS names environments with no app env-vars secret: ${unknown.join(', ')}`
90+
`TRIGGER_ENV_SYNC_TARGETS must be a comma list of ${Object.keys(APP_ENV_SECRET_IDS).join(', ')} with no spaces; refused: ${invalid.map((target) => JSON.stringify(target)).join(', ')}`
9191
)
9292
}
9393
return new Set(targets)

0 commit comments

Comments
 (0)