Skip to content

Stop carrying contact endpoints over the mesh and drop malformed raw detail - #122

Merged
jamesarich merged 1 commit into
mainfrom
fix/mesh-contact-endpoint-and-raw-detail
Jul 26, 2026
Merged

Stop carrying contact endpoints over the mesh and drop malformed raw detail#122
jamesarich merged 1 commit into
mainfrom
fix/mesh-contact-endpoint-and-raw-detail

Conversation

@thebentern

@thebentern thebentern commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Two fixes to how the SDK rebuilds a CoT event, applied consistently across all five bindings.

1. A peer's <contact> endpoint no longer travels over the mesh

The parser stored any endpoint attribute that wasn't one of the two known defaults (0.0.0.0:4242:tcp, *:-1:stcp), and the builder re-emitted it on rebuild.

That endpoint is a host on the sending peer's own LAN. Every other mesh member that rebuilds the event hands its TAK client an address it has no route to, and the client fails to open the connection — it surfaces as a transmission socket that could not be created for <peer-lan-ip>:4243.

Now the parser never stores a contact endpoint, and the builder always emits the *:-1:stcp "reply via this server" form — the same form real ATAK presents server-relayed contacts with, and what makes ATAK route directed GeoChat / TAK-Talk (<marti>) back down the server stream.

Side benefits: ~20 bytes per message off the wire, and it removes the one place the Kotlin builder interpolated a value into an XML attribute without escaping it (the replacement is a constant).

2. Raw-detail fragments that would unbalance the wrapper are dropped

The raw-detail fallback re-emits its stored bytes verbatim as the inner content of the single <detail> element the builder is writing into.

Well-formed CoT escapes a literal < as &lt;, so that inner content can never legitimately contain an <event> or <detail> tag token. A fragment that does carry one is malformed, and re-emitting it as-is closes the wrapper early — the receiver is handed a document it can no longer parse as a single event.

The builder now leaves such fragments out and builds the rest of the event normally, so the rebuilt document still parses. Benign fragments are unaffected and still pass through byte-for-byte.

Tests

New RebuildHygiene suite in each of the five bindings (5 tests × 5 bindings):

  • a fragment closing the wrapper is dropped, and the rebuild still contains exactly one event
  • a benign fragment (<foo bar="1"/>) still round-trips verbatim
  • a packet carrying a concrete endpoint rebuilds as *:-1:stcp, with the concrete host absent
  • parsing a <contact endpoint="…"> leaves the packet's endpoint empty
  • a mixed-case fragment (</DETAIL></Event><Event …>) is dropped too

That last one closes a real gap: the guard case-folds before matching, but every other test used lowercase tokens, so removing the case-folding would have gone unnoticed. Each binding's test was checked against a mutant with the case-folding removed — the new test fails there while the others still pass.

Verification

Binding Before After
Kotlin (canonical) 327 332
C# 282 287
TypeScript 245 250
Python 229 234
Swift 39 44

No golden files changed — testdata/golden/, testdata/protobuf/ and compression-report.md are untouched, since neither fix alters the parsed protobuf for any existing fixture (every cot_xml fixture carries only *:-1:stcp, which already normalized to empty).

Note: Swift's suite needs the Xcode toolchain (DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer); under CommandLineTools alone the test target can't compile because of the pre-existing import Testing in CompatibilityTests.swift.

Summary by CodeRabbit

  • Bug Fixes

    • TAK XML rebuilding now consistently routes replies through the server stream endpoint.
    • Contact endpoint values are no longer retained when parsing incoming XML.
    • Malformed raw XML fragments are excluded to keep rebuilt messages well-formed.
    • Valid raw-detail content continues to be preserved.
  • Documentation

    • Updated endpoint optimization guidance to reflect the new behavior.

@thebentern
thebentern requested a review from jamesarich as a code owner July 26, 2026 12:11
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@thebentern, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c16b6af8-5bcd-449f-992b-d8bf70520c7f

📥 Commits

Reviewing files that changed from the base of the PR and between d4695e0 and 182c83c.

