Linux-System SBOM visualisiert als Graph - für YUM!

23.08.2026

Ich schrieb vor einiger Zeit über die Visualisierung und Analyse von Abhängigkeiten in Linux-Distributionen, die auf Debian aufbauen. Damals habe ich noch behauptet, dass andere Distributionen, die zum Beispiel auf yum als Paketmanagement-Lösung setzen nur schwer in dieser Art und Weise analysierbar sind. Ich habe weiter recherchiert und nun auch dafür eine Lösung gefunden.

Ich habe, nachdem ich herausgefunden hatte, wie diese Informationen mitelsa yum zu erlangen sind zwei Skripte geschrieben - eines davon erzeugt eine Graphen-Definition für graphviz ähnlich zum damaligen Vorgehen:

!/bin/bash

ssh 192.168.10.69 'rpm -qa --qf "%{NAME}\n"' >nodes.txt

cat >graph.gviz <<EOF # Attributes in here apply to the graph itself. pad="1" outputorder=edgesfirst bgcolor=transparent

# layout= specifies a layout engine: # circo — for circular layout of graphs # dot — for drawing directed graphs (the default) # fdp — for drawing undirected graphs # neato — for drawing undirected graphs # osage — for drawing large undirected graphs # twopi — for radial layouts of graphs #layout=neato #layout=dot #layout=twopi

# Default node attributes node [ shape = circle style="filled,bold" color=black fillcolor="#F2F2F2" fontname=Inter ]

# Uncomment this to hide labels #node [ label="" ]

# Uncomment this to arrange nodes in a grid #layout=osage edge [style=invis] EOF

while IFS="" read -r p || [ -n "$p" ] do printf '"%s"\n' "$p" >>graph.gviz done < nodes.txt

while IFS="" read -r p || [ -n "$p" ] do dependent=$(ssh -n 192.168.10.69 "yum repoquery --installed --whatrequires \"$p\" --qf \"%{NAME}\n\"") if [ -n "$dependent" ] ; then while IFS= read -r i do if [ -n "$i" ] ; then echo "dep $p $i" printf '"%s" -> "%s"\n' "$p" "$i" >>graph.gviz fi done <<< "$dependent" fi done < nodes.txt

Die zweite (da die Visualisierung mit den enthaltenen 312 Paketen mittels Graphviz sehr unübersichtlich ist) Variante erzeugt den Graphen im Format GEFX, das man zum Beispiel mittels gephi visualisieren kann:

#!/bin/bash

ssh 192.168.10.69 'rpm -qa --qf "%{NAME}\n"' >nodes.txt

cat >graph.gexf <<EOF <?xml version="1.0" encoding="UTF-8"?> <gexf xmlns="http://gexf.net/1.2" version="1.2"> <meta lastmodifieddate="2009-03-20"> <creator>Gexf.net</creator> <description>A hello world! file</description> </meta> <graph mode="static" defaultedgetype="directed"> <nodes> EOF

while IFS="" read -r p || [ -n "$p" ] do printf ' <node id="%s" label="%s"/>\n' "$p" "$p" >>graph.gexf done < nodes.txt

cat >>graph.gexf <<EOF </nodes> <edges> EOF

while IFS="" read -r p || [ -n "$p" ] do dependent=$(ssh -n 192.168.10.69 "yum repoquery --installed --whatrequires \"$p\" --qf \"%{NAME}\n\"") if [ -n "$dependent" ] ; then while IFS= read -r i do if [ -n "$i" ] ; then echo "dep $p $i" printf ' <edge id="%s_%s" source="%s" target="%s"/>\n' "$p" "$i" "$p" "$i" >>graph.gexf fi done <<< "$dependent" fi done < nodes.txt

cat >>graph.gexf <<EOF </edges> </graph> </gexf> EOF

In gephi ist es möglich, die Knoten und Kanten eines Graphen sehr flexibel zu filtern - so würde etwa folgender Code als Filter dafür sorgen, lediglich Pakete anzuzeigen, die direkte Abhängigkeiten von python3 sind oder die von python3 abhängen:

/**
 * Filtering function.
 *
 * @param {string} id ID of the item
 * @param {Object.<string, number | string | boolean | undefined | null>} attributes Attributes of the item
 * @param {FullGraph} full graph (data and rendering attributes + topology) dataset
 * @return {boolean} TRUE if the item should be kept in the graph, FALSE to filter it
 */
