fix(tasks): declare the owned FetchUrlTask entitlements on FileLoaderTask - #847
Merged
sroussey merged 1 commit intoAug 20, 2026
Merged
Conversation
…Task The cross-platform FileLoaderTask owns a FetchUrlTask created inside execute(), so it is absent from the graph-start snapshot computeGraphEntitlements takes over graph.getTasks(). With no static or instance entitlements() the task declared nothing, and under enforceEntitlements a graph containing the browser-build class reached a private destination without the enforcer ever seeing it. Declare the fetch's entitlements on the base class, mirroring FileGrepTask, and consolidate the server build's static declaration onto super.entitlements(). BREAKING CHANGE: under enforceEntitlements, a graph containing a browser-build FileLoaderTask that previously ran because it declared nothing is now denied unless the profile grants network:http / network:private. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LowBJQsCghLDiHwPN6FgUT
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||
sroussey
deleted the
claude/zealous-allen-ata6g5-fileloader-entitlements
branch
August 20, 2026 15:39
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The owned-child snapshot gap
computeGraphEntitlementstakes its snapshot overgraph.getTasks()at graph start, before anyexecute()runs. A child created insideexecute()viacontext.own(...)is therefore never in that snapshot, so its entitlements are invisible to the enforcer unless the owning task declares them itself. That is exactly whatfetchUrlEntitlementsForexists for, and its docblock says so.The cross-platform
FileLoaderTaskowns aFetchUrlTaskinsideexecute()and declared nothing at all — no staticentitlements(), no instanceentitlements(), nohasDynamicEntitlements.Why this one was missed
The recent batch added the owned-child declaration to
FileGrepTask,FileSedTaskandFileLoaderTask.server— the two grep/sed cross-platform bases and the loader's server subclass. The loader's cross-platform base is the one that fell through the gap, and it is the classbrowser.tsregisters intoTaskRegistry, so the build with no filesystem branch at all — the one whose entire surface IS the owned fetch — was the one declaring none of it.Failure scenario
Under
enforceEntitlements: truewith a registered enforcer, a graph containing a browser-buildFileLoaderTaskpointed athttp://169.254.169.254/latest/meta-data/…runs to completion. The enforcer sees an empty declaration for the node; the ownedFetchUrlTaskthen runs withallowPrivate: trueand its own resolved-destination check is satisfied, because the child's input url IS the private one. The link-local metadata endpoint is reached while the task declared neithernetwork:httpnornetwork:private— the destination the enforcer exists to gate never reaches it.The fix
FileLoaderTask.tsgains the same three membersFileGrepTaskcarries:hasDynamicEntitlements, a staticentitlements()returningFetchUrlTask.entitlements(), and an instanceentitlements()returningfetchUrlEntitlementsFor(this.runInputData?.url)— which fails closed to an unscopednetwork:privatewhen the url is not yet known.FileLoaderTask.server.tsnow merges itsfilesystem:readontosuper.entitlements()instead of restatingFetchUrlTask.entitlements(), matchingFileGrepTask.server.tsandFileSedTask.server.ts. The base now supplies the same value, so this is behaviour-preserving; it removes the second place to edit whenFetchUrlTask's declaration changes.FetchUrlTaskbecame unused in that file and was dropped from the import. The server's instanceentitlements(), which already handles the http-vs-path branches, is unchanged.Breaking Changes
Under enforcement, a graph containing a browser-build
FileLoaderTaskthat previously ran because it declared nothing will now be denied unless the profile grantsnetwork:http/network:private. That is the fix — the task always did the network access, it just never said so — but it is a behaviour change for any embedder runningenforceEntitlements: truewith a policy that did not grant the fetch. It belongs under a Breaking Changes CHANGELOG heading for@workglow/tasks, alongside 0.3.48's "contain the server filesystem tasks by default".The server build's effective declaration is unchanged, and nothing changes for an embedder not running with an enforcer.
Test
New
packages/test/src/test/task/FileLoaderEntitlements.test.ts, modelled onFileGrepEntitlements.test.ts.@workglow/tasksresolves to the node build under vitest, so the cross-platform class is imported from its own module (../../../../tasks/src/task/FileLoaderTask) — with the precedent ofHFT_CheckpointSessions.test.ts.registerSafeFetchis stubbed on that same source module, since the sourceFetchUrlTaskholds a different safeFetch slot from the built package's; the enforcer is what is under test, not the network.Five pins: the metadata-endpoint denial, a public load that still resolves, the http declaration (
network:httpand nofilesystem:read), the fail-closed unscopednetwork:privatefor an unknown url, and — for thesuper.entitlements()consolidation — that the server build still carries bothnetwork:httpandfilesystem:read.Three of the five fail on
main(verified by stashing the source change), including the enforcement pin:With the fix,
bun scripts/test.ts task vitest:🤖 Generated with Claude Code
https://claude.ai/code/session_01LowBJQsCghLDiHwPN6FgUT
Generated by Claude Code