go/cli: carry mesh-cli's standard input with its line, bounded at 64 KiB, for the controller's terminal alone
mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)
mesh/delivery covered after its stop: a later walk that contains it was delivered: plan-1791659640519725407 (novox/mesh-sdk at 19cfe0ef into main, done, …
mesh/delivery-group group feat/a-terminal-line-takes-standard-input delivered: every member is delivered
mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)
mesh/delivery covered after its stop: a later walk that contains it was delivered: plan-1791659640519725407 (novox/mesh-sdk at 19cfe0ef into main, done, …
mesh/delivery-group group feat/a-terminal-line-takes-standard-input delivered: every member is delivered
A secret given at the terminal (secret accept … --from -) has no way through mesh-cli today: the controller runs every line with no standard input. The request now carries it, refused over 64 KiB by mesh-cli and by the reader alike, and MaxRequest grows by what base64 makes of that bound.
This commit is contained in:
+29
-6
@@ -24,9 +24,13 @@ import (
|
||||
// address (ADR 0272 §3).
|
||||
const Socket = "/run/nox-mesh-host/cli.sock"
|
||||
|
||||
// MaxRequest bounds what the node-engine reads of a request. A command line is a few hundred bytes; settings given
|
||||
// inline are a few kilobytes.
|
||||
const MaxRequest = 64 << 10
|
||||
// MaxStdin bounds the standard input mesh-cli carries for a line: a secret, a key, a settings file — kilobytes. More
|
||||
// is refused by mesh-cli before anything is asked, and by the node-engine and the controller again.
|
||||
const MaxStdin = 64 << 10
|
||||
|
||||
// MaxRequest bounds what the node-engine reads of a request: a command line of a few hundred bytes, settings given
|
||||
// inline of a few kilobytes, and standard input of at most MaxStdin, which JSON writes as base64 (4 bytes for every 3).
|
||||
const MaxRequest = 64<<10 + MaxStdin*4/3 + 4
|
||||
|
||||
// MaxAnswer bounds what mesh-cli reads of an answer. The controller cuts what it runs to fit one bus message, and
|
||||
// says so; this is the local bound above that.
|
||||
@@ -36,9 +40,16 @@ const MaxAnswer = 16 << 20
|
||||
// and a minute for the link on either side.
|
||||
const AskWithin = 6 * time.Minute
|
||||
|
||||
// Request is what mesh-cli asks: the controller's command line, as words.
|
||||
// Request is what mesh-cli asks: the controller's command line, as words, and what mesh-cli's own standard input
|
||||
// held when it was not a terminal.
|
||||
//
|
||||
// **Stdin is for the controller's terminal alone** (novox/hq ADR 0259 §10, ADR 0272): a secret given with
|
||||
// `secret accept … --from -`, so it never stands on a command line. The controller gives it to a line that runs as
|
||||
// its terminal and refuses any other line that carries it; nobody keeps it — not the node-engine, not the
|
||||
// controller's record of calls, not its journal.
|
||||
type Request struct {
|
||||
Line []string `json:"line"`
|
||||
Line []string `json:"line"`
|
||||
Stdin []byte `json:"stdin,omitempty"`
|
||||
}
|
||||
|
||||
// Answer is what comes back: what the command printed, how it exited, and whether it ran as the controller's
|
||||
@@ -87,6 +98,9 @@ func ReadRequest(r io.Reader) (Request, error) {
|
||||
if len(req.Line) == 0 {
|
||||
return Request{}, errors.New("the request names no command")
|
||||
}
|
||||
if len(req.Stdin) > MaxStdin {
|
||||
return Request{}, fmt.Errorf("%w: standard input of more than %d bytes", ErrTooLong, MaxStdin)
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
@@ -102,9 +116,18 @@ func ReadAnswer(r io.Reader) (Answer, error) {
|
||||
|
||||
// Ask is mesh-cli's side: connect to the socket, ask, and wait for the answer within AskWithin or ctx.
|
||||
func Ask(ctx context.Context, socket string, line []string) (Answer, error) {
|
||||
return AskWith(ctx, socket, Request{Line: line})
|
||||
}
|
||||
|
||||
// AskWith asks a whole request: a line, and the standard input it carries, at most MaxStdin.
|
||||
func AskWith(ctx context.Context, socket string, req Request) (Answer, error) {
|
||||
line := req.Line
|
||||
if len(line) == 0 {
|
||||
return Answer{}, errors.New("no command given")
|
||||
}
|
||||
if len(req.Stdin) > MaxStdin {
|
||||
return Answer{}, fmt.Errorf("standard input is more than %d bytes, which mesh-cli does not carry; nothing was asked", MaxStdin)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, AskWithin)
|
||||
defer cancel()
|
||||
var d net.Dialer
|
||||
@@ -116,7 +139,7 @@ func Ask(ctx context.Context, socket string, line []string) (Answer, error) {
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
_ = conn.SetDeadline(deadline)
|
||||
}
|
||||
if err := WriteRequest(conn, Request{Line: line}); err != nil {
|
||||
if err := WriteRequest(conn, req); err != nil {
|
||||
return Answer{}, fmt.Errorf("asking the node-engine: %w", err)
|
||||
}
|
||||
a, err := ReadAnswer(conn)
|
||||
|
||||
Reference in New Issue
Block a user