Turn network exports into a clearer picture of your device vendors.
ShadowVendor reads saved MAC address tables, ARP tables, and MAC lists. It identifies vendors from address prefixes and produces interactive HTML charts, CSV reports, and timestamped summaries. Use those outputs to review a change window, investigate an unfamiliar vendor, or feed your SIEM.
Try the sample · See the output · Data boundaries · Documentation
Scope: analysis of saved text files. ShadowVendor does not scan your network or connect to switches. A vendor match is a clue about an address prefix, not proof of device identity, ownership, or compromise.
| Your question | Where to look |
|---|---|
| Which vendors appear in this export? | Vendor distribution chart and device CSV |
| How are those entries spread across VLANs and ports? | VLAN charts and, for MAC tables, the port report |
| Has the vendor mix changed? | Archived summaries and the drift CSV |
| How can I correlate this with other events? | CSV/JSONL exports tagged with site and environment |
Drift shows changes in aggregate vendor percentages. Alert rules, collection schedules, and device-level comparisons belong in your surrounding workflow or SIEM.
Use Python 3.11 or newer for this walkthrough. Run these commands from a terminal on macOS or Linux:
git clone https://github.com/StewAlexander-com/ShadowVendor.git
cd ShadowVendor
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txtSeed the working cache from the repository’s bundled snapshot. This first-use step keeps the sample run offline and preserves any existing cache:
mkdir -p output/data
cp -n shadowvendor/data/oui_cache.json output/data/oui_cache.json
python ShadowVendor.py --offline docs/examples/mac-table.txtOpen output/vendor_distribution.html in your browser. With the bundled cache, the synthetic sample produces 12 unique MAC entries, 5 vendor labels, and 4 VLANs. The included addresses are demonstration data, not a captured network inventory.
Two separate network boundaries: dependency installation needs internet access; --offline disables vendor lookup requests during analysis. Opening the generated dashboard still loads Plotly from a CDN. CSV and text reports remain usable without that browser dependency.
Windows PowerShell setup
Use Python 3.11+ and run the environment’s interpreter directly; no activation-policy change is needed.
git clone https://github.com/StewAlexander-com/ShadowVendor.git
cd ShadowVendor
py -3 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
New-Item -ItemType Directory -Force output/data | Out-Null
if (!(Test-Path output/data/oui_cache.json)) {
Copy-Item shadowvendor/data/oui_cache.json output/data/oui_cache.json
}
.\.venv\Scripts\python.exe ShadowVendor.py --offline docs/examples/mac-table.txtOpen output/vendor_distribution.html in your browser. The same CDN requirement applies.
The responsive dashboard puts the vendor overview and four VLAN analyses in separate panels, with readable typography, a dark theme, and data tables alongside the charts. Panels adapt to window width and height; vendor controls let you hide or show slices and stacked series.
These screenshots show actual generated output from the 48-address simulated VLAN inventory, using the bundled vendor cache. They contain 48 unique MAC addresses, 5 vendor labels, and 4 VLANs. The records are demonstration data, not a production network capture. Chart values and tables are checked against independently counted source records in the accuracy tests.
Cisco accounts for 14 addresses (29.2%), Dell 12 (25.0%), VMware 10 (20.8%), Apple 8 (16.7%), and Intel 4 (8.3%). The vendor list scrolls; Intel's row is below the visible portion of this capture. Percentages are rounded independently.
View the inventory summary
Counts describe parsed MAC addresses, not verified physical devices. Missing vendor lookups are included under the label Unknown.
Where are entries concentrated? VLANs 10, 20, 30, and 40 contain 12, 8, 18, and 10 unique MAC addresses respectively. The visible hover detail confirms VLAN 10's count of 12.
What makes up each total? The stacked chart shows the vendor mix. For example, VLAN 30 contains four Cisco, four Dell, four VMware, and six Apple addresses. This view includes the five most common vendors overall; other vendors are excluded when an inventory has more than five labels.
Explore vendor diversity and the heatmap
How varied is each VLAN? The same export contains 3, 2, 4, and 2 vendor labels across VLANs 10, 20, 30, and 40. Diversity adds context; it is not a security score.
Which vendor appears where? The heatmap shows counts for the ten most common vendors overall. Dark navy represents zero and gold represents six in this example. The visible tooltip reads VLAN 10, Apple, 0; Intel has four addresses in the adjacent cell. Read the tooltip's vendor label to identify the selected cell.
Each chart scales its own axis. Compare values and units rather than bar heights across different charts. The smaller quickstart example has three addresses and two vendor labels in every VLAN, so its bars are correctly equal within each of those views.
Reproduce these screenshots' data
After the quick-start installation and bundled-cache setup:
python ShadowVendor.py --offline docs/examples/vlan-review.txtOpen output/vendor_distribution.html. This replaces the previous sample's dashboard and summary. The HTML supports hover details, vendor toggles, zoom/reset, and PNG export; the README images are static. Expand Read data table to inspect the underlying values. Embedded tables remain readable when JavaScript or the chart CDN is unavailable.
VLAN and port detail depends on what the input contains and the parser recognizes; missing fields appear as N/A. See image provenance for the source and verified counts.
| Report | What it contains |
|---|---|
| Device CSV | Unique MAC entries with vendor, VLAN, and port |
| Port CSV | Port grouping for MAC-table inputs |
| HTML dashboard | Vendor and VLAN charts |
| Text summary | Vendor counts and percentages |
| History archive | Timestamped summary and metadata, saved by default |
Where the sample files are saved
output/
├── mac-table-Devices.csv
├── mac-table-Ports.csv
├── vendor_distribution.html
└── vendor_summary.txt
history/
└── timestamped summaries
and companion metadata
Device and port filenames use the input filename without its extension. See the usage reference for optional exports and archive naming.
Shared output filenames are overwritten on later runs; archive reports you need to retain. Duplicate MAC addresses collapse to one entry, so counts describe unique addresses in the supplied file.
Input files and full MAC addresses are processed locally. In default online mode, uncached prefixes may be sent to MACVendors or MACLookup. Use --offline when external vendor lookups are unwanted. An uncached prefix becomes Unknown.
The script reads its working cache from output/data/oui_cache.json; it does not automatically load the bundled snapshot. Cache age and randomized or spoofed MAC addresses limit what vendor labels can establish. Treat generated reports as network inventory data when sharing them.
Replace the sample path with your own saved export. Use ShadowVendor.py for these flags; shadowvendor and python -m shadowvendor are basic file-only entry points.
Review a change window — keep comparable exports from the same scope together:
python ShadowVendor.py --offline \
--history-dir history/DC1 --analyze-drift \
--site DC1 --change-ticket CHG-12345 my-switch.txtThis adds the latest summary to the archive and writes history/DC1/vendor_drift.csv. Two or more comparable snapshots make a trend meaningful; a percentage shift alone does not explain its cause.
Prepare SIEM ingestion — export locally, then configure your collector separately:
python ShadowVendor.py --offline --siem-export \
--site DC1 --environment prod my-switch.txtRead output/siem/shadowvendor_siem.csv or output/siem/shadowvendor_siem.json. The .json file contains JSON Lines, one event per line. Exporting does not send events to a SIEM.
| Next step | Guide |
|---|---|
| Flags, input examples, cache behavior, output details | Usage reference |
| Recurring jobs and configuration formats | Configuration |
| SIEM ingestion and operational patterns | Advanced guide |
| Architecture and the Python API | Technical tutorial · API source |
| Execution paths and existing tests | Execution paths · Test coverage |
The usage reference records current implementation limits where older guides describe broader behavior.
Open an issue with the command, Python version, and a sanitized input example. For a code contribution, include a focused regression test and run python -m pytest tests after installing requirements-dev.txt. See SECURITY.md for private vulnerability reporting.
MIT License · Copyright © 2024–2026 Stewart Alexander.
Created by Stewart Alexander — independent thinking, practical work.





