mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-25 21:20:00 +00:00
Keep captured audit findings past early watchdog cleanup
The full installer race suite exposed report deletion despite the subshell-counter guard. Bind EXIT cleanup to the original process using the direct child shell parent PID, including before the inherited counter is initialised. Retain strict and unchanged-graph verdicts, exact arguments and parent cleanup. Align the stale channel-copy assertion with the existing non-stable message; executable consent controls remain unchanged. Change-source: pulse-maintainer
This commit is contained in:
@@ -15,6 +15,22 @@
|
||||
|
||||
## Purpose
|
||||
|
||||
### Audit report cleanup belongs to the original invoking process
|
||||
|
||||
The npm audit wrapper retains a captured advisory until the parent classifies
|
||||
it. An early-stopped watchdog can inherit the parent's zero subshell counter;
|
||||
that counter alone cannot authorise report deletion. The EXIT hook instead
|
||||
asks a direct `/bin/sh` child whether its actual parent PID is the invoking
|
||||
script's PID. This keeps the report in an inherited trap while retaining parent
|
||||
cleanup on Linux and native Bash 3.2. Strict findings still fail; unchanged-graph
|
||||
findings still warn with their captured detail. No advisory policy, endpoint,
|
||||
retry budget or severity threshold changes.
|
||||
|
||||
`TestNpmAuditReportCleanupRemainsParentOwned` executes the actual hook with
|
||||
normal and pre-initialisation child counters, rejects the prior counter-only
|
||||
and unconditional hooks, and checks parent cleanup. The existing native-scope
|
||||
argument controls continue to require findings, exact argv and a single audit.
|
||||
|
||||
### Community auto-updates require a verified installed edition
|
||||
|
||||
Before release discovery or installation, the unattended updater requires a
|
||||
|
||||
@@ -5292,11 +5292,13 @@ func TestNpmAuditReportCleanupRemainsParentOwned(t *testing.T) {
|
||||
t.Fatal("audit report cleanup hook is missing")
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
name, hook string
|
||||
status int
|
||||
name, hook, childSetup string
|
||||
status int
|
||||
}{
|
||||
{"actual_parent_owned_hook", hook, 0},
|
||||
{"legacy_child_cleanup_rejected", `trap 'rm -f "${report_file}"' EXIT`, 23},
|
||||
{"actual_parent_owned_hook", hook, "", 0},
|
||||
{"actual_hook_before_subshell_counter_initialization", hook, "BASH_SUBSHELL=0\n", 0},
|
||||
{"legacy_child_cleanup_rejected", `trap 'rm -f "${report_file}"' EXIT`, "", 23},
|
||||
{"legacy_counter_guard_rejected_before_initialization", `trap 'if [ "$BASH_SUBSHELL" -eq 0 ]; then rm -f "${report_file}"; fi' EXIT`, "BASH_SUBSHELL=0\n", 23},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "report")
|
||||
@@ -5304,7 +5306,7 @@ func TestNpmAuditReportCleanupRemainsParentOwned(t *testing.T) {
|
||||
// the early-signal window, without timing a background process.
|
||||
// It must retain the report there, and still remove it at parent exit.
|
||||
script := "report_file=\"$REPORT_FIXTURE\"\nprintf retained >\"$report_file\"\n" + tc.hook +
|
||||
"\n(\n" + tc.hook + "\n)\ntest -f \"$report_file\" || exit 23\n"
|
||||
"\n(\n" + tc.hook + "\n" + tc.childSetup + ")\ntest -f \"$report_file\" || exit 23\n"
|
||||
cmd := exec.Command("bash", "-c", script)
|
||||
cmd.Env = append(os.Environ(), "REPORT_FIXTURE="+path)
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
@@ -817,8 +817,8 @@ func TestPrereleaseUpdateCopyUsesPreviewFraming(t *testing.T) {
|
||||
t.Fatalf("read pulse-auto-update.sh: %v", err)
|
||||
}
|
||||
autoUpdateScript := string(autoUpdate)
|
||||
if !strings.Contains(autoUpdateScript, `Prerelease channel detected; unattended auto-updates run only on stable`) {
|
||||
t.Fatalf("pulse-auto-update.sh missing prerelease channel log message")
|
||||
if !strings.Contains(autoUpdateScript, `Non-stable channel detected; unattended auto-updates run only on stable`) {
|
||||
t.Fatalf("pulse-auto-update.sh missing non-stable channel log message")
|
||||
}
|
||||
if strings.Contains(autoUpdateScript, `RC channel detected; unattended auto-updates run only on stable`) {
|
||||
t.Fatalf("pulse-auto-update.sh preserved stale release-candidate channel log message")
|
||||
|
||||
@@ -231,9 +231,11 @@ else:
|
||||
|
||||
report_file="$(mktemp)"
|
||||
# A watchdog stopped before its subshell initialises can run this inherited
|
||||
# EXIT trap. Only the invoking shell owns cleanup: the captured verdict must
|
||||
# remain available after the watchdog is stopped, on Bash 3.2 as well as 5.x.
|
||||
trap 'if [ "$BASH_SUBSHELL" -eq 0 ]; then rm -f "${report_file}"; fi' EXIT
|
||||
# EXIT trap with the parent's BASH_SUBSHELL value. $$ is inherited too. Ask a
|
||||
# direct child shell for its actual parent PID instead of trusting that counter:
|
||||
# only the original invoking process may remove the captured verdict. PPID in
|
||||
# /bin/sh also works on native Bash 3.2, where BASHPID is unavailable.
|
||||
trap 'if /bin/sh -c "[ \"\$PPID\" = \"\$1\" ]" sh "$$"; then rm -f "${report_file}"; fi' EXIT
|
||||
|
||||
attempt=1
|
||||
delay="${DELAY}"
|
||||
|
||||
Reference in New Issue
Block a user