You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(tasks)!: contain the server filesystem tasks by default
BREAKING CHANGE: `registerCommonTasks()` in the node and electron builds no
longer registers `FileGrepTask`, `FileLoaderTask` or `FileSedTask`. Call the
new `registerFileSystemTasks()` to restore them. Separately, a local path now
has to resolve inside `config.roots`, which defaults to `[process.cwd()]`
rather than to "anywhere"; pass explicit `roots`, or `allowAnyRoot: true`, to
read outside the working directory. The classes are exported unchanged — only
ambient registry availability and the containment default moved.
Two states existed and neither contained anything. `resolveLocalFilePath`
skipped containment whenever `roots` was `undefined`, on the stated grounds
that "the enforced control is the `filesystem:read` entitlement". It is not:
`TaskGraphRunner` consults an enforcer only under `enforceEntitlements`, and
`ENTITLEMENT_ENFORCER` has no default factory, so the flag without a
registered enforcer throws. Neither is a default. An embedder that called
`registerCommonTasks()` and ran a graph therefore had a task in the ambient
registry that read any path the process could open, and
`assertResolvedPathDeclared` did not narrow it — it recomputes the path from
the same input through the same resolver, so with no roots both sides agree
and every path passes.
`FileLoaderTask.server` was worse than the other two and is why the fix cannot
stop at `roots`: it declared no entitlement at all, honoured no roots, and
reached the filesystem through `url.slice(7)`, which neither percent-decodes
nor rejects a `file://` host. It now runs the same resolver and carries the
same `configSchema()` / static + instance `entitlements()` pair its siblings
do, with `metadata.url` still the caller's path so the output shape is
unchanged.
The registration split is the half that survives untrusted input. A serialized
node is built as `{...item.config, id, defaults}`, so a graph that names
`FileGrepTask` supplies its own config and can state `roots: ["/"]` itself —
no default this package picks constrains it. The only control left is the type
not resolving. The cwd default is the defence for the other case, a trusting
embedder that authored the graph itself.
Root resolution is also order-independent now. `realpathSync(root)` ran inside
the `some()` predicate, which short-circuits: `[good, missing]` returned true
without ever resolving the broken root while `[missing, good]` threw, on
identical input. Every root is resolved before any containment verdict, so a
misconfigured one fails always rather than sometimes — the deterministic
direction and the safer one.
The ~35 existing server tests that constructed these tasks with no `roots`
now state `roots: [testDir]`. That churn is the point: each one says which
directory it means to read.
0 commit comments