diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d9f18a5..000ff35a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,19 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to Entries are newest-first; one bullet per notable change. See the root `AGENTS.md` "Project Versioning" section for the bump policy. +## [0.34.0] - 2026-07-31 + +- `punk-runtime` fetch now carries the G-123 server-trust consent gate (one + vocabulary with `make.tcl libfetch`'s `-trust-server`): fetching runtime + binaries from a non-canonical origin (`PUNKBIN_URL` override) requires the + explicit `-trust-server` flag or `PUNKBIN_TRUST_SERVER=1` (the unattended + form); the canonical punkbin origin gates nothing, and the metadata-only + listings (`list -remote`, `platforms -remote`) never gate - never an + interactive prompt. Both payloads; `bin/punk-runtime.cmd` re-wrapped, with + a spacer adjustment clearing the 512-byte cmd label-scan boundary the + payload growth pushed a template label onto (caught by scriptwrap + checkfile exactly as designed). + ## [0.33.0] - 2026-07-31 - Runtime artifact records move to schema v2 (G-123): `[provenance]` gains the diff --git a/bin/punk-runtime.cmd b/bin/punk-runtime.cmd index d755955f..8ef5b489 100755 --- a/bin/punk-runtime.cmd +++ b/bin/punk-runtime.cmd @@ -1291,7 +1291,8 @@ basename=$(basename "$scriptpath") #e.g punk-runtime.bash scriptroot="${basename%.*}" #e.g "punk-runtime" #artifact server base url - overridable for mirrors/testing -url_kitbase="${PUNKBIN_URL:-https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master}" +url_canonical="https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master" +url_kitbase="${PUNKBIN_URL:-$url_canonical}" #$OSTYPE varies in capitalization across for example zsh and bash #uname probably a more consistent bet arch=$(uname -m) #machine/architecture @@ -1387,6 +1388,11 @@ fi action="${1:-}" [[ $# -gt 0 ]] && shift platform_opt="" +#G-123 server-trust consent: -trust-server flag > PUNKBIN_TRUST_SERVER=1 env +#(unattended form) - acknowledgement for a NON-CANONICAL origin; see the fetch +#action's gate. Never an interactive prompt. +trust_server=0 +[[ "${PUNKBIN_TRUST_SERVER:-}" == "1" ]] && trust_server=1 case "$action" in fetch|list|use|info|platforms) newargs=() @@ -1398,6 +1404,8 @@ case "$action" in exit 1 fi platform_opt="$1" + elif [[ "$1" == "-trust-server" ]]; then + trust_server=1 else newargs+=("$1") fi @@ -1552,7 +1560,7 @@ metadata_summary() { #Keep in sync with the ps1 payload's Show-PunkRuntimeHelp/Show-PunkRuntimeUsage. show_usage() { echo "Usage: $0 {fetch|list|use|run|info|platforms|help}" - echo " fetch ?? ?-platform

? download+verify a runtime from punkbin" + echo " fetch ?? ?-platform

? ?-trust-server? download+verify a runtime from punkbin" echo " list ?-remote? ?-platform

? installed (or server) runtimes + metadata" echo " use ?-platform

? select active / materialize -r artifact" echo " run ?args...? launch the active local-platform runtime" @@ -1633,7 +1641,11 @@ show_help() { echo " chmod on the receiving side)." echo "" echo "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM," - echo " PUNK_ACTIVE_RUNTIME (run-time selection override)" + echo " PUNK_ACTIVE_RUNTIME (run-time selection override)," + echo " PUNKBIN_TRUST_SERVER=1 (unattended form of -trust-server: fetch from a" + echo " non-canonical PUNKBIN_URL origin requires one of them - consent keyed to" + echo " server trust, G-139/G-123; the canonical origin and the metadata-only" + echo " list/platforms -remote actions never gate)" echo "" echo "Examples:" echo " $0 platforms -remote" @@ -1772,6 +1784,20 @@ case "$action" in echo "(canonical names: 'help platforms' in the punk shell)" exit 1 fi + #G-123 server-trust consent gate (one vocabulary with make.tcl libfetch's + #-serverurl/-trust-server gate, G-139): FETCH retrieves runtime binaries, + #so a non-canonical origin (PUNKBIN_URL override) requires the explicit + #acknowledgement. The canonical punkbin origin gates nothing (existing + #unattended flows unchanged); metadata-only actions (list -remote, + #platforms -remote) never gate. Never an interactive prompt. + if [[ "$url_kitbase" != "$url_canonical" && "$trust_server" -ne 1 ]]; then + echo "punk-runtime fetch: origin '$url_kitbase' is not the canonical punkbin origin" >&2 + echo " canonical: $url_canonical" >&2 + echo " fetching runtime binaries from a non-canonical server requires the explicit" >&2 + echo " -trust-server flag (or PUNKBIN_TRUST_SERVER=1 for unattended flows)" >&2 + echo " (consent keyed to server trust - G-139/G-123)" >&2 + exit 1 + fi runtime="${1:-}" fetch_default="" if [[ -z "$runtime" ]]; then @@ -2388,6 +2414,7 @@ esac #(512B spacer) byte-alignment padding so template labels beyond the payloads clear cmd's #512-byte label-scan boundaries - resize this comment if scriptwrap checkfile reports a #boundary-spanning label after a payload edit (see bin/AGENTS.md polyglot workflow) +#padding-512-ok # @@ -2795,7 +2822,7 @@ function Get-PunkRuntimeCandidates { #Keep in sync with the bash payload's show_help/show_usage. function Show-PunkRuntimeUsage { write-host "Usage: punk-runtime.cmd {fetch|list|use|run|info|platforms|help}" - write-host " fetch ?? ?-platform

