Commit Graph

291 Commits

Author SHA1 Message Date
benjamin.baker
c3875b0eec Add a standalone endpoint agent uninstaller
Reverses everything the installer and site-agent generator create: stops and deletes the service, kills agent/tray processes, removes the tray Run key and the HKLM\SOFTWARE\GE\ShopDB credential hive in both registry views, drops the Defender path exclusion, and deletes the install directory and ProgramData tree. Every step is best-effort and idempotent so it is safe to re-run and safe on a partially installed host.
2026-08-30 14:41:33 -04:00
benjamin.baker
5420ac432d Serve the dev/test stack over HTTPS with a mounted self-signed cert
Terminate TLS in gunicorn using a host-generated cert mounted read-only under ./certs, so the browser sees a secure context. Secure-context-only web APIs (navigator.clipboard and others) refuse to run over plain http on a non-localhost origin, which the FQDN-accessed test box hits. Healthcheck now probes https with -k to match the TLS listener. A site swaps in its own cert or fronts this with its own IIS/nginx proxy.
2026-08-30 14:41:22 -04:00
benjamin.baker
bd8eacfe05 Agent: execute run_script and update_app delivery pushes
Bumps the agent to 1.4.1. run_script fetches a ShopDB-hosted script by digest and runs it in the script's configured context (LocalSystem or the interactive logged-on user), replacing the local scriptpaths allowlist; user-context runs wait for exit and report the real exit code instead of fire-and-forget. update_app resolves an app-level or version-level install source, fetches the pinned bytes, verifies sha256, and runs silently. Both stage into a fixed Defender-excludable folder (C:\ProgramData\ShopDB\EndpointAgent) instead of the LocalSystem temp path. Adds the DeliveryClaim contract for the claim payload.
2026-08-30 14:41:10 -04:00
benjamin.baker
188296ef58 Add endpoint software delivery: run_script and update_app push
Adds the two delivery actions to the endpoints command allowlist, each gated on a new endpoints.deploy permission ON TOP OF endpoints.command so pushing software is a heavier grant than a routine toggle. Enqueue validates update_app appversionid ownership and installer presence before queueing. New collector-authenticated download routes let the agent fetch installer packages and script bodies by digest rather than pre-staging them or sending bodies over the command wire. Wires the scriptsApi client, the siteagent generator routes, and a temporary dev-only Delivery test bench (view/route/nav) for exercising the path while the production UI is built.
2026-08-30 14:40:51 -04:00
benjamin.baker
0c2b816a46 Add installer/script delivery metadata to the core software model
AppVersion gains sha256 and silentargs so the endpoint update_app push can pin exact installer bytes and carry per-version silent switches; Application gains installersha256 for the app-level install source. New ShopDB-hosted script library (Script model plus scripts API) backs run_script delivery, with the body fetched by digest on push rather than pre-staged or sent over the command wire. Two idempotent migrations with real downgrades. Script added to the shopdb.api contract surface for plugin use.
2026-08-30 14:39:58 -04:00
benjamin.baker
dc2f4266bc Self-heal endpoint command queue wedged by a dead in-flight command
The command queue is at-most-one-in-flight, but nothing ever expired a stuck command. When an agent claimed a command (RUNNING) then died before reporting a result - killed, uninstalled, or offline mid-flight - the row stayed in-flight forever and every operator action for that endpoint returned 409 'already in flight'. Reap in-flight rows past a staleness ceiling (15m for RUNNING, 60m for PENDING) to a new STATUS_EXPIRED at enqueue time, inside the same transaction, so the queue self-heals lazily the next time someone queues work. No background job and no schema migration - status is free-text.
2026-08-30 14:32:08 -04:00
benjamin.baker
9db832d105 Harden endpoint agent command channel against request storms
A misconfigured BaseUrl (plaintext http:// against the TLS port) made the command long-poll fail at connect in milliseconds. The worker misread that fast failure as a normal idle hold-window close and re-polled with no delay, spinning into a request storm. Gate the idle-close path on the poll having stayed open at least MinIdlePollDuration, so a sub-second transport failure takes the transient backoff instead; add a MinPollInterval floor so no path can exceed a safe request rate. Also reject a non-loopback http:// BaseUrl at startup with a single clear error, turning a silent runtime flood into a fast fail. Loopback http stays valid for local dev.
2026-08-30 14:30:16 -04:00
benjamin.baker
d3a8491078 Ignore local dev scratch and delivery build output
Several days of endpoint-agent work left DB dumps, sample datasets, captured command output, generated maps, vuln scan output, merge-kit exports, and staged agent publish trees in the tree. None are product source. Ignore them so status shows only real work and they cannot be committed by accident. exports/ stays on disk for reference. agent/.gitignore also reserves dev-bin/ and _stage/ for delivery-build scratch.
2026-08-30 14:28:50 -04:00
benjamin.baker
b9f6815161 Green up
Tracking docs/openapi.json (so the app can serve the spec at runtime) makes docs/ exist on the published GitHub mirror, which defeated the skipif(not DOCS.is_dir()) guard in the docs publish/version tests: they then ran against a prose-less checkout and failed on CI. Key the skip off docs/*.md prose instead, matching the canonical published-checkout definition in scripts/check-gates-ran.sh.

Also: the OpenAPI generator emitted a trailing-slash path (/api/docs/), which a strict proxy like IIS treats as a redirecting resource a subpath-mounted client cannot follow; normalise trailing slashes in _norm_path and correct the committed spec. And allow docx (python-docx) as an undeclared import: it is used only by scripts/docs/generate_docx.py, a developer tool that never runs on a deployed site.
2026-08-30 11:59:01 -04:00
benjamin.baker
9725c47814 Persist and surface endpoint installed-software (SBOM)
The endpoint agent already reports an installed-software list (SBOM), but ingress dropped it: the collector schema had no entry for it and there was nowhere to store it, so operators could not see what is installed on a PC.

Persist it as a per-asset snapshot in a new endpointapplications table, replaced on every report (current inventory is what operators ask for, not install history). Add applications to the collector schema and KNOWN_PAYLOAD_KEYS so ingress stops discarding it, a read route for the raw collection, and a collapsed-by-default Installed software panel to surface it. appid is nullable groundwork for a future Managed Applications link; no matcher is wired yet.
2026-08-30 11:20:45 -04:00
benjamin.baker
345c7cefff Merge origin/main into wilm: reconcile endpoint agent dev with release channel
# Conflicts:
#	plugins/endpoints/api/routes.py
2026-08-30 08:46:04 -04:00
benjamin.baker
6ff7594996 Add agentless adoption tool for endpoints
Standalone one-shot enrollment tool that creates a ShopDB PC + Endpoint entry using only the endpoints collector key, reusing the existing collector adopt path. Collects identity, WMI hardware, and installed-software SBOM; requires admin. Server-side packaging route attaches to the existing endpoints blueprint; no core contract change.
2026-08-30 08:36:13 -04:00
benjamin.baker
b70cc1f6be collect installed-software SBOM from endpoint agent
The agent now reports installed applications from both Uninstall registry hives (64-bit and WOW6432Node) as {displayname, displayversion, publisher}, mirroring the Assess-Orchestrator applications collection. Registry-only, no Win32_Product/WMIC self-repair and no AppX. The server logs it as an unpersisted key until the SBOM store is built.
2026-08-30 08:36:13 -04:00
benjamin.baker
d89682fd27 log ignored agent collector keys for later UI enhancements
The endpoint agent now sends disk, bios, memory, processor, osinfo, and domain telemetry. The server does not persist these yet, so ingest logs any unpersisted payload keys with a compact preview. This makes incoming telemetry observable in the logs before any UI or schema work, so we can confirm the agent is sending it and enhance the UI later.
2026-08-30 08:36:13 -04:00
8098a5e82e Fleet bulk actions 2026-08-28 23:16:37 -04:00
09d06399d3 Fleet console gap closures: critical tile, filters, policy card, action placement 2026-08-28 23:00:37 -04:00
7e779a6829 Fleet console: triage band, activity feed, live state first 2026-08-28 22:35:16 -04:00
2a94c3c2d2 Fleet list rebuild and the endpoints permission split 2026-08-28 21:23:35 -04:00
062d180e3d Stop sending the redundant per_page parameter on every list request 2026-08-28 18:04:29 -04:00
6e72a157e8 Register the endpoints list without a trailing slash, and stop hiding its errors 2026-08-28 17:48:21 -04:00
benjamin.baker
a5ed364a46 Resolve Fleet list route without a trailing slash
The Fleet list is the only endpoints route on the bare blueprint prefix, so it registered as /api/endpoints/ while the SPA calls /api/endpoints. With strict_slashes on, Flask 308-redirects to add the slash; behind the /shopdb reverse proxy (IIS ARR) that redirect Location drops the path prefix, so the list call fails while suffix routes like /api/endpoints/<id> keep working. That is why a healthy, reporting endpoint showed on its PC detail page but never in Fleet. strict_slashes=False makes both URL forms resolve directly and removes the fragile redirect.
2026-08-28 17:40:31 -04:00
765496a4cf File the Fleet nav item under Assets and store policy JSON as TEXT 2026-08-28 16:23:21 -04:00
7bda8e6d60 Store the policy JSON columns as TEXT for MySQL 5.6
The branch stored scopejson, desiredstate and params as sa.JSON, the only JSON
columns in the product. MySQL 5.6 has no JSON type - it arrived in 5.7 - and the
migration failed there with a bare syntax error near the word JSON.

They are TEXT now, through a JSONText type decorator, so the seventeen call sites
still assign and read dicts. The convention elsewhere is already TEXT:
guides.blocksjson, geenforce.manifestjson.

Spec regenerated: 331 paths, 477 operations across 22 surfaces.
2026-08-28 16:18:34 -04:00
80bb7b5ba1 Merge wilm: endpoints plugin, agent, policies, CSV import, Entra fields
Thirteen commits from benjamin.baker plus the review fixes. Everything he built
is here: the endpoints plugin and its command queue, the .NET endpoint agent,
the computers policies subsystem, the site-asset CSV import with its adapters
and collector, Entra device identity, and the catalog hand-merge.

His branch predates four commits on main (section ticking, the generator share
links, Secure Send copy message, and the smoke-test workflow), so those come
through untouched - nothing of ours is removed by this merge.

wilm stays as its own branch; this does not close it.
2026-08-28 15:56:58 -04:00
cproudlock
75627d4398 Close the review findings on the policy routes, installer and import
Applied on top of af13337 so nothing already on wilm is rewritten.

AUTHORIZATION. The six new policy GETs were @jwt_required(optional=True) with
no permission at all, so an anonymous caller could list configuration and
business policies - gated only by a feature flag that 404s while off. They now
require computers.view. The six policy write routes stacked @jwt_required()
above @require_permission, the same pattern removed from nine endpoints routes
in this very push: the outer decorator answers an anonymous request 401 before
require_permission can consult the guest role, so a grant to guests silently
does nothing. Removed, and the now-unused import with them.

tests/test_core/test_public_read_surface.py failed at af13337 with seven ungated
GETs. It passes now. The seventh, endpoints.claim_next_command, is genuinely
agent-facing - authenticated in the handler by the endpoints collector key,
because a bay has no user session - so it joins PUBLIC_ENDPOINTS with its reason
written down, exactly as report_command_result already does in test_authz.py.
Note which half is public: claiming a queued command is the agent's business,
while ENQUEUEING one is the real authority and stays gated on endpoints.command.

INSTALLER. -BaseUrl and -CollectorKey are now mandatory with no defaults, and
https is enforced unless -AllowInsecureHttp is passed for a local test box. The
defaults were a real hostname (ADR-015) and a live collector key: a key in
source is a key in git history, and that key is also the command channel's kill
switch. Removing it here does not undo the disclosure - it must be rotated on
the server, and purged from af13337 - but it stops the next clone carrying it.

IMPORT. A committed import wrote thousands of rows and left no audit trail; it
now writes one AuditLog row inside the same transaction, so it cannot survive a
rollback or go missing from a commit. The computer-type dropdown asked for
per_page: 200, which the API ignores in favour of perpage and clamps to 100
anyway - it uses a new computersApi.types.listAll(), the pattern already applied
to the other selectors. And the comment claiming the template example rows are
"emitted as commented rows the engine skips" was the opposite of the truth: they
import as live data, the emitted instructions say so, and a test depends on it.

Not fixed here, deliberately: rotating the leaked key, purging it from history,
and the endpoint agent's own hardening (per-host credentials, a protected
process denylist so stop cannot kill security tooling, signing before the
PowerShell runner is enabled, registry ACLs, and the 25-second long poll that
will pin a worker thread per bay).
2026-08-28 15:52:15 -04:00
2de9c005e8 Merge the smoke-test commits made directly on GitHub
Two commits were made in the GitHub web editor to test whether the self-hosted
runner group was shared with this repository - it is. They branched from
e60945e, while the publication export produced its own two commits from the same
parent, so main here and main on GitHub had diverged and the ff-only push was
correctly refused.

Merging rather than force-pushing: the commits are real history somebody made,
and the wilm branch has already merged them, so discarding them from main would
only delay their return.
2026-08-28 15:33:24 -04:00
a775b87db7 gate guard, runtime spec, Node 24 actions 2026-08-28 15:28:07 -04:00
Proudlock, Cameron1 (GE Aerospace, US)
fa8beca1e2 Fix formatting in smoke.yml workflow file 2026-08-28 15:16:49 -04:00
Proudlock, Cameron1 (GE Aerospace, US)
c0435e7ed1 Add smoke test workflow for runner validation 2026-08-28 15:14:56 -04:00
benjamin.baker
af133372b4 Gate endpoint UI refresh on fresh telemetry and land endpoint agent
A command reaching 'succeeded' only proves the agent ran the action, not that it posted a new telemetry snapshot. Reloading panels on command success alone re-rendered stale service/process rows, so a stopped process could still show running right after Refresh Status.

Add a lightweight telemetrymeta probe exposing lastreportedat and gate both the header Refresh Status action and inline row Start/Stop on that stamp advancing past a pre-action baseline. Polling is bounded so an offline or idle agent never hangs the button, and messaging distinguishes a confirmed refresh from a queued one.

Bring the .NET endpoint agent source (service, tray, installer) into the repo so Cameron can merge and test, with an agent/.gitignore keeping bin/obj/publish out. Includes the DncMain launch-path fix, tray single-file host env scrub, and best-effort file logger.
2026-08-28 15:07:20 -04:00
98badcbc26 publish the runtime spec; gate guard 2026-08-28 14:04:10 -04:00
e60945e56e generator share links; secure send copy message 2026-08-28 11:46:56 -04:00
benjamin.baker
c001657582 Merge remote-tracking branch 'origin/main' into wilm
# Conflicts:
#	shopdb/__init__.py
2026-08-28 09:30:35 -04:00
benjamin.baker
01734e5449 harden CSV import reconciliation and preserve saved model selections
Default fuzzy reconcile threshold to 0 and refuse merges when digit runs differ so numbered labels like Bay 311 never bind to Bay 310. Keep edit forms from clearing saved model fields when category filtering hides untyped rows. Includes in-progress computers policy and endpoints command feature work.
2026-08-28 09:29:09 -04:00
8e02235a71 section ticking covers the section 2026-08-27 15:43:00 -04:00
6dcff467d7 tools.view for the Tech Tools entry 2026-08-27 15:12:18 -04:00
e8f1de811f nav items declare their permission 2026-08-27 14:51:04 -04:00
0c1fbfcc1e read permissions on every GET 2026-08-27 14:26:17 -04:00
4da6c6aa36 guest role; guide section ticking 2026-08-27 13:02:35 -04:00
d38a599292 guides export/import bundles 2026-08-27 11:47:01 -04:00
28798e6a32 flask guides import-html; retention docstring 2026-08-27 11:27:30 -04:00
5f069b1a5f Email report on every report page; kiosk long-name fix 2026-08-27 11:01:28 -04:00
4dfbf5f8ec Kiosk drive filter and wider picker 2026-08-27 10:37:10 -04:00
60c340b07f Plugin reports render; USB check-in/out audit report; kiosk drive lists 2026-08-27 10:20:59 -04:00
376298dc02 Navigation sections declared, guide editor drag + references, USB settings, permission refresh 2026-08-27 09:30:08 -04:00
0b8c455b99 USB kiosk sidebar link 2026-08-27 08:56:27 -04:00
2326f8f096 USB kiosk: guest check-out/check-in without a login 2026-08-27 08:19:03 -04:00
benjamin.baker
d68c6db9ab fix endpoints plugin CI failures
Migration tests knew no endpoints head revision, so the parametrized guards raised KeyError. Register it in EXPECTED_HEAD_REVISION.

EndpointState declared lastreportedat both index=True and an explicit db.Index over the same column, and the baseline migration named single-column indexes differently from what create_all builds. On a pre-existing table the name-keyed guard could not see the create_all index and added a duplicate, tripping the idempotency guard. Drop the redundant declaration and rename migration indexes to ix_<table>_<column> to match.

endpoints was absent from the universal site profile, so the installer would not offer it and prune-schema would drop its tables at provisioning. Add it.

The agent design record lived under docs/, which is stripped to the public wiki and normally absent from the tree; a lone file there un-skipped the wiki-publish guards. Move it into the plugin.
2026-08-27 07:10:43 -04:00
benjamin.baker
3deff4f716 Merge remote-tracking branch 'origin/main' into wilm
# Conflicts:
#	frontend/src/views/settings/SiteSettings.vue
2026-08-27 05:45:52 -04:00
benjamin.baker
8b1ba3c310 add endpoints plugin with fleet list view and monitoring drafts
Server-side RMM telemetry plugin: current-snapshot endpointstates plus append-only curated endpointevents, resolved to existing Computer assets by hostname. Adds ADR-010 asset panels (status, services, recent events), dashboard quiet and stability cards, and a minimal Fleet list view that links into existing PC detail pages. Registers the activity sidebar icon and a dev-only sample seeder. Ingest arrives on the generic collector route; the future C# agent design record lives in docs/DESIGN-DECISIONS-agent.md.
2026-08-27 05:41:06 -04:00