sync: auto-sync from HOWARD-HOME at 2026-06-21 19:32:30
Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-06-21 19:32:30
This commit is contained in:
@@ -119,6 +119,18 @@ def cmd_session(client, args):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_build_installer(client, args):
|
||||||
|
url = client.build_installer_url(
|
||||||
|
platform=args.platform, name=args.name,
|
||||||
|
company=args.company, site=args.site, tag=args.tag,
|
||||||
|
)
|
||||||
|
if args.json:
|
||||||
|
_emit({"installer_url": url, "platform": args.platform}, True)
|
||||||
|
return 0
|
||||||
|
print(url)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def cmd_send_command(client, args):
|
def cmd_send_command(client, args):
|
||||||
if not _gated(f"run command on session {args.session} : {args.run_command!r}", args.confirm):
|
if not _gated(f"run command on session {args.session} : {args.run_command!r}", args.confirm):
|
||||||
return 3
|
return 3
|
||||||
@@ -183,9 +195,19 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
sp = sub.add_parser("sessions", help="List sessions by Name (verified).", parents=[common])
|
sp = sub.add_parser("sessions", help="List sessions by Name (verified).", parents=[common])
|
||||||
sp.add_argument("--name", default="", help="Session Name filter (blank = unattended).")
|
sp.add_argument("--name", default="", help="Session Name filter (blank = unattended).")
|
||||||
|
|
||||||
sp = sub.add_parser("session", help="Session detail (pending unlock).", parents=[common])
|
sp = sub.add_parser("session", help="Session detail.", parents=[common])
|
||||||
sp.add_argument("session_id")
|
sp.add_argument("session_id")
|
||||||
|
|
||||||
|
sp = sub.add_parser("build-installer",
|
||||||
|
help="Build a parameterized Access installer URL.",
|
||||||
|
parents=[common])
|
||||||
|
sp.add_argument("--platform", default="msi",
|
||||||
|
help="msi | exe | pkg | deb | rpm | sh (default msi).")
|
||||||
|
sp.add_argument("--name", help="Session name (defaults to machine name if omitted).")
|
||||||
|
sp.add_argument("--company", help="CP1 = Company.")
|
||||||
|
sp.add_argument("--site", help="CP2 = Site.")
|
||||||
|
sp.add_argument("--tag", help="CP3 = Tag.")
|
||||||
|
|
||||||
sp = sub.add_parser("send-command", help="Run a backstage command (gated; pending unlock).",
|
sp = sub.add_parser("send-command", help="Run a backstage command (gated; pending unlock).",
|
||||||
parents=[common])
|
parents=[common])
|
||||||
sp.add_argument("--session", required=True)
|
sp.add_argument("--session", required=True)
|
||||||
@@ -219,6 +241,7 @@ HANDLERS = {
|
|||||||
"status": cmd_status,
|
"status": cmd_status,
|
||||||
"sessions": cmd_sessions,
|
"sessions": cmd_sessions,
|
||||||
"session": cmd_session,
|
"session": cmd_session,
|
||||||
|
"build-installer": cmd_build_installer,
|
||||||
"send-command": cmd_send_command,
|
"send-command": cmd_send_command,
|
||||||
"send-message": cmd_send_message,
|
"send-message": cmd_send_message,
|
||||||
"set-properties": cmd_set_properties,
|
"set-properties": cmd_set_properties,
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import urllib.error
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import httpx # type: ignore
|
import httpx # type: ignore
|
||||||
@@ -234,6 +235,42 @@ class ScreenConnectClient:
|
|||||||
"""Call any RESTful API Manager method directly (power use / probing)."""
|
"""Call any RESTful API Manager method directly (power use / probing)."""
|
||||||
return self.call(method, body, http_method=http_method)
|
return self.call(method, body, http_method=http_method)
|
||||||
|
|
||||||
|
# ======================================================================
|
||||||
|
# INSTALLER BUILDER (no API call — constructs the parameterized access
|
||||||
|
# installer URL so a device self-tags into the right Company/Site/Tag).
|
||||||
|
# ======================================================================
|
||||||
|
def build_installer_url(
|
||||||
|
self,
|
||||||
|
platform: str = "msi",
|
||||||
|
name: Optional[str] = None,
|
||||||
|
company: Optional[str] = None,
|
||||||
|
site: Optional[str] = None,
|
||||||
|
tag: Optional[str] = None,
|
||||||
|
extra_props: Optional[list] = None,
|
||||||
|
) -> str:
|
||||||
|
"""Build a parameterized ScreenConnect ACCESS installer URL.
|
||||||
|
|
||||||
|
VERIFIED LIVE 2026-06-22: the cloud instance serves a pre-keyed installer at
|
||||||
|
/Bin/ScreenConnect.ClientSetup.<platform>?e=Access&y=Guest ; append `t=`
|
||||||
|
(session name) and repeated `c=` for the custom properties (order =
|
||||||
|
CP1=Company, CP2=Site, CP3=Tag, ... up to 8). The installed agent self-tags
|
||||||
|
with these, so it lands under the matching session-group filters.
|
||||||
|
|
||||||
|
platform: msi | exe | pkg | deb | rpm | sh
|
||||||
|
"""
|
||||||
|
props = [company or "", site or "", tag or ""]
|
||||||
|
if extra_props:
|
||||||
|
props.extend(extra_props)
|
||||||
|
params = ["e=Access", "y=Guest"]
|
||||||
|
if name:
|
||||||
|
params.append("t=" + quote(name, safe=""))
|
||||||
|
for p in props:
|
||||||
|
params.append("c=" + quote(p, safe=""))
|
||||||
|
return (
|
||||||
|
f"{self.base_url}/Bin/ScreenConnect.ClientSetup.{platform}?"
|
||||||
|
+ "&".join(params)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
"""Minimal self-check: load secret (no network call)."""
|
"""Minimal self-check: load secret (no network call)."""
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ Categories (the `[type]` tag): _(none)_ = skill/command execution failure ·
|
|||||||
|
|
||||||
<!-- Append entries below this line -->
|
<!-- Append entries below this line -->
|
||||||
|
|
||||||
|
2026-06-22 | Howard-Home | guruscan/GuruScan.psm1 | HitmanPro exit-code misparse: real HitmanPro returns bitmask (exit 5 = 36 threats quarantined + reboot required) but code mapped only {1,2}; reported total_threats=0 reboot_required=False on a real 36-threat removal, so reboot-cleanup lifecycle never fired. Fixed: bit0=threats, bit4=reboot [ctx: host=DESKTOP-MS42HNC engine=HitmanPro-3.8.50]
|
||||||
|
|
||||||
|
2026-06-22 | Howard-Home | screenconnect/browser-automation | [friction] HOWARD-HOME: ff.py Firefox daemon won't launch (port 9333 dead, silent 60s timeout) AND cdp.py ModuleNotFoundError 'websocket' (websocket-client not installed) -> can't drive the SC website to build the access installer. Fix: pip install websocket-client; verify playwright firefox installed for ff.py. [ctx: machine=HOWARD-HOME tool=ff.py,cdp.py]
|
||||||
|
|
||||||
2026-06-22 | Howard-Home | deploy/cpanel | [friction] cPanel deploy served STALE files post-upload (opcache) - page showed only top buttons + api.php 403; fix: opcache_reset via one-off _oc.php hit through external-IP origin path (127.0.0.1 vhost 404s) then browser hard-reload
|
2026-06-22 | Howard-Home | deploy/cpanel | [friction] cPanel deploy served STALE files post-upload (opcache) - page showed only top buttons + api.php 403; fix: opcache_reset via one-off _oc.php hit through external-IP origin path (127.0.0.1 vhost 404s) then browser hard-reload
|
||||||
|
|
||||||
2026-06-22 | Howard-Home | ssh/windows | [friction] native Windows OpenSSH (System32 ssh.exe) SSH_ASKPASS fails 'CreateProcessW error:193' on a .sh askpass; for non-interactive password auth use MSYS bare 'ssh' (Git-for-Windows) which execs the shell askpass (as pfsense-ssh.sh does)
|
2026-06-22 | Howard-Home | ssh/windows | [friction] native Windows OpenSSH (System32 ssh.exe) SSH_ASKPASS fails 'CreateProcessW error:193' on a .sh askpass; for non-interactive password auth use MSYS bare 'ssh' (Git-for-Windows) which execs the shell askpass (as pfsense-ssh.sh does)
|
||||||
|
|||||||
@@ -151,3 +151,41 @@ buttons at the top." Diagnosed and resolved.
|
|||||||
### Net state
|
### Net state
|
||||||
security.azcomputerguru.com is LIVE and working (new scoring wizard + larger UI + dual export + fixed backend),
|
security.azcomputerguru.com is LIVE and working (new scoring wizard + larger UI + dual export + fixed backend),
|
||||||
verified server-side and confirmed loading in Howard's browser. Remaining: #1 RMM prefill (deferred, infra), FR-1 portal (deferred, auth decision).
|
verified server-side and confirmed loading in Howard's browser. Remaining: #1 RMM prefill (deferred, infra), FR-1 portal (deferred, auth decision).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Update: 19:31 PT — post-deploy live fixes (wrong-customer lookup bug + New/clear button)
|
||||||
|
|
||||||
|
Two issues Howard found while using the live site, both fixed + deployed + verified.
|
||||||
|
|
||||||
|
### Bug: phone lookup returned the WRONG customer
|
||||||
|
- Symptom: entering `5205851310` pulled up "Marjolaine Deslauriers" (an unrelated record).
|
||||||
|
- Root cause: Syncro's `GET /customers?phone=<n>` param does NOT filter — it returns the full customer
|
||||||
|
list (5079 across 51 pages); the code took `customers[0]` (Marjolaine, phone 5203310273). Reproduced
|
||||||
|
via the Syncro API (vault `msp-tools/syncro-howard` cred field `credentials.credential`): `?phone=` →
|
||||||
|
count 100 unfiltered; `?query=5205851310` → count 0 (number not in Syncro); `?query=5208880000`
|
||||||
|
(Insty-Prints' real number) → count 1 (so `?query=` DOES search phone by digits).
|
||||||
|
- Fix (api.php lookup): use `?query=<digits>` and VERIFY a candidate's phone digit-tail matches the
|
||||||
|
entered digits before prefilling (handles leading-1 / 7-vs-10 digits). No match → return a clear
|
||||||
|
"No customer matches that phone number" + suggestions, and prefill NOTHING. index.php doLookup updated
|
||||||
|
to show the message/suggestions and not prefill on no-match. Format was never the issue (input is
|
||||||
|
digit-normalized, so 5205851310 == (520) 585-1310).
|
||||||
|
- Verified live (web POST via the external-IP path): `5205851310` → no-match; `5208880000` → Insty-Prints
|
||||||
|
full prefill; `(520) 585-1310` → no-match (same as digits).
|
||||||
|
|
||||||
|
### Feature: "+ New" button (Howard's request)
|
||||||
|
- After loading a client, `state.id` persisted, so a fresh lookup + save could overwrite the previous
|
||||||
|
client's record. Added a top-bar **+ New** button -> `clearAll()` resets id/data/consent/sec/view +
|
||||||
|
URL + topbar, so the next save INSERTs a new record. Confirms before clearing if work is in progress.
|
||||||
|
|
||||||
|
### Deploy mechanics (each fix)
|
||||||
|
- Upload changed file(s) to `.new` -> `php -l` -> atomic `mv` -> flush opcache via a one-off `_oc.php`
|
||||||
|
hit through `--resolve ...:443:72.194.62.5` (external IP; 127.0.0.1 vhost 404s) -> verify served HTML.
|
||||||
|
Backups `*.bak-<ts>` in the docroot. Browser needs Ctrl+Shift+R to pick up index.php changes.
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- `ef03b4e` lookup phone-match fix; `a9c85a7` + New button. claudetools pins advanced (`c462f50`, `5b44a3e`).
|
||||||
|
|
||||||
|
### Net
|
||||||
|
Phone search works correctly (verified by Howard), wrong-customer risk eliminated, and the + New button
|
||||||
|
prevents cross-record contamination. Site fully functional.
|
||||||
|
|||||||
Reference in New Issue
Block a user