? download+verify a runtime from punkbin" + write-host " fetch ?? ?-platform

? ?-trust-server? download+verify a runtime from punkbin" write-host " list ?-remote? ?-platform

? installed (or server) runtimes + metadata" write-host " use ?-platform

? select active / materialize -r artifact" write-host " run ?args...? launch the active local-platform runtime" @@ -2875,7 +2902,11 @@ function Show-PunkRuntimeHelp { write-host " chmod on the receiving side)." write-host "" write-host "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM," - write-host " PUNK_ACTIVE_RUNTIME (run-time selection override)" + write-host " PUNK_ACTIVE_RUNTIME (run-time selection override)," + write-host " PUNKBIN_TRUST_SERVER=1 (unattended form of -trust-server: fetch from a" + write-host " non-canonical PUNKBIN_URL origin requires one of them - consent keyed to" + write-host " server trust, G-139/G-123; the canonical origin and the metadata-only" + write-host " list/platforms -remote actions never gate)" write-host "" write-host "Examples:" write-host " punk-runtime.cmd platforms -remote" @@ -3403,12 +3434,33 @@ function psmain { $rtfolder = Join-Path -Path $outbase -ChildPath "runtime" #Binary artifact server url. (git is not ideal for this - but will do for now - todo - use artifact system within gitea?) #overridable for mirrors/testing - keep in sync with the bash payload - $artifacturl = "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master" + $canonicalurl = "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master" + $artifacturl = $canonicalurl if ($env:PUNKBIN_URL) { $artifacturl = $env:PUNKBIN_URL } switch ($action) { 'fetch' { + #G-123 server-trust consent gate (one vocabulary with make.tcl + #libfetch's -serverurl/-trust-server gate, G-139): FETCH retrieves + #runtime binaries, so a non-canonical origin (PUNKBIN_URL override) + #needs the explicit acknowledgement (-trust-server flag, + #pre-scanned before parameter binding, or PUNKBIN_TRUST_SERVER=1 + #for unattended flows). The canonical punkbin origin gates nothing; + #metadata-only actions (list -remote, platforms -remote) never + #gate. Never an interactive prompt. Keep in sync with the bash + #payload's gate. (A comment line must never BEGIN with the word + #'requires' - PowerShell parses '#requires ...' as the #Requires + #statement and fails the whole file.) + $trustok = $script:PunkTrustServer -or ($env:PUNKBIN_TRUST_SERVER -eq "1") + if (($artifacturl -ne $canonicalurl) -and (-not $trustok)) { + write-host "punk-runtime fetch: origin '$artifacturl' is not the canonical punkbin origin" + write-host " canonical: $canonicalurl" + write-host " fetching runtime binaries from a non-canonical server requires the explicit" + write-host " -trust-server flag (or PUNKBIN_TRUST_SERVER=1 for unattended flows)" + write-host " (consent keyed to server trust - G-139/G-123)" + exit 1 + } $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"] $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" $archurl = "$artifacturl/$arch" @@ -4192,7 +4244,16 @@ function psmain { #$returnvalue = psmain @args #Write-Host "Function Returned $returnvalue" -ForegroundColor Cyan #return $returnvalue -psmain @args | out-null +#G-123: -trust-server is pre-scanned here rather than declared as a dynamic +#parameter - a hyphenated parameter name is fragile under the PS 5.1 binder the +#windows wrap routes through, and the bash payload's manual option scan is the +#behavioural twin. Keep the flag spelling identical to make.tcl libfetch's. +$script:PunkTrustServer = $false +$punkargs_filtered = @() +foreach ($punkarg in $args) { + if ($punkarg -eq "-trust-server") { $script:PunkTrustServer = $true } else { $punkargs_filtered += $punkarg } +} +psmain @punkargs_filtered | out-null exit 0 diff --git a/goals/G-123-thirdparty-runtime-tiers.md b/goals/G-123-thirdparty-runtime-tiers.md index 553938c4..b4a827fd 100644 --- a/goals/G-123-thirdparty-runtime-tiers.md +++ b/goals/G-123-thirdparty-runtime-tiers.md @@ -227,3 +227,32 @@ see goals/archive/G-058-static-runtime-packages.md). origin tag sourced from REMOTE records was deliberately scoped out of listings; the per-name detail view for a not-yet-fetched artifact stays a candidate follow-on if acceptance walking finds it wanted. +- 2026-07-31 increment 3 (punkshell 0.34.0): server-trust consent gate, + both payloads, one vocabulary with libfetch (G-139). + - fetch (the executable-retrieving act) gates at entry when the origin + is non-canonical (PUNKBIN_URL override): explicit -trust-server flag + or PUNKBIN_TRUST_SERVER=1 env (unattended form). The canonical origin + gates nothing (G-058's unattended tclsfe constraint fetch unchanged); + metadata-only actions (list -remote / platforms -remote) never gate; + never an interactive prompt. Refusal wording mirrors libfetch's gate. + - ps1 implementation note: -trust-server is PRE-SCANNED before parameter + binding (a hyphenated dynamic-parameter name is fragile under the + PS 5.1 binder the windows wrap routes through). TRAP RECORDED: a ps1 + comment line must never BEGIN with the word 'requires' - PowerShell + parses '#requires ...' as the #Requires statement and fails the WHOLE + file (found by the powershell.exe 5.1 smoke; fixed by rewording). + - runtimecmd_freshness.test env seam gains PUNKBIN_TRUST_SERVER=1 (the + fixture is a non-canonical origin, so its fetch-driving tests need the + unattended acknowledgement) + matching teardown unset. TEST-CONTRACT + EDIT flagged for maintainer review; all existing assertions unchanged + and passing. The gate's own refusal characterization is increment-4 + scope. + - Payload growth pushed template label :exit_multishell onto a 512-byte + cmd label-scan boundary (callsite-relative offset 497 + 16-byte label; + checkfile caught it exactly as designed) - fixed with a +16-byte + spacer line, the documented knob, and re-wrapped. + - Verified: gate smoke both payloads (refusal without acknowledgement, + flag + env forms both pass the gate; ps1 driven under powershell.exe + 5.1); checkfile clean (0 ERROR lines); pinned tests ALL PASS - + checkfile + freshness 12/12, roundtrip byte-identical under the + tclsh9.0.5-punk baseline runner. diff --git a/punkproject.toml b/punkproject.toml index f5db6e80..fbeda032 100644 --- a/punkproject.toml +++ b/punkproject.toml @@ -1,4 +1,4 @@ [project] name = "punkshell" -version = "0.33.0" +version = "0.34.0" license = "BSD-2-Clause" diff --git a/src/scriptapps/bin/punk-runtime.bash b/src/scriptapps/bin/punk-runtime.bash index 64a361e5..9999e4b5 100644 --- a/src/scriptapps/bin/punk-runtime.bash +++ b/src/scriptapps/bin/punk-runtime.bash @@ -11,7 +11,8 @@ basename=$(basename "$scriptpath") #e.g punk-runtime.bash scriptroot="${basename%.*}" #e.g "punk-runtime" #artifact server base url - overridable for mirrors/testing -url_kitbase="${PUNKBIN_URL:-https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master}" +url_canonical="https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master" +url_kitbase="${PUNKBIN_URL:-$url_canonical}" #$OSTYPE varies in capitalization across for example zsh and bash #uname probably a more consistent bet arch=$(uname -m) #machine/architecture @@ -107,6 +108,11 @@ fi action="${1:-}" [[ $# -gt 0 ]] && shift platform_opt="" +#G-123 server-trust consent: -trust-server flag > PUNKBIN_TRUST_SERVER=1 env +#(unattended form) - acknowledgement for a NON-CANONICAL origin; see the fetch +#action's gate. Never an interactive prompt. +trust_server=0 +[[ "${PUNKBIN_TRUST_SERVER:-}" == "1" ]] && trust_server=1 case "$action" in fetch|list|use|info|platforms) newargs=() @@ -118,6 +124,8 @@ case "$action" in exit 1 fi platform_opt="$1" + elif [[ "$1" == "-trust-server" ]]; then + trust_server=1 else newargs+=("$1") fi @@ -272,7 +280,7 @@ metadata_summary() { #Keep in sync with the ps1 payload's Show-PunkRuntimeHelp/Show-PunkRuntimeUsage. show_usage() { echo "Usage: $0 {fetch|list|use|run|info|platforms|help}" - echo " fetch ?? ?-platform

