Skip to content

feature: NinjaRMM runaway script-log purge tool#102

Open
AlrightLad wants to merge 4 commits into
mainfrom
feature/ninjarmm-runaway-log-purge
Open

feature: NinjaRMM runaway script-log purge tool#102
AlrightLad wants to merge 4 commits into
mainfrom
feature/ninjarmm-runaway-log-purge

Conversation

@AlrightLad

Copy link
Copy Markdown

## What this does

Adds a NinjaRMM remediation script that reclaims disk consumed by runaway custom-script output transcripts under C:\\ProgramData\\NinjaRMMAgent\\scripting. Scans for \*-output.txt files over a configurable size threshold (default 500MB), kills the writing process if one is still active, and hard-deletes the file.

## Why

Custom scripts that hit an interactive branch in a console-less context (e.g. Read-Host under NonInteractive) can spin a loop that writes into the script's output transcript until the disk fills — observed at 20GB+ on a single file. This tool is the cleanup/remediation capability; the underlying interactive-branch bug in the offending script(s) is tracked separately.

## Design notes

- **Dual-mode**: runs identically interactive or via RMM ($env:RMM -eq "1"). No required user input — Description defaults silently, ThresholdMB is optional. No interactive prompt by design (an interactive prompt is the exact failure mode this tool cleans up after).

- **Self-protecting**: excludes its own PID from process kills ($\_.ProcessId -ne $myPid) and anchors the writer match on both the scripting path and the gen-id, so it cannot kill its own run. A locked file is treated as a counted failure (EXIT 1), not a silent success.

- **64-bit relaunch guard**: NinjaRMM launches 32-bit PowerShell by default; the script relaunches under the native host so behavior is identical across invocation paths.

- **Exit codes**: 0 = completed (no failures), 1 = one or more deletion failures, 2 = scripting dir not found.

## Testing performed

Smoke-tested both modes on a workstation (LT-5226):

- Interactive: scanned, no files over threshold, EXIT 0.

- RMM simulation ($env:RMM="1", ThresholdMB=500): same path, EXIT 0.

PSScriptAnalyzer: clean except PSAvoidUsingWriteHost (known repo convention — output goes to Start-Transcript/RMM console capture).

## RMM compatibility

Reads $env:RMM, $env:Description, $env:ThresholdMB per repo input-handling convention. Logs via Start-Transcript to $env:RMMScriptPath\\logs\\ (RMM) or $env:WINDIR\\logs\\ (interactive/fallback).

Reclaims disk from runaway custom-script output transcripts under the
NinjaRMM agent scripting directory. Scans for *-output.txt over a
configurable threshold (default 500MB), kills the writing process if
still active, and hard-deletes the file.

Dual-mode (interactive + RMM), runs silently with no required input.
Self-protecting: excludes its own PID from process kills and treats a
locked file as a counted failure rather than silent success. 64-bit
relaunch guard for NinjaRMM's 32-bit default host.

Tested both modes on a workstation: EXIT 0 nothing-to-clean.
@AlrightLad AlrightLad requested a review from Gumbees as a code owner June 24, 2026 20:33
@AlrightLad AlrightLad added type:enhancement New work that adds or improves capability (Halo: Enhancement) category:feature Net-new capability the customer/operator didn't have labels Jun 24, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization's overage spend limit has been reached.

Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.

Once credits are available, push a new commit or reopen this pull request to trigger a review.

The initial command-line substring match failed to identify the writer
on a live runaway (multi-process cmd/powershell tree, GUID-style
customscript_src_<guid> filename). Replaced with Windows Restart Manager
(rstrtmgr.dll) interop to query the exact PIDs holding each oversized
output file, with a hard guard that never terminates the NinjaRMM agent
or this script's own process. Added 3x delete retry for non-instant
handle release. Fixes $hpid parse error in kill-failure path.

Validated destructive path on a live 9.3GB runaway: holders identified,
terminated, file deleted, EXIT 0.
The threshold-only scan missed freshly-respawned runaways still small at
scan time (confirmed: tool reported nothing over 500MB while a NinjaRMM
customscript spun a Read-Host NonInteractive loop into its output). Added
a 3s growth-sampling pass: a file is a runaway if over ThresholdMB, OR
growing while held by a NinjaRMM customscript process, OR its holder has
run past RuntimeCeilingMin (default 30, backstop). Holder lookup factored
into Get-ScriptHolder with proper error handling. Restart Manager kill,
agent self-protection, and 3x delete retry unchanged.
@AlrightLad

Copy link
Copy Markdown
Author

Added growth-based detection. Threshold-only scan missed runaways still small at scan time. Tool now flags a file as runaway on any of: over ThresholdMB, actively growing while held by a NinjaRMM customscript process (3s sample), or holder past RuntimeCeilingMin (default 30, backstop). New optional RMM var: RuntimeCeilingMin.

… output

Threshold-only scan missed freshly-respawned runaways still small at scan
time, and the tool false-failed trying to delete its own live output file
when run via NinjaRMM. Now: snapshots sizes, samples 3s growth, flags a
file as runaway if over ThresholdMB OR growing while held by a NinjaRMM
customscript process OR holder past RuntimeCeilingMin (default 30). Captures
own PID+parent up front and excludes files they hold open, so the tool never
targets its own transcript. Restart Manager kill, agent self-protection,
3x delete retry unchanged.

Validated via NinjaRMM against a live 11.48GB runaway: real file cleaned;
prior false-positive on own output eliminated.
@AlrightLad

Copy link
Copy Markdown
Author

Added growth detection + self-output exclusion. Tool now flags a file as runaway on any of: over ThresholdMB, growing while held by a NinjaRMM customscript process (3s sample), or holder past RuntimeCeilingMin (default 30, backstop). Also captures its own PID+parent and excludes files they hold open, fixing a false-positive FAILURE where the tool tried to delete its own live output transcript when run via NinjaRMM. New optional RMM var: RuntimeCeilingMin. Validated via NinjaRMM against a live 11.48GB runaway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

category:feature Net-new capability the customer/operator didn't have type:enhancement New work that adds or improves capability (Halo: Enhancement)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant