Add log collection, script output capture, and timestamp fixes

Log Collector: a new collect_log action reads a plaintext log file from a
known path and returns its tail for viewing and download. Rides the existing
endpoints.updateapp grant rather than inventing one, since collecting a log is
the same trust level as pushing an application. Collected logs land in their
own table with per-asset retention of 20 instead of overloading the command
audit row, and the agent refuses UNC, traversal, wildcards, directories and
non-text content independently of the server check.

collect_log is POLICY_EXEMPT. Every existing Configuration Policy predates the
action and so cannot name it, and the intersection in ALLOWED_ACTIONS_FOR drops
anything unnamed - so every policied endpoint refused log collection outright,
including for admins, who saw a permission-shaped error for a grant they held.
Exemption from policy is not exemption from permission: the elevated grant
still applies, and a regression test asserts both halves so a future edit
cannot quietly loosen the grant while "fixing" the policy gate.

Script output capture: successful runs can return output, gated per script by
captureoutput. Failures always returned it; success never did, which left a
green badge as the only evidence a run produced.

Timestamps: Activity and Operator Reports rendered date-only because the shared
formatter's 'date' branch drops the clock time. Run Scripts additionally
labels its timestamp, since a bare time meant "completed" on a terminal row but
"queued" on an in-flight one - indistinguishable, and the wrong reading.

Agent 1.16.0. Minor: collect_log is a new action name, so an older agent asked
for it fails closed on the unknown-action default.
This commit is contained in:
benjamin.baker
2026-09-09 19:23:43 -04:00
parent 494b837bae
commit b49fb60c0c
19 changed files with 2207 additions and 21 deletions

View File

@@ -206,7 +206,8 @@ class EndpointsPlugin(BasePlugin):
'already registered and allowlisted on the agent)', 'endpoints'),
('endpoints.updateapp',
'Push application installers to endpoints (runs a pinned '
'installer as SYSTEM)', 'endpoints'),
'installer as SYSTEM) and collect plaintext log files from '
'endpoints by path (read-only, text files only)', 'endpoints'),
# Blunt on purpose. This is not "system-level changes" - it is
# arbitrary code execution as SYSTEM unless the script descriptor
# pins a user context. Whoever grants it must see that here.
@@ -503,7 +504,12 @@ class EndpointsPlugin(BasePlugin):
},
'meta': [
{'key': 'detail'},
{'key': 'eventtime', 'label': 'At', 'format': 'date'},
# datetime, not date: 'date' renders via
# toLocaleDateString and drops the clock time entirely,
# which made every event on a given day look
# simultaneous. Ordering a boot against the logon that
# followed it is the whole point of this feed.
{'key': 'eventtime', 'label': 'At', 'format': 'datetime'},
],
# Expands in place on click. The message body is several
# hundred words of Windows boilerplate around the one or