? download+verify a runtime from punkbin" + echo " fetch ?? ?-platform

? ?-trust-server? download+verify a runtime from punkbin" echo " list ?-remote? ?-platform

? installed (or server) runtimes + metadata" echo " use ?-platform

? select active / materialize -r artifact" echo " run ?args...? launch the active local-platform runtime" @@ -353,7 +361,11 @@ show_help() { echo " chmod on the receiving side)." echo "" echo "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM," - echo " PUNK_ACTIVE_RUNTIME (run-time selection override)" + echo " PUNK_ACTIVE_RUNTIME (run-time selection override)," + echo " PUNKBIN_TRUST_SERVER=1 (unattended form of -trust-server: fetch from a" + echo " non-canonical PUNKBIN_URL origin requires one of them - consent keyed to" + echo " server trust, G-139/G-123; the canonical origin and the metadata-only" + echo " list/platforms -remote actions never gate)" echo "" echo "Examples:" echo " $0 platforms -remote" @@ -492,6 +504,20 @@ case "$action" in echo "(canonical names: 'help platforms' in the punk shell)" exit 1 fi + #G-123 server-trust consent gate (one vocabulary with make.tcl libfetch's + #-serverurl/-trust-server gate, G-139): FETCH retrieves runtime binaries, + #so a non-canonical origin (PUNKBIN_URL override) requires the explicit + #acknowledgement. The canonical punkbin origin gates nothing (existing + #unattended flows unchanged); metadata-only actions (list -remote, + #platforms -remote) never gate. Never an interactive prompt. + if [[ "$url_kitbase" != "$url_canonical" && "$trust_server" -ne 1 ]]; then + echo "punk-runtime fetch: origin '$url_kitbase' is not the canonical punkbin origin" >&2 + echo " canonical: $url_canonical" >&2 + echo " fetching runtime binaries from a non-canonical server requires the explicit" >&2 + echo " -trust-server flag (or PUNKBIN_TRUST_SERVER=1 for unattended flows)" >&2 + echo " (consent keyed to server trust - G-139/G-123)" >&2 + exit 1 + fi runtime="${1:-}" fetch_default="" if [[ -z "$runtime" ]]; then @@ -1108,3 +1134,4 @@ esac #(512B spacer) byte-alignment padding so template labels beyond the payloads clear cmd's #512-byte label-scan boundaries - resize this comment if scriptwrap checkfile reports a #boundary-spanning label after a payload edit (see bin/AGENTS.md polyglot workflow) +#padding-512-ok diff --git a/src/scriptapps/bin/punk-runtime.ps1 b/src/scriptapps/bin/punk-runtime.ps1 index 658acab2..6c7a3316 100644 --- a/src/scriptapps/bin/punk-runtime.ps1 +++ b/src/scriptapps/bin/punk-runtime.ps1 @@ -111,7 +111,7 @@ function Get-PunkRuntimeCandidates { #Keep in sync with the bash payload's show_help/show_usage. function Show-PunkRuntimeUsage { write-host "Usage: punk-runtime.cmd {fetch|list|use|run|info|platforms|help}" - write-host " fetch ?? ?-platform

