fix: connector path/id/webrtc hardening (#538, #539, part of #536) - #18
Merged
Conversation
…leak - validatePath: enforce allowlist/containment on a path boundary instead of startsWith, so a sibling dir sharing a prefix (e.g. /custom-plugins-evil) is no longer accepted (part of #536). - MatchData.id: restrict to path-safe characters so it cannot escape /pod-manifests when interpolated into file paths; route the derived manifest paths through basename as defense-in-depth (#538). - WebrtcService: close native PeerConnections and drop them from pcMap on terminal ICE state, dedupe on re-offer, and close all on shutdown, fixing an unbounded native-resource/map leak per region latency test (#539). Adds focused specs for the path boundary and id validation.
…eview follow-up) Review caught a race: node-datachannel delivers a connection's 'closed' state change on a later tick, so a re-offer that replaced the map entry would have its stale event close the NEW connection (key-only lookup). closePeerConnection now takes the specific instance, only evicts the map entry when it still points at that instance, and a WeakSet prevents double-close. onStateChange closes its own connection; dedupe/shutdown target the current entry.
Contributor
Author
|
Independent code review run on the diff. It caught a real race in the WebRTC cleanup: node-datachannel delivers a connection's |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hardens three findings from the 2026-07 audit that are self-contained and non-breaking. Closes #538, #539; partially addresses #536 (see note).
Changes
Path traversal on offline-match ids (#538)
MatchData.idis now validated with@Matches(/^[A-Za-z0-9_-]+$/). It is interpolated into/pod-manifests/<id>.yamland.json, so a..//value could escape that directory. Match ids are UUIDs, which the pattern accepts.offline-matches.service.tsnow go through amanifestPath()helper that appliespath.basename, as defense-in-depth.File-operations path boundary (part of #536)
validatePathusedstartsWith, so a sibling directory sharing a prefix (for example/custom-plugins-evil) was accepted as an allowed base. Replaced with anisWithinboundary check (child === root || child.startsWith(root + sep)) for both the allowlist and the traversal containment check. Legitimate/servers/<id>and/custom-plugins[/...]paths are unaffected.WebRTC connection leak (#539)
WebrtcServicenever closedPeerConnections or removed them frompcMap, so every region latency test leaked a native connection (and its closures) for the pod's lifetime. Now: connections are closed and removed on terminal ICE state (disconnected/failed/closed), a re-offer for the samepeerIdcloses the previous connection first, andonModuleDestroycloses any remaining.node-datachannelrequires an explicitclose()to free native resources.Not included here (needs a decision, deliberately deferred)
The authentication halves of #536 and #537 are not in this PR. The api's
FileManagerServicecalls/file-operations/*directly overhttp://<nodeIP>:8585with noAuthorizationheader (it relies on api-side admin checks), and the RCON WS gateway has the same caller coupling. AddingBasicGuardGuard(the issue's suggested fix) would break the file manager and RCON unless the api is taught to present a shared credential, or the connector's HTTP/WS port is restricted by a NetworkPolicy. That is an architectural choice that overlaps with #413, so it is left for a coordinated change. Details noted on #536/#537.Testing
yarn build: clean.file-operations.service.spec.ts(4, path boundary incl. the sibling-prefix regression) andMatchData.spec.ts(3, id validation) pass. Note: the connector's existing jest suite is broken on main (nosrc/path-aliasmoduleNameMapper, and class-transformer needsreflect-metadata); the new specs use relative imports and importreflect-metadata, so they run. The broken suite config is a separate pre-existing issue worth its own ticket.