wigle

package
v0.783.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2026 License: AGPL-3.0 Imports: 9 Imported by: 0

Documentation

Overview

Package wigle encodes GPS-stamped WiFi observations into the WiGLE "WigleWifi-1.4" CSV interchange format — the de-facto wardrive export consumed by wigle.net and compatible analysis tools.

It composes the two capture primitives PromptZero already has — Marauder AP scans (SSID / BSSID / RSSI / channel) and GPS NMEA fixes (lat / lon / altitude) — into the file a wardriver imports or uploads. Encoding is offline and deterministic: the same observations always produce the same bytes. Upload itself is intentionally out of scope — it is an outward-facing, authenticated action the operator performs explicitly.

The one genuinely tricky correctness point is that SSIDs are attacker-controllable and may contain commas, quotes, or newlines; all record fields are written through encoding/csv so they are RFC 4180 quoted/escaped and a hostile SSID can never break the row structure or inject extra columns.

Index

Constants

View Source
const (
	EncOpen    = "open"
	EncWEP     = "wep"
	EncWPA     = "wpa"
	EncWPA2    = "wpa2"
	EncWPA3    = "wpa3"
	EncUnknown = "unknown"
)

Encryption buckets for triage. Open and WEP are the soft targets a reviewer cares about first.

Variables

This section is empty.

Functions

func Classify added in v0.753.0

func Classify(authMode string) string

Classify maps a WiGLE AuthMode capability string to a coarse encryption bucket (one of the Enc* constants). Order matters: "WPA3"/"WPA2" both contain "WPA", so the strongest match is tested first. A capability with only [ESS]/[IBSS] (or empty) is an open network.

func Encode

func Encode(meta Metadata, appVersion string, obs []Observation) ([]byte, error)

Encode renders observations as a WigleWifi-1.4 CSV document. It returns an error if there are no observations, too many, or any single observation fails validation — a partial/corrupt wardrive file is worse than a clear error, so encoding is all-or-nothing.

Types

type BoundingBox added in v0.753.0

type BoundingBox struct {
	MinLatitude  float64 `json:"min_latitude"`
	MinLongitude float64 `json:"min_longitude"`
	MaxLatitude  float64 `json:"max_latitude"`
	MaxLongitude float64 `json:"max_longitude"`
	CenterLat    float64 `json:"center_latitude"`
	CenterLon    float64 `json:"center_longitude"`
}

BoundingBox is the geographic extent of the fixed observations.

type MergeResult added in v0.754.0

type MergeResult struct {
	// Observations is the deduplicated set, one per BSSID, sorted by BSSID
	// for deterministic, diffable output.
	Observations []Observation
	// InputCount is the total observations seen before merging.
	InputCount int
	// Duplicates is how many observations were folded away
	// (InputCount - len(Observations)).
	Duplicates int
}

MergeResult reports a wardrive consolidation.

func Merge added in v0.754.0

func Merge(obs []Observation) MergeResult

Merge consolidates wardrive observations from one or more sessions, deduplicating by BSSID. Operators accumulate overlapping drives over time; concatenating the CSVs leaves the same AP listed many times, so a real merge has to pick one representative sighting per radio.

