You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .github/copilot-instructions.md
+60Lines changed: 60 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -102,3 +102,63 @@ Kotlin `TakPacketV2Data` uses camelCase field names. Wire-generated proto types
102
102
- Don't change the `Team.Unspecifed_Color` typo — it's the canonical Wire-generated name
103
103
- Don't retrain dictionaries without coordinating a version bump — new dicts break wire compatibility with old receivers
104
104
- Don't use `toInt()` for `(longitude * 1e7)` comparisons — IEEE 754 rounding requires `roundToInt()`
105
+
106
+
## PII and test-fixture sanitization (CRITICAL)
107
+
108
+
**Never commit unredacted real-world ATAK captures.** CoT XML carries position
109
+
data (lat/lon to sub-meter precision), Android device IDs that act as
110
+
permanent device fingerprints, private LAN IPs that reveal home network
111
+
topology, and personal callsigns. Anything captured from an operator's actual
112
+
deployment must be sanitized BEFORE landing in `testdata/`, the SDK README,
113
+
or the proto submodule — once it's in a git commit pushed to a public mirror,
114
+
no amount of force-pushing scrubs it from caches, forks, or pre-existing
115
+
clones reliably.
116
+
117
+
### Required replacements when sanitizing a real capture
118
+
119
+
| What | Pattern | Replace with |
120
+
|---|---|---|
121
+
| GPS coordinates | any real lat/lon | Public landmark coords (e.g. `38.8895, -77.0353` Washington Monument; `38.8814, -77.0502` Lincoln Memorial). For routes/polygons, pick distinct landmark pairs so the path topology survives. |
122
+
| Stationary / not-a-place markers | — |`lat="0.0" lon="0.0"` (matches ATAK's "null island" convention for non-positional events like `m-t-t` and `y-`) |
123
+
| Android device IDs (`ANDROID-[16 hex chars]`) | real device fingerprint | Sequential placeholders: `ANDROID-0000000000000001`, `…02`, etc. (Test fakes are already in this format — extend the numbering.) |
124
+
| Private LAN IPs (`192.168.x.x`, `10.x.x.x`, `172.16-31.x.x`) | real network IP | RFC 5737 documentation range: `192.0.2.1`, `198.51.100.1`, `203.0.113.1` (these ranges are reserved for examples and never route on the public internet). |
125
+
| MAC addresses | real radio MAC | Random or `00:00:00:00:00:0X` placeholder |
126
+
| Real callsigns | personally-identifying handles | Generic operator handles: `ALPHA-1`, `BRAVO-2`, `ASPEN`, `ETHEL`, `CHARLIE` are fine — they're well-worn placeholders. Avoid the actual operator's first name, military rank+last-name, or amateur radio call. |
127
+
| UUIDs | (no action) | Random UUIDs are high-entropy and not by themselves identifying. Keep them. |
128
+
| Voice profile IDs, room IDs, chatgrp UIDs | (no action unless they look like a real account ID) | Usually safe — they're UUIDs. |
129
+
130
+
### Workflow when adding a new fixture from a real capture
131
+
132
+
1.**Diff-redact, don't just rename.** Pull the source XML into `/tmp/` and edit there. Apply every replacement above. Save back into `testdata/cot_xml/`.
133
+
2.**Scan before staging.** Run this against the new file:
Hits on the coord regex with 5+ decimal places, on a non-sequential ANDROID hex ID, or on an RFC 1918 IP, mean it isn't sanitized yet. The sequential `ANDROID-0+\d+` pattern is fine — that's the test-fake convention.
138
+
3.**Add a redaction note** in the commit message: "redacted: coords → DC landmarks; android ids → 000…0NN; LAN IP → 192.0.2.1".
139
+
4.**Regenerate goldens twice.**`gradle jvmTest --tests CompressionTest.generate compression report --rerun-tasks` — the first pass mints `.pb`/`.bin`; rerun to confirm steady state.
140
+
5.**Never assume binary blobs auto-sanitize.** Filter-repo's `--replace-text` skips files containing null bytes, so the `.pb` protobuf intermediates and `.bin` golden files retain whatever bytes were generated. After scrubbing the source XML, ALWAYS regenerate the derived binaries.
141
+
142
+
### Pre-push self-check
143
+
144
+
Before `git push`, especially when pushing fixture changes, run from repo root:
Empty output = clean. Any hit gets eyeballed before push.
155
+
156
+
### If real PII slips through
157
+
158
+
If you discover real PII in a commit that has already been pushed:
159
+
1.**Stop pushing** anything that builds on the leak.
160
+
2. Use `git filter-repo --replace-text` for text-format leaks (XML, README, code).
161
+
3. Follow up with `git filter-repo --blob-callback` for binary leaks (`.pb`, `.bin`) — filter-repo's `--replace-text` SKIPS files with null bytes, so binaries need an explicit callback that swaps the bad blob SHA for a clean re-baselined version.
162
+
4. Force-push every branch and tag (`git push --force origin master --tags`).
163
+
5. Open a GitHub Support ticket to purge the orphaned commits from cache — they remain accessible by SHA URL for 30+ days otherwise.
164
+
6. Reach out to fork owners — your force-push doesn't propagate.
Copy file name to clipboardExpand all lines: CLAUDE.md
+35Lines changed: 35 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -158,3 +158,38 @@ cd kotlin && gradle jvmTest
158
158
159
159
-**Meshtastic-Android** (`core/takserver`): depends on `takpacket-sdk-jvm` via JitPack, proto submodule at `core/proto/src/main/proto`
160
160
-**Meshtastic-Apple**: depends on `MeshtasticTAK` Swift package via remote SPM URL, proto submodule at `protobufs/`, regenerated `atak.pb.swift` at `MeshtasticProtobufs/Sources/meshtastic/`
161
+
162
+
## PII / sensitive-data handling — read before adding any fixture
163
+
164
+
The repo has had real-world ATAK captures land in test fixtures by accident
165
+
before. The compressed `.bin` and `.pb` intermediates retain the leaked
166
+
identifiers even after the source XML is fixed, because filter-repo's text
167
+
replacement skips files containing null bytes. Treat this as a recurring
168
+
hazard.
169
+
170
+
**Patterns that must never appear in committed fixtures or docs:**
171
+
172
+
| Pattern | What it leaks | Required substitute |
173
+
|---|---|---|
174
+
| High-precision lat/lon (5+ decimals) that's not a public landmark | Operator's actual location, often a home | DC-area public landmarks: `38.8895,-77.0353` (Washington Monument), `38.8814,-77.0502` (Lincoln Memorial), or `0.0,0.0` for non-positional events like `m-t-t` / `y-`|
175
+
|`ANDROID-[16 hex chars]` where the hex isn't all-zeros | A specific Android device's ANDROID_ID — never changes, can fingerprint the device for years | Sequential placeholder: `ANDROID-0000000000000001`, `…02`, etc. |
| MAC addresses | Hardware fingerprint |`00:00:00:00:00:0X` placeholder |
178
+
| Real operator callsigns (first names, military rank+name, ham calls) | Identity | Generic operator handles: `ALPHA-1`, `BRAVO-2`, `ASPEN`, `ETHEL`|
179
+
180
+
**When you're handed a real CoT Explorer capture / pg_dump excerpt to add as a fixture:**
181
+
182
+
1. Don't drop it into `testdata/` raw. Edit a redacted copy in `/tmp/` first.
183
+
2. Apply every substitution above. Random UUIDs (message IDs, room IDs, chatgrp UIDs) can stay — they're high-entropy and not identifying on their own.
Hits on coords with 5+ decimal places, non-sequential ANDROID hex, or RFC 1918 IPs mean it's still dirty. (Sequential `ANDROID-0+\d+` is allowed — that's the test-fake convention.)
189
+
4. Regenerate goldens via `gradle jvmTest --tests "CompressionTest.generate compression report" --rerun-tasks` so the derived `.pb` / `.bin` artifacts pick up the clean strings. The Kotlin test writes both into `testdata/golden/` and `testdata/protobuf/`.
190
+
191
+
**If a leak ships to master:**
192
+
193
+
The recovery is `git filter-repo` in two passes — text substitution for the XML/code/docs, then a `--blob-callback` for the binary `.pb` and `.bin` files (filter-repo skips text replacement on files with null bytes, so binaries need an explicit callback that swaps the bad blob SHAs for clean re-baselined ones). Force-push every branch and tag. Open a GitHub Support ticket to purge orphaned commits — they remain reachable by SHA URL until cache expires (~30 days). Forks aren't touched by the force-push.
194
+
195
+
The detailed playbook is in `.github/copilot-instructions.md` under "PII and test-fixture sanitization."
0 commit comments