📒 Files selected for processing (18)
  • README.md
  • csharp/src/Meshtastic.TAK/CotXmlBuilder.cs
  • csharp/src/Meshtastic.TAK/CotXmlParser.cs
  • csharp/tests/Meshtastic.TAK.Tests/RebuildHygieneTests.cs
  • kotlin/src/commonMain/kotlin/org/meshtastic/tak/CotXmlBuilder.kt
  • kotlin/src/commonMain/kotlin/org/meshtastic/tak/CotXmlParser.kt
  • kotlin/src/commonMain/kotlin/org/meshtastic/tak/TakPacketV2Data.kt
  • kotlin/src/jvmTest/kotlin/org/meshtastic/tak/RebuildHygieneTest.kt
  • python/src/meshtastic_tak/cot_xml_builder.py
  • python/src/meshtastic_tak/cot_xml_parser.py
  • python/tests/test_rebuild_hygiene.py
  • swift/Sources/MeshtasticTAK/CotXmlBuilder.swift
  • swift/Sources/MeshtasticTAK/CotXmlParser.swift
  • swift/Tests/MeshtasticTAKTests/RebuildHygieneTests.swift
  • typescript/src/CotXmlBuilder.ts
  • typescript/src/CotXmlParser.ts
  • typescript/src/types.ts
  • typescript/tests/rebuildHygiene.test.ts
📝 Walkthrough

Walkthrough

CoT builders across C#, Kotlin, Python, Swift, and TypeScript now reject raw-detail fragments containing wrapper tags and always emit the server-reply endpoint. Parsers no longer retain contact endpoints, with cross-language tests and documentation updated accordingly.

Changes

CoT rebuild hygiene

