From 8fde10c7438b01353e7571ed85cf360fba641c59 Mon Sep 17 00:00:00 2001 From: Howard Enos Date: Sun, 21 Jun 2026 20:57:10 -0700 Subject: [PATCH] sync: auto-sync from HOWARD-HOME at 2026-06-21 20:56:44 Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-06-21 20:56:44 --- .claude/scripts/guruscan-agent-test.sh | 55 ++++++- ...-21-howard-screenconnect-skill-finalize.md | 137 ++++++++++++++++++ 2 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 session-logs/2026-06/2026-06-21-howard-screenconnect-skill-finalize.md diff --git a/.claude/scripts/guruscan-agent-test.sh b/.claude/scripts/guruscan-agent-test.sh index 55c4ba23..66183c3a 100644 --- a/.claude/scripts/guruscan-agent-test.sh +++ b/.claude/scripts/guruscan-agent-test.sh @@ -23,6 +23,7 @@ set -u TARGET="${1:-}" PHASE="${2:-all}" +SCANNER_ARG="${3:-}" if [ -z "$TARGET" ]; then echo "[ERROR] Usage: bash guruscan-agent-test.sh " >&2 @@ -530,11 +531,63 @@ PS post_alert "[RMM] GuruScan verify-each on $AGENT_HOST complete - see per-engine detect/remove matrix" } +# =========================================================================== +# scan-one : fully automated single-scanner test. ONE command does it +# all, hands-off: restore the malware-lab samples, clear stale cleanup state, +# run the scanner DETACHED + NO-CAP via the same path production uses, then +# collect and report (detections, removal, results.json, reboot-cleanup task). +# Mirrors how we proved Emsisoft -- no manual stitching. +# =========================================================================== +phase_scan_one() { + local scanner="${SCANNER_ARG:-HitmanPro}" + echo ""; echo "=== PHASE: scan-one ($scanner) - automated, detached, no-cap ===" + + # Setup: free the mutex (kill any prior GuruScan run + scanner procs), clear + # stale cleanup task/state, and restore the malware lab samples. + local sf="$WORK_DIR/one_setup.ps1" + cat > "$sf" <<'PS' +$ErrorActionPreference='Continue' +Get-ScheduledTask -EA SilentlyContinue | Where-Object { $_.TaskName -like 'GuruScan-*' } | ForEach-Object { try{ Stop-ScheduledTask -TaskName $_.TaskName -EA SilentlyContinue }catch{}; try{ Unregister-ScheduledTask -TaskName $_.TaskName -Confirm:$false -EA SilentlyContinue }catch{} } +foreach($n in @('a2cmd','HitmanPro_x64','rkill','EmsisoftCommandlineScanner64')){ Get-Process -Name $n -EA SilentlyContinue | Stop-Process -Force -EA SilentlyContinue } +Start-Sleep 3 +Get-ScheduledTask -TaskName 'GuruRMM-ScannerCleanup' -EA SilentlyContinue | Unregister-ScheduledTask -Confirm:$false -EA SilentlyContinue +Remove-Item 'C:\GuruScan\cleanup-state.json' -Force -EA SilentlyContinue +$zip = Get-ChildItem 'C:\Users\Owner\Downloads' -Filter '*.zip' -EA SilentlyContinue | Where-Object { $_.Name -match 'malware' } | Select-Object -First 1 +if($zip){ Expand-Archive -Path $zip.FullName -DestinationPath 'C:\Users\Owner\Desktop' -Force -EA SilentlyContinue } +$n=(Get-ChildItem 'C:\Users\Owner\Desktop\malware-samples-master' -Recurse -File -EA SilentlyContinue).Count +Set-Content 'C:\GuruScan\_one_before.txt' $n +Write-Output ("setup done - malware samples present: $n") +PS + run_ps "$sf" 240 70 "setup" || { echo "[ERROR] setup failed"; return 1; } + + # Run the scanner the production way: detached scheduled task, unlimited time. + gs_launch_detached "-Scanners $scanner -Headless" "one" || { echo "[ERROR] launch failed"; return 1; } + gs_wait_detached "one" "$scanner" || true + + # Report the outcome (parsed from what GuruScan actually wrote). + local rf="$WORK_DIR/one_report.ps1" + cat > "$rf" <<'PS' +$ErrorActionPreference='Continue' +$before=Get-Content 'C:\GuruScan\_one_before.txt' -EA SilentlyContinue +$after=(Get-ChildItem 'C:\Users\Owner\Desktop\malware-samples-master' -Recurse -File -EA SilentlyContinue).Count +Write-Output ("samples: before=$before after=$after REMOVED=" + ([int]$before-[int]$after)) +$d=Get-ChildItem 'C:\ScanLogs' -Directory -EA SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 +if($d -and (Test-Path (Join-Path $d.FullName 'results.json'))){ $r=Get-Content (Join-Path $d.FullName 'results.json') -Raw|ConvertFrom-Json + Write-Output ('results.json -> total_threats=' + $r.total_threats + ' reboot_required=' + $r.reboot_required) + $r.scanners|ForEach-Object{ Write-Output (' ' + $_.name + ': exit=' + $_.exit_code + ' threats=' + $_.threats_found) } } +$ct=Get-ScheduledTask -TaskName 'GuruRMM-ScannerCleanup' -EA SilentlyContinue +if($ct){ Write-Output ('reboot-cleanup task -> REGISTERED (state=' + $ct.State + ', logon-delay=' + $ct.Triggers[0].Delay + ')') } else { Write-Output 'reboot-cleanup task -> NOT registered' } +PS + run_ps "$rf" 60 24 "report" || true + post_alert "[RMM] GuruScan automated scan-one ($scanner) complete on $AGENT_HOST" +} + case "$PHASE" in prep) phase_prep ;; scan) phase_scan ;; + scan-one) phase_scan_one ;; collect) phase_collect ;; verify-each) phase_verify_each ;; all) phase_prep && phase_scan && phase_collect ;; - *) echo "[ERROR] Unknown phase '$PHASE' (prep|scan|collect|verify-each|all)" >&2; exit 1 ;; + *) echo "[ERROR] Unknown phase '$PHASE' (prep|scan|scan-one |collect|verify-each|all)" >&2; exit 1 ;; esac diff --git a/session-logs/2026-06/2026-06-21-howard-screenconnect-skill-finalize.md b/session-logs/2026-06/2026-06-21-howard-screenconnect-skill-finalize.md new file mode 100644 index 00000000..7543a777 --- /dev/null +++ b/session-logs/2026-06/2026-06-21-howard-screenconnect-skill-finalize.md @@ -0,0 +1,137 @@ +## User +- **User:** Howard Enos (howard) +- **Machine:** Howard-Home +- **Role:** tech + +## Session Summary + +Finalized and validated the new `screenconnect` skill (ConnectWise Control / ScreenConnect +RESTful API Manager extension), built earlier in the parent session the same way as the +`bitdefender` skill: gather the full API surface, then test until everything works. This +session picked up at the validation/finalization stage. + +Ran the `skill-creator` skill against the screenconnect skill to confirm rule compliance. +All checks passed except one: the SKILL.md did not document the errorlog behavior (the +skill-creator mandate wants an explicit line, even though the CLI code already logs genuine +failures and skips expected conditions). Added an "Error logging" section to SKILL.md. +Cleaned up stale "pending unlock" help text in `sc.py` (the control methods are verified +live now, so the text was inaccurate). Re-ran the selftest: 12/12 passing. Committed the +skill finalization to the parent repo (`3a1edb7`). + +Captured the RMM-integration vision as **Feature 7** in the GuruRMM `RMM_THOUGHTS.md`: +when a device is flagged for ScreenConnect in the RMM, the RMM builds the correct +parameterized access installer from the device's client/site/tags, pushes it via the agent +so the SC client self-tags into the right Company/Site/Tag, then controls the session and +keeps the SC custom properties in sync. Status: Raw; needs Mike's go before building. + +Per Howard's choice, did NOT tear down the SC test on RMM-TEST-MACHINE — kept the +ScreenConnect Client service running and the test session live as a fixture for the +upcoming RMM-integration phase. Only removed the two stray test temp files +(`sc-control-test.txt`, `sc-access.msi`) via an RMM PowerShell command; posted the required +`[RMM]` write-op alert to #dev-alerts. + +Fixed two flags raised at the end of the skill work, both in the `guru-rmm` submodule's +`RMM_THOUGHTS.md`: (1) the Feature 7 edit was uncommitted in a detached-HEAD submodule +full of other sessions' in-progress files; (2) the file contained 3 raw NUL bytes that +made it read as binary to grep. Delivered both fixes on an isolated branch off origin/main +(`docs/rmm-thoughts-sc-feature7`, commit `4f10149`, pushed) without disturbing the parent +gitlink or the other 8 dirty files. The NUL bytes turned out to be intentional `^@` +examples inside a jsonb-bug writeup, so they were escaped to readable `\x00` rather than +deleted (meaning preserved). + +## Key Decisions + +- Ran skill-creator as a validation pass against an existing skill (not to create a new + one) — used its Quality Checklist + mandatory-errorlog rule as the rubric. +- Kept SC installed on RMM-TEST-MACHINE rather than tearing down: the upcoming + GuruRMM<->ScreenConnect integration (Feature 7) needs exactly that fixture. Only removed + test litter (marker file + downloaded msi). +- Delivered the submodule doc change via a branch off origin/main rather than committing in + the detached-HEAD checkout — committing on detached HEAD amid 8 other dirty files risks + orphaning their work. Branch handed off for Mike to merge (the standard submodule + workflow: merge = deploy). +- Reset the redundant uncommitted Feature 7 out of the shared dirty checkout (set the live + file back to origin/main) so it cannot double-merge once the branch lands. The other 8 + modified files were left untouched. +- Escaped the NUL bytes to `\x00` instead of stripping them: cat -v revealed they were + deliberate illustrative `^@` characters in a Postgres jsonb-NUL bug writeup; stripping + would have turned "contains a `^@` (NUL)" into empty backticks and lost the point. + +## Problems Encountered + +- skill-creator validation surfaced one deviation (SKILL.md not documenting errorlog + behavior) -> added an Error-logging section to SKILL.md. +- RMM command dispatch failed twice with "invalid escape" / JSON parse errors because the + PowerShell payload used Windows backslash paths inside the JSON body -> switched to + forward-slash paths (PowerShell accepts them), command then ran clean. +- NUL-byte replacement repeatedly no-op'd: the Bash tool's heredoc was collapsing `\x00` + escapes before Python's string parser saw them, so both the search and replacement + became a bare NUL (replace = no-op). Confirmed via an isolated in-memory test, then fixed + by writing a real script file (`.nulfix.py`) that builds the bytes numerically + (`bytes([0])` and `bytes([0x5C,0x78,0x30,0x30])`) with no backslash escapes in source. +- `git worktree remove` failed with "Permission denied" (Windows file lock) -> force-removed + the directory with PowerShell `Remove-Item -Recurse -Force`, then `git worktree prune`. + Important because the worktree lived under C:\claudetools\ and would have been swept by + /save's `git add -A`. + +## Configuration Changes + +Modified / committed (parent repo, commit `3a1edb7`): +- `.claude/skills/screenconnect/scripts/sc.py` — removed stale "pending unlock" help text. + +Modified earlier in parent session, already committed before this session: +- `.claude/skills/screenconnect/SKILL.md` (incl. the Error-logging section added this + session), `scripts/sc_client.py`, `scripts/selftest.py`, `references/api-reference.md`. + +guru-rmm submodule (branch `docs/rmm-thoughts-sc-feature7`, commit `4f10149`, pushed): +- `docs/RMM_THOUGHTS.md` — added Feature 7 (ScreenConnect parameterized deploy + control) + and index line; escaped 3 raw NUL bytes to `\x00`. + +Removed (scratch): `C:\claudetools\.nulfix.py`, `C:\claudetools\.nultest.md`, +worktree `C:\claudetools\.gr-wt-feature7`. + +## Credentials & Secrets + +No new credentials. The screenconnect skill loads its API secret from the SOPS vault +`msp-tools/screenconnect.sops.yaml` field `credentials.api_secret` (never hardcoded); +auth is the `CTRLAuthHeader` (raw secret, no "Basic" prefix) + `Origin` header. + +## Infrastructure & Servers + +- ScreenConnect instance: `https://computerguru.screenconnect.com`; RESTful API Manager + extension GUID `2d558935-686a-4bd0-9991-07539f5fe749`. +- RMM-TEST-MACHINE active agent id `99d6d692-99e0-4359-9f9c-f43be89f49e5` (a stale + re-enrollment `7d3456f5-...` also exists; last_seen distinguishes them). SC client + service on it: `ScreenConnect Client (1912bf3444b41a08)` = Running (kept as fixture). +- SC test session: `a5d867ab-cbf2-4b00-99a0-80b082ec5ddd` (kept live). +- GuruRMM API `http://172.16.3.30:3001`; Gitea remote + `https://git.azcomputerguru.com/azcomputerguru/gururmm.git`. + +## Commands & Outputs + +- Selftest: `CLAUDETOOLS_ROOT=C:/claudetools python scripts/selftest.py` -> 12/12 passed. +- RMM cleanup dispatch (forward-slash paths) -> command `d935cd06-...`, completed exit 0: + `REMOVED: C:/Windows/Temp/sc-control-test.txt, C:/Windows/Temp/sc-access.msi` / + `SC SERVICE KEPT: ScreenConnect Client (1912bf3444b41a08) = Running`. +- NUL fix verify: source NULs 3 -> dst NULs 0, 3 literal `\x00` tokens, spans read + "contains a `\x00` (NUL)", "forbids `\x00`", "grep for `\x00`". + +## Pending / Incomplete Tasks + +- Feature 7 branch `docs/rmm-thoughts-sc-feature7` awaits Mike's merge into gururmm main + (the branch also escapes the 3 NUL bytes; merging fixes them in origin/main). +- GuruRMM<->ScreenConnect integration itself (Feature 7) — needs Mike's go before building. +- SC GetSessions full-fleet inventory gap: the RESTful API Manager extension does not expose + GetSessions/GetAllSessions/GetSessionGroups ("web method does not exist"); needs Mike to + update the extension. Tracked via coord msg 60d9e876. By-name lookup works post-install. +- SC test fixture remains on RMM-TEST-MACHINE intentionally (client service + session live). + +## Reference Information + +- Parent commit: `3a1edb7` (screenconnect finalize). +- Submodule branch/commit: `docs/rmm-thoughts-sc-feature7` / `4f10149` (off origin/main + `1dce66d`). +- RMM write-op alert message_id `1518461799618449428` (#dev-alerts). +- Skill path: `.claude/skills/screenconnect/` (SKILL.md, scripts/{sc.py,sc_client.py, + selftest.py}, references/api-reference.md). +- ScreenConnect docs: https://docs.connectwise.com/ScreenConnect_Documentation