FNNDSC / FNNDSC/mise

Redirection bypasses host control: > and >> write the daemon's disk without the files tier

Open
#415 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
0
Forks
0
Avg merge
1h 35m
Merged PRs (30d)
333

Description

`>` and `>>` write to the engine's host filesystem without consulting `engineFilesystem`, so a surface attached to a daemon writes files on the daemon's machine even when the operator who started it never granted the `files` tier.

## The declared boundary

`SurfaceCapabilities.engineFilesystem` exists to answer exactly this, and says so:

> The surface's operator sits at the same filesystem the engine runs on, so a path the engine resolves is a path they can open. True only for an in-process local shell. When false, a builtin that would write to disk must deliver through the surface instead, because the engine's disk is somebody else's machine.

`packages/calypso/src/daemon/launch.ts` sets it deliberately:

```ts
// The daemon's disk belongs to nobody attending this session — unless
// the operator who started it declared otherwise with the `files`
// tier of --host-control, in which case it is theirs by declaration.
engineFilesystem: policy.tiers.has('files'),
```

`upload` and `download` both honour it and refuse by name without it.

## What actually happens

`redirect_execute` in `packages/brasa/src/core/dispatch.ts` calls node's `writeFileSync` / `appendFileSync` on a path resolved by `redirectTarget_resolve` (`statSync`). Neither consults the capability. There is no `capability_require` on that path at all — where `pipe_execute`, one function away, does gate itself with `capability_require('pipeSegments', …)`.

Measured against a private daemon started as plain `chell --daemon`, with no `--host-control`, driven from a *remote* surface:

```
version >
→ 93 bytes written on the daemon's host, containing the version table
```

The daemon never declared the `files` tier. The write happened anyway.

## Why it matters

Host control is the whole mechanism by which an operator decides whether a daemon may touch its own machine, and it is refused by default. `!` is gated, pipe segments are gated, `upload` and `download` are gated. `>` is the hole in that fence, and it is the most ordinary shell gesture there is — an operator using it does not think of themselves as reaching for a host capability.

The exposure is bounded by who can attach: a token holder can already run commands. But that is precisely the boundary `--host-control` was built to draw *inside*, and a declared boundary that one common path ignores is worse than no boundary, because the daemon's banner says the tier is off.

## Also wrong: the help text

`packages/brasa/src/builtins/help.ts` describes redirection as:

```
Output Redirection Write/append to virtual files: ls -lh > output.txt
```

They are not virtual files. This is where I got the claim from before checking the code, and the operator corrected me; it will mislead anyone else the same way.

## What to do

Gate `redirect_execute` on `engineFilesystem`, refusing by name in the shape `upload` already uses, and fix the help text to say what redirection actually does. Whether a redirect should instead *deliver through the surface* when the capability is absent — the way `download` does — is a design question worth answering rather than assuming, and it bears directly on epic #400, where a cohort's `--csv` output has to land somewhere.

Found while grilling #400.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with redirect_execute and redirectTarget_resolve in packages/brasa/src/core/dispatch.ts, then compare their capability handling with pipe_execute and the upload/download paths. Review packages/brasa/src/builtins/help.ts and determine the intended behavior when engineFilesystem is absent; done means redirection respects that boundary and the help text accurately describes it.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.