mesh-build-node: run the type-check, the test script, tsc and esbuild in bubblewrap, so a change's code reaches no key, login or network (issue 1309, S1 S7 S8 S9, ADR 0448)
Each run step goes through mesh-sdk's buildstep.Isolated: no network, none of the account, the workspace read-write, the toolchain read-only, the npm cache beneath a throwaway layer unless the build step is told publish (a trunk build). The module declares the bubblewrap package and refuses a step, unhealthy, while bwrap is missing or older than 0.10. The proof test runs a probe script through the real test step.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
//go:build integration
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// probeScript is a package's test script that tries what a change's code must not reach and writes what it saw.
|
||||
const probeScript = `import fs from "node:fs";
|
||||
import dns from "node:dns/promises";
|
||||
import net from "node:net";
|
||||
const [key, login, port, sock, cache] = process.argv.slice(2);
|
||||
const seen = {};
|
||||
const read = (p) => { try { fs.readFileSync(p); return true; } catch { return false; } };
|
||||
seen.key = read(key);
|
||||
seen.login = read(login);
|
||||
try { await dns.lookup("git.novox.be"); seen.resolved = true; } catch { seen.resolved = false; }
|
||||
const reach = (opts) => new Promise((ok) => { const c = net.connect(opts); c.on("connect", () => { c.destroy(); ok(true); }); c.on("error", () => ok(false)); });
|
||||
seen.tcp = await reach({ host: "127.0.0.1", port: Number(port) });
|
||||
seen.socket = await reach({ path: sock });
|
||||
try { fs.writeFileSync(cache + "/written-by-a-candidate", "x"); seen.wroteCache = true; } catch { seen.wroteCache = false; }
|
||||
fs.writeFileSync("seen.json", JSON.stringify(seen));
|
||||
`
|
||||
|
||||
// 1309-S9 (ADR 0448's check): a package's test script, run by the test step, reads no key and no login of the account,
|
||||
// resolves no name, reaches no socket of the machine (a TCP port on its loopback, a unix socket), and leaves the
|
||||
// shared cache unchanged.
|
||||
func TestARunStepReachesNoKeyLoginNameOrSocketAndLeavesTheCacheUnchanged(t *testing.T) {
|
||||
if _, err := exec.LookPath("bwrap"); err != nil {
|
||||
t.Skip("NOT RUN: no bubblewrap installed where this check runs; every build node has it (the module declares it)")
|
||||
}
|
||||
m, work := realMachine(t)
|
||||
home := m.Toolchain.Paths.Home
|
||||
key, login := filepath.Join(home, ".ssh", "mesh_ed25519"), filepath.Join(home, "broker")
|
||||
write(t, key, "a key", 0o600)
|
||||
write(t, login, "a login", 0o600)
|
||||
tcp, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tcp.Close()
|
||||
sockDir, err := os.MkdirTemp("", "sock")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(sockDir)
|
||||
sock := filepath.Join(sockDir, "s")
|
||||
unix, err := net.Listen("unix", sock)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer unix.Close()
|
||||
if err := os.MkdirAll(m.npmCache(), 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
port := strconv.Itoa(tcp.Addr().(*net.TCPAddr).Port)
|
||||
dir := filepath.Join(work, "w1", "tree", "modules", "m")
|
||||
write(t, filepath.Join(dir, "probe.mjs"), probeScript, 0o644)
|
||||
write(t, filepath.Join(dir, "package.json"), `{"name":"m","type":"module","scripts":{"test":"node probe.mjs `+key+` `+login+` `+port+` `+sock+` `+m.npmCache()+`"}}`, 0o644)
|
||||
out, err := m.test(context.Background(), map[string]any{"workspace": "w1", "module": "modules/m", "level": "unit"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if r := out.(*TestResult); !r.Passed {
|
||||
t.Fatalf("the probe did not run: %+v", r)
|
||||
}
|
||||
raw, err := os.ReadFile(filepath.Join(dir, "seen.json"))
|
||||
if err != nil {
|
||||
t.Fatalf("the probe wrote nothing: %v", err)
|
||||
}
|
||||
var seen map[string]bool
|
||||
if err := json.Unmarshal(raw, &seen); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for what, reached := range seen {
|
||||
if reached && what != "wroteCache" {
|
||||
t.Errorf("the run step reached %s: %s", what, raw)
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(m.npmCache(), "written-by-a-candidate")); err == nil {
|
||||
t.Errorf("a candidate's step changed the shared cache")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user