diff --git a/Data/Agent/Roles/role_ScriptExec_CURRENTUSER.py b/Data/Agent/Roles/role_ScriptExec_CURRENTUSER.py index 0cf0a04..c31b16e 100644 --- a/Data/Agent/Roles/role_ScriptExec_CURRENTUSER.py +++ b/Data/Agent/Roles/role_ScriptExec_CURRENTUSER.py @@ -266,6 +266,17 @@ class Role: def register_events(self): sio = self.ctx.sio + hooks = getattr(self.ctx, 'hooks', {}) or {} + log_agent_hook = hooks.get('log_agent') + + def _log(message: str, *, error: bool = False) -> None: + if callable(log_agent_hook): + try: + log_agent_hook(message) + if error: + log_agent_hook(message, fname='agent.error.log') + except Exception: + pass @sio.on('quick_job_run') async def _on_quick_job_run(payload): @@ -280,8 +291,11 @@ class Role: run_mode = (payload.get('run_mode') or 'current_user').lower() if run_mode == 'system': return + job_label = job_id if job_id is not None else 'unknown' + _log(f"quick_job_run(currentuser) received payload job_id={job_label}") script_bytes = decode_script_bytes(payload.get('script_content'), payload.get('script_encoding')) if script_bytes is None: + _log(f"quick_job_run(currentuser) invalid script payload job_id={job_label}", error=True) await sio.emit('quick_job_result', { 'job_id': job_id, 'status': 'Failed', @@ -293,6 +307,7 @@ class Role: sig_alg = (payload.get('sig_alg') or 'ed25519').lower() signing_key = payload.get('signing_key') if sig_alg and sig_alg not in ('ed25519', 'eddsa'): + _log(f"quick_job_run(currentuser) unsupported signature algorithm job_id={job_label} alg={sig_alg}", error=True) await sio.emit('quick_job_result', { 'job_id': job_id, 'status': 'Failed', @@ -301,6 +316,7 @@ class Role: }) return if not isinstance(signature_b64, str) or not signature_b64.strip(): + _log(f"quick_job_run(currentuser) missing signature job_id={job_label}", error=True) await sio.emit('quick_job_result', { 'job_id': job_id, 'status': 'Failed', @@ -311,6 +327,7 @@ class Role: http_client_fn = getattr(self.ctx, 'hooks', {}).get('http_client') if hasattr(self.ctx, 'hooks') else None client = http_client_fn() if callable(http_client_fn) else None if client is None: + _log(f"quick_job_run(currentuser) missing http_client hook job_id={job_label}", error=True) await sio.emit('quick_job_result', { 'job_id': job_id, 'status': 'Failed', @@ -319,6 +336,7 @@ class Role: }) return if not verify_and_store_script_signature(client, script_bytes, signature_b64, signing_key): + _log(f"quick_job_run(currentuser) signature verification failed job_id={job_label}", error=True) await sio.emit('quick_job_result', { 'job_id': job_id, 'status': 'Failed', @@ -326,6 +344,7 @@ class Role: 'stderr': 'Rejected script payload due to invalid signature', }) return + _log(f"quick_job_run(currentuser) signature verified job_id={job_label}") content = script_bytes.decode('utf-8', errors='replace') raw_env = payload.get('environment') env_map = _sanitize_env_map(raw_env) diff --git a/Data/Agent/Roles/role_ScriptExec_SYSTEM.py b/Data/Agent/Roles/role_ScriptExec_SYSTEM.py index f8fe1f3..0e367c0 100644 --- a/Data/Agent/Roles/role_ScriptExec_SYSTEM.py +++ b/Data/Agent/Roles/role_ScriptExec_SYSTEM.py @@ -281,6 +281,17 @@ class Role: def register_events(self): sio = self.ctx.sio + hooks = getattr(self.ctx, 'hooks', {}) or {} + log_agent_hook = hooks.get('log_agent') + + def _log(message: str, *, error: bool = False) -> None: + if callable(log_agent_hook): + try: + log_agent_hook(message) + if error: + log_agent_hook(message, fname='agent.error.log') + except Exception: + pass @sio.on('quick_job_run') async def _on_quick_job_run(payload): @@ -295,8 +306,11 @@ class Role: return job_id = payload.get('job_id') script_type = (payload.get('script_type') or '').lower() + job_label = job_id if job_id is not None else 'unknown' + _log(f"quick_job_run(system) received payload job_id={job_label}") script_bytes = decode_script_bytes(payload.get('script_content'), payload.get('script_encoding')) if script_bytes is None: + _log(f"quick_job_run(system) invalid script payload job_id={job_label}", error=True) await sio.emit('quick_job_result', { 'job_id': job_id, 'status': 'Failed', @@ -308,6 +322,7 @@ class Role: sig_alg = (payload.get('sig_alg') or 'ed25519').lower() signing_key = payload.get('signing_key') if sig_alg and sig_alg not in ('ed25519', 'eddsa'): + _log(f"quick_job_run(system) unsupported signature algorithm job_id={job_label} alg={sig_alg}", error=True) await sio.emit('quick_job_result', { 'job_id': job_id, 'status': 'Failed', @@ -316,6 +331,7 @@ class Role: }) return if not isinstance(signature_b64, str) or not signature_b64.strip(): + _log(f"quick_job_run(system) missing signature job_id={job_label}", error=True) await sio.emit('quick_job_result', { 'job_id': job_id, 'status': 'Failed', @@ -326,6 +342,7 @@ class Role: http_client_fn = getattr(self.ctx, 'hooks', {}).get('http_client') if hasattr(self.ctx, 'hooks') else None client = http_client_fn() if callable(http_client_fn) else None if client is None: + _log(f"quick_job_run(system) missing http_client hook job_id={job_label}", error=True) await sio.emit('quick_job_result', { 'job_id': job_id, 'status': 'Failed', @@ -334,6 +351,7 @@ class Role: }) return if not verify_and_store_script_signature(client, script_bytes, signature_b64, signing_key): + _log(f"quick_job_run(system) signature verification failed job_id={job_label}", error=True) await sio.emit('quick_job_result', { 'job_id': job_id, 'status': 'Failed', @@ -341,6 +359,7 @@ class Role: 'stderr': 'Rejected script payload due to invalid signature', }) return + _log(f"quick_job_run(system) signature verified job_id={job_label}") content = script_bytes.decode('utf-8', errors='replace') raw_env = payload.get('environment') env_map = _sanitize_env_map(raw_env) diff --git a/Data/Agent/agent.py b/Data/Agent/agent.py index 1c9eadb..f2a506c 100644 --- a/Data/Agent/agent.py +++ b/Data/Agent/agent.py @@ -3138,6 +3138,7 @@ if __name__=='__main__': 'send_service_control': send_service_control, 'get_server_url': get_server_url, 'http_client': http_client, + 'log_agent': lambda message, **kwargs: _log_agent(message, **kwargs), } if not SYSTEM_SERVICE_MODE: # Load interactive-context roles (tray/UI, current-user execution, screenshot, etc.) @@ -3192,10 +3193,12 @@ if __name__=='__main__': if target and target not in ('unknown', '*', '(unknown)') and target != hostname.lower(): return job_id = payload.get('job_id') + job_label = job_id if job_id is not None else 'unknown' script_type = (payload.get('script_type') or '').lower() encoding_hint = payload.get('script_encoding') script_bytes = _decode_script_bytes(payload.get('script_content'), encoding_hint) run_mode = (payload.get('run_mode') or 'current_user').lower() + _log_agent(f"quick_job_run received payload job_id={job_label} run_mode={run_mode}") if script_bytes is None: err = 'Invalid script payload (unable to decode)' await sio.emit('quick_job_result', { @@ -3204,6 +3207,7 @@ if __name__=='__main__': 'stdout': '', 'stderr': err, }) + _log_agent(err) _log_agent(err, fname='agent.error.log') return signature_b64 = payload.get('signature') @@ -3217,6 +3221,7 @@ if __name__=='__main__': 'stdout': '', 'stderr': err, }) + _log_agent(err) _log_agent(err, fname='agent.error.log') return if not isinstance(signature_b64, str) or not signature_b64.strip(): @@ -3227,6 +3232,7 @@ if __name__=='__main__': 'stdout': '', 'stderr': err, }) + _log_agent(err) _log_agent(err, fname='agent.error.log') return client = http_client() @@ -3238,8 +3244,10 @@ if __name__=='__main__': 'stdout': '', 'stderr': err, }) + _log_agent(err) _log_agent(err, fname='agent.error.log') return + _log_agent(f"quick_job_run signature verified job_id={job_label}") content = script_bytes.decode('utf-8', errors='replace') if script_type != 'powershell': await sio.emit('quick_job_result', { 'job_id': job_id, 'status': 'Failed', 'stdout': '', 'stderr': f"Unsupported type: {script_type}" }) @@ -3248,6 +3256,7 @@ if __name__=='__main__': if run_mode == 'system': if not SYSTEM_SERVICE_MODE: # Let the SYSTEM service handle these exclusively + _log_agent(f"quick_job_run ignored job_id={job_label} (system payload routed to SYSTEM service)") return try: # Save last SYSTEM script for debugging