Deploy-Brass: extend manifest verification to all fields

The previous check covered version, launcherVersion/Url, and a single
sampled self-hosted URL. Expanded to cover every field that has ever
been a foot-gun:

  Required (must always be present and match pack.lock.json):
    name, version, minecraft.version, loader.type, loader.version,
    launcherVersion, launcherUrl

  Optional but consistency-checked: if pack.lock.json declares the
  field, the manifest MUST also have it (and defaultServer must include
  a non-empty ip). Catches the case where someone removes the field
  from pack.lock or Build-Pack drops it silently:
    panelUrl, defaultShader, defaultServer

  files[]: every entry validated for path/url/sha1/size shape; every
  self-hosted (non-CDN) url checked for the expected host. Catches
  partial-update bugs where some URLs migrate hosts but others don't.

URL host checks use the configured $ManifestPublicUrl host as the
source of truth; CDN-hosted mods (modrinth/forgecdn/curseforge) are
intentionally exempt.
This commit is contained in:
Matt Sijbers
2026-05-20 21:37:55 +01:00
parent 4e31f5ab1a
commit ed70ff52e9
+50 -12
View File
@@ -259,30 +259,68 @@ if ($shouldRunPack -and -not $DryRun -and $ManifestPublicUrl) {
catch { throw "Failed to fetch deployed manifest: $($_.Exception.Message)" } catch { throw "Failed to fetch deployed manifest: $($_.Exception.Message)" }
$expectedHost = ([uri]$ManifestPublicUrl).Host $expectedHost = ([uri]$ManifestPublicUrl).Host
$problems = @()
$lock = Get-Content (Join-Path $repoRoot 'pack\pack.lock.json') -Raw | ConvertFrom-Json $lock = Get-Content (Join-Path $repoRoot 'pack\pack.lock.json') -Raw | ConvertFrom-Json
if ($live.version -ne $lock.version) { $problems = @()
$problems += "version mismatch: live=$($live.version), local=$($lock.version)" $hasLockField = { param($name) ($lock.PSObject.Properties.Name -contains $name) -and $lock.$name }
}
# ── Top-level required fields (sourced from pack.lock) ──
if (-not $live.name) { $problems += "name missing" }
elseif ($live.name -ne $lock.name) { $problems += "name mismatch: live=$($live.name), local=$($lock.name)" }
if (-not $live.version) { $problems += "version missing" }
elseif ($live.version -ne $lock.version) { $problems += "version mismatch: live=$($live.version), local=$($lock.version)" }
if (-not $live.minecraft.version) { $problems += "minecraft.version missing" }
elseif ($live.minecraft.version -ne $lock.minecraft) { $problems += "minecraft.version mismatch: live=$($live.minecraft.version), local=$($lock.minecraft)" }
if (-not $live.loader.type) { $problems += "loader.type missing" }
elseif ($live.loader.type -ne $lock.loader.type) { $problems += "loader.type mismatch: live=$($live.loader.type), local=$($lock.loader.type)" }
if (-not $live.loader.version) { $problems += "loader.version missing" }
elseif ($live.loader.version -ne $lock.loader.version) { $problems += "loader.version mismatch: live=$($live.loader.version), local=$($lock.loader.version)" }
# ── Launcher metadata (always required once a launcher has ever shipped) ──
if (-not $live.launcherVersion) { $problems += "launcherVersion missing (old launchers won't see upgrade banner)" } if (-not $live.launcherVersion) { $problems += "launcherVersion missing (old launchers won't see upgrade banner)" }
if (-not $live.launcherUrl) { $problems += "launcherUrl missing" } if (-not $live.launcherUrl) { $problems += "launcherUrl missing" }
if ($live.launcherUrl -and ([uri]$live.launcherUrl).Host -ne $expectedHost) { if ($live.launcherUrl -and ([uri]$live.launcherUrl).Host -ne $expectedHost) {
$problems += "launcherUrl host is $([uri]$live.launcherUrl).Host, expected $expectedHost" $problems += "launcherUrl host is $(([uri]$live.launcherUrl).Host), expected $expectedHost"
}
# ── Optional fields: consistency check. If pack.lock has it, manifest must too. ──
if ((& $hasLockField 'panelUrl') -and -not $live.panelUrl) {
$problems += "panelUrl missing from manifest but set in pack.lock.json (friend whitelist requests will fail)"
}
if ((& $hasLockField 'defaultShader') -and -not $live.defaultShader) {
$problems += "defaultShader missing from manifest but set in pack.lock.json"
}
if ((& $hasLockField 'defaultServer') -and (-not $live.defaultServer -or -not $live.defaultServer.ip)) {
$problems += "defaultServer missing/incomplete in manifest but set in pack.lock.json"
}
# ── files[]: every entry has the shape clients depend on; self-hosted URLs use expected host ──
if (-not $live.files -or $live.files.Count -eq 0) {
$problems += "files array is empty"
} else {
$i = 0
foreach ($f in $live.files) {
if (-not $f.path) { $problems += "files[$i].path missing" }
if (-not $f.url) { $problems += "files[$i].url missing (path=$($f.path))" }
if (-not $f.sha1 -or $f.sha1.Length -ne 40) { $problems += "files[$i].sha1 invalid (path=$($f.path))" }
if (-not $f.size -or $f.size -le 0) { $problems += "files[$i].size invalid (path=$($f.path))" }
# Self-hosted (non-CDN) URLs must use the expected host. CDN mods are exempt.
if ($f.url -and $f.url -notmatch 'modrinth|forgecdn|curseforge') {
$h = ([uri]$f.url).Host
if ($h -ne $expectedHost) {
$problems += "files[$i] self-hosted URL host is $h, expected $expectedHost (path=$($f.path))"
}
}
$i++
} }
# Sample a self-hosted file (skip CDN-hosted mods)
$local = $live.files | Where-Object { $_.url -notmatch 'modrinth|forgecdn|curseforge' } | Select-Object -First 1
if ($local -and ([uri]$local.url).Host -ne $expectedHost) {
$problems += "self-hosted file URL host is $([uri]$local.url).Host, expected $expectedHost (path: $($local.path))"
} }
if ($problems.Count -gt 0) { if ($problems.Count -gt 0) {
Write-Host "" Write-Host ""
Write-Host "DEPLOYED MANIFEST FAILED VERIFICATION:" -ForegroundColor Red Write-Host "DEPLOYED MANIFEST FAILED VERIFICATION:" -ForegroundColor Red
foreach ($p in $problems) { Write-Host " - $p" -ForegroundColor Red } foreach ($p in $problems) { Write-Host " - $p" -ForegroundColor Red }
throw "Deployed manifest is missing required fields. Fix and redeploy." throw "Deployed manifest has $($problems.Count) issue(s). Fix and redeploy."
} }
Write-Host (" version={0} launcherVersion={1} ok" -f $live.version, $live.launcherVersion) -ForegroundColor DarkGray Write-Host (" version={0} launcherVersion={1} files={2} ok" -f $live.version, $live.launcherVersion, $live.files.Count) -ForegroundColor DarkGray
} }
} }