function edgeFilter(id, attributes, graph) {
  // Your code goes here
  return id.endsWith('_python3')||id.startsWith('python3_');
}

Nachdem ich so weit gekommen war, wollte ich noch einen Schritt weiter gehen: Mit den gezeigten Filtern kann man nur direkte Abhänggikeiten behandeln. Möchte man aber zum Beispiel alle Pakete herausfinden, die direkte oder transiente Abhängigkeiten eines Pakets darstellen, kommt man mit diesen Filtern nicht zum gewünschten Ergebnis. In dem früheren Artikel habe ich aber bereits über Visualisierungsbibliotheken berichtet, die es erlauben, sehr flexibel und dynamisch mit Graphen zu arbeiten... Daher überlegte ich mir, dass ich die GEFX-Dateien in ein Java-Programm einlesen wollen würde, um dann mit ein wenig Code zur Analyse der Graphen sehr mächtige und flexible Filter und Abfragen zu realisieren un deren Ergebnisse in ansprechender Form zu visualisieren.

Als ersten Schritt dahin überlegte ich mir, ein XSLT-Stylesheet zu entwerfen, mit dem ich das GEFX-Format on-the-fly über den XMLDecoder in Java-Objekte deserialisieren könnte.

Ich nahm zunächst also dieses Problem in Angriff - allerdings nur mit dem absolut notwendigen Minimum. Würde man das vollständige Featureset des Formats GEFX umsetzen, müsste das Template noch deutlich umgangreicher ausfallen. Aber die Minimallösung funktionierte für meinen Proof-of-Concept sehr gut:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:gefx="http://gexf.net/1.2">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="/">
  <xsl:element name="java">
  <xsl:attribute name="version">17.0.2"</xsl:attribute>
  <xsl:attribute name="class">java.beans.XMLDecoder</xsl:attribute>
  <xsl:element name="object">
    <xsl:attribute name="class">de.elbosso.algorithms.graph.Graph</xsl:attribute>
    <xsl:element name="object">
      <xsl:attribute name="class">java.util.LinkedList</xsl:attribute>
      <xsl:for-each select="gefx:gexf/gefx:graph/gefx:nodes/gefx:node">
        <xsl:element name="void">
          <xsl:attribute name="method">add</xsl:attribute>
          <xsl:element name="object">
            <xsl:attribute name="class">de.elbosso.algorithms.graph.Vertex</xsl:attribute>
            <xsl:attribute name="id"><xsl:value-of select="./@id"/></xsl:attribute>
            <xsl:element name="string"><xsl:value-of select="@label"/></xsl:element>
            <xsl:element name="null"/>
            <xsl:element name="object">
              <xsl:attribute name="class">de.elbosso.util.lang.collections.TypeSafeHashMap</xsl:attribute>
            </xsl:element>
          </xsl:element>
        </xsl:element>
      </xsl:for-each>
    </xsl:element>
    <xsl:element name="object">
      <xsl:attribute name="class">java.util.HashSet</xsl:attribute>
      <xsl:for-each select="gefx:gexf/gefx:graph/gefx:edges/gefx:edge">
      <xsl:element name="void">
      <xsl:attribute name="method">add</xsl:attribute>
      <xsl:element name="object">
      <xsl:attribute name="class">de.elbosso.algorithms.graph.Edge</xsl:attribute>
      <xsl:element name="object">
      <xsl:attribute name="idref"><xsl:value-of select="./@source"/></xsl:attribute>
      </xsl:element>
      <xsl:element name="object">
      <xsl:attribute name="idref"><xsl:value-of select="./@target"/></xsl:attribute>
      </xsl:element>
      <xsl:element name="null"/>
      <xsl:element name="void">
      <xsl:attribute name="property">directed</xsl:attribute>
        <xsl:element name="boolean">true</xsl:element>
      </xsl:element>
      </xsl:element>
      </xsl:element>
      </xsl:for-each>
    </xsl:element>
  </xsl:element>
  </xsl:element> 
  </xsl:template>
</xsl:stylesheet>

Damit konnte ich nahtlos die Beschreibung des Dependency-Graphen in meine Domain-Objekte zur Arbeit mit Graphen umwandeln und darauf dann Visualisierungen vornehmen wie die folgende:

Screenshot Visualisierung transienter Abhängigkeiten des Pakets curl in Alma Linux (dargestellt werden pakete, die curl als direkte oder transiente Abhängigkeit benötigen)

Auch die implementierten Algorithmen lassen sich so auf die Dependencies anwenden - die Suche nach Paketen, die keine outgoing Connections haben und damit keine Abhängigkeiten anderer Pakete darstellen ergab folgendes Ergebnis:

on the edge (50): 
[Vertex{id=almalinux-repos }, 
Vertex{id=glibc-minimal-langpack },
Vertex{id=rpm-plugin-audit }, 
Vertex{id=hostname }, 
Vertex{id=binutils }, 
Vertex{id=initscripts-service }, 
Vertex{id=logrotate }, 
Vertex{id=kpartx }, 
Vertex{id=selinux-policy-targeted }, 
Vertex{id=tpm2-tools }, 
Vertex{id=samba-common-libs }, 
Vertex{id=python3-gobject-base-noarch }, 
Vertex{id=kdump-utils }, 
Vertex{id=xfsprogs }, 
Vertex{id=NetworkManager-tui }, 
Vertex{id=sg3_utils }, 
Vertex{id=iproute-tc }, 
Vertex{id=kernel-tools },
Vertex{id=irqbalance }, 
Vertex{id=dnf-plugins-core },
Vertex{id=yum }, 
Vertex{id=firewalld },
Vertex{id=crypto-policies-scripts }, 
Vertex{id=python3-libselinux }, 
Vertex{id=sssd-kcm }, 
Vertex{id=kernel }, 
Vertex{id=grub2-pc }, 
Vertex{id=dracut-config-rescue },
Vertex{id=rsyslog }, 
Vertex{id=audit}, 
Vertex{id=lvm2 }, 
Vertex{id=openssh-clients }, 
Vertex{id=openssh-server }, 
Vertex{id=chrony }, 
Vertex{id=microcode_ctl }, 
Vertex{id=parted }, 
Vertex{id=man-db }, 
Vertex{id=e2fsprogs }, 
Vertex{id=sudo }, 
Vertex{id=initscripts-rename-device }, 
Vertex{id=rpm-plugin-selinux }, 
Vertex{id=prefixdevname }, 
Vertex{id=vim-minimal }, 
Vertex{id=lshw }, 
Vertex{id=libsysfs }, 
Vertex{id=lsscsi }, 
Vertex{id=ncurses },
Vertex{id=iwlwifi-dvm-firmware }, 
Vertex{id=iwlwifi-mvm-firmware }, 
Vertex{id=rootfiles }]     

Alle Artikel rss Wochenübersicht Monatsübersicht Codeberg Repositories Mastodon Über mich home xmpp


Vor 5 Jahren hier im Blog

  • Styles für GeoJSON in EBMap4D

    22.08.2021

    Ich habe bereits darüber berichtet, dass EBMap4D jetzt über die Möglichkeit verfügt, beliebige GeoJSON-Layer darzustellen. Nun ist die Anwendung um die Möglichkeit erweitert worden, in diesen GeoJSON-Daten enthaltene Stilinformationen für die Darstellung zu nutzen.

    Weiterlesen

Neueste Artikel

  • 35 Jahre Antipatterns in Datenmodellen

    Ich habe inzwischen 35 Jahre Softwareentwicklung auf dem Buckel - viele davon für Geld...

    Weiterlesen
  • Highlights der DefCon 34

    Hier meine Highlights der DefCon 34 in Las Vegas

    Weiterlesen
  • PlantUML Text Encoding in Bash

    Ich habe neulich darüber geschrieben, dass ich mit in meinem Homelab von Gitlab verabschieden möchte und nach Alternativen gesucht habe. Bei meiner Suche stieß ich auf verteilte Issue Tracker, die die vertraute Integration von PlantUML nicht bieten. Daher suchte ich nach einem Weg, auch dieses Feature in solchen Lösungen zu integrieren.

    Weiterlesen

Manche nennen es Blog, manche Web-Seite - ich schreibe hier hin und wieder über meine Erlebnisse, Rückschläge und Erleuchtungen bei meinen Hobbies.

Wer daran teilhaben und eventuell sogar davon profitieren möchte, muss damit leben, daß ich hin und wieder kleine Ausflüge in Bereiche mache, die nichts mit IT, Administration oder Softwareentwicklung zu tun haben.

Ich wünsche allen Lesern viel Spaß und hin und wieder einen kleinen AHA!-Effekt...

PS: Meine öffentlichen Codeberg-Repositories findet man hier.