Skip to content

Add Hierarchy Table class - #79

Open
sjanusz-r7 wants to merge 1 commit into
rapid7:masterfrom
sjanusz-r7:add-hierarchy-table
Open

Add Hierarchy Table class#79
sjanusz-r7 wants to merge 1 commit into
rapid7:masterfrom
sjanusz-r7:add-hierarchy-table

Conversation

@sjanusz-r7

Copy link
Copy Markdown
Contributor

In draft as I move some table code from Framework back into this repo.
This PR has been coded with the help of Kiro.

This PR adds a new Hierarchy Table class. It allows for storing relationship data as part of the table rows, which allows for printing the relationships out to the console as text.

As this is a spike, this PR allows for two ways of printing out the table to the console.

Option 1

host       port  proto  name           state  info
----       ----  -----  ----           -----  ----
192.0.2.1  111   tcp    tcp            open
192.0.2.1  111   tcp    └─ sunrpc      open   100000 v2
192.0.2.1  111   udp    udp            open
192.0.2.1  111   udp    └─ sunrpc      open
192.0.2.1  111   udp       └─ portmap  open   100000 v2 TCP(111), UDP(111)

Option 2

host / port / proto / name  state  info
--------------------------  -----  ----
192.0.2.1
└─ 111
   ├─ tcp                   open
   │  └─ sunrpc             open   100000 v2
   └─ udp                   open
      └─ sunrpc             open
         └─ portmap         open   100000 v2 TCP(111), UDP(111)

Example API usage

The API with row registration took some inspiration from the create_service API, where we can provide parents to the rows.
Using Metasploit Framework's services for this example:

require 'rex/text/hierarchy_table'

table = Rex::Text::HierarchyTable.new(
  header: 'Services',
  columns: ['Service', 'State', 'Info'],
  hierarchy_column: 'Service',
  hierarchy_style: :unicode,
  max_chars: { 'Info' => 40 }
)

port = table.add_row(['111', '', ''])

tcp = port.add_child(['tcp', 'open', ''])
tcp.add_child(['sunrpc', 'open', '100000 v2'])

udp = port.add_child(['udp', 'open', ''])
sunrpc = udp.add_child(['sunrpc', 'open', ''])
sunrpc.add_child(['portmap', 'open', '100000 v2 TCP(111), 100000 v2 UDP(111)'])

puts table

which results in the following:

Services
========

Service           State  Info
-------           -----  ----
111
├─ tcp            open
│  └─ sunrpc      open   100000 v2
└─ udp            open
   └─ sunrpc      open
      └─ portmap  open   100000 v2 TCP(111), 100000 v2 UDP(111)

This code supports ASCII and Unicode glyphs for the relationship hierarchy display. For example, in ASCII, we see:

192.168.207.183  111    udp    udp            open                                             {}
192.168.207.183  111    udp    \- sunrpc      open                                             {}
192.168.207.183  111    udp       \- portmap  open   100000 v2 TCP(111), 100000 v2 UDP(111...  {}

In Unicode mode we see:

192.168.207.183  111    udp    udp            open                                             {}
192.168.207.183  111    udp    └─ sunrpc      open                                             {}
192.168.207.183  111    udp       └─ portmap  open   100000 v2 TCP(111), 100000 v2 UDP(111...  {}

Testing

  • Verify the API works
  • Verify the table outputs the expected hierarchies
  • Verify the tests pass using bundle exec rspec spec
  • Confirm which UX we prefer more and if we want to support both or

@sjanusz-r7
sjanusz-r7 marked this pull request as ready for review September 2, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant