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:
@@ -110,6 +110,24 @@ COMMAND_ALLOWLIST = {
|
||||
'PCDetail.vue).',
|
||||
'targetkind': 'script',
|
||||
},
|
||||
'collect_log': {
|
||||
'label': 'Collect log file',
|
||||
'description': 'Read a plaintext log file from a known path on the '
|
||||
'endpoint and return its contents to ShopDB for '
|
||||
'viewing and download. Read-only: the agent opens the '
|
||||
'file for reading with sharing permitted (so a log a '
|
||||
'running application holds open can still be read), '
|
||||
'never writes, and never deletes. Only the TAIL of a '
|
||||
'file is returned, and only for plaintext extensions - '
|
||||
'a binary or oversized file is refused rather than '
|
||||
'mangled. Gated on endpoints.updateapp because the '
|
||||
'reason to collect a log is almost always to diagnose '
|
||||
'an application push, so it belongs with app lifecycle '
|
||||
'rights rather than as a separate grant. Note this '
|
||||
'still reads a caller-supplied path as SYSTEM: it is '
|
||||
'bounded to text files, but it is not a sandbox.',
|
||||
'targetkind': 'logpath',
|
||||
},
|
||||
'update_app': {
|
||||
'label': 'Install/update application',
|
||||
'description': 'Push a ShopDB-hosted installer for an application (by app '
|
||||
@@ -192,11 +210,18 @@ COMMAND_ALLOWLIST = {
|
||||
# update_app - runs a pinned installer
|
||||
# run_script - arbitrary code as SYSTEM unless the descriptor pins a
|
||||
# user context
|
||||
#
|
||||
# collect_log rides endpoints.updateapp rather than taking a grant of its own.
|
||||
# The reason to read a log is nearly always to find out why an app push behaved
|
||||
# the way it did, so the right to install an application and the right to read
|
||||
# that application's log are the same job. It is read-only and strictly weaker
|
||||
# than the installer push the same permission already allows.
|
||||
ELEVATED_ACTION_PERMISSIONS = {
|
||||
'system_restart': 'endpoints.restart',
|
||||
'scheduledtask_run': 'endpoints.corptools',
|
||||
'run_script': 'endpoints.runscript',
|
||||
'update_app': 'endpoints.updateapp',
|
||||
'collect_log': 'endpoints.updateapp',
|
||||
}
|
||||
|
||||
|
||||
@@ -339,6 +364,19 @@ POLICY_EXEMPT_ACTIONS = frozenset({
|
||||
# policy is non-empty - a denial no operator can act on, because the fix
|
||||
# lives in a policy that was never about this action.
|
||||
'collect_now',
|
||||
# Read-only diagnostic retrieval. Policy describes how a machine should be
|
||||
# shaped - which monitors run, which service primitives are reachable - and
|
||||
# reading a log file shapes nothing. An existing policy was authored before
|
||||
# this action existed and so can never mention it, which under the
|
||||
# intersection means every endpoint carrying any policy at all refuses log
|
||||
# collection, including for an admin. That denial is also unactionable in
|
||||
# the way that matters: logs are what an operator reaches for when an
|
||||
# endpoint is already misbehaving, so gating them behind editing that same
|
||||
# endpoint's policy withholds the evidence exactly when it is needed.
|
||||
# Exemption from POLICY is not exemption from PERMISSION - collect_log
|
||||
# still requires endpoints.updateapp via ELEVATED_ACTION_PERMISSIONS, and
|
||||
# the agent still refuses UNC, traversal, directories and non-text files.
|
||||
'collect_log',
|
||||
})
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user