Lucene search
+L

Load UCS Package Manager Data

🗓️ 27 Oct 2026 00:00:00Reported by TenableType 
nessus
 nessus
🔗 www.tenable.com👁 5 Views

Load UCS Package Manager Data preprocesses Linux package manager data for SSH info-gathering.

Code
#%NASL_MIN_LEVEL 80900
##
# (C) Tenable, Inc.
##

include('compat.inc');

if (description)
{
  script_id(372413);
  script_version("1.1");
  script_set_attribute(attribute:"plugin_modification_date", value:"2026/10/27");

  script_name(english:"Load UCS Package Manager Data");

  script_set_attribute(attribute:"synopsis", value:
    "Preprocesses Linux package manager data collected by UCS for the SSH info-gathering pipeline.");
  script_set_attribute(attribute:"description", value:
    "This plugin reads in UCS formatted package manager data and formats it
    to the expected format for the SSH info-gathering pipeline.");

  script_set_attribute(attribute:"agent", value:"unix");
  script_set_attribute(attribute:"always_run", value:TRUE);
  script_set_attribute(attribute:"solution", value:"N/A");
  script_set_attribute(attribute:"risk_factor", value:"None");
  script_set_attribute(attribute:"plugin_type", value:"local");
  script_set_attribute(attribute:"plugin_publication_date", value:"2026/10/27");
  script_end_attributes();

  script_category(ACT_INIT);
  script_family(english:"General");

  script_copyright(english:"This script is Copyright (C) 2026 and is owned by Tenable, Inc. or an Affiliate thereof.");

  exit(0);
}

namespace ucs_packages
{

  function get_ucs_package_data_file_name()
  {
    var pkgdata_path = strcat(nessus_get_dir(N_LIB_DIR), "/ucs/PackageManagerData.json");
    replace_kb_item(name:"ucs/PackageManagerData/FilePath", value:pkgdata_path);
    return pkgdata_path;
  }

  ##
  # Read and parse the PackageManagerData.json file.
  #
  # @return array with keys: state ("success"|"error"), msg on error, data (parsed JSON) on success
  ##
  function read_package_data()
  {
    var pkgdata_path = get_ucs_package_data_file_name();
    dbg::detailed_log(lvl:1,msg:strcat("Looking for PackageManagerData.json at: ", pkgdata_path));

    if (!file_exists(pkgdata_path))
    {
      return {"state":"error","msg":"PackageManagerData.json is not present"}; # UCS PackageManagerData.json is not present
    }

    var pkgdata_string = fread(pkgdata_path);
    if (empty_or_null(pkgdata_string))
    {
      return {"state":"error","msg":"PackageManagerData.json is empty or cannot be read"}; # UCS PackageManagerData.json is empty or cannot be read
    }

    var pkgdata = deserialize(pkgdata_string);
    if (isnull(pkgdata) || isnull(pkgdata["PackageData"]))
    {
      return {"state":"error","msg":"Failed to parse PackageManagerData.json or missing PackageData key"};
    }

    var packages = pkgdata["PackageData"];
    replace_kb_item(name:"ucs/PackageManagerData/Count", value:max_index(packages));
    if (max_index(packages) == 0)
    {
      return {"state":"error","msg":"No packages found in PackageManagerData.json"}; # No packages found in PackageManagerData.json
    }

    dbg::detailed_log(lvl:1,msg:strcat("Loaded ", max_index(packages), " packages from PackageManagerData.json"));
    return {"state":"success","data":pkgdata};
  }

  ##
  # Map DPKG architecture names to uname-style architecture names.
  #
  # @param [arch:string] DPKG architecture (e.g., "amd64", "arm64", "i386")
  #
  # @return string uname-style architecture, or the input unchanged if no mapping
  ##
  function arch_map(arch)
  {
    if (arch == "amd64")
      return "x86_64";
    else if (arch == "arm64")
      return "aarch64";
    else if (arch == "i386")
      return "i686";
    else if (arch == "armhf")
      return "armv7l";
    else if (arch == "armel")
      return "armv5tel";
    else if (arch == "s390x")
      return "s390x";
    else if (arch == "ppc64el")
      return "ppc64le";

    return arch;
  }


  ##
  # Build the release file cmd_cache KB entries from Metadata.ReleaseFiles.
  #
  # UCS collects the OS release files directly from the scanned container or
  # system, so each one is cached as-is, exactly as `cat <path>` would return it.
  # No distro detection is done here.
  #
  # @param [release_files:list] Metadata.ReleaseFiles from PackageManagerData.json:
  #                             list of {"ReleaseFile": <absolute path>, "value": <file contents>}
  #
  # @return array of KB name => value (empty if no supported release files are present)
  ##
  function build_release_cmd_cache(release_files)
  {
    var release_kb_map = {
      "/etc/redhat-release"  : "ucs/cmd_cache/redhat_release",
      "/etc/system-release"  : "ucs/cmd_cache/system_release",
      "/etc/oracle-release"  : "ucs/cmd_cache/oracle_release",
      "/etc/SuSE-release"    : "ucs/cmd_cache/suse_release",
      "/etc/os-release"      : "ucs/cmd_cache/os_release",
      "/etc/lsb-release"     : "ucs/cmd_cache/lsb_release",
      "/etc/debian_version"  : "ucs/cmd_cache/debian_version"
    };
    var cmd_cache = {};
    var release_file, release_path, kb_name;

    if (empty_or_null(release_files))
      return cmd_cache;

    foreach release_file (release_files)
    {
      release_path = release_file["ReleaseFile"];
      kb_name = release_kb_map[release_path];
      if (isnull(kb_name) || isnull(release_file["value"]))
      {
        dbg::detailed_log(lvl:3,msg:strcat("Not caching release file ", release_path));
        continue;
      }

      cmd_cache[kb_name] = release_file["value"];
      dbg::detailed_log(lvl:2,msg:strcat("Caching ", release_path, " as ", kb_name));
    }

    return cmd_cache;
  }


  ##
  # Store preprocessed package manager data into the KB for the ssh_get_info2 pipeline.
  #
  # @param [cmd_cache:array] KB name => value pairs (release files and package manager output)
  # @param [cpu:string]      Package architecture (e.g., "amd64", "x86_64"), or NULL
  # @param [hostname:string] Hostname of the scanned container or system, or NULL
  ##
  function store_kb_items(cmd_cache, cpu, hostname)
  {
    var kb_name;

    replace_kb_item(name:"ucs/PackageManagerData/ran", value:true);

    foreach kb_name (keys(cmd_cache))
    {
      if (!isnull(cmd_cache[kb_name]))
        replace_kb_item(name:kb_name, value:cmd_cache[kb_name]);
    }

    if (!isnull(hostname))
      replace_kb_item(name:"Host/hostname", value:hostname);

    var arch = "x86_64";
    if (!isnull(cpu)) arch = ucs_packages::arch_map(arch:cpu);
    replace_kb_item(name:"Host/cpu", value:arch);

    replace_kb_item(name:"Host/uname", value:"Linux container notavailable-generic " + arch + " GNU/Linux");
    replace_kb_item(name:"Host/uname-r", value:"notavailable-generic");
    replace_kb_item(name:"ucs/cmd_cache/uname_m", value:arch);
    replace_kb_item(name:"ucs/cmd_cache/uname_a", value:"Linux container notavailable-generic " + arch + " GNU/Linux");
  }

  ##
  # Record a summary of the preprocessed package data and emit CLI diagnostics.
  #
  # @param [package_manager:string]   "rpm" or "dpkg"
  # @param [package_count:int]        Number of packages processed
  # @param [package_lines:string]     Concatenated package manager output
  # @param [cpu:string]               CPU architecture, or NULL
  # @param [release_cmd_cache:array]  Release file KB name => contents that were cached
  ##
  function log_summary(package_manager, package_count, package_lines, cpu, release_cmd_cache)
  {
    var pm_display = toupper(package_manager);
    var release_kb_names = "";
    var kb_name;

    foreach kb_name (keys(release_cmd_cache))
    {
      if (strlen(release_kb_names) > 0) release_kb_names += ", ";
      release_kb_names += kb_name;
    }

    var summary = strcat(
      "UCS PackageManagerData preprocessed: ", package_count, " ", pm_display, ' packages cached.',
      "Release files cached: ", release_kb_names, ". ",
      "CPU: ", cpu
    );
    replace_kb_item(name:"ucs/package_manager_data/summary", value:summary);

    dbg::detailed_log(lvl:1,msg:summary);
    dbg::detailed_log(lvl:1,msg:strcat("CPU: ", cpu));
    dbg::detailed_log(lvl:2,msg:strcat("Release files: ", serialize(release_cmd_cache)));
    dbg::detailed_log(lvl:4,msg:strcat(pm_display, ' package cache:\n', package_lines));
  }


  ##
  # Determine which package manager the package data came from by
  # returning the first supported PackageManager value found.
  #
  # @param [packages:array] List of package objects from the "PackageData" key in PackageManagerData.json
  #
  # @return string "rpm" or "dpkg", or NULL if no supported package manager is found
  ##
  function detect_package_manager(packages)
  {
    var pkg;

    foreach pkg (packages)
    {
      if (pkg["PackageManager"] == "rpm" || pkg["PackageManager"] == "dpkg")
      {
        dbg::detailed_log(lvl:1,msg:strcat("Detected package manager: ", pkg["PackageManager"]));
        replace_kb_item(name:"ucs/PackageManagerData/PackageManager", value:pkg["PackageManager"]);
        return pkg["PackageManager"];
      }
    }

    dbg::detailed_log(lvl:1,msg:'No supported package manager found in PackageManagerData.json');
    return NULL;
  }

  ###############################################################################
  # RPM Functions
  ###############################################################################

  namespace rpm
  {
    ##
    # Build an RPM line in the rpm -qa --qf output format.
    # Format: NAME-VERSION-RELEASE|EPOCH INSTALLDATE|VENDOR|PACKAGER
    #
    # @param [pkg_name:string]     Package name
    # @param [version:string]      Version-Release string (e.g., "5.1.8-6.el9_1")
    # @param [epoch:int]           Epoch value (0 or NULL means no epoch)
    # @param [install_date:string] Formatted install date
    # @param [vendor:string]       Package vendor
    # @param [packager:string]     Package packager
    #
    # @return string in format "name-version|epoch date|vendor|packager"
    ##
    function build_line(pkg_name, version, epoch, install_date, vendor, packager)
    {
      var line = pkg_name + "-" + version;

      var epoch_str = "(none)";
      if (!isnull(epoch) && epoch != 0 && epoch != "0")
        epoch_str = string(epoch);

      var date_str = "";
      if (!isnull(install_date) && strlen(install_date) > 0)
        date_str = " " + install_date;

      line += "|" + epoch_str + date_str;

      line += "|";
      if (!isnull(vendor) && strlen(vendor) > 0)
        line += vendor;

      line += "|";
      if (!isnull(packager) && strlen(packager) > 0)
        line += packager;

      return line;
    }


    ##
    # Process RPM packages from the package list: build rpm-qa formatted
    # output and identify the CPU architecture.
    #
    # @param [packages:array] List of package objects from PackageManagerData.json
    #
    # @return array with keys: rpm_lines, rpm_count, cpu
    #         or NULL if no RPM packages were found
    ##
    function process_packages(packages)
    {
      var rpm_lines = "";
      var rpm_count = 0;
      var cpu = NULL;
      var pkg, line;

      foreach pkg (packages)
      {
        if (pkg["PackageManager"] != "rpm")
          continue;

        if (pkg["PackageName"] == "gpg-pubkey")
          continue;

        line = ucs_packages::rpm::build_line(
          pkg_name     : pkg["PackageName"],
          version      : pkg["Version"],
          epoch        : pkg["Epoch"],
          install_date : pkg["InstallDate"],
          vendor       : pkg["Maintainer"],
          packager     : pkg["Packager"]
        );

        rpm_lines += line + '\n';
        rpm_count++;

        if (isnull(cpu) && !isnull(pkg["Architecture"]) && pkg["Architecture"] != "noarch" && strlen(pkg["Architecture"]) > 0)
          cpu = pkg["Architecture"];
      }

      if (rpm_count == 0)
      {
        dbg::detailed_log(lvl:1,msg:'No RPM packages found in PackageManagerData');
        return NULL;
      }

      dbg::detailed_log(lvl:1,msg:strcat("Processed ", rpm_count, " RPM packages"));
      if (isnull(cpu))
        dbg::detailed_log(lvl:1,msg:'CPU architecture not found in RPM PackageManagerData');

      return {
        "rpm_lines" : rpm_lines,
        "rpm_count" : rpm_count,
        "cpu"       : cpu
      };
    }


    ##
    # Process, store and report RPM package data.
    #
    # @param [pkgdata:array]           Content from PackageManagerData.json
    # @param [release_cmd_cache:array] Release file KB name => contents
    #
    # @return array with keys: state ("success"|"error"), msg
    ##
    function run_rpm(pkgdata, release_cmd_cache)
    {
      var rpm_result = ucs_packages::rpm::process_packages(packages:pkgdata["PackageData"]);
      if (isnull(rpm_result)) return {"state":"error","msg":"No RPM packages found in PackageManagerData.json"};

      var cmd_cache = release_cmd_cache;
      cmd_cache["ucs/cmd_cache/rpm_qa"] = rpm_result["rpm_lines"];

      ucs_packages::store_kb_items(
        cmd_cache : cmd_cache,
        cpu       : rpm_result["cpu"],
        hostname  : pkgdata["Metadata"]["Hostname"]
      );

      ucs_packages::log_summary(
        package_manager   : "rpm",
        package_count     : rpm_result["rpm_count"],
        package_lines     : rpm_result["rpm_lines"],
        cpu               : rpm_result["cpu"],
        release_cmd_cache : release_cmd_cache
      );

      return {"state":"success","msg":"UCS RPM Package Manager Data loaded successfully"};
    }
  }


  ###############################################################################
  # DPKG Functions
  ###############################################################################

  namespace dpkg
  {
    ##
    # Build a dpkg-query output line.
    # Format: "ii  NAME  VERSION  ARCH  DESCRIPTION"
    # (fields separated by exactly two spaces)
    #
    # @param [pkg_name:string]    Package name
    # @param [version:string]     Package version (may include epoch)
    # @param [architecture:string] Architecture (e.g., "amd64", "all")
    #
    # @return string formatted dpkg-query line
    ##
    function build_line(pkg_name, version, architecture)
    {
      var arch_str = "";
      if (!isnull(architecture) && strlen(architecture) > 0)
        arch_str = architecture;

      return "ii  " + pkg_name + "  " + version + "  " + arch_str + "  ";
    }


    ##
    # Process DPKG packages from the package list: build dpkg-query formatted
    # output and identify the CPU architecture.
    #
    # @param [packages:array] List of package objects from PackageManagerData.json
    #
    # @return array with keys: dpkg_lines, dpkg_count, cpu
    #         or NULL if no DPKG packages were found
    ##
    function process_packages(packages)
    {
      var dpkg_lines = "";
      var dpkg_count = 0;
      var cpu = NULL;
      var pkg, line;

      foreach pkg (packages)
      {
        if (pkg["PackageManager"] != "dpkg")
          continue;

        if (!isnull(pkg["Status"]) && "installed" >!< pkg["Status"])
          continue;

        line = ucs_packages::dpkg::build_line(
          pkg_name     : pkg["PackageName"],
          version      : pkg["Version"],
          architecture : pkg["Architecture"]
        );

        dpkg_lines += line + '\n';
        dpkg_count++;

        if (
          isnull(cpu) &&
          !isnull(pkg["Architecture"]) &&
          pkg["Architecture"] != "all" &&
          strlen(pkg["Architecture"]) > 0
        )
        {
          cpu = pkg["Architecture"];
        }
      }

      if (dpkg_count == 0)
      {
        dbg::detailed_log(lvl:1,msg:'No installed DPKG packages found in PackageManagerData');
        return NULL;
      }

      dbg::detailed_log(lvl:1,msg:strcat("Processed ", dpkg_count, " DPKG packages"));
      if (isnull(cpu)) dbg::detailed_log(lvl:1,msg:'CPU architecture not found in DPKG PackageManagerData');

      return {
        "dpkg_lines" : dpkg_lines,
        "dpkg_count" : dpkg_count,
        "cpu"        : cpu
      };
    }


    ##
    # Process, store and report DPKG package data.
    #
    # @param [pkgdata:array]           Content from PackageManagerData.json
    # @param [release_cmd_cache:array] Release file KB name => contents
    #
    # @return array with keys: state ("success"|"error"), msg
    ##
    function run_dpkg(pkgdata, release_cmd_cache)
    {
      var dpkg_result = ucs_packages::dpkg::process_packages(packages:pkgdata["PackageData"]);
      if (isnull(dpkg_result)) return {"state":"error","msg":"No installed DPKG packages found in PackageManagerData.json"};

      var cmd_cache = release_cmd_cache;
      cmd_cache["ucs/cmd_cache/dpkg_query"] = dpkg_result["dpkg_lines"];

      ucs_packages::store_kb_items(
        cmd_cache : cmd_cache,
        cpu       : dpkg_result["cpu"],
        hostname  : pkgdata["Metadata"]["Hostname"]
      );

      ucs_packages::log_summary(
        package_manager   : "dpkg",
        package_count     : dpkg_result["dpkg_count"],
        package_lines     : dpkg_result["dpkg_lines"],
        cpu               : dpkg_result["cpu"],
        release_cmd_cache : release_cmd_cache
      );

      return {"state":"success","msg":"UCS DPKG Package Manager Data loaded successfully"};
    }
  }

  ##
  # Entry point to run the UCS package loading process
  #
  # @return array with keys: state ("success"|"error"), msg
  ##
  function run()
  {
    var pkgdata = ucs_packages::read_package_data();
    if (pkgdata.state == "error") return {"state":"error","msg":pkgdata.msg};
    pkgdata = pkgdata.data;

    var release_cmd_cache = ucs_packages::build_release_cmd_cache(release_files:pkgdata["Metadata"]["ReleaseFiles"]);
    if (empty_or_null(release_cmd_cache))
      return {"state":"error","msg":"No supported OS release files found in PackageManagerData.json Metadata.ReleaseFiles"};

    var package_manager = ucs_packages::detect_package_manager(packages:pkgdata["PackageData"]);
    if (package_manager == "rpm") return ucs_packages::rpm::run_rpm(pkgdata:pkgdata, release_cmd_cache:release_cmd_cache);
    else if (package_manager == "dpkg") return ucs_packages::dpkg::run_dpkg(pkgdata:pkgdata, release_cmd_cache:release_cmd_cache);

    return {"state":"error","msg":"No RPM or DPKG packages found in PackageManagerData.json"};
  }
}

##
# Load UCS packages plugin begin
##
function load_ucs_packages()
{
  if (CLI_SCAN)
  {
    dbg::enable();
    dbg::set_log_level(4);
    dbg::detailed_log(lvl:1,msg:'Running from CLI\n');
  }

  var run_result = ucs_packages::run();
  replace_kb_item(name:"ucs/PackageManagerData/RunResult", value:run_result.msg);
  if (run_result.state == "error")
  {
    exit(0, run_result.msg);
  }

  if (CLI_SCAN)
  { ## Used for developer testing from the CLI
    while (true)
    {
      var kb_name = prompt("Enter KB item to query, e.g. ucs/cmd_cache/rpm_qa ('quit' to exit) :");
      if (kb_name == "quit") break;
      var output = get_kb_table(kb_name);
      dbg::detailed_log(lvl:1,msg:serialize(output));
    }
  }
}

global_var CLI_SCAN = false;
if (empty_or_null(get_preference("plugins_folder")))
{
  CLI_SCAN = true;
}
else
{
  # prevent UCS from running unless it is specifically configured to run in the preferences
  var ucs_enabled = get_preference("ucs_enabled");
  if (empty_or_null(ucs_enabled)) exit(0, "UCS is not enabled");
}

load_ucs_packages();

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

27 Oct 2026 00:00Current
5.9Medium risk
Vulners AI Score5.9
5