fix(selectors): resolve shared environment references server-side - #7095
fix(selectors): resolve shared environment references server-side#7095BillLeoutsakosvl346 wants to merge 10 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
2402184 to
2434652
Compare
2434652 to
ad5a672
Compare
ad5a672 to
0b8f18c
Compare
fafded6 to
0486f57
Compare
0486f57 to
17726cb
Compare
Greptile SummaryThis PR adds shared infrastructure for resolving authorized selector environment references server-side while keeping secret values out of browser query keys and state.
Confidence Score: 5/5The PR appears safe to merge; no concrete changed-code failure remains reachable in the reviewed changes. The authorization path derives canonical scope before environment resolution, credential-backed requests enforce workspace agreement, and browser caches are partitioned and invalidated without exposing resolved secret values.
|
| Filename | Overview |
|---|---|
| apps/sim/lib/selectors/application/resolve-authorized-context.ts | Adds the central authorized application operation for canonical workspace derivation, credential checks, delegated scope enforcement, and exact environment-reference resolution. |
| apps/sim/lib/selectors/application/credential-provider.ts | Adds provider compatibility checks before credential secret access; the helper is not yet reached by a production caller in this changeset. |
| apps/sim/hooks/selectors/context-resolution.ts | Preserves server-resolved references and creates stable opaque cache revisions without exposing raw dependency values in query keys. |
| apps/sim/hooks/selectors/use-selector-query.ts | Partitions opted-in selector caches by canonical workspace, workflow, and opaque dependency scope while retaining legacy keys for other selectors. |
| apps/sim/hooks/queries/environment.ts | Refreshes environment queries first and then invalidates selector-dependent caches after successful environment mutations. |
| apps/sim/lib/atlassian/discovery.ts | Adds a sanitized discovery mode that excludes provider-controlled response bodies from errors and separates sanitized and standard cache entries. |
| apps/sim/stores/index.ts | Expands user-data reset from environment-only removal to clearing the complete React Query cache. |
Sequence Diagram
sequenceDiagram
participant Browser
participant Route as Selector route
participant Auth as Principal authorization
participant Env as Effective environment
participant Provider
Browser->>Route: "Opaque context with exact {{KEY}} reference"
Route->>Auth: Authenticate principal and authorize workflow/credential
Auth-->>Route: Canonical workspace and credential scope
Route->>Env: Resolve authorized exact references
Env-->>Route: Resolved server-only context
Route->>Provider: Fetch selector options
Provider-->>Route: Provider result
Route-->>Browser: Sanitized selector options
Note over Browser: Query key contains workspace/workflow/opaque revision, not plaintext
Reviews (1): Last reviewed commit: "fix(selectors): refresh caches after env..." | Re-trigger Greptile
There was a problem hiding this comment.
All reported issues were addressed across 37 files
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
17726cb to
aeaa7ec
Compare
There was a problem hiding this comment.
All reported issues were addressed across 40 files
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
|
Addressed the latest Cubic P2 in eb0d42e. Successful personal Copilot secret updates now invalidate both the personal environment query and the workspace-environment prefix before invalidating the four selector cache namespaces, matching the regular personal-save mutation. The focused stream suite (13 tests), app/root type-checks, lint, format, strict API validation, client-boundary, React Query audit, and diff check all pass on the rebuilt combined stack. |
|
@cubic-dev-ai review this PR |
@BillLeoutsakosvl346 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 40 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
Server-resolved selectors: visual architecture guideThese diagrams summarize the original failure, the new secure request path, cache behavior, affected integrations, file ownership, and the stacked merge sequence. 1. Before: browser-dependent environment resolutionflowchart TB
INPUT["Block field<br/>Domain = JIRA_DOMAIN reference"]
SETUP["useSelectorSetup tries to build<br/>literal selector context"]
CACHE{"Can the browser read<br/>the environment value?"}
PERSONAL["Personal variable<br/>resolved to plaintext"]
SHARED["Workspace/shared secret<br/>cannot be viewed by browser"]
GOOD_REQUEST["API receives literal domain"]
BAD_REQUEST["API receives unresolved reference"]
SUCCESS["Projects/pages load"]
FAILURE["Validation or provider request fails"]
INPUT --> SETUP --> CACHE
CACHE -->|"Yes"| PERSONAL
PERSONAL --> GOOD_REQUEST --> SUCCESS
CACHE -->|"No"| SHARED
SHARED --> BAD_REQUEST --> FAILURE
The original implementation only worked when the browser already possessed the value. This is why personal variables could work while shared secrets failed. 2. After: authorized server-side resolutionflowchart LR
subgraph Browser
INPUT["Block field<br/>Domain = JIRA_DOMAIN reference"]
REQUEST["Preserve reference<br/>and send authorized scope"]
RESULT["Normalized options<br/>IDs and labels only"]
INPUT --> REQUEST
end
subgraph Server
AUTH["Authenticate principal"]
SCOPE["Authorize workflow,<br/>credential and provider"]
RESOLVE["Resolve reference<br/>server-side"]
AUTH --> SCOPE --> RESOLVE
end
subgraph ExternalProvider["External provider"]
API["Provider receives<br/>literal domain"]
end
REQUEST --> AUTH
RESOLVE --> API
API --> RESULT
The browser now carries references; the authorized server owns plaintext resolution. 3. Complete secure request flowsequenceDiagram
actor User
participant UI as Selector UI
participant Cache as React Query
participant Route as Selector API
participant Auth as Authorized resolver
participant Env as Effective environment
participant Provider as Jira / Slack / AWS / IMAP
User->>UI: Open dependent selector
UI->>UI: Preserve literal or exact KEY reference
UI->>Cache: Query with workspace + workflow + opaque revision
Cache->>Route: Raw reference + authorized scope
Route->>Auth: Authenticate session/executor
Auth->>Auth: Load canonical workflow/workspace
Auth->>Auth: Authorize credential use
Auth->>Auth: Verify credential provider compatibility
Auth->>Env: Resolve allowlisted exact references
Env-->>Auth: Decrypted values in server memory
Auth-->>Route: Authorized principal + resolved context
Route->>Provider: Request using resolved values
Provider-->>Route: Provider resources
Route-->>Cache: Normalized options or sanitized error
Cache-->>UI: IDs and labels
Note over UI,Cache: No shared-secret plaintext
Note over Auth,Provider: Plaintext exists only server-side
4. Permission and secret boundaryflowchart LR
subgraph Browser["Browser boundary"]
UI["Selector UI"]
REF["Raw exact reference"]
KEY["Opaque query-key revision"]
OPTIONS["Normalized IDs and labels"]
end
subgraph Server["Authorized server boundary"]
PRINCIPAL["Authenticated Principal"]
WORKFLOW["Canonical workflow/workspace"]
CRED["Credential-use authorization"]
BIND["Provider binding"]
RESOLVE["Effective environment resolution"]
PLAIN["Resolved plaintext"]
end
subgraph SecretStores["Secret sources"]
SHARED["Workspace/shared secret<br/>may use does not mean may view"]
PERSONAL["Viewer personal variable"]
end
subgraph External["External provider"]
API["Provider API"]
end
UI --> REF
REF --> PRINCIPAL
KEY --> PRINCIPAL
PRINCIPAL --> WORKFLOW --> CRED --> BIND --> RESOLVE
SHARED --> RESOLVE
PERSONAL --> RESOLVE
RESOLVE --> PLAIN --> API
API --> OPTIONS --> UI
SHARED -. "never returned" .-> UI
PLAIN -. "never enters query keys" .-> KEY
Key invariants:
5. Cache privacy and freshnessflowchart TB
RAW["Raw selector dependencies<br/>kept privately in component memory"]
REGISTRY["Opaque revision registry"]
RANDOM["Random revision<br/>for this component lifetime"]
WORKSPACE["Workspace ID"]
WORKFLOW["Workflow ID"]
SELECTOR["Selector name"]
KEY["React Query key<br/>selector + workspace + workflow + opaque revision"]
REQUEST["Selector request"]
RAW --> REGISTRY --> RANDOM --> KEY
WORKSPACE --> KEY
WORKFLOW --> KEY
SELECTOR --> KEY
KEY --> REQUEST
subgraph Mutation["When an environment value changes"]
SAVE["Personal/workspace/Copilot mutation succeeds"]
REFRESH["Refresh environment query"]
INVALIDATE["Invalidate selector-bearing namespaces"]
REFETCH["Mounted selectors refetch"]
SAVE --> REFRESH --> INVALIDATE --> REFETCH
end
INVALIDATE --> REQUEST
subgraph Namespaces["Invalidated namespaces"]
PRIMARY["Primary selector options"]
DETAIL["Canvas dynamic-detail labels"]
SEARCH["Workflow-search details"]
REPLACE["Workflow replacement options"]
end
INVALIDATE --> PRIMARY
INVALIDATE --> DETAIL
INVALIDATE --> SEARCH
INVALIDATE --> REPLACE
RESET["Sign-out / identity reset"] --> CLEAR["Clear entire QueryClient"]
CLEAR --> PRIMARY
CLEAR --> DETAIL
CLEAR --> SEARCH
CLEAR --> REPLACE
The opaque revision handles changed raw dependencies. Mutation invalidation handles an unchanged reference whose underlying value changes. 6. Why these integrations were affectedflowchart TB
START["Does the selector depend on another configured field?"]
SECRET{"Can that dependency<br/>contain a secret reference?"}
LITERAL{"Does the provider request<br/>need the resolved literal?"}
AFFECTED["Affected by the original bug"]
SAFE["Not directly affected"]
START -->|"No"| SAFE
START -->|"Yes"| SECRET
SECRET -->|"No: ordinary resource ID"| SAFE
SECRET -->|"Yes"| LITERAL
LITERAL -->|"No: opaque credential ID<br/>already resolved server-side"| SAFE
LITERAL -->|"Yes"| AFFECTED
AFFECTED --> JIRA["Jira<br/>Domain → Projects → Issues"]
AFFECTED --> CONF["Confluence<br/>Domain → Spaces → Pages"]
AFFECTED --> JSM["JSM<br/>Domain → Service desks → Request types"]
AFFECTED --> CW["CloudWatch<br/>AWS config → Log groups → Streams"]
AFFECTED --> SLACK["Slack<br/>Token/credential → Channels and users"]
AFFECTED --> IMAP["IMAP<br/>Connection fields → Mailboxes"]
Most selectors were already safe because they send opaque credential IDs, depend only on non-secret resource IDs, or do not carry secret-bearing selector context. 7. Shared infrastructure and provider-specific codeflowchart LR
subgraph BrowserInfra["Shared browser infrastructure — PR #7095"]
TYPES["selectors/types.ts<br/>serverResolvedContextFields"]
SETUP["use-selector-setup.ts<br/>preserve references"]
CONTEXT["context-resolution.ts<br/>opaque revision registry"]
KEYS["query-keys.ts<br/>scoped private keys"]
QUERY["use-selector-query.ts<br/>fetch options"]
INVALIDATE["cache-invalidation.ts<br/>freshness after mutations"]
TYPES --> SETUP --> CONTEXT --> KEYS --> QUERY
INVALIDATE --> QUERY
end
subgraph ServerInfra["Shared server infrastructure — PR #7095"]
ADAPTER["server/resolve-authorized-context.ts<br/>authenticate adapter"]
USECASE["application/resolve-authorized-context.ts<br/>canonical authorization"]
PROVIDER["credential-provider.ts<br/>provider compatibility"]
ENV["getEffectiveDecryptedEnv<br/>server-only resolution"]
ERRORS["provider-errors.ts<br/>sanitized failures"]
ADAPTER --> USECASE
USECASE --> PROVIDER
USECASE --> ENV
PROVIDER --> ERRORS
ENV --> ERRORS
end
BrowserInfra --> ServerInfra
ServerInfra --> ATL["#7122 Jira + Confluence<br/>routes, selectors, contracts"]
ServerInfra --> JSMPR["#7114 JSM<br/>routes, selectors, contracts"]
ServerInfra --> SLACKPR["#7115 Slack<br/>credential resolver and routes"]
ServerInfra --> CWPR["#7116 CloudWatch<br/>dedicated selector routes"]
ServerInfra --> IMAPPR["#7117 IMAP<br/>resolved connection route"]
ATLHELP["Shared Atlassian helpers<br/>atlassian-credential.ts<br/>atlassian/discovery.ts"] --> ATL
ATLHELP --> JSMPR
8. Pull-request stackflowchart TB
STAGING["staging"]
PR1["#7095<br/>Shared secure selector infrastructure<br/>+ reusable Atlassian support"]
ATL["#7122<br/>Jira + Confluence"]
JSM["#7114<br/>Jira Service Management"]
SLACK["#7115<br/>Slack"]
CW["#7116<br/>CloudWatch"]
IMAP["#7117<br/>IMAP"]
STAGING --> PR1
PR1 --> ATL
PR1 --> JSM
PR1 --> SLACK
PR1 --> CW
PR1 --> IMAP
ATL -.-|"no sibling file overlap"| JSM
JSM -.-|"no sibling file overlap"| SLACK
SLACK -.-|"no sibling file overlap"| CW
CW -.-|"no sibling file overlap"| IMAP
9. Files grouped by responsibilityflowchart TB
subgraph PR1["#7095 — 40 files"]
P1A["Browser context and cache<br/>use-selector-setup.ts<br/>context-resolution.ts<br/>query-keys.ts<br/>use-selector-query.ts"]
P1B["Authorization<br/>resolve-authorized-context.ts<br/>credential-provider.ts"]
P1C["Freshness and teardown<br/>environment.ts<br/>cache-invalidation.ts<br/>stores/index.ts"]
P1D["Shared Atlassian safety<br/>atlassian-credential.ts<br/>atlassian/discovery.ts<br/>provider-errors.ts"]
P1E["15 focused test files"]
end
subgraph ATLPR["#7122 — 13 files"]
A1["Jira routes<br/>projects/route.ts<br/>issues/route.ts"]
A2["Confluence routes<br/>selector-spaces<br/>selector-pages<br/>selector-page"]
A3["Jira/Confluence selectors and contracts"]
A4["3 focused test files"]
end
subgraph JSMPR["#7114 — 6 files"]
J1["Service Desk route"]
J2["Request Type route"]
J3["JSM selector + contract"]
J4["2 focused test files"]
end
subgraph SLACKPR["#7115 — 12 files"]
S1["slack-credential.ts"]
S2["Channels and Users routes"]
S3["Raw dependency transport"]
S4["Slack selectors and contract"]
S5["3 focused test files"]
end
subgraph CWPR["#7116 — 10 files"]
C1["Dedicated group/stream routes"]
C2["Reusable AWS listing utilities"]
C3["CloudWatch selector + contracts"]
C4["4 focused test files"]
end
subgraph IMAPPR["#7117 — 7 files"]
I1["Mailbox route"]
I2["Wire/resolved contracts"]
I3["Safe host validation"]
I4["3 focused test files"]
end
PR1 --> ATLPR
PR1 --> JSMPR
PR1 --> SLACKPR
PR1 --> CWPR
PR1 --> IMAPPR
10. Landing sequenceflowchart LR
R1["Merge #7095"]
R2["Record old PR1 tip"]
R3["Rebase each child<br/>onto staging"]
R4["Retarget child PRs<br/>to staging"]
R5["Run focused tests<br/>and confirm independence"]
R6["Merge children<br/>in any order"]
R1 --> R2 --> R3 --> R4 --> R5 --> R6
Core modelflowchart LR
BROWSER["Browser<br/>reference only"]
SERVER["Authorized server<br/>plaintext allowed"]
PROVIDER["External provider"]
RESULT["Browser<br/>options only"]
BROWSER --> SERVER --> PROVIDER --> SERVER --> RESULT
|
Summary
Adds the shared, authorized infrastructure for dependent selectors whose context may contain environment-secret references. This PR contains no Jira, Confluence, JSM, Slack, CloudWatch, or IMAP selector migration; those are independent sibling PRs stacked on this branch.
The browser carries literals or opaque exact
{{KEY}}references only. Authorized resolution happens server-side, and resolved shared-secret plaintext never enters selector query keys, responses, or client state.Shared infrastructure in this PR:
serverResolvedContextFieldsmetadata and exact-reference preservationSlack report: https://sim-ai.slack.com/archives/C093DF8MA21/p1786822370070229
Security invariant
Type of Change
Focused coverage
sim:selectorsdelegation audience.Verification
Verified after rebuilding on the latest
origin/staging:lint:checkandformat:checkpassed. Lint reports one unrelated existing unused-suppression warning inshell-layout.test.ts.git diff --checkpassed.The final review follow-up adds behavior-level regressions for connector dependency-scoped cache identity, Copilot personal/workspace secret-update invalidation, and sanitized Atlassian response cancellation without duplicating provider matrices.
Combined browser acceptance
Credential not foundresponse. The temporary blocks and values were undone, and reload confirmed the workflow returned to its original Start-only state.Intentional scope
Checklist
Stacked children
Each child is a draft sibling based directly on this PR's tip. Review each diff against
fix/server-resolved-selector-context; do not merge a child until #7095 lands and that child is rebased and retargeted tostaging.