aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-15 14:24:25 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-15 14:24:25 +0200
commit5949532e6450d1aebc681caa8db2192a43b11f12 (patch)
treee457c84ab3a5e54061d10d2233c61fdb6ff194cd
parenta86bced1640ac1a695b95ac68b95e3fc581e687d (diff)
downloadmeshbay-5949532e6450d1aebc681caa8db2192a43b11f12.tar.gz
fix(packaging): ship only the TMDB token line of QE/default.env
build-node.sh and build-node-runtime.ps1 copied QE/default.env whole into every deb, rpm and Windows build after checking only its first 30 bytes. The file is unversioned and sits beside other QE notes, so any line added to it later would have gone out publicly. Only its first line ships now, and only once it is exactly MESHBAY_TMDB_DEFAULT_TOKEN=<JWT>; a missing file, a BOM, a token not on the first line or an empty one still stop the build. The Linux step is run by a test against a scratch repo; the PowerShell one has the same rule and was not run here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--packages/meshbay-node/tests/test_packaging_default_env.py74
-rwxr-xr-xpackaging/build/build-node.sh22
-rw-r--r--packaging/win/build-node-runtime.ps115
3 files changed, 96 insertions, 15 deletions
diff --git a/packages/meshbay-node/tests/test_packaging_default_env.py b/packages/meshbay-node/tests/test_packaging_default_env.py
new file mode 100644
index 0000000..60bbb78
--- /dev/null
+++ b/packages/meshbay-node/tests/test_packaging_default_env.py
@@ -0,0 +1,74 @@
+"""
+What the Linux packages ship of QE/default.env.
+
+The file is unversioned and lives beside other QE notes; the packages are
+public. build-node.sh once copied it whole after checking only its first 30
+bytes, so any line added to it later would have gone out in every deb and rpm.
+Only the validated token line may ship.
+
+These run the build step itself (the lines from `DEFAULT_ENV=` to its echo)
+under bash against a scratch repo, rather than reading its text.
+"""
+
+import shutil
+import subprocess
+from pathlib import Path
+
+import pytest
+
+ROOT = Path(__file__).resolve().parents[3]
+BUILD_NODE = ROOT / "packaging" / "build" / "build-node.sh"
+
+pytestmark = [
+ pytest.mark.skipif(not BUILD_NODE.exists(), reason="packaging not present"),
+ pytest.mark.skipif(shutil.which("bash") is None, reason="no bash"),
+]
+
+TOKEN = "MESHBAY_TMDB_DEFAULT_TOKEN=eyJhbGciOi.eyJzdWIi-x_y"
+
+
+def _step() -> str:
+ lines = BUILD_NODE.read_text(encoding="utf-8").splitlines()
+ start = next(i for i, l in enumerate(lines) if l.startswith("DEFAULT_ENV="))
+ end = next(i for i in range(start, len(lines)) if "default.env:" in lines[i] and lines[i].lstrip().startswith("echo"))
+ return "\n".join(lines[start : end + 1]) + "\n"
+
+
+def _build(tmp_path: Path, content: bytes | None) -> Path | None:
+ (tmp_path / "QE").mkdir()
+ share = tmp_path / "root" / "opt" / "meshbay-node" / "share"
+ share.mkdir(parents=True)
+ if content is not None:
+ (tmp_path / "QE" / "default.env").write_bytes(content)
+ script = tmp_path / "step.sh"
+ script.write_text(_step(), encoding="utf-8")
+ run = subprocess.run(
+ ["bash", str(script)],
+ env={"REPO": str(tmp_path), "ROOT": str(tmp_path / "root"), "PATH": "/usr/bin:/bin"},
+ capture_output=True,
+ )
+ out = share / "default.env"
+ return out if run.returncode == 0 else None
+
+
+def test_only_the_token_line_ships(tmp_path):
+ out = _build(tmp_path, (TOKEN + "\r\nANOTHER_SECRET=hunter2\n# a note\n").encode())
+ assert out is not None
+ assert out.read_bytes() == (TOKEN + "\n").encode()
+ assert out.stat().st_mode & 0o777 == 0o644
+
+
+@pytest.mark.parametrize(
+ "content",
+ [
+ None,
+ b"",
+ b"\xef\xbb\xbf" + TOKEN.encode() + b"\n",
+ b"# token below\n" + TOKEN.encode() + b"\n",
+ b"MESHBAY_TMDB_DEFAULT_TOKEN=eyJ\n",
+ b"MESHBAY_TMDB_DEFAULT_TOKEN=eyJa b\n",
+ ],
+ ids=["missing", "empty", "bom", "not-first", "no-token", "space"],
+)
+def test_no_valid_token_line_no_build(tmp_path, content):
+ assert _build(tmp_path, content) is None
diff --git a/packaging/build/build-node.sh b/packaging/build/build-node.sh
index b9d542c..c7e7577 100755
--- a/packaging/build/build-node.sh
+++ b/packaging/build/build-node.sh
@@ -45,18 +45,22 @@ ln -sf /opt/meshbay-common/venv/bin/meshbay-node "$ROOT/usr/bin/meshbay-node"
# --- Node-specific assets -------------------------------------------------
mkdir -p "$ROOT/opt/meshbay-node/share"
-# default.env: the shared TMDB token, copied as is from QE/default.env (never
-# versioned), one line: MESHBAY_TMDB_DEFAULT_TOKEN=eyJ... The daemon reads it
-# beneath <config>/node.env and the operator's own token, so it is only the
-# fallback. 0644: a per-user node must read it, and it is the same token in
-# every copy of the package. No token, no build: an empty one goes unnoticed.
+# default.env: the shared TMDB token, taken from QE/default.env (never
+# versioned). Only its first line ships, and only once it is exactly
+# MESHBAY_TMDB_DEFAULT_TOKEN=<JWT>: anything else written in that file stays
+# out of a public package. The daemon reads it beneath <config>/node.env and
+# the operator's own token, so it is only the fallback. 0644: a per-user node
+# must read it, and it is the same token in every copy of the package. No
+# token, no build: an empty one goes unnoticed.
DEFAULT_ENV="$REPO/QE/default.env"
-if [ "$(head -c 30 "$DEFAULT_ENV" 2>/dev/null)" != "MESHBAY_TMDB_DEFAULT_TOKEN=eyJ" ]; then
- echo "!! $DEFAULT_ENV missing, or not starting with MESHBAY_TMDB_DEFAULT_TOKEN=eyJ... (no BOM)" >&2
+TOKEN_LINE="$(head -n 1 "$DEFAULT_ENV" 2>/dev/null | tr -d '\r')"
+if ! [[ "$TOKEN_LINE" =~ ^MESHBAY_TMDB_DEFAULT_TOKEN=eyJ[A-Za-z0-9_.-]+$ ]]; then
+ echo "!! $DEFAULT_ENV missing, or its first line is not MESHBAY_TMDB_DEFAULT_TOKEN=eyJ... (no BOM)" >&2
exit 1
fi
-install -m 644 "$DEFAULT_ENV" "$ROOT/opt/meshbay-node/share/default.env"
-echo " default.env copied from QE/"
+printf '%s\n' "$TOKEN_LINE" > "$ROOT/opt/meshbay-node/share/default.env"
+chmod 644 "$ROOT/opt/meshbay-node/share/default.env"
+echo " default.env: token line taken from QE/"
# --- Systemd units --------------------------------------------------------
mkdir -p "$ROOT/usr/lib/systemd/system"
diff --git a/packaging/win/build-node-runtime.ps1 b/packaging/win/build-node-runtime.ps1
index 28b96e8..3d26ec2 100644
--- a/packaging/win/build-node-runtime.ps1
+++ b/packaging/win/build-node-runtime.ps1
@@ -140,8 +140,9 @@ else {
}
# --- 5. default.env (shared TMDB token) ------------------------------
-# Copied as is from QE\default.env (never versioned), one line:
-# MESHBAY_TMDB_DEFAULT_TOKEN=eyJ... Beside the exe, where
+# Taken from QE\default.env (never versioned). Only its first line ships, and
+# only once it is exactly MESHBAY_TMDB_DEFAULT_TOKEN=<JWT>: anything else
+# written in that file stays out of a public package. Beside the exe, where
# platform.packaged_default_env() looks for it. The daemon reads it beneath
# %LOCALAPPDATA%\meshbay\node.env and the operator's own token, so it is only
# the fallback. No token, no build: an empty one goes unnoticed. The check is
@@ -150,11 +151,13 @@ $defaultEnv = Join-Path $Repo "QE\default.env"
$text = if (Test-Path -LiteralPath $defaultEnv) {
[System.Text.Encoding]::ASCII.GetString([System.IO.File]::ReadAllBytes($defaultEnv))
} else { "" }
-if (-not $text.StartsWith("MESHBAY_TMDB_DEFAULT_TOKEN=eyJ")) {
- throw "$defaultEnv missing, or not starting with MESHBAY_TMDB_DEFAULT_TOKEN=eyJ... (no BOM)"
+$tokenLine = ($text -split "`n", 2)[0].TrimEnd("`r")
+if ($tokenLine -cnotmatch '^MESHBAY_TMDB_DEFAULT_TOKEN=eyJ[A-Za-z0-9_.-]+$') {
+ throw "$defaultEnv missing, or its first line is not MESHBAY_TMDB_DEFAULT_TOKEN=eyJ... (no BOM)"
}
-Copy-Item -LiteralPath $defaultEnv (Join-Path $frozen "default.env")
-Step "default.env copied from QE\"
+[System.IO.File]::WriteAllText((Join-Path $frozen "default.env"), "$tokenLine`n",
+ (New-Object System.Text.UTF8Encoding $false))
+Step "default.env: token line taken from QE\"
# --- 6. publish ----------------------------------------------------
Move-Item $frozen $OutDir