LCOV - code coverage report
Current view: top level - pkg/selinux - setroubleshoot-client.js Coverage Total Hit
Test: cockpit Lines: 71.4 % 98 70
Test Date: 2026-06-17 06:28:00

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2016 Red Hat, Inc.
       3              :  * SPDX-License-Identifier: LGPL-2.1-or-later
       4              :  */
       5              : 
       6            2 : import cockpit from "cockpit";
       7              : 
       8            2 : const _ = cockpit.gettext;
       9              : 
      10            2 : export const client = {};
      11              : 
      12            2 : const busName = "org.fedoraproject.Setroubleshootd";
      13            2 : const dbusInterface = "org.fedoraproject.SetroubleshootdIface";
      14            2 : const dbusPath = "/org/fedoraproject/Setroubleshootd";
      15              : 
      16            2 : const busNameFixit = "org.fedoraproject.SetroubleshootFixit";
      17            2 : const dbusInterfaceFixit = busNameFixit;
      18            2 : const dbusPathFixit = "/org/fedoraproject/SetroubleshootFixit/object";
      19              : 
      20            2 : client.init = function() {
      21            2 :     client.connected = false;
      22            2 :     const dbusClientSeTroubleshoot = cockpit.dbus(busName, { superuser: "try" });
      23            2 :     client.proxy = dbusClientSeTroubleshoot.proxy(dbusInterface, dbusPath);
      24              : 
      25            2 :     client.proxyFixit = cockpit.dbus(busNameFixit, { superuser: "try" }).proxy(dbusInterfaceFixit, dbusPathFixit);
      26              : 
      27            2 :     const connectPromise = new Promise((resolve, reject) => {
      28            2 :         client.proxy.wait(() => {
      29              :             // HACK setroubleshootd seems to drop connections if we don't start explicitly
      30            2 :             client.proxy.call("start", [])
      31            2 :                     .then(() => {
      32            2 :                         client.connected = true;
      33            2 :                         resolve();
      34            2 :                     })
      35            0 :                     .catch(ex => reject(new Error(_("Unable to start setroubleshootd"))));
      36            2 :         });
      37            2 :     });
      38              : 
      39            2 :     client.alertCallback = null;
      40              : 
      41            0 :     function handleSignal(event, name, args) {
      42            0 :         if (client.alertCallback && name == "alert") {
      43            0 :             const level = args[0];
      44            0 :             const localId = args[1];
      45            0 :             client.alertCallback(level, localId);
      46            0 :         }
      47            0 :     }
      48              : 
      49              :     // register to receive calls whenever a new alert becomes available
      50              :     // signature for the alert callback: (level, localId)
      51            2 :     client.handleAlert = function(callback) {
      52              :         // if we didn't listen to events before, do so now
      53            2 :         if (!client.alertCallback) {
      54            2 :             client.proxy.addEventListener("signal", handleSignal);
      55            2 :         }
      56            2 :         client.alertCallback = callback;
      57            2 :     };
      58              : 
      59              :     // returns a promise
      60            2 :     client.getAlerts = function(since) {
      61            2 :         return new Promise((resolve, reject) => {
      62            2 :             let call;
      63            2 :             if (since !== undefined)
      64            1 :                 call = client.proxy.call("get_all_alerts_since", [since]);
      65              :             else
      66            2 :                 call = client.proxy.call("get_all_alerts", []);
      67            2 :             call
      68            2 :                     .then(result => {
      69            2 :                         resolve(result[0].map(entry => ({
      70            2 :                             localId: entry[0],
      71            2 :                             summary: entry[1],
      72            2 :                             reportCount: entry[2],
      73            2 :                         })));
      74            2 :                     })
      75            0 :                     .catch(ex => reject(ex));
      76            2 :         });
      77            2 :     };
      78              : 
      79              :     /* Return an alert with summary, audit events, fix suggestions (by id)
      80              :       localId: an alert id
      81              :       summary: a brief description of an alert. E.g.
      82              :                   "SELinux is preventing /usr/bin/bash from ioctl access on the unix_stream_socket unix_stream_socket."
      83              :       reportCount: count of reports of this alert
      84              :       auditEvent: an array of audit events (AVC, SYSCALL) connected to the alert
      85              :       pluginAnalysis: an array of plugin analysis structure
      86              :           ifText
      87              :           thenText
      88              :           doText
      89              :           analysisId: plugin id. It can be used in org.fedoraproject.SetroubleshootFixit.run_fix()
      90              :           fixable: True when an alert is fixable by a plugin
      91              :           reportBug: True when an alert should be reported
      92              :       firstSeen: when the alert was seen for the first time, timestamp in ms
      93              :       lastSeen: when the alert was seen for the last time, timestamp in ms
      94              :       level: "green", "yellow" or "red"
      95              :     */
      96            2 :     client.getAlert = localId => client.proxy.call("get_alert", [localId])
      97            2 :             .then(result => {
      98            2 :                 const details = {
      99            2 :                     localId: result[0],
     100            2 :                     summary: result[1],
     101            2 :                     reportCount: result[2],
     102            2 :                     auditEvent: result[3],
     103            2 :                     pluginAnalysis: result[4],
     104            2 :                     firstSeen: result[5] / 1000,
     105            2 :                     lastSeen: result[6] / 1000,
     106            2 :                     level: result[7],
     107            2 :                 };
     108              :                 // cleanup analysis
     109            2 :                 details.pluginAnalysis = details.pluginAnalysis.map(itm => ({
     110            2 :                     ifText: itm[0],
     111            2 :                     thenText: itm[1],
     112            2 :                     doText: itm[2],
     113            2 :                     analysisId: itm[3],
     114            2 :                     fixable: itm[4],
     115            2 :                     reportBug: itm[5],
     116            2 :                 }));
     117            2 :                 return details;
     118            2 :             })
     119            0 :             .catch(ex => {
     120            0 :                 console.warn("Unable to get alert for id", localId, ":", JSON.stringify(ex));
     121            0 :                 return Promise.reject(new Error(`Unable to get alert for id ${localId}: ${ex.toString()}`));
     122            0 :             });
     123              : 
     124              :     /* Run a fix via SetroubleshootFixit
     125              :        The analysisId is given as part of pluginAnalysis entries in alert details
     126              :      */
     127            0 :     client.runFix = (alertId, analysisId) => client.proxyFixit.call("run_fix", [alertId, analysisId])
     128            0 :             .then(result => result[0])
     129            0 :             .catch(ex => {
     130            0 :                 console.warn("Unable to run fix:", JSON.stringify(ex));
     131            0 :                 return Promise.reject(new Error(cockpit.format(_("Unable to run fix: $0"), ex.toString())));
     132            0 :             });
     133              : 
     134              :     /* Delete an alert from the database (will be removed for all users), returns true on success
     135              :      * Only assign this to the client variable if the dbus interface actually supports the operation
     136              :      */
     137            0 :     client.deleteAlert = localId => client.proxy.call("delete_alert", [localId])
     138            0 :             .then(success => {
     139            0 :                 if (!success)
     140            0 :                     return Promise.reject(new Error(cockpit.format(_("Failed to delete alert: $0"), localId)));
     141            0 :             })
     142            0 :             .catch(ex => {
     143            0 :                 console.warn("Unable to delete alert with id", localId, ":", JSON.stringify(ex));
     144            0 :                 return Promise.reject(new Error(cockpit.format(_("Failed to delete alert: $0"), ex.toString())));
     145            0 :             });
     146              : 
     147              :     // connect to dbus and start setroubleshootd
     148            2 :     return connectPromise;
     149            2 : };
        

Generated by: LCOV version 2.0-1