Security hardening
I'm still undecided on this.
In his original spec @pfrazee planned for the creation of RPC system to execute untrusted commands in a sandbox outside the normal application context. On the other hand there's the bash approach of giving programs full access on the environment, leaving it up to the user to decide if a script is to be trusted or not.
Any arguments on either side of this issue very much appreciated!
I'd focus on how trusted you expect programs to be. Unix originally assumed the users would have a fairly intimate knowledge or strong trust in all their programs. That might not be an accurate assumption anymore.
I've been playing around with a shim of the in-progress javascript Realm standard. And I think I can use it to introduce an acceptable security compromise.
Some breaking changes will be needed though. Basically, commands will not have access to variables outside their function scope:
import someModule from './somewhere.js'
let someVar = 'stuff'
export function command () {
let otherVar = 'stuff'
console.log(someModule) // undefined
console.log(someVar) // undefined
console.log(otherVar) // 'stuff'
}
As shown by the example above, this means static imports will no longer work. And the behaviour for dynamic imports inside a Realm hasn't been standardised yet, so for now those are completely blocked as well. I'd still like to to give command authors the option to import external modules though, so I'm inclined to expose a safeImport function along these lines (pseudo-code):
async function safeImport (url) {
if (hasPermission(url) || requestPermission(url)) {
return import(url)
}
}
Hmm, I almost wonder if we should be exploring a way to give dterm a safe execution context (like an iframe or worker). EDIT: the tradeoff being that you have to message in/out to other contexts.
That's what realms-shim does behind the scenes I think. At least I noticed it adds an iframe to the DOM every time a Realm is being created, so I'm assuming it uses that as an execution sandbox.
As an alternative, I've tried to move all the prompt interaction and evaluation stuff to a separate iframe, as you suggested @pfrazee. It then uses postMessage to send the results to the parent window to be rendered. This would result in slightly more work (serializing/deserializing DOM nodes in particular might be a bit of a pain, but doable nonetheless). It would result in much less breaking changes though, so I definitely prefer it to the Realms solution.
However, this solution is currently blocked by https://github.com/beakerbrowser/beaker/issues/932, because to execute commands in the iframe, the DatArchive API needs to be available. It seems though that the underlying Electron issue is solved now, so maybe it's worth it to wait for this to be implemented in Beaker.
I'm not entirely sure how to handle permissions for iframes. What if it asks to write to an archive? How should the browser represent that request? It might be better for a DatArchive shim to send the requests to the parent frame, where it can implement its own permissioning.
On further reflection, I agree that a generic implementation in Beaker probably wouldn't work for dterm. So shimming the DatArchive API myself will probably be the way to go.
I've started introducing the breaking changes required in version 0.3.0 (see https://github.com/dfurl/dterm/pull/11). The DOM node parsing for postMessage is already implemented, except that now it's still just used in the main window. The actual iframe sandbox with DatArchive support is going to be quite a bit of extra work, so that'll hopefully make the next non-breaking 0.3.1 release.
Sorry to put so much work in your hands! I'm very excited for this project.
No worries, it's probably still less work than the Realms solution (handling DOM nodes turned out much easer than expected), so it's still a net gain :)