For each BSSID it keeps the sighting with the strongest signal — that observation has the most reliable fix — where "strongest" treats an RSSI of 0 as "unknown / weakest" (0 is the common no-measurement sentinel, so a real -80 dBm beats it). Ties break by most-recent FirstSeen, then deterministically by coordinate. If the kept sighting has no SSID but another sighting of the same BSSID named it, the name (and its AuthMode, when the kept one's is empty) is adopted — an AP seen hidden in one pass and named in another should carry the name. Output is sorted by BSSID.

type Metadata

type Metadata struct {
	AppRelease string
	Model      string
	Release    string
	Device     string
	Display    string
	Board      string
	Brand      string
}

Metadata is the WigleWifi pre-header line describing the capturing device. Empty fields fall back to PromptZero defaults in Encode. Commas and newlines in values are stripped, since this line is not CSV-quoted.

type Observation

type Observation struct {
	// BSSID is the AP hardware address. It is accepted in any case and with
	// ':', '-', '.' or no separators, and is normalised to the canonical
	// upper-case colon form (AA:BB:CC:DD:EE:FF) on encode.
	BSSID string
	// SSID is the network name. It may be empty (hidden network) and may
	// contain CSV metacharacters — those are escaped, not stripped.
	SSID string
	// AuthMode is the WiGLE capability string, e.g. "[WPA2-PSK-CCMP][ESS]".
	// Empty is allowed (auth unknown) — Marauder scans don't always report it.
	AuthMode string
	// Channel is the 802.11 channel (0 = unknown).
	Channel int
	// RSSI is signal strength in dBm (negative for a real measurement).
	RSSI int
	// Latitude / Longitude are decimal degrees from the GPS fix.
	Latitude  float64
	Longitude float64
	// AltitudeM is altitude in metres; AccuracyM is the fix's horizontal
	// accuracy in metres (0 = unknown).
	AltitudeM float64
	AccuracyM float64
	// FirstSeen is when the AP was observed; encoded as UTC. Required — a
	// wardrive row without a timestamp is not useful to WiGLE.
	FirstSeen time.Time
}

Observation is one GPS-stamped WiFi access-point sighting.

type ParseResult added in v0.753.0

type ParseResult struct {
	Observations []Observation
	// SkippedRows is data rows dropped because they were malformed (bad MAC
	// or unparseable coordinates) or non-WiFi (a BT/BLE/GSM Type row).
	SkippedRows int
	// DataRows is the total number of data rows seen (excludes the header
	// and the optional WigleWifi pre-header line).
	DataRows int
}

ParseResult is the outcome of parsing a WiGLE CSV: the WiFi observations recovered, plus counts so the caller can report how much was skipped.

func ParseCSV added in v0.753.0

func ParseCSV(data []byte) (*ParseResult, error)

ParseCSV reads a WiGLE-style wardrive CSV — the WigleWifi-1.4 export and close variants (Kismet, files with extra columns) — into WiFi observations. It is the inverse of Encode.

Parsing is deliberately resilient, not fail-closed: this ingests operator-supplied capture files, where dropping a 5000-AP wardrive over one malformed line would be worse than skipping that line. Columns are matched by header name (not position) so extra/re-ordered columns from other exporters are tolerated; a row with a bad MAC or coordinate, or a non-WiFi Type, is skipped and counted. A genuinely unusable input — not CSV, or no recognisable MAC column — is a hard error.

type SSIDCount added in v0.753.0

type SSIDCount struct {
	SSID  string `json:"ssid"`
	Count int    `json:"count"`
}

SSIDCount is one entry in the most-common-SSID ranking.

type Summary added in v0.753.0

type Summary struct {
	AccessPoints int `json:"access_points"` // total WiFi observations summarised
	UniqueBSSIDs int `json:"unique_bssids"` // distinct hardware addresses
	HiddenSSIDs  int `json:"hidden_ssids"`  // observations with an empty SSID

	// Encryption is the count per coarse bucket (open/wep/wpa/wpa2/wpa3/unknown).
	Encryption map[string]int `json:"encryption"`
	// SoftTargets is open + WEP — the networks worth a reviewer's first look.
	SoftTargets int `json:"soft_targets"`

	// Channels is observation count per 802.11 channel.
	Channels map[int]int `json:"channels"`

	// WithFix / NoFix split observations by whether they carry a real GPS
	// position (NoFix = 0,0, a "no lock" sentinel common in wardrive files).
	WithFix int `json:"with_fix"`
	NoFix   int `json:"no_fix"`
	// BBox is the extent over WithFix observations; nil when none have a fix.
	BBox *BoundingBox `json:"bounding_box,omitempty"`

	// TopSSIDs is the most frequently-seen non-empty SSIDs, highest first.
	TopSSIDs []SSIDCount `json:"top_ssids,omitempty"`
}

Summary is a security-oriented overview of a parsed wardrive.

func Summarize added in v0.753.0

func Summarize(obs []Observation, topN int) Summary

Summarize computes a security-oriented overview of the observations. topN caps the TopSSIDs list (<=0 selects a default of 10).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL