From fe7b2822c51ac65dd2b58463a028b8da4aaea734 Mon Sep 17 00:00:00 2001 From: wtclaude Date: Sat, 26 Sep 2026 21:25:35 -0500 Subject: [PATCH] feat(egg): install every plugin file the release lists, helpers included (D182) Rust-Plugins now releases a ZoneManager helper, RunicGatewayZones.cs, beside the bridge (docs/modules/rust/PLAN_FIXES.md D181, D182), installed by default. The egg copied only RunicGateway.cs out of the tarball. It now takes every .cs the plugin manifest lists in `files`, checks each against its own sha256 before anything is placed (the bridge's own checksum was never checked by the egg before), and refuses a name that is not a plain .cs. A release older than helpers lists only the bridge and installs exactly what it did before. Exercised in an Alpine shell against both, a tampered helper, a missing one, `../evil.cs` and `evil.dll`. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY --- egg/install.sh | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/egg/install.sh b/egg/install.sh index ad8eee0..ce78fea 100755 --- a/egg/install.sh +++ b/egg/install.sh @@ -148,7 +148,25 @@ rg_install() { mkdir -p /mnt/server/rust-link "${plugins}" install -m 755 "${work}/rust-link-sidecar" /mnt/server/rust-link/rust-link-sidecar install -m 755 "${work}/with-sidecar.sh" /mnt/server/rust-link/with-sidecar.sh - install -m 644 "${work}/runicgateway-rust-plugin/RunicGateway.cs" "${plugins}/RunicGateway.cs" + # Every .cs the release's manifest lists: the bridge, and the helpers shipped + # beside it (docs/modules/rust/PLAN_FIXES.md D182 - today RunicGatewayZones.cs, + # the ZoneManager helper), each checked against its own sha256 first. A name + # is written into the plugins directory, so only a plain .cs is taken. + local file sha + for file in $(jq -r '.files | keys[]' "${manifest}"); do + case "${file}" in + *[!A-Za-z0-9_.]* | .* | *..* | *[!s] ) echo "Runic Gateway: the plugin manifest lists ${file}, which is not a plugin file - refusing it"; return 1 ;; + esac + [ "${file%.cs}" != "${file}" ] || { echo "Runic Gateway: the plugin manifest lists ${file}, which is not a .cs file - refusing it"; return 1; } + [ -f "${work}/runicgateway-rust-plugin/${file}" ] || { echo "Runic Gateway: the plugin manifest lists ${file} but the tarball has none"; return 1; } + sha="$(jq -r --arg f "${file}" '.files[$f]' "${manifest}")" + echo "${sha} ${work}/runicgateway-rust-plugin/${file}" | sha256sum -c --quiet - \ + || { echo "Runic Gateway: ${file} does not match the plugin manifest's sha256 - refusing it"; return 1; } + done + [ -f "${work}/runicgateway-rust-plugin/RunicGateway.cs" ] || { echo "Runic Gateway: the plugin tarball has no RunicGateway.cs"; return 1; } + for file in $(jq -r '.files | keys[]' "${manifest}"); do + install -m 644 "${work}/runicgateway-rust-plugin/${file}" "${plugins}/${file}" + done # What is installed, readable from the panel's file manager. jq --arg framework "${FRAMEWORK}" --arg installed "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ '{ bundle, protocol, framework: $framework, installed: $installed, -- 2.49.1