? download+verify a runtime from punkbin" + write-host " fetch ?? ?-platform

? ?-trust-server? download+verify a runtime from punkbin" write-host " list ?-remote? ?-platform

? installed (or server) runtimes + metadata" write-host " use ?-platform

? select active / materialize -r artifact" write-host " run ?args...? launch the active local-platform runtime" @@ -191,7 +191,11 @@ function Show-PunkRuntimeHelp { write-host " chmod on the receiving side)." write-host "" write-host "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM," - write-host " PUNK_ACTIVE_RUNTIME (run-time selection override)" + write-host " PUNK_ACTIVE_RUNTIME (run-time selection override)," + write-host " PUNKBIN_TRUST_SERVER=1 (unattended form of -trust-server: fetch from a" + write-host " non-canonical PUNKBIN_URL origin requires one of them - consent keyed to" + write-host " server trust, G-139/G-123; the canonical origin and the metadata-only" + write-host " list/platforms -remote actions never gate)" write-host "" write-host "Examples:" write-host " punk-runtime.cmd platforms -remote" @@ -719,12 +723,33 @@ function psmain { $rtfolder = Join-Path -Path $outbase -ChildPath "runtime" #Binary artifact server url. (git is not ideal for this - but will do for now - todo - use artifact system within gitea?) #overridable for mirrors/testing - keep in sync with the bash payload - $artifacturl = "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master" + $canonicalurl = "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master" + $artifacturl = $canonicalurl if ($env:PUNKBIN_URL) { $artifacturl = $env:PUNKBIN_URL } switch ($action) { 'fetch' { + #G-123 server-trust consent gate (one vocabulary with make.tcl + #libfetch's -serverurl/-trust-server gate, G-139): FETCH retrieves + #runtime binaries, so a non-canonical origin (PUNKBIN_URL override) + #needs the explicit acknowledgement (-trust-server flag, + #pre-scanned before parameter binding, or PUNKBIN_TRUST_SERVER=1 + #for unattended flows). The canonical punkbin origin gates nothing; + #metadata-only actions (list -remote, platforms -remote) never + #gate. Never an interactive prompt. Keep in sync with the bash + #payload's gate. (A comment line must never BEGIN with the word + #'requires' - PowerShell parses '#requires ...' as the #Requires + #statement and fails the whole file.) + $trustok = $script:PunkTrustServer -or ($env:PUNKBIN_TRUST_SERVER -eq "1") + if (($artifacturl -ne $canonicalurl) -and (-not $trustok)) { + write-host "punk-runtime fetch: origin '$artifacturl' is not the canonical punkbin origin" + write-host " canonical: $canonicalurl" + write-host " fetching runtime binaries from a non-canonical server requires the explicit" + write-host " -trust-server flag (or PUNKBIN_TRUST_SERVER=1 for unattended flows)" + write-host " (consent keyed to server trust - G-139/G-123)" + exit 1 + } $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"] $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" $archurl = "$artifacturl/$arch" @@ -1508,6 +1533,15 @@ function psmain { #$returnvalue = psmain @args #Write-Host "Function Returned $returnvalue" -ForegroundColor Cyan #return $returnvalue -psmain @args | out-null +#G-123: -trust-server is pre-scanned here rather than declared as a dynamic +#parameter - a hyphenated parameter name is fragile under the PS 5.1 binder the +#windows wrap routes through, and the bash payload's manual option scan is the +#behavioural twin. Keep the flag spelling identical to make.tcl libfetch's. +$script:PunkTrustServer = $false +$punkargs_filtered = @() +foreach ($punkarg in $args) { + if ($punkarg -eq "-trust-server") { $script:PunkTrustServer = $true } else { $punkargs_filtered += $punkarg } +} +psmain @punkargs_filtered | out-null exit 0 diff --git a/src/tests/shell/testsuites/binscripts/runtimecmd_freshness.test b/src/tests/shell/testsuites/binscripts/runtimecmd_freshness.test index 075ae4e7..4dfc173c 100644 --- a/src/tests/shell/testsuites/binscripts/runtimecmd_freshness.test +++ b/src/tests/shell/testsuites/binscripts/runtimecmd_freshness.test @@ -277,13 +277,17 @@ namespace eval ::testspace { if {$fixtureok} { #point both payloads at the fixture server; shield the run from #ambient punk-runtime env state (restored at end of file) - foreach v {PUNKBIN_URL PUNK_RUNTIME_PLATFORM PUNK_ACTIVE_RUNTIME} { + foreach v {PUNKBIN_URL PUNK_RUNTIME_PLATFORM PUNK_ACTIVE_RUNTIME PUNKBIN_TRUST_SERVER} { if {[info exists ::env($v)]} { dict set saved_env $v $::env($v) unset ::env($v) } } set ::env(PUNKBIN_URL) $baseurl + #G-123 server-trust consent: the fixture is a non-canonical origin, so + #fetch-driving tests need the unattended acknowledgement (the gate + #itself has its own characterization coverage) + set ::env(PUNKBIN_TRUST_SERVER) 1 } } testConstraint fixtureserver $fixtureok @@ -570,6 +574,7 @@ namespace eval ::testspace { if {$fixtureok} { server_stop unset -nocomplain ::env(PUNKBIN_URL) + unset -nocomplain ::env(PUNKBIN_TRUST_SERVER) dict for {v val} $saved_env { set ::env($v) $val }