Layer / File(s) Summary
Raw-detail wrapper safety
csharp/src/Meshtastic.TAK/CotXmlBuilder.cs, kotlin/src/commonMain/.../CotXmlBuilder.kt, python/src/meshtastic_tak/cot_xml_builder.py, swift/Sources/.../CotXmlBuilder.swift, typescript/src/CotXmlBuilder.ts, */tests/*RebuildHygiene*
Raw-detail fragments containing <event> or <detail> wrapper tokens are omitted, while benign fragments remain embeddable; tests cover normal and mixed-case tokens.
Endpoint parse and rebuild handling
README.md, */src/*CotXmlBuilder*, */src/*CotXmlParser*, kotlin/src/commonMain/.../TakPacketV2Data.kt, typescript/src/types.ts, */tests/*RebuildHygiene*
Builders always emit *:-1:stcp, parsers leave parsed endpoints empty, and documentation and tests cover the contract.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: jamesarich

Poem

I’m a rabbit guarding tags in a row,
Dropping stray wrappers before they grow.
Endpoints hop to the server stream,
Parsers keep packets clean and lean.
Tests twitch their noses: all XML is sound!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: removing carried contact endpoints and dropping malformed raw-detail fragments.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
python/tests/test_rebuild_hygiene.py (1)

16-16: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Wildcard import flagged by Ruff (F403).

Explicit imports of the used constants would resolve the static analysis warning and avoid namespace pollution.

♻️ Suggested fix
-from meshtastic_tak.cot_type_mapper import *
+from meshtastic_tak.cot_type_mapper import COTTYPE_A_F_G_U_C, COTHOW_M_G
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/tests/test_rebuild_hygiene.py` at line 16, Replace the wildcard import
from meshtastic_tak.cot_type_mapper in test_rebuild_hygiene.py with explicit
imports for only the constants and symbols used by the test, eliminating the
Ruff F403 warning and namespace pollution.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@csharp/src/Meshtastic.TAK/CotXmlBuilder.cs`:
- Around line 219-223: Route-specific contact output still omits the fixed
endpoint. Update EmitRoute in csharp/src/Meshtastic.TAK/CotXmlBuilder.cs and the
route case in swift/Sources/MeshtasticTAK/CotXmlBuilder.swift to emit
endpoint="*:-1:stcp"; add corresponding route-packet endpoint assertions in
csharp/tests/Meshtastic.TAK.Tests/RebuildHygieneTests.cs and
swift/Tests/MeshtasticTAKTests/RebuildHygieneTests.swift.

In `@README.md`:
- Line 810: Update TakPacketV2Serializer to omit or clear data.endpoint before
protobuf serialization, preventing direct callers from emitting concrete
endpoints; add a serialization-level regression test covering this behavior. In
README.md lines 810-810, retain the endpoint-elision guarantee once
serializer-side handling is implemented.

---

Nitpick comments:
In `@python/tests/test_rebuild_hygiene.py`:
- Line 16: Replace the wildcard import from meshtastic_tak.cot_type_mapper in
test_rebuild_hygiene.py with explicit imports for only the constants and symbols
used by the test, eliminating the Ruff F403 warning and namespace pollution.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 06b4ffd2-263b-40b6-862c-8054dcd98975

📥 Commits

Reviewing files that changed from the base of the PR and between 247bc52 and d4695e0.

📒 Files selected for processing (18)
  • README.md
  • csharp/src/Meshtastic.TAK/CotXmlBuilder.cs
  • csharp/src/Meshtastic.TAK/CotXmlParser.cs
  • csharp/tests/Meshtastic.TAK.Tests/RebuildHygieneTests.cs
  • kotlin/src/commonMain/kotlin/org/meshtastic/tak/CotXmlBuilder.kt
  • kotlin/src/commonMain/kotlin/org/meshtastic/tak/CotXmlParser.kt
  • kotlin/src/commonMain/kotlin/org/meshtastic/tak/TakPacketV2Data.kt
  • kotlin/src/jvmTest/kotlin/org/meshtastic/tak/RebuildHygieneTest.kt
  • python/src/meshtastic_tak/cot_xml_builder.py
  • python/src/meshtastic_tak/cot_xml_parser.py
  • python/tests/test_rebuild_hygiene.py
  • swift/Sources/MeshtasticTAK/CotXmlBuilder.swift
  • swift/Sources/MeshtasticTAK/CotXmlParser.swift
  • swift/Tests/MeshtasticTAKTests/RebuildHygieneTests.swift
  • typescript/src/CotXmlBuilder.ts
  • typescript/src/CotXmlParser.ts
  • typescript/src/types.ts
  • typescript/tests/rebuildHygiene.test.ts

Comment thread csharp/src/Meshtastic.TAK/CotXmlBuilder.cs
Comment thread README.md
| Optimization | Savings | Description |
|-------------|---------|-------------|
| **Endpoint normalization** | ~20 B/msg | Default endpoints (`0.0.0.0:4242:tcp`, `*:-1:stcp`) normalized to empty; builder restores the default on reconstruction |
| **Endpoint dropped** | ~20 B/msg | The `<contact>` endpoint never travels over the mesh: the parser stores none, and the builder always re-emits the `*:-1:stcp` "reply via this server" form. A peer's concrete host is not reachable by other mesh members, so forwarding it would only point the receiving client at a socket it cannot open |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Endpoint elision stops at parsing, not Kotlin serialization. TakPacketV2Serializer.kt still copies data.endpoint into the protobuf, so direct callers can send a concrete endpoint despite these unconditional documentation claims.

  • README.md#L810-L810: retain this guarantee only after serializer-side endpoint elision is implemented.
  • kotlin/src/commonMain/kotlin/org/meshtastic/tak/TakPacketV2Data.kt#L48-L49: update TakPacketV2Serializer.kt to omit or clear endpoint, and add a serialization-level regression test.
📍 Affects 2 files
  • README.md#L810-L810 (this comment)
  • kotlin/src/commonMain/kotlin/org/meshtastic/tak/TakPacketV2Data.kt#L48-L49
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 810, Update TakPacketV2Serializer to omit or clear
data.endpoint before protobuf serialization, preventing direct callers from
emitting concrete endpoints; add a serialization-level regression test covering
this behavior. In README.md lines 810-810, retain the endpoint-elision guarantee
once serializer-side handling is implemented.

…detail

Two rebuild fixes, applied to CotXmlParser/CotXmlBuilder in all five bindings.

Contact endpoint
----------------
The parser stored any <contact endpoint="..."> that was not one of the two known
defaults, and the builder re-emitted it. Over the mesh that host belongs to the
sending peer's own LAN, so every other member rebuilding the event pointed its
TAK client at an address it cannot reach and the client failed to create the
transmission socket. The parser now never stores an endpoint, and the builder
always emits the "*:-1:stcp" reply-via-this-server form, which is what makes ATAK
route directed GeoChat / TAK-Talk back down the server stream. This also saves
~20 bytes per message and removes the one spot where the builder interpolated a
value into an attribute without escaping it.

Raw detail passthrough
----------------------
The raw-detail fallback re-emitted its stored bytes verbatim as the inner content
of the single <detail> element being built. Well-formed CoT escapes a literal '<'
as '&lt;', so that content can never legitimately hold an <event> or <detail> tag
token; a fragment carrying one unbalances the wrapper and hands the receiver a
document it can no longer parse as a single event. Those fragments are now left
out, and the rest of the event is still built normally.

Adds RebuildHygiene tests to all five bindings covering both invariants, plus a
mixed-case fragment so the guard's case-insensitivity cannot regress unnoticed.

Verified: Kotlin 332, C# 287, TypeScript 250, Python 234, Swift 44 - all passing.
No golden files changed.
@thebentern
thebentern force-pushed the fix/mesh-contact-endpoint-and-raw-detail branch from d4695e0 to 182c83c Compare July 26, 2026 12:33
@jamesarich
jamesarich added this pull request to the merge queue Jul 26, 2026
Merged via the queue into main with commit cd287fe Jul 26, 2026
12 checks passed
@thebentern
thebentern deleted the fix/mesh-contact-endpoint-and-raw-detail branch July 26, 2026 23:58
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.

2 participants