* fix: require variables:read for the token variable of an mcp resource
A token scoped to resources:read/write on an MCP resource could have the API
decrypt the variable the resource's `token` points at and send it as a bearer
to the resource's URL. The url and header `$var:` references already required
variables:read on the variable; the token reference now does too, checked before
the OAuth refresh touches that variable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: let an mcp resource's scope cover the variables it references
A scoped token reading or calling an MCP resource resolved its token variable
with the resource scope alone, but its url and header variables also needed
variables:read on each. The resource scope now covers all three; RLS still
bounds them to secrets the caller can read. Reverts the token-side check from
the previous commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: scope resource references when they are written, not when they are read
A token scoped to a resource now reads everything that resource references
(`$var:`, `$jsonvar:`, `$res:`), through get_value_interpolated and the MCP
tool routes alike. What makes that safe moves to the write: create, update,
update_value and version restore refuse a scoped token adding a reference it
holds no read scope on. References the stored value already carries are kept,
since whoever put them there was allowed to.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: hold every resource reference writer to its own user's read rights
A resource-scoped reader resolves stored references as itself, so an unscoped
session could plant a reference to a secret its own user cannot read and have
a reader's scoped token resolve it. Adding a reference now also requires the
writer's user to see its target under RLS. A reference to nothing yet stays
allowed, since a promotion pushes resources before the secrets they use.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: vet only variable references on write and keep nested resources scoped
A nested `$res:` brought along the variables of a resource the writer could see
but whose references it could not read, and what it references can change after
it is written. It needs its own read scope again, and the write check covers
only `$var:`/`$jsonvar:`.
An MCP token stored as a bare path resolved as a variable without being vetted
on write. It now needs its own variables:read scope, and the `$var:` prefix is
parsed with strip_prefix, as the write check does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep variable scopes on result-cache resources
The worker writes job output to g/results/<hash> straight to the table, so its
references never pass the write check. A token scoped to such a resource still
needs read scope on the variables it references.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: require read rights on kept references and pin result-cache resources to g/results/
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: check resource variable references at read time, with wildcard resource scopes covering matching variables
Returns to the read-time rule: a variable a resource references needs
variables:read on it, unless it is the resource's linked secret or a wildcard
pattern in the token's resources: scopes matches it. The MCP token reference is
now held to that rule like the url and header references.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix: hold interpolated references and captures to the token path scopes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: let a resource read cover its own linked secret variable
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: resolve policy-granted app upload resources on the viewer's rls
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: cover multi-secret linked variables and keep capture paths out of refusals
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat: label resource types and integrations with hub display names
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: load hub integration names in the app and flow pickers
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: load hub resource type names where drawers title a type
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: store resource type display names and drop the hardcoded list
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: leave display_name out of the fork comparison
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: ignore over-long synced display names, move name loaders
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: share the hub integration list cache, backfill admins only
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep a name over a nameless duplicate, retry failed hub reads
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* perf: cap resource content sent to the search modal
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review — fence the LATERAL, flag partial search, add cap test
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: pluralize the truncation notice and link the cap to its openapi doc
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(mcp): serve the 2026-07-28 spec alongside the legacy protocol
* fix(mcp): keep oauth discovery strict and preserve request limits
* fix(mcp): allow the protocol's own headers through CORS
* fix(mcp): expose the auth challenge to browser clients
* chore: update ee-repo-ref to c1665a881b61616f96ffe7702b44840905304660
This commit updates the EE repository reference after PR #711 was merged in windmill-ee-private.
Previous ee-repo-ref: bc1c001e3e386342415dfb8ac31c6b97f6629320
New ee-repo-ref: c1665a881b61616f96ffe7702b44840905304660
Automated by sync-ee-ref workflow.
---------
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix(resources): apply resource_type changes on update (git-sync pull)
The OpenAPI spec and generated CLI client both declare `resource_type` on
EditResource, but the backend's EditResource struct omitted the field, so
serde silently dropped it and update_resource never changed a resource's
type. Switching a resource between two types in git and pulling back into
the workspace therefore left the workspace out of sync with git (GIT-932).
Add resource_type to EditResource and persist it in the UPDATE.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(resources): trim resource_type regression comment to durable rationale
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The variable and resource value caches (backing
`GET /api/w/{w}/variables/get_value/{path}?allow_cache=true` and
`.../resources/get_value_interpolated/{path}?allow_cache=true`) are consulted
before the per-folder RLS query and store the already-decrypted value. The
resource cache was keyed only by `workspace:path` with no caller identity, so a
cache entry warmed by a privileged peer using `allow_cache=true` could be
returned to a caller with no access to the resource's folder on a cache hit
within the 30s TTL — leaking another folder's decrypted secrets.
Scope both caches to the caller's full authorization identity. The key is now
`auth_identity(authed):workspace:path`, where `auth_identity` is a SHA-256 of the
caller's effective authorization context (email, username, is_admin, is_operator,
sorted groups, sorted folders, sorted scopes) — mirroring
`job_read_access_cache_key`. Email alone is insufficient: the same email can
resolve to different effective permissions via job/owner-scoped tokens, so a
lower-privilege context must not reuse a higher-privilege context's entry.
Job-context resource interpolation is handled correctly: only `$WM_*` contextual
variables are resolved (and only when a `job_id` is present). The interpolation
reports whether the value contains a `$WM_*` placeholder
(`transform_json_value_tracked` + an `AtomicBool`). A value containing one is
job-dependent — even on a no-job read where it's left unresolved — and is never
cached (so a later job read never gets a stale placeholder or another job's
context). Any value without a `$WM_*` placeholder is job-independent and cached
under the identity key, shared across job contexts, so reads carrying a `job_id`
still hit the cache.
BEHAVIOR CHANGE: custom workspace environment variables are no longer interpolated
into resource values via `$NAME` (this was undocumented and prevented caching of
any `$`-prefixed value). Custom envs remain available to scripts/workers as before.
Built-in `$WM_*` contextual variables in resource values are unchanged.
The variable cache previously wrote with an identity-scoped key but read with the
unscoped key, so it never hit (a latent functional bug that happened to be safe).
Aligning the read path enables the cache and makes it identity-scoped by
construction. Secret variables are cached too, but the entry carries the
`is_secret` flag so a cache hit re-runs the per-read side effects a secret read
performs — the EE `variables.decrypt_secret` audit and running-job secret
registration (factored into `audit_decrypt_secret`, shared by both paths).
The unused `invalidate_{variable,resource}_cache` helpers can no longer target
identity-scoped entries; documented the constraint and refreshed the stale
key-format docs on the cache statics.
Tests:
- integration regression for both caches: a folder-scoped user warms the cache via
allow_cache=true, then a user without folder access is denied (401) and never
receives the cached value.
- integration regression that variables (secret included) are served from cache.
- integration regression for job context: plain and non-`$WM_` `$`-string resources
stay cached and are served under a job_id, while a `$WM_*` resource (warmed without
a job_id) is not cached.
- unit tests for `auth_identity`.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: expose getJob and getJobLogs as MCP tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add integration test for getJob/getJobLogs MCP endpoint tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add MCP client integration test for getJob and getJobLogs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a new "fileset" resource type that represents a collection of files
stored as a relpath→content map. This enables resource types to manage
multiple files (e.g., config directories, template sets) instead of just
a single file.
Backend:
- Add is_fileset column to resource_type table
- Update CRUD operations and workspace duplication to handle is_fileset
- Add integration tests for fileset resource types
Frontend:
- Add FilesetEditor component with file explorer + Monaco editor
- Extract shared FileExplorer component from RawAppSidebar (dedup)
- Add fileset toggle to EditableSchemaWrapper
- Show fileset editor in ResourceEditor and ApiConnectForm
- Show folder icon for fileset resource types in IconedResourceType
CLI:
- Support fileset resources in sync pull (expand to .fileset/ directory)
- Support fileset resources in sync push (reconstruct from directory)
- Handle !inline_fileset YAML tag in resource resolution
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>