Files
XC_VM/tools/ci/verify-lb-archive.sh
T
Divarion-D baf6c8e231 build: ship a production-only vendor; install dev tools via Composer
Switch from "commit vendor with dev deps + strip at release" to the standard
application model: the committed src/vendor/ is PRODUCTION-ONLY, and dev tooling
(PHPStan, PHP-CS-Fixer + ~37 transitive deps) is installed on demand with
"composer install".

- Regenerate the committed src/vendor/ via "composer install --no-dev"
  (34 MB -> ~0.5 MB; only the Composer autoloader + gemorroj/m3u-parser +
  chrisyue/php-m3u8 remain). This also stops PHPStan\PharAutoloader registering
  in production.
- Commit src/composer.lock (un-ignored) — this is an application, so the lock is
  committed to make "composer install" reproducible across dev/CI.
- Revert the release-time strip step (Makefile hooks + tools/build/
  strip-dev-vendor.sh) — no longer needed; the archive ships the prod vendor as-is.
- CI: the phpstan and code-style jobs now run "composer install --working-dir=src"
  (with tools: composer) to obtain the dev tools before running.
- New gate tools/ci/check-vendor-prod-only.sh (+ make check-vendor-prod-only,
  wired into "make gates"): asserts no require-dev package from composer.lock is
  committed under src/vendor/ — guards against accidentally committing a
  dev-bloated vendor. Inspects git-tracked files, so it is correct even in a CI
  job that already ran "composer install".
- Fix verify-lb-archive.sh: LB legitimately ships most of Cli/Commands and
  Cli/CronJobs (edge commands + certbot/cache/cleanup crons), so flag only the
  genuinely privileged dirs + the specific install/root files, not the whole dirs.
- .gitignore / composer.json notes updated.

Verified before pruning: PHPStan no errors, PHPUnit 303, cs + gates green. After
pruning: PHPUnit 303 (prod-only vendor), all three gates green. Local dev tools
restored afterwards with "composer install" (not committed).
2026-06-25 21:51:13 +03:00

68 lines
2.7 KiB
Bash

#!/usr/bin/env bash
#
# Security gate (plan blocker 1): the LoadBalancer archive must NOT contain
# privileged code. The LB build copies LB_DIRS and then removes LB_DIRS_TO_REMOVE
# / LB_FILES_TO_REMOVE. After the PascalCase rename (Фаза 1) a stale lowercase
# remove path would silently miss, leaking Admin/Reseller controllers, the
# user/device domain and cron jobs to an internet-facing DMZ node.
#
# This reproduces the Makefile's LB file selection from the real LB_* variables
# (no tarball needed) and asserts the sensitive trees are absent.
set -euo pipefail
cd "$(dirname "$0")/../.."
LB_DIRS=$(make -s print-LB_DIRS)
RM_DIRS=$(make -s print-LB_DIRS_TO_REMOVE)
RM_FILES=$(make -s print-LB_FILES_TO_REMOVE)
# Shipped manifest: tracked files under LB_DIRS, paths relative to src/.
manifest=$(for d in $LB_DIRS; do git ls-files "src/$d" 2>/dev/null; done | sed 's#^src/##')
# Apply directory removals.
if [ -n "${RM_DIRS// }" ]; then
rm_re=$(printf '%s' "$RM_DIRS" | tr -s ' ' '|')
manifest=$(printf '%s\n' "$manifest" | grep -Ev "^(${rm_re})/" || true)
fi
# Apply file removals.
for f in $RM_FILES; do
manifest=$(printf '%s\n' "$manifest" | grep -vxF "$f" || true)
done
# Privileged paths that must never reach an LB (internet-facing) node. These are
# the genuinely admin/reseller/install-only trees and files. NOTE: LB legitimately
# ships most of Cli/Commands and Cli/CronJobs (it runs edge commands + crons like
# certbot/cache/cleanup), so only the specific privileged ones are listed — not
# the whole dirs. Each entry must be removed by the Makefile's LB_DIRS_TO_REMOVE /
# LB_FILES_TO_REMOVE; this asserts the removal actually took effect (catches a
# silent rm miss after a rename — security blocker 1).
SENSITIVE=(
# Admin / reseller / player UI + the privileged domains (dir-level removes).
"Public/Controllers/Admin"
"Public/Controllers/Reseller"
"Public/Controllers/Player"
"Domain/User"
"Domain/Device"
# Install / provisioning commands and root-privileged cron jobs (file-level).
"Cli/Commands/ServerInstallCommand.php"
"Cli/Commands/LbInstallFlow.php"
"Cli/Commands/ProxyInstallFlow.php"
"Cli/Commands/MigrateCommand.php"
"Cli/CronJobs/RootMysqlCronJob.php"
)
fail=0
for s in "${SENSITIVE[@]}"; do
# Match a directory prefix ("$s/") or an exact file path ("$s").
if printf '%s\n' "$manifest" | grep -qE "^${s}(/|$)"; then
echo "LEAK: '${s}' would ship to the LB archive (privileged code)."
printf '%s\n' "$manifest" | grep -E "^${s}(/|$)" | sed 's/^/ /' | head -5
fail=1
fi
done
if [ "$fail" -ne 0 ]; then
echo "FAIL: LB archive contains privileged code — check Makefile LB_DIRS_TO_REMOVE/LB_FILES_TO_REMOVE."
exit 1
fi
echo "OK: LB manifest excludes all privileged trees ($(printf '%s\n' "$manifest" | grep -c . ) files shipped)."