Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package netbird for openSUSE:Factory checked 
in at 2026-10-27 18:23:29
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/netbird (Old)
 and      /work/SRC/openSUSE:Factory/.netbird.new.2252680 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "netbird"

Tue Oct 27 18:23:29 2026 rev:52 rq:1393129 version:0.81.5

Changes:
--------
--- /work/SRC/openSUSE:Factory/netbird/netbird.changes  2026-10-26 
09:58:55.267990578 +0100
+++ /work/SRC/openSUSE:Factory/.netbird.new.2252680/netbird.changes     
2026-10-27 18:24:02.055338042 +0100
@@ -1,0 +2,9 @@
+Mon Oct 26 20:34:39 UTC 2026 - Marcus Rueckert <[email protected]>
+
+- Update to 0.81.5
+  - [client] Validate and bound SSH session requests and channels
+    (#8344) by @lixmal in #8368
+  - [client] Bound the SSH session request queue and keep resizes
+    from blocking the session loop (#8363) by @lixmal in #8369
+
+-------------------------------------------------------------------

Old:
----
  netbird-0.81.4.obscpio

New:
----
  netbird-0.81.5.obscpio
  update.sh

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ netbird.spec ++++++
--- /var/tmp/diff_new_pack.7Wk9td/_old  2026-10-27 18:24:07.623570785 +0100
+++ /var/tmp/diff_new_pack.7Wk9td/_new  2026-10-27 18:24:07.625570868 +0100
@@ -32,7 +32,7 @@
 %bcond_with stub_config
 
 Name:           netbird
-Version:        0.81.4
+Version:        0.81.5
 Release:        0
 Summary:        Mesh VPN based on WireGuard
 License:        AGPL-3.0-only AND BSD-3-Clause
@@ -43,6 +43,7 @@
 Source3:        %{name}-management.service
 Source4:        %{name}-signal.service
 Source5:        series
+Source99:       update.sh
 Patch0:         service-install-cli-change.patch
 Patch1:         skip-ui.patch
 BuildRequires:  fdupes
@@ -351,6 +352,7 @@
   TestWaitJWTToken_DropsTokenFromASessionThatEndedBeforeTheWait
   TestLogin_ProfileSwitchRejectsJWTObtainedUnderPreviousConfig
   TestExchangeWithFallback_TCPTruncatesToClientSize
+  TestSession_ResizeWhileRunningIsApplied
 )
 # Assemble skip string by replacing spaces with a pipe.
 disable=$(echo ${failing_tests[*]} | sed 's/ /|/g')

++++++ _service ++++++
--- /var/tmp/diff_new_pack.7Wk9td/_old  2026-10-27 18:24:07.673572874 +0100
+++ /var/tmp/diff_new_pack.7Wk9td/_new  2026-10-27 18:24:07.681573209 +0100
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/netbirdio/netbird.git</param>
     <param name="scm">git</param>
     <param name="package-meta">yes</param>
-    <param name="revision">refs/tags/v0.81.4</param>
+    <param name="revision">refs/tags/v0.81.5</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="changesgenerate">disable</param>

++++++ netbird-0.81.4.obscpio -> netbird-0.81.5.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/.git/FETCH_HEAD 
new/netbird-0.81.5/.git/FETCH_HEAD
--- old/netbird-0.81.4/.git/FETCH_HEAD  2026-10-24 10:56:12.000000000 +0200
+++ new/netbird-0.81.5/.git/FETCH_HEAD  2026-10-26 17:02:31.000000000 +0100
@@ -33,6 +33,7 @@
 691a90516df4dc029a00a61f0d60dbefda708c40       not-for-merge   branch 
'bug/ios-hanging-reconection' of https://github.com/netbirdio/netbird
 1474f977319a8cc310422a0de38d824b4170004d       not-for-merge   branch 
'bug/rw-active-profile' of https://github.com/netbirdio/netbird
 ab987f17f072b2e0a4840fe4c87307e7313f637e       not-for-merge   branch 
'bug/update-ios-client-code-build-tags' of https://github.com/netbirdio/netbird
+0965f8a8888c5817b09a336d410a365d4078fc6c       not-for-merge   branch 
'bump/wails-v3.0.0' of https://github.com/netbirdio/netbird
 5b975f4681c919247dbf119f2e536f09cc04cc92       not-for-merge   branch 
'bump_wireguard_go' of https://github.com/netbirdio/netbird
 5ebd39ad3349caaa29bfbdf5c60b93267701eb90       not-for-merge   branch 
'cached-serial-check-on-sync' of https://github.com/netbirdio/netbird
 e6d8591a6a3c9e0983ffc402ecab810bb3728f6e       not-for-merge   branch 
'chore/benchmark-with-large-runner' of https://github.com/netbirdio/netbird
@@ -62,6 +63,7 @@
 8c5648bb7b522ca79c60d4d1481aa947249a6a92       not-for-merge   branch 
'coderabbitai/docstrings/b7e98ac' of https://github.com/netbirdio/netbird
 c11622721cf6d278562ae26e32065eb8d111e1e7       not-for-merge   branch 
'coderabbitai/utg/1e5b0a5' of https://github.com/netbirdio/netbird
 cb1eaf9e0d30daf1300dfa005abe9b10fe1a1adc       not-for-merge   branch 
'coderabbitai/utg/8ae8f20' of https://github.com/netbirdio/netbird
+af1a9f667e4c6d750493c6df5990c172f1e6fbd0       not-for-merge   branch 
'combined-disable-stun-relay' of https://github.com/netbirdio/netbird
 572bea6a718be862bfcd6729e510d95e0ad37a6f       not-for-merge   branch 
'conntrack-stats' of https://github.com/netbirdio/netbird
 20a79c5c555b6ab78c7837b4ebf7f80b5967db98       not-for-merge   branch 
'crowdsec-selfhosted' of https://github.com/netbirdio/netbird
 f44040feb083c8c569ec7eb16138eaf5129163b3       not-for-merge   branch 
'daemon-ipc-acl' of https://github.com/netbirdio/netbird
@@ -169,6 +171,7 @@
 ec4d09215c956909fc691110585ebd75ffa28264       not-for-merge   branch 
'feat/better-daemon-connection-lost-message' of 
https://github.com/netbirdio/netbird
 c262c1b2520fb6489d7594635e0c119ed1f861d4       not-for-merge   branch 
'feat/buffer-account-peers-update' of https://github.com/netbirdio/netbird
 d23f993fa3b1ac9deb92930ddfe9229d0cb92fcf       not-for-merge   branch 
'feat/client-hebrew-rtl' of https://github.com/netbirdio/netbird
+95dceb4b51e8fac08c8b9a3e245215799e83da39       not-for-merge   branch 
'feat/crowdsec-appsec-installer' of https://github.com/netbirdio/netbird
 4d69874770aed598b5a0e575bc3a3b7ae0625b93       not-for-merge   branch 
'feat/events-metrics' of https://github.com/netbirdio/netbird
 f74d66c9ad3e235101dd56ccd33afa20e90a75a0       not-for-merge   branch 
'feat/getting-started-unified-wizard' of https://github.com/netbirdio/netbird
 9291e3134bedcaaad6931adb5a439d2bd0efe3e6       not-for-merge   branch 
'feat/integrate-zitadel' of https://github.com/netbirdio/netbird
@@ -389,7 +392,7 @@
 97ad3307ddb9e07f40a71eaa9489ca74947a6791       not-for-merge   branch 
'log/conn-disconn' of https://github.com/netbirdio/netbird
 a69dc29e7e7e24cf6cf7746632d7b8046c0a70e7       not-for-merge   branch 
'log/getaccount-by-peer' of https://github.com/netbirdio/netbird
 0886b67ce951e5827d87571ac079b6f516027915       not-for-merge   branch 
'logs/peerlogs-addpeer' of https://github.com/netbirdio/netbird
-9dd51ca42e975780bc6c9691111a712b7f08f430       not-for-merge   branch 'main' 
of https://github.com/netbirdio/netbird
+2c68a84a976e8e3fb7f90aa59347fe2271e8de1d       not-for-merge   branch 'main' 
of https://github.com/netbirdio/netbird
 b03343bc4d249af295a4833161289d8339a0052a       not-for-merge   branch 
'manual-peer-logout' of https://github.com/netbirdio/netbird
 6efc1a61fe8568c3ae0704329aa2589c537e801b       not-for-merge   branch 
'merged-fixes' of https://github.com/netbirdio/netbird
 82cec5fb1d4c2127be0b7590481aceec508c7c4f       not-for-merge   branch 
'mlsmaycon-patch-1' of https://github.com/netbirdio/netbird
@@ -421,7 +424,6 @@
 8f7949ca945fb4692938b06a931b113fec9e47ff       not-for-merge   branch 
'poc/goose-migration' of https://github.com/netbirdio/netbird
 0252da7b243f91c66225e0b7c7cff27432f89495       not-for-merge   branch 
'poc/goose-migration-sqlc' of https://github.com/netbirdio/netbird
 0c4c2a8cb24f48c44d74fc213f7cf5150c51bd1f       not-for-merge   branch 
'poc/netstack' of https://github.com/netbirdio/netbird
-bd4a844f4a96f8356016a44ceaf769b3f299bd76       not-for-merge   branch 
'poc/nmdata-on-read-replica' of https://github.com/netbirdio/netbird
 51eee4c5acecec3a9cf93e6fc67a4c66b25f8ea5       not-for-merge   branch 
'poc/preprocessed-map' of https://github.com/netbirdio/netbird
 0ef8371627a8fae0816fe560f81878cc7ff4ee8a       not-for-merge   branch 
'poc/udpmux-txid-routing' of https://github.com/netbirdio/netbird
 ea01ef79f7aa6753b2dad8ab81360272f0376e07       not-for-merge   branch 
'posture-checks-poc' of https://github.com/netbirdio/netbird
@@ -429,7 +431,7 @@
 59cc28702ba85660ba0ab4c7e8431328cea8775e       not-for-merge   branch 
'profile-bindings-ios' of https://github.com/netbirdio/netbird
 0a61d7b20e99aa4852d68e996806265d42e14f38       not-for-merge   branch 
'profile-id' of https://github.com/netbirdio/netbird
 ac6ee89cd5196fa0da052f019a0e5f5026d86fc8       not-for-merge   branch 
'profile-id-name-test' of https://github.com/netbirdio/netbird
-df6c55f1a4b15d2fc604487eab112c1cd017db24       not-for-merge   branch 
'profile-ownership' of https://github.com/netbirdio/netbird
+4ab791ba23bf4ed23f7d9f2552b5ade191fb166a       not-for-merge   branch 
'profile-ownership' of https://github.com/netbirdio/netbird
 3d6591b053555fa1065a3641a619541a1ea407e7       not-for-merge   branch 
'profile-ownership-cleanup' of https://github.com/netbirdio/netbird
 4605d0feba9a68f28fa5df8b424a152e5eb66963       not-for-merge   branch 
'profile-ownership-migration' of https://github.com/netbirdio/netbird
 04b8bd27efb70dc40ad4b3eaec58cec1e3d3fbe1       not-for-merge   branch 
'profile-test-merge' of https://github.com/netbirdio/netbird
@@ -469,6 +471,9 @@
 42867c7a59fde64ad6771feabc71daa48c20cbe0       not-for-merge   branch 
'refactor/simplify-affected-peers' of https://github.com/netbirdio/netbird
 093b8cb41d1a11023690e71958b633df1656057b       not-for-merge   branch 
'refactor/wails-update-105' of https://github.com/netbirdio/netbird
 5568be0bb1266e3c25ce873fae1d64c4fd671859       not-for-merge   branch 
'relay-hmac-cleanup' of https://github.com/netbirdio/netbird
+e7f2a00cce4b856f2b6acaced8a73af5210a51cc       not-for-merge   branch 
'relay-only-session-restart' of https://github.com/netbirdio/netbird
+173d5eaf41935665cb187057aa2bbba75b3a639e       not-for-merge   branch 
'relay-peer-reregistration' of https://github.com/netbirdio/netbird
+69d4676d2835f71860989504083c6bed0e902437       not-for-merge   branch 
'relay-picker-abort-losing-dials' of https://github.com/netbirdio/netbird
 95ad62ea85f9c79888ad66856ab22d76c7ea657f       not-for-merge   branch 
'relay/2800_quic' of https://github.com/netbirdio/netbird
 32fbb7da5125b80e9c471b49d07367ef91e46f53       not-for-merge   branch 
'relay/debug' of https://github.com/netbirdio/netbird
 56badd75351be4fc1d1662db90886285a01a4912       not-for-merge   branch 
'relay/eliminate-gob' of https://github.com/netbirdio/netbird
@@ -476,15 +481,19 @@
 300b6695012158bdede367738a0389a25d83cf04       not-for-merge   branch 
'release-0.78' of https://github.com/netbirdio/netbird
 396795ebb6822402ad4eff88c90682d99f77d987       not-for-merge   branch 
'release-0.79' of https://github.com/netbirdio/netbird
 fca64287cf51a85552a41b022013abbfdb335452       not-for-merge   branch 
'release-0.80' of https://github.com/netbirdio/netbird
-7e791d3e2972ddb763bb0e5c3bff4f9e14f70033       not-for-merge   branch 
'release-0.81' of https://github.com/netbirdio/netbird
+396976b8be081fbf0d3d959698352585e80618e3       not-for-merge   branch 
'release-0.81' of https://github.com/netbirdio/netbird
 099b3bdf442fba90930a2085b9f03c0d337bc234       not-for-merge   branch 
'release-0.81-bump-quic-go' of https://github.com/netbirdio/netbird
 be52f270d5edf74b2866f1566fe12e27de678714       not-for-merge   branch 
'release-0.81-certificate-posture-followups' of 
https://github.com/netbirdio/netbird
 2d1905ae31585cf91e3ffdeaf9eaaad65840c9a4       not-for-merge   branch 
'release-0.81-emudhra-distrust' of https://github.com/netbirdio/netbird
 e308df9c57cafde06b8799a8a92c204546bd3efd       not-for-merge   branch 
'release-0.81-fix-affected-snapshot' of https://github.com/netbirdio/netbird
 0cd8fa6c4fc5aa5f75f6c9f6990cc78586fdc418       not-for-merge   branch 
'release-0.81-is-child' of https://github.com/netbirdio/netbird
 6e7e21fa30116635dee20b263ba552a81e18c2e4       not-for-merge   branch 
'release-0.81-signal-registration-wait' of https://github.com/netbirdio/netbird
+082918ea123c1358d2ab5418e1ae9e3dc97e3745       not-for-merge   branch 
'release-0.81-ssh-session-limits' of https://github.com/netbirdio/netbird
+fa49fb67aaf9c8acbead4ebf8eaf47a5ce75316c       not-for-merge   branch 
'release-0.81-ssh-session-limits-followup' of 
https://github.com/netbirdio/netbird
 3d9333a2b82b6aa3e9b5650b572f6b6bd1cd9533       not-for-merge   branch 
'release-0.81-udpmux-discovery-outside-bind-open' of 
https://github.com/netbirdio/netbird
+2c68a84a976e8e3fb7f90aa59347fe2271e8de1d       not-for-merge   branch 
'release-0.82' of https://github.com/netbirdio/netbird
 3dd34c920ec30f3797aad0ec87fb3d92a328dda1       not-for-merge   branch 
'release/0.68.3' of https://github.com/netbirdio/netbird
+91bfdd9fb4d434e54c097d817289e78d95eee1eb       not-for-merge   branch 
'remove-ios-bump-trigger' of https://github.com/netbirdio/netbird
 1bb306fae42e6990b573ffba1de3695574e4a1fc       not-for-merge   branch 
'remove-new-routing' of https://github.com/netbirdio/netbird
 1e273c901716b779f621334cb859ee8bb9f58bb4       not-for-merge   branch 
'remove-ubuntu2004-runners' of https://github.com/netbirdio/netbird
 fae27656c238c0c9e60719919aa7eb60172ee9fd       not-for-merge   branch 
'reorder-dns-shutdown' of https://github.com/netbirdio/netbird
@@ -503,6 +512,7 @@
 c187dcac0a0ad250261ed2a20c50a14b2849d5f2       not-for-merge   branch 
'revert-ice-5820' of https://github.com/netbirdio/netbird
 2bb55b6c8856067b582fd911af340b5670db8d2e       not-for-merge   branch 
'revert/component-types-hookup' of https://github.com/netbirdio/netbird
 460778abb996670802611016419ef00d2ce938f3       not-for-merge   branch 
'revert/component-types-test-suite' of https://github.com/netbirdio/netbird
+b06ebaa6295a836785d00183aa2c80b4207f75b6       not-for-merge   branch 
'rosenpass_deprecation_notice' of https://github.com/netbirdio/netbird
 ef55b9eccc22bf01743b5476738d85bef70e1688       not-for-merge   branch 
'route-get-account-refactoring' of https://github.com/netbirdio/netbird
 13537e6640ea7c3fc79c72f707f859201ce5c859       not-for-merge   branch 
'router-dns' of https://github.com/netbirdio/netbird
 945f8809ee49a6bd4a39ee0eed0ca15fd22147e3       not-for-merge   branch 
'rp_key_persistency' of https://github.com/netbirdio/netbird
@@ -525,7 +535,6 @@
 6e76a14a8fef933e245de8108f2349812f52572e       not-for-merge   branch 
'sqlite-async-peer-status' of https://github.com/netbirdio/netbird
 c45f1083d667d042b08d46d2142b3c94070a07c8       not-for-merge   branch 'ssh' of 
https://github.com/netbirdio/netbird
 d28b229baf4cbe538b2dc78d565c8482e916c906       not-for-merge   branch 
'ssh-permitted-targets' of https://github.com/netbirdio/netbird
-70a8c1522560a6cf009fb9a4f3e02e5d754e5d29       not-for-merge   branch 
'status-peers-table' of https://github.com/netbirdio/netbird
 5f5d597c59b1553489691bc114b1962670132589       not-for-merge   branch 
'stop-using-locking-share' of https://github.com/netbirdio/netbird
 447b06c62bf7c1162d68599a76dbe4760c805230       not-for-merge   branch 
'swap-dns-prio' of https://github.com/netbirdio/netbird
 5236c71d2f930a47dc127a15b9274c0de036ef29       not-for-merge   branch 
'sync-serial-bump' of https://github.com/netbirdio/netbird
@@ -600,12 +609,14 @@
 d48fe5360935f249af33101ef48cae47b5c376bf       not-for-merge   branch 
'windows-dns-firewall' of https://github.com/netbirdio/netbird
 e3d1b9ca880ff4f1e6624e306b6957aa6c30e5c1       not-for-merge   branch 
'windows-search-domains' of https://github.com/netbirdio/netbird
 25fe7d13d91a4227f67bddf1b6aeabf842bd611c       not-for-merge   branch 
'windows-sleep-detector' of https://github.com/netbirdio/netbird
+e1bd29ad07ac486a5d8f1b0f264867d5201b80be       not-for-merge   branch 
'windows-toast-injection' of https://github.com/netbirdio/netbird
 9b05593a4e2f6d9e38e6e59d688009e20ad11fd1       not-for-merge   branch 
'work/admiring-planck-1l2xh2' of https://github.com/netbirdio/netbird
 5d9b1fc444fd2d188e3204120988789ab3438f52       not-for-merge   branch 
'work/amazing-wright-6xehqh' of https://github.com/netbirdio/netbird
 df45a84ba7ed531e6fbcfc4e9645b002e0af9d05       not-for-merge   branch 
'work/bold-faraday-dcn47i' of https://github.com/netbirdio/netbird
 9bd8c57153ee853cc35cbc12bdfd5ca47f799c91       not-for-merge   branch 
'work/cool-fermat-awn9fu' of https://github.com/netbirdio/netbird
-ba8a99df320c0ebcc75db30c26566e5057a0d14e       not-for-merge   branch 
'work/cool-pasteur-erog5e' of https://github.com/netbirdio/netbird
-2b95e0f563424b52b1bb2fed0bcffcca4cf6295d       not-for-merge   branch 
'work/cool-pasteur-erog5e-mcp' of https://github.com/netbirdio/netbird
+28589e215fcfc7452ae2e3d78d73b8bffc32d6a5       not-for-merge   branch 
'work/cool-pasteur-erog5e' of https://github.com/netbirdio/netbird
+2df979ab7faec0acc370115fa642a3ae1a9e7596       not-for-merge   branch 
'work/cool-pasteur-erog5e-guides' of https://github.com/netbirdio/netbird
+6bfe0f5a16a4cd32c08a21bcc411dc5d311fb624       not-for-merge   branch 
'work/cool-pasteur-erog5e-mcp' of https://github.com/netbirdio/netbird
 487075af74a16d396eef52cc2d85ca8caba4aa43       not-for-merge   branch 
'work/exciting-lamport-q85fl9' of https://github.com/netbirdio/netbird
 c60b71adb6ac982bfb4d5a8e24f1de18eb5030ee       not-for-merge   branch 
'work/funny-goodall-vw54vx' of https://github.com/netbirdio/netbird
 8f0e0c67d82329460b4b5e116c44cd51600a047a       not-for-merge   branch 
'work/optimistic-ptolemy-krc00k' of https://github.com/netbirdio/netbird
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/.git/HEAD new/netbird-0.81.5/.git/HEAD
--- old/netbird-0.81.4/.git/HEAD        2026-10-24 10:56:12.000000000 +0200
+++ new/netbird-0.81.5/.git/HEAD        2026-10-26 17:02:31.000000000 +0100
@@ -1 +1 @@
-7e791d3e2972ddb763bb0e5c3bff4f9e14f70033
+396976b8be081fbf0d3d959698352585e80618e3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/.git/ORIG_HEAD 
new/netbird-0.81.5/.git/ORIG_HEAD
--- old/netbird-0.81.4/.git/ORIG_HEAD   2026-10-24 10:56:12.000000000 +0200
+++ new/netbird-0.81.5/.git/ORIG_HEAD   2026-10-26 17:02:31.000000000 +0100
@@ -1 +1 @@
-7e791d3e2972ddb763bb0e5c3bff4f9e14f70033
+396976b8be081fbf0d3d959698352585e80618e3
Binary files old/netbird-0.81.4/.git/index and new/netbird-0.81.5/.git/index 
differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/.git/logs/HEAD 
new/netbird-0.81.5/.git/logs/HEAD
--- old/netbird-0.81.4/.git/logs/HEAD   2026-10-24 10:56:12.000000000 +0200
+++ new/netbird-0.81.5/.git/logs/HEAD   2026-10-26 17:02:31.000000000 +0100
@@ -60,3 +60,4 @@
 838378b4c2913bafb43f9f933f6b88e39f3ba43d 
28657607c14d4b5135c48cb79fcb9f4fd0600b42 Marcus 'darix' Rückert 
<[email protected]> 1792115424 +0200      reset: moving to 
refs/tags/v0.81.1
 28657607c14d4b5135c48cb79fcb9f4fd0600b42 
f47ace7d52a182d2ee75e6d821c26260e601c89e Marcus Rückert <[email protected]> 
1792444443 +0200 checkout: moving from 28657607c14d4b5135c48cb79fcb9f4fd0600b42 
to v0.81.3
 f47ace7d52a182d2ee75e6d821c26260e601c89e 
7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 Marcus Rückert <[email protected]> 
1792850852 +0200 checkout: moving from f47ace7d52a182d2ee75e6d821c26260e601c89e 
to v0.81.4
+7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 
396976b8be081fbf0d3d959698352585e80618e3 Marcus Rückert <[email protected]> 
1793046869 +0100 checkout: moving from 7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 
to v0.81.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/bump/wails-v3.0.0 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/bump/wails-v3.0.0
--- old/netbird-0.81.4/.git/logs/refs/remotes/origin/bump/wails-v3.0.0  
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/bump/wails-v3.0.0  
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
0965f8a8888c5817b09a336d410a365d4078fc6c Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/combined-disable-stun-relay 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/combined-disable-stun-relay
--- 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/combined-disable-stun-relay    
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/combined-disable-stun-relay    
    2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
af1a9f667e4c6d750493c6df5990c172f1e6fbd0 Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/feat/crowdsec-appsec-installer 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/feat/crowdsec-appsec-installer
--- 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/feat/crowdsec-appsec-installer 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/feat/crowdsec-appsec-installer 
    2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
95dceb4b51e8fac08c8b9a3e245215799e83da39 Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/main 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/main
--- old/netbird-0.81.4/.git/logs/refs/remotes/origin/main       2026-10-24 
10:56:12.000000000 +0200
+++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/main       2026-10-26 
17:02:31.000000000 +0100
@@ -54,3 +54,5 @@
 afd5c6df6a26eec3c0a81df2374dfe1ce9983ea8 
28657607c14d4b5135c48cb79fcb9f4fd0600b42 Marcus Rückert <[email protected]> 
1792115337 +0200 pull: fast-forward
 28657607c14d4b5135c48cb79fcb9f4fd0600b42 
8fef487e4256e592c1ff916fd9cd155f1849b881 Marcus Rückert <[email protected]> 
1792444414 +0200 pull: fast-forward
 8fef487e4256e592c1ff916fd9cd155f1849b881 
9dd51ca42e975780bc6c9691111a712b7f08f430 Marcus Rückert <[email protected]> 
1792850847 +0200 pull: fast-forward
+9dd51ca42e975780bc6c9691111a712b7f08f430 
dd38fabc82d7314c11500d7fb8f1ee8161258250 Marcus Rückert <[email protected]> 
1792880562 +0200 fetch --tags: fast-forward
+dd38fabc82d7314c11500d7fb8f1ee8161258250 
2c68a84a976e8e3fb7f90aa59347fe2271e8de1d Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: fast-forward
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/poc/nmdata-on-read-replica 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/poc/nmdata-on-read-replica
--- old/netbird-0.81.4/.git/logs/refs/remotes/origin/poc/nmdata-on-read-replica 
2026-10-24 10:56:12.000000000 +0200
+++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/poc/nmdata-on-read-replica 
1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-0000000000000000000000000000000000000000 
bd4a844f4a96f8356016a44ceaf769b3f299bd76 Marcus Rückert <[email protected]> 
1792850847 +0200 pull: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/profile-ownership 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/profile-ownership
--- old/netbird-0.81.4/.git/logs/refs/remotes/origin/profile-ownership  
2026-10-24 10:56:12.000000000 +0200
+++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/profile-ownership  
2026-10-26 17:02:31.000000000 +0100
@@ -5,3 +5,4 @@
 b7fada83b9d43adcd708a24b33c7cc5f38b42d91 
6167431e0a505ea8c9451e3ab2251c2f1cc49879 Marcus Rückert <[email protected]> 
1792075781 +0200 pull: fast-forward
 6167431e0a505ea8c9451e3ab2251c2f1cc49879 
69ecec2161afb91cdb336cc66ed8c5d552de00be Marcus Rückert <[email protected]> 
1792444414 +0200 pull: fast-forward
 69ecec2161afb91cdb336cc66ed8c5d552de00be 
df6c55f1a4b15d2fc604487eab112c1cd017db24 Marcus Rückert <[email protected]> 
1792850847 +0200 pull: fast-forward
+df6c55f1a4b15d2fc604487eab112c1cd017db24 
4ab791ba23bf4ed23f7d9f2552b5ade191fb166a Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: fast-forward
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/relay-only-session-restart 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/relay-only-session-restart
--- old/netbird-0.81.4/.git/logs/refs/remotes/origin/relay-only-session-restart 
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/relay-only-session-restart 
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
e7f2a00cce4b856f2b6acaced8a73af5210a51cc Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/relay-peer-reregistration 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/relay-peer-reregistration
--- old/netbird-0.81.4/.git/logs/refs/remotes/origin/relay-peer-reregistration  
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/relay-peer-reregistration  
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
173d5eaf41935665cb187057aa2bbba75b3a639e Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/relay-picker-abort-losing-dials
 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/relay-picker-abort-losing-dials
--- 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/relay-picker-abort-losing-dials
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/relay-picker-abort-losing-dials
    2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
69d4676d2835f71860989504083c6bed0e902437 Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.81 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.81
--- old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.81       
2026-10-24 10:56:12.000000000 +0200
+++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.81       
2026-10-26 17:02:31.000000000 +0100
@@ -2,3 +2,4 @@
 c0f090448c14fe106144a248669e3f854d383f65 
bb90c9787a8a3591bcb3cccd106a024d1a30b0ef Marcus Rückert <[email protected]> 
1792115337 +0200 pull: fast-forward
 bb90c9787a8a3591bcb3cccd106a024d1a30b0ef 
f47ace7d52a182d2ee75e6d821c26260e601c89e Marcus Rückert <[email protected]> 
1792444414 +0200 pull: fast-forward
 f47ace7d52a182d2ee75e6d821c26260e601c89e 
7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 Marcus Rückert <[email protected]> 
1792850847 +0200 pull: fast-forward
+7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 
396976b8be081fbf0d3d959698352585e80618e3 Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: fast-forward
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits
 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits
--- 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits
    2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
082918ea123c1358d2ab5418e1ae9e3dc97e3745 Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits-followup
 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits-followup
--- 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits-followup
   1970-01-01 01:00:00.000000000 +0100
+++ 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits-followup
   2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
fa49fb67aaf9c8acbead4ebf8eaf47a5ce75316c Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.82 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.82
--- old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.82       
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.82       
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
2c68a84a976e8e3fb7f90aa59347fe2271e8de1d Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/remove-ios-bump-trigger 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/remove-ios-bump-trigger
--- old/netbird-0.81.4/.git/logs/refs/remotes/origin/remove-ios-bump-trigger    
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/remove-ios-bump-trigger    
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
91bfdd9fb4d434e54c097d817289e78d95eee1eb Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/rosenpass_deprecation_notice 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/rosenpass_deprecation_notice
--- 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/rosenpass_deprecation_notice   
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/rosenpass_deprecation_notice   
    2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
b06ebaa6295a836785d00183aa2c80b4207f75b6 Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/status-peers-table 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/status-peers-table
--- old/netbird-0.81.4/.git/logs/refs/remotes/origin/status-peers-table 
2026-10-24 10:56:12.000000000 +0200
+++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/status-peers-table 
1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-0000000000000000000000000000000000000000 
70a8c1522560a6cf009fb9a4f3e02e5d754e5d29 Marcus Rückert <[email protected]> 
1792850847 +0200 pull: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/windows-toast-injection 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/windows-toast-injection
--- old/netbird-0.81.4/.git/logs/refs/remotes/origin/windows-toast-injection    
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/windows-toast-injection    
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
e1bd29ad07ac486a5d8f1b0f264867d5201b80be Marcus Rückert <[email protected]> 
1793046868 +0100 fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e
--- old/netbird-0.81.4/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e   
2026-10-24 10:56:12.000000000 +0200
+++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e   
2026-10-26 17:02:31.000000000 +0100
@@ -1 +1,2 @@
 0000000000000000000000000000000000000000 
ba8a99df320c0ebcc75db30c26566e5057a0d14e Marcus Rückert <[email protected]> 
1792850847 +0200 pull: storing head
+ba8a99df320c0ebcc75db30c26566e5057a0d14e 
28589e215fcfc7452ae2e3d78d73b8bffc32d6a5 Marcus Rückert <[email protected]> 
1792880562 +0200 fetch --tags: fast-forward
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-guides
 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-guides
--- 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-guides
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-guides
    2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
2df979ab7faec0acc370115fa642a3ae1a9e7596 Marcus Rückert <[email protected]> 
1792880562 +0200 fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-mcp 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-mcp
--- 
old/netbird-0.81.4/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-mcp   
    2026-10-24 10:56:12.000000000 +0200
+++ 
new/netbird-0.81.5/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-mcp   
    2026-10-26 17:02:31.000000000 +0100
@@ -1 +1,2 @@
 0000000000000000000000000000000000000000 
2b95e0f563424b52b1bb2fed0bcffcca4cf6295d Marcus Rückert <[email protected]> 
1792850847 +0200 pull: storing head
+2b95e0f563424b52b1bb2fed0bcffcca4cf6295d 
6bfe0f5a16a4cd32c08a21bcc411dc5d311fb624 Marcus Rückert <[email protected]> 
1792880562 +0200 fetch --tags: fast-forward
Binary files 
old/netbird-0.81.4/.git/objects/pack/pack-3ac4f806d90d51ee545b47197ab92dd4376c6bf7.idx
 and 
new/netbird-0.81.5/.git/objects/pack/pack-3ac4f806d90d51ee545b47197ab92dd4376c6bf7.idx
 differ
Binary files 
old/netbird-0.81.4/.git/objects/pack/pack-3ac4f806d90d51ee545b47197ab92dd4376c6bf7.pack
 and 
new/netbird-0.81.5/.git/objects/pack/pack-3ac4f806d90d51ee545b47197ab92dd4376c6bf7.pack
 differ
Binary files 
old/netbird-0.81.4/.git/objects/pack/pack-3ac4f806d90d51ee545b47197ab92dd4376c6bf7.rev
 and 
new/netbird-0.81.5/.git/objects/pack/pack-3ac4f806d90d51ee545b47197ab92dd4376c6bf7.rev
 differ
Binary files 
old/netbird-0.81.4/.git/objects/pack/pack-86453fec019843479730691baac169c57c3401f7.idx
 and 
new/netbird-0.81.5/.git/objects/pack/pack-86453fec019843479730691baac169c57c3401f7.idx
 differ
Binary files 
old/netbird-0.81.4/.git/objects/pack/pack-86453fec019843479730691baac169c57c3401f7.pack
 and 
new/netbird-0.81.5/.git/objects/pack/pack-86453fec019843479730691baac169c57c3401f7.pack
 differ
Binary files 
old/netbird-0.81.4/.git/objects/pack/pack-86453fec019843479730691baac169c57c3401f7.rev
 and 
new/netbird-0.81.5/.git/objects/pack/pack-86453fec019843479730691baac169c57c3401f7.rev
 differ
Binary files 
old/netbird-0.81.4/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.idx
 and 
new/netbird-0.81.5/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.idx
 differ
Binary files 
old/netbird-0.81.4/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.pack
 and 
new/netbird-0.81.5/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.pack
 differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.promisor
 
new/netbird-0.81.5/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.promisor
--- 
old/netbird-0.81.4/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.promisor
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/netbird-0.81.5/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.promisor
 2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+4efc597ebb09637baa373b2aa7b13b57688cd793 
4efc597ebb09637baa373b2aa7b13b57688cd793
Binary files 
old/netbird-0.81.4/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.rev
 and 
new/netbird-0.81.5/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.rev
 differ
Binary files 
old/netbird-0.81.4/.git/objects/pack/pack-d0d78b136593b4c930c4456f71dcffcf907278e6.idx
 and 
new/netbird-0.81.5/.git/objects/pack/pack-d0d78b136593b4c930c4456f71dcffcf907278e6.idx
 differ
Binary files 
old/netbird-0.81.4/.git/objects/pack/pack-d0d78b136593b4c930c4456f71dcffcf907278e6.pack
 and 
new/netbird-0.81.5/.git/objects/pack/pack-d0d78b136593b4c930c4456f71dcffcf907278e6.pack
 differ
Binary files 
old/netbird-0.81.4/.git/objects/pack/pack-d0d78b136593b4c930c4456f71dcffcf907278e6.rev
 and 
new/netbird-0.81.5/.git/objects/pack/pack-d0d78b136593b4c930c4456f71dcffcf907278e6.rev
 differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/.git/packed-refs 
new/netbird-0.81.5/.git/packed-refs
--- old/netbird-0.81.4/.git/packed-refs 2026-10-24 10:56:12.000000000 +0200
+++ new/netbird-0.81.5/.git/packed-refs 2026-10-26 17:02:31.000000000 +0100
@@ -423,7 +423,6 @@
 8f7949ca945fb4692938b06a931b113fec9e47ff 
refs/remotes/origin/poc/goose-migration
 0252da7b243f91c66225e0b7c7cff27432f89495 
refs/remotes/origin/poc/goose-migration-sqlc
 0c4c2a8cb24f48c44d74fc213f7cf5150c51bd1f refs/remotes/origin/poc/netstack
-bd4a844f4a96f8356016a44ceaf769b3f299bd76 
refs/remotes/origin/poc/nmdata-on-read-replica
 51eee4c5acecec3a9cf93e6fc67a4c66b25f8ea5 
refs/remotes/origin/poc/preprocessed-map
 0ef8371627a8fae0816fe560f81878cc7ff4ee8a 
refs/remotes/origin/poc/udpmux-txid-routing
 ea01ef79f7aa6753b2dad8ab81360272f0376e07 refs/remotes/origin/posture-checks-poc
@@ -527,7 +526,6 @@
 6e76a14a8fef933e245de8108f2349812f52572e 
refs/remotes/origin/sqlite-async-peer-status
 c45f1083d667d042b08d46d2142b3c94070a07c8 refs/remotes/origin/ssh
 d28b229baf4cbe538b2dc78d565c8482e916c906 
refs/remotes/origin/ssh-permitted-targets
-70a8c1522560a6cf009fb9a4f3e02e5d754e5d29 refs/remotes/origin/status-peers-table
 5f5d597c59b1553489691bc114b1962670132589 
refs/remotes/origin/stop-using-locking-share
 447b06c62bf7c1162d68599a76dbe4760c805230 refs/remotes/origin/swap-dns-prio
 5236c71d2f930a47dc127a15b9274c0de036ef29 refs/remotes/origin/sync-serial-bump
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/bump/wails-v3.0.0 
new/netbird-0.81.5/.git/refs/remotes/origin/bump/wails-v3.0.0
--- old/netbird-0.81.4/.git/refs/remotes/origin/bump/wails-v3.0.0       
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/bump/wails-v3.0.0       
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+0965f8a8888c5817b09a336d410a365d4078fc6c
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/combined-disable-stun-relay 
new/netbird-0.81.5/.git/refs/remotes/origin/combined-disable-stun-relay
--- old/netbird-0.81.4/.git/refs/remotes/origin/combined-disable-stun-relay     
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/combined-disable-stun-relay     
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+af1a9f667e4c6d750493c6df5990c172f1e6fbd0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/feat/crowdsec-appsec-installer 
new/netbird-0.81.5/.git/refs/remotes/origin/feat/crowdsec-appsec-installer
--- old/netbird-0.81.4/.git/refs/remotes/origin/feat/crowdsec-appsec-installer  
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/feat/crowdsec-appsec-installer  
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+95dceb4b51e8fac08c8b9a3e245215799e83da39
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/main 
new/netbird-0.81.5/.git/refs/remotes/origin/main
--- old/netbird-0.81.4/.git/refs/remotes/origin/main    1970-01-01 
01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/main    2026-10-26 
17:02:31.000000000 +0100
@@ -0,0 +1 @@
+2c68a84a976e8e3fb7f90aa59347fe2271e8de1d
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/profile-ownership 
new/netbird-0.81.5/.git/refs/remotes/origin/profile-ownership
--- old/netbird-0.81.4/.git/refs/remotes/origin/profile-ownership       
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/profile-ownership       
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+4ab791ba23bf4ed23f7d9f2552b5ade191fb166a
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/relay-only-session-restart 
new/netbird-0.81.5/.git/refs/remotes/origin/relay-only-session-restart
--- old/netbird-0.81.4/.git/refs/remotes/origin/relay-only-session-restart      
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/relay-only-session-restart      
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+e7f2a00cce4b856f2b6acaced8a73af5210a51cc
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/relay-peer-reregistration 
new/netbird-0.81.5/.git/refs/remotes/origin/relay-peer-reregistration
--- old/netbird-0.81.4/.git/refs/remotes/origin/relay-peer-reregistration       
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/relay-peer-reregistration       
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+173d5eaf41935665cb187057aa2bbba75b3a639e
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/relay-picker-abort-losing-dials 
new/netbird-0.81.5/.git/refs/remotes/origin/relay-picker-abort-losing-dials
--- old/netbird-0.81.4/.git/refs/remotes/origin/relay-picker-abort-losing-dials 
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/relay-picker-abort-losing-dials 
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+69d4676d2835f71860989504083c6bed0e902437
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/release-0.81 
new/netbird-0.81.5/.git/refs/remotes/origin/release-0.81
--- old/netbird-0.81.4/.git/refs/remotes/origin/release-0.81    1970-01-01 
01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/release-0.81    2026-10-26 
17:02:31.000000000 +0100
@@ -0,0 +1 @@
+396976b8be081fbf0d3d959698352585e80618e3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/release-0.81-ssh-session-limits 
new/netbird-0.81.5/.git/refs/remotes/origin/release-0.81-ssh-session-limits
--- old/netbird-0.81.4/.git/refs/remotes/origin/release-0.81-ssh-session-limits 
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/release-0.81-ssh-session-limits 
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+082918ea123c1358d2ab5418e1ae9e3dc97e3745
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/release-0.81-ssh-session-limits-followup
 
new/netbird-0.81.5/.git/refs/remotes/origin/release-0.81-ssh-session-limits-followup
--- 
old/netbird-0.81.4/.git/refs/remotes/origin/release-0.81-ssh-session-limits-followup
        1970-01-01 01:00:00.000000000 +0100
+++ 
new/netbird-0.81.5/.git/refs/remotes/origin/release-0.81-ssh-session-limits-followup
        2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+fa49fb67aaf9c8acbead4ebf8eaf47a5ce75316c
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/release-0.82 
new/netbird-0.81.5/.git/refs/remotes/origin/release-0.82
--- old/netbird-0.81.4/.git/refs/remotes/origin/release-0.82    1970-01-01 
01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/release-0.82    2026-10-26 
17:02:31.000000000 +0100
@@ -0,0 +1 @@
+2c68a84a976e8e3fb7f90aa59347fe2271e8de1d
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/remove-ios-bump-trigger 
new/netbird-0.81.5/.git/refs/remotes/origin/remove-ios-bump-trigger
--- old/netbird-0.81.4/.git/refs/remotes/origin/remove-ios-bump-trigger 
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/remove-ios-bump-trigger 
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+91bfdd9fb4d434e54c097d817289e78d95eee1eb
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/rosenpass_deprecation_notice 
new/netbird-0.81.5/.git/refs/remotes/origin/rosenpass_deprecation_notice
--- old/netbird-0.81.4/.git/refs/remotes/origin/rosenpass_deprecation_notice    
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/rosenpass_deprecation_notice    
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+b06ebaa6295a836785d00183aa2c80b4207f75b6
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/windows-toast-injection 
new/netbird-0.81.5/.git/refs/remotes/origin/windows-toast-injection
--- old/netbird-0.81.4/.git/refs/remotes/origin/windows-toast-injection 
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/windows-toast-injection 
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+e1bd29ad07ac486a5d8f1b0f264867d5201b80be
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/work/cool-pasteur-erog5e 
new/netbird-0.81.5/.git/refs/remotes/origin/work/cool-pasteur-erog5e
--- old/netbird-0.81.4/.git/refs/remotes/origin/work/cool-pasteur-erog5e        
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/work/cool-pasteur-erog5e        
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+28589e215fcfc7452ae2e3d78d73b8bffc32d6a5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/work/cool-pasteur-erog5e-guides 
new/netbird-0.81.5/.git/refs/remotes/origin/work/cool-pasteur-erog5e-guides
--- old/netbird-0.81.4/.git/refs/remotes/origin/work/cool-pasteur-erog5e-guides 
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/work/cool-pasteur-erog5e-guides 
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+2df979ab7faec0acc370115fa642a3ae1a9e7596
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/.git/refs/remotes/origin/work/cool-pasteur-erog5e-mcp 
new/netbird-0.81.5/.git/refs/remotes/origin/work/cool-pasteur-erog5e-mcp
--- old/netbird-0.81.4/.git/refs/remotes/origin/work/cool-pasteur-erog5e-mcp    
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/remotes/origin/work/cool-pasteur-erog5e-mcp    
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1 @@
+6bfe0f5a16a4cd32c08a21bcc411dc5d311fb624
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/.git/refs/tags/v0.81.5 
new/netbird-0.81.5/.git/refs/tags/v0.81.5
--- old/netbird-0.81.4/.git/refs/tags/v0.81.5   1970-01-01 01:00:00.000000000 
+0100
+++ new/netbird-0.81.5/.git/refs/tags/v0.81.5   2026-10-26 17:02:31.000000000 
+0100
@@ -0,0 +1 @@
+396976b8be081fbf0d3d959698352585e80618e3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/.git/refs/tags/v0.82.0-rc.1 
new/netbird-0.81.5/.git/refs/tags/v0.82.0-rc.1
--- old/netbird-0.81.4/.git/refs/tags/v0.82.0-rc.1      1970-01-01 
01:00:00.000000000 +0100
+++ new/netbird-0.81.5/.git/refs/tags/v0.82.0-rc.1      2026-10-26 
17:02:31.000000000 +0100
@@ -0,0 +1 @@
+ecd07be76f20f4b05be57d4d94c31fb0f78756a2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/client/ssh/server/command_execution_windows.go 
new/netbird-0.81.5/client/ssh/server/command_execution_windows.go
--- old/netbird-0.81.4/client/ssh/server/command_execution_windows.go   
2026-10-24 10:56:12.000000000 +0200
+++ new/netbird-0.81.5/client/ssh/server/command_execution_windows.go   
2026-10-26 17:02:31.000000000 +0100
@@ -267,7 +267,7 @@
        return env
 }
 
-func (s *Server) handlePtyLogin(logger *log.Entry, session ssh.Session, 
privilegeResult PrivilegeCheckResult, ptyReq ssh.Pty, _ <-chan ssh.Window) bool 
{
+func (s *Server) handlePtyLogin(logger *log.Entry, session ssh.Session, 
privilegeResult PrivilegeCheckResult, ptyReq ssh.Pty, winCh <-chan ssh.Window) 
bool {
        if privilegeResult.User == nil {
                logger.Errorf("no user in privilege result")
                return false
@@ -276,7 +276,7 @@
        shell := getUserShell(privilegeResult.User.Uid)
        logger.Infof("starting interactive shell: %s", shell)
 
-       s.executeCommandWithPty(logger, session, nil, privilegeResult, ptyReq, 
nil)
+       s.executeCommandWithPty(logger, session, nil, privilegeResult, ptyReq, 
winCh)
        return true
 }
 
@@ -376,7 +376,9 @@
 }
 
 // executeCommandWithPty executes a command with PTY allocation on Windows 
using ConPty
-func (s *Server) executeCommandWithPty(logger *log.Entry, session ssh.Session, 
_ *exec.Cmd, privilegeResult PrivilegeCheckResult, ptyReq ssh.Pty, _ <-chan 
ssh.Window) bool {
+func (s *Server) executeCommandWithPty(logger *log.Entry, session ssh.Session, 
_ *exec.Cmd, privilegeResult PrivilegeCheckResult, ptyReq ssh.Pty, winCh <-chan 
ssh.Window) bool {
+       discardWindowChanges(winCh)
+
        localUser := privilegeResult.User
        if localUser == nil {
                logger.Errorf("no user in privilege result")
@@ -406,3 +408,18 @@
        logger.Debug("ConPty execution completed")
        return true
 }
+
+// discardWindowChanges drains the session's window-change channel. The ConPty 
is sized
+// once at start, and an undrained channel blocks the session's request loop 
on the
+// next resize.
+func discardWindowChanges(winCh <-chan ssh.Window) {
+       if winCh == nil {
+               return
+       }
+       go func() {
+               for range winCh {
+                       // The ConPty keeps the size it was started with.
+                       continue
+               }
+       }()
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/client/ssh/server/server.go 
new/netbird-0.81.5/client/ssh/server/server.go
--- old/netbird-0.81.4/client/ssh/server/server.go      2026-10-24 
10:56:12.000000000 +0200
+++ new/netbird-0.81.5/client/ssh/server/server.go      2026-10-26 
17:02:31.000000000 +0100
@@ -58,6 +58,10 @@
 // the forwarded destination before rejecting the SSH channel.
 const directTCPIPDialTimeout = 30 * time.Second
 
+// defaultHandshakeTimeout bounds how long a connection may take to complete 
the SSH
+// handshake and authentication before it is dropped.
+const defaultHandshakeTimeout = 60 * time.Second
+
 var (
        ErrPrivilegedUserDisabled = errors.New(msgPrivilegedUserDisabled)
        ErrUserNotFound           = errors.New("user not found")
@@ -165,6 +169,7 @@
        allowRemotePortForwarding bool
        allowRootLogin            bool
        allowSFTP                 bool
+       handshakeTimeout          time.Duration
        jwtEnabled                bool
 
        netstackNet *netstack.Net
@@ -227,6 +232,7 @@
                jwtEnabled:             config.JWT != nil,
                jwtConfig:              config.JWT,
                authorizer:             sshauth.NewAuthorizer(),
+               handshakeTimeout:       defaultHandshakeTimeout,
        }
 
        if config.Auth != nil {
@@ -884,15 +890,16 @@
                },
                HostSigners: []ssh.Signer{},
                ChannelHandlers: map[string]ssh.ChannelHandler{
-                       "session":      ssh.DefaultSessionHandler,
+                       "session":      s.sessionChannelHandler,
                        "direct-tcpip": s.directTCPIPHandler,
                },
                RequestHandlers: map[string]ssh.RequestHandler{
                        "tcpip-forward":        s.tcpipForwardHandler,
                        "cancel-tcpip-forward": s.cancelTcpipForwardHandler,
                },
-               ConnCallback: s.connectionValidator,
-               Version:      serverVersion,
+               ConnCallback:     s.connectionValidator,
+               HandshakeTimeout: s.handshakeTimeout,
+               Version:          serverVersion,
        }
 
        if s.jwtEnabled {
@@ -934,6 +941,14 @@
 func (s *Server) directTCPIPHandler(srv *ssh.Server, conn 
*cryptossh.ServerConn, newChan cryptossh.NewChannel, ctx ssh.Context) {
        logger := s.getRequestLogger(ctx)
 
+       release, ok := acquireConnSlot(ctx, directTCPIPCountKey{}, 
maxDirectTCPIPPerConn)
+       if !ok {
+               logger.Warnf("local port forwarding denied: too many forwarded 
connections")
+               _ = newChan.Reject(cryptossh.ResourceShortage, "too many 
forwarded connections")
+               return
+       }
+       defer release()
+
        var payload struct {
                Host           string
                Port           uint32
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/client/ssh/server/session_handlers.go 
new/netbird-0.81.5/client/ssh/server/session_handlers.go
--- old/netbird-0.81.4/client/ssh/server/session_handlers.go    2026-10-24 
10:56:12.000000000 +0200
+++ new/netbird-0.81.5/client/ssh/server/session_handlers.go    2026-10-26 
17:02:31.000000000 +0100
@@ -53,13 +53,20 @@
                logger.Infof("SSH session closed after %v", duration)
        }()
 
+       ptyReq, winCh, isPty := session.Pty()
+       if isPty {
+               // The request loop blocks sending a resize nobody reads, and 
only sees its request
+               // stream close once that send completes. Covers the paths 
below that return
+               // without consuming window changes.
+               defer drainWindowChanges(winCh)
+       }
+
        privilegeResult, err := s.userPrivilegeCheck(session.User())
        if err != nil {
                s.handlePrivError(logger, session, err)
                return
        }
 
-       ptyReq, winCh, isPty := session.Pty()
        hasCommand := session.RawCommand() != ""
 
        if isPty && !hasCommand {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/client/ssh/server/session_hardening_test.go 
new/netbird-0.81.5/client/ssh/server/session_hardening_test.go
--- old/netbird-0.81.4/client/ssh/server/session_hardening_test.go      
1970-01-01 01:00:00.000000000 +0100
+++ new/netbird-0.81.5/client/ssh/server/session_hardening_test.go      
2026-10-26 17:02:31.000000000 +0100
@@ -0,0 +1,402 @@
+//go:build !js
+
+package server
+
+import (
+       "bytes"
+       "encoding/binary"
+       "errors"
+       "io"
+       "net"
+       "os/user"
+       "runtime"
+       "strings"
+       "testing"
+       "time"
+
+       cryptossh "golang.org/x/crypto/ssh"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+
+       nbssh "github.com/netbirdio/netbird/client/ssh"
+)
+
+func dialNoAuthServer(t *testing.T, addr string, hostKey []byte) 
*cryptossh.Client {
+       t.Helper()
+
+       clientKey, err := nbssh.GeneratePrivateKey(nbssh.ED25519)
+       require.NoError(t, err)
+       signer, err := cryptossh.ParsePrivateKey(clientKey)
+       require.NoError(t, err)
+       hostSigner, err := cryptossh.ParsePrivateKey(hostKey)
+       require.NoError(t, err)
+       current, err := user.Current()
+       require.NoError(t, err)
+
+       client, err := cryptossh.Dial("tcp", addr, &cryptossh.ClientConfig{
+               User:            current.Username,
+               Auth:            
[]cryptossh.AuthMethod{cryptossh.PublicKeys(signer)},
+               HostKeyCallback: cryptossh.FixedHostKey(hostSigner.PublicKey()),
+               Timeout:         3 * time.Second,
+       })
+       require.NoError(t, err)
+       t.Cleanup(func() { _ = client.Close() })
+       return client
+}
+
+func startNoAuthServer(t *testing.T) (string, []byte) {
+       t.Helper()
+
+       hostKey, err := nbssh.GeneratePrivateKey(nbssh.ED25519)
+       require.NoError(t, err)
+       srv := New(&Config{HostKeyPEM: hostKey})
+       srv.SetAllowRootLogin(true)
+       addr := StartTestServer(t, srv)
+       t.Cleanup(func() { require.NoError(t, srv.Stop()) })
+       return addr, hostKey
+}
+
+// ptyReqPayload builds a pty-req payload whose term string claims the given 
length.
+func ptyReqPayload(termLen uint32, term string) []byte {
+       buf := make([]byte, 4, 64)
+       binary.BigEndian.PutUint32(buf, termLen)
+       buf = append(buf, term...)
+       buf = append(buf, make([]byte, 16)...)
+       return buf
+}
+
+// A pty-req with a term length close to the uint32 maximum must be refused 
and must
+// not take the daemon down.
+func TestSession_MalformedPtyReqDoesNotCrashServer(t *testing.T) {
+       addr, hostKey := startNoAuthServer(t)
+       client := dialNoAuthServer(t, addr, hostKey)
+
+       session, err := client.NewSession()
+       require.NoError(t, err)
+
+       ok, err := session.SendRequest("pty-req", true, 
ptyReqPayload(0xFFFFFFFF, "xterm"))
+       require.NoError(t, err)
+       assert.False(t, ok, "malformed pty-req should be rejected")
+       _ = session.Close()
+
+       // The server must still serve a fresh connection.
+       again := dialNoAuthServer(t, addr, hostKey)
+       s2, err := again.NewSession()
+       require.NoError(t, err)
+       require.NoError(t, s2.Close())
+}
+
+// Env requests past the per-session cap are refused in order, and the ones 
before it
+// are still accepted.
+func TestSession_EnvRequestsAreCapped(t *testing.T) {
+       addr, hostKey := startNoAuthServer(t)
+       client := dialNoAuthServer(t, addr, hostKey)
+
+       session, err := client.NewSession()
+       require.NoError(t, err)
+       t.Cleanup(func() { _ = session.Close() })
+
+       payload := cryptossh.Marshal(struct{ Key, Value string }{"FOO", "bar"})
+       accepted := 0
+       for i := 0; i < maxEnvRequests+50; i++ {
+               ok, err := session.SendRequest("env", true, payload)
+               require.NoError(t, err)
+               if ok {
+                       accepted++
+               }
+       }
+       assert.Equal(t, maxEnvRequests, accepted, "only the first 
maxEnvRequests env requests should be accepted")
+
+       oversized := cryptossh.Marshal(struct{ Key, Value string }{"BIG", 
string(make([]byte, maxEnvPayload))})
+       fresh, err := client.NewSession()
+       require.NoError(t, err)
+       t.Cleanup(func() { _ = fresh.Close() })
+       ok, err := fresh.SendRequest("env", true, oversized)
+       require.NoError(t, err)
+       assert.False(t, ok, "an env request over the payload limit should be 
refused")
+}
+
+// A flood of window-change requests before the command starts must not stall 
the
+// session's request loop: the next request still gets its reply.
+func TestSession_WindowChangeFloodDoesNotStallRequests(t *testing.T) {
+       addr, hostKey := startNoAuthServer(t)
+       client := dialNoAuthServer(t, addr, hostKey)
+
+       session, err := client.NewSession()
+       require.NoError(t, err)
+       t.Cleanup(func() { _ = session.Close() })
+
+       require.NoError(t, session.RequestPty("xterm", 24, 80, 
cryptossh.TerminalModes{}))
+
+       winch := cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 
}{100, 40, 0, 0})
+       for i := 0; i < 500; i++ {
+               _, err := session.SendRequest("window-change", false, winch)
+               require.NoError(t, err)
+       }
+
+       done := make(chan bool, 1)
+       go func() {
+               ok, err := session.SendRequest("env", true, 
cryptossh.Marshal(struct{ Key, Value string }{"A", "b"}))
+               done <- err == nil && ok
+       }()
+       select {
+       case ok := <-done:
+               assert.True(t, ok, "env request after the flood should be 
accepted")
+       case <-time.After(5 * time.Second):
+               t.Fatal("session request loop stalled behind window-change 
requests")
+       }
+}
+
+func TestSession_PerConnectionSessionLimit(t *testing.T) {
+       addr, hostKey := startNoAuthServer(t)
+       client := dialNoAuthServer(t, addr, hostKey)
+
+       for i := 0; i < maxSessionsPerConn; i++ {
+               s, err := client.NewSession()
+               require.NoError(t, err, "session %d should open", i)
+               t.Cleanup(func() { _ = s.Close() })
+       }
+
+       _, err := client.NewSession()
+       assert.Error(t, err, "session beyond the limit should be refused")
+
+       // The limit is per connection: another connection is unaffected.
+       other := dialNoAuthServer(t, addr, hostKey)
+       s, err := other.NewSession()
+       require.NoError(t, err, "a second connection should still open 
sessions")
+       require.NoError(t, s.Close())
+}
+
+func TestValidPtyReqPayload(t *testing.T) {
+       assert.True(t, validPtyReqPayload(ptyReqPayload(5, "xterm")))
+       assert.False(t, validPtyReqPayload(nil), "empty payload")
+       assert.False(t, validPtyReqPayload([]byte{0, 0, 0}), "short length 
prefix")
+       assert.False(t, validPtyReqPayload(ptyReqPayload(0xFFFFFFFF, "xterm")), 
"length that wraps uint32")
+       assert.False(t, validPtyReqPayload(ptyReqPayload(1000, "xterm")), 
"length beyond the payload")
+}
+
+// A resize sent while the command runs reaches the pty.
+func TestSession_ResizeWhileRunningIsApplied(t *testing.T) {
+       if runtime.GOOS == "windows" {
+               t.Skip("stty is not available")
+       }
+       addr, hostKey := startNoAuthServer(t)
+       client := dialNoAuthServer(t, addr, hostKey)
+
+       session, err := client.NewSession()
+       require.NoError(t, err)
+       t.Cleanup(func() { _ = session.Close() })
+
+       var out bytes.Buffer
+       session.Stdout = &out
+       require.NoError(t, session.RequestPty("xterm", 24, 80, 
cryptossh.TerminalModes{}))
+       require.NoError(t, session.Start("sleep 1; stty size"))
+
+       // Let the handler read the initial window before resizing.
+       time.Sleep(300 * time.Millisecond)
+       require.NoError(t, session.WindowChange(50, 132))
+       require.NoError(t, session.Wait())
+       // Only the first line is the resize report. Coverage-instrumented 
binaries can append
+       // runtime warnings to the same output.
+       firstLine := strings.TrimSpace(strings.SplitN(out.String(), "\n", 2)[0])
+       assert.Equal(t, "50 132", firstLine, "pty should have the resized 
dimensions")
+}
+
+// Forwarded connections beyond the per-connection cap are refused, and a 
closed one
+// frees its slot.
+func TestDirectTCPIP_PerConnectionLimit(t *testing.T) {
+       hostKey, err := nbssh.GeneratePrivateKey(nbssh.ED25519)
+       require.NoError(t, err)
+       srv := New(&Config{HostKeyPEM: hostKey})
+       srv.SetAllowRootLogin(true)
+       srv.SetAllowLocalPortForwarding(true)
+       addr := StartTestServer(t, srv)
+       t.Cleanup(func() { require.NoError(t, srv.Stop()) })
+
+       // Target that holds each connection open until the peer half-closes, 
then closes it
+       // like a regular server.
+       target, err := net.Listen("tcp", "127.0.0.1:0")
+       require.NoError(t, err)
+       t.Cleanup(func() { _ = target.Close() })
+       go func() {
+               for {
+                       c, err := target.Accept()
+                       if err != nil {
+                               return
+                       }
+                       go func() {
+                               _, _ = io.Copy(io.Discard, c)
+                               _ = c.Close()
+                       }()
+               }
+       }()
+
+       client := dialNoAuthServer(t, addr, hostKey)
+
+       conns := make([]net.Conn, 0, maxDirectTCPIPPerConn)
+       for i := 0; i < maxDirectTCPIPPerConn; i++ {
+               c, err := client.Dial("tcp", target.Addr().String())
+               require.NoError(t, err, "forward %d should open", i)
+               conns = append(conns, c)
+       }
+
+       _, err = client.Dial("tcp", target.Addr().String())
+       assert.Error(t, err, "forward beyond the limit should be refused")
+
+       // The limit is per connection: another connection is unaffected.
+       other := dialNoAuthServer(t, addr, hostKey)
+       oc, err := other.Dial("tcp", target.Addr().String())
+       require.NoError(t, err, "a second connection should still open 
forwards")
+       require.NoError(t, oc.Close())
+
+       require.NoError(t, conns[0].Close())
+       assert.Eventually(t, func() bool {
+               c, err := client.Dial("tcp", target.Addr().String())
+               if err != nil {
+                       return false
+               }
+               _ = c.Close()
+               return true
+       }, 3*time.Second, 50*time.Millisecond, "a closed forward should free 
its slot")
+
+       for _, c := range conns[1:] {
+               _ = c.Close()
+       }
+}
+
+// A connection that never completes the SSH handshake is dropped, and one 
that does
+// complete it is not subject to the handshake deadline afterwards.
+func TestHandshakeTimeout(t *testing.T) {
+       hostKey, err := nbssh.GeneratePrivateKey(nbssh.ED25519)
+       require.NoError(t, err)
+       srv := New(&Config{HostKeyPEM: hostKey})
+       srv.handshakeTimeout = 500 * time.Millisecond
+       addr := StartTestServer(t, srv)
+       t.Cleanup(func() { require.NoError(t, srv.Stop()) })
+
+       stalled, err := net.Dial("tcp", addr)
+       require.NoError(t, err)
+       t.Cleanup(func() { _ = stalled.Close() })
+
+       // The server sends its version line first; after that it waits for 
ours.
+       require.NoError(t, 
stalled.SetReadDeadline(time.Now().Add(5*time.Second)))
+       _, err = io.Copy(io.Discard, stalled)
+       var nerr net.Error
+       assert.False(t, errors.As(err, &nerr) && nerr.Timeout(), "server should 
close the stalled connection, not leave it open")
+
+       client := dialNoAuthServer(t, addr, hostKey)
+       time.Sleep(time.Second)
+       session, err := client.NewSession()
+       require.NoError(t, err, "an established connection must outlive the 
handshake deadline")
+       require.NoError(t, session.Close())
+}
+
+// Refused requests are forwarded to the session loop under a type it answers 
with a
+// failure, so every reply is sent by the loop in request order.
+func TestFilterSessionRequests_RefusedRequestsStayInOrder(t *testing.T) {
+       in := make(chan *cryptossh.Request)
+       out := filterSessionRequests(nil, in)
+
+       validWinch := cryptossh.Marshal(struct{ Columns, Rows, Width, Height 
uint32 }{100, 40, 0, 0})
+       reqs := []*cryptossh.Request{
+               {Type: "pty-req", Payload: ptyReqPayload(0xFFFFFFFF, "xterm")},
+               {Type: "pty-req", Payload: ptyReqPayload(5, "xterm")},
+               {Type: "window-change", Payload: validWinch},
+               {Type: "shell"},
+               {Type: "window-change", Payload: validWinch},
+               {Type: "window-change", Payload: validWinch},
+       }
+       want := []string{
+               refusedRequestType, // malformed pty-req
+               "pty-req",
+               refusedRequestType, // resize before the command starts
+               "shell",
+               refusedRequestType, // older of two queued resizes
+               "window-change",
+       }
+
+       // Hold all requests in the filter's queue before the loop consumes 
any, so the
+       // coalescing of the two trailing resizes is exercised.
+       for _, r := range reqs {
+               in <- r
+       }
+       for i, w := range want {
+               select {
+               case got := <-out:
+                       assert.Equal(t, w, got.Type, "request %d", i)
+               case <-time.After(2 * time.Second):
+                       t.Fatalf("request %d not delivered", i)
+               }
+       }
+
+       close(in)
+       select {
+       case _, ok := <-out:
+               assert.False(t, ok, "filter should close its output when the 
input closes")
+       case <-time.After(2 * time.Second):
+               t.Fatal("filter did not close its output")
+       }
+}
+
+// A rejected subsystem starts no command, so nothing drains the window 
channel. A resize
+// after it must be refused instead of blocking the session's request loop.
+func TestSession_ResizeAfterRejectedSubsystemDoesNotStallLoop(t *testing.T) {
+       addr, hostKey := startNoAuthServer(t)
+       client := dialNoAuthServer(t, addr, hostKey)
+
+       session, err := client.NewSession()
+       require.NoError(t, err)
+       t.Cleanup(func() { _ = session.Close() })
+
+       require.NoError(t, session.RequestPty("xterm", 24, 80, 
cryptossh.TerminalModes{}))
+       ok, err := session.SendRequest("subsystem", true, 
cryptossh.Marshal(struct{ Name string }{"nope"}))
+       require.NoError(t, err)
+       require.False(t, ok, "unknown subsystem must be rejected")
+
+       winch := cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 
}{100, 40, 0, 0})
+       _, err = session.SendRequest("window-change", false, winch)
+       require.NoError(t, err)
+
+       done := make(chan bool, 1)
+       go func() {
+               ok, err := session.SendRequest("env", true, 
cryptossh.Marshal(struct{ Key, Value string }{"A", "b"}))
+               done <- err == nil && ok
+       }()
+       select {
+       case ok := <-done:
+               assert.True(t, ok, "env request after the resize should be 
accepted")
+       case <-time.After(5 * time.Second):
+               t.Fatal("session request loop stalled behind a resize with no 
command running")
+       }
+}
+
+// A resize that reaches the loop while the command handler never reads window 
changes
+// (sftp) must not leave the loop goroutine behind once the handler returns.
+func TestSession_ResizeDuringSFTPDoesNotLeakRequestLoop(t *testing.T) {
+       addr, hostKey := startNoAuthServer(t)
+       loops := func() int {
+               buf := make([]byte, 1<<22)
+               return strings.Count(string(buf[:runtime.Stack(buf, true)]), 
"ssh.(*session).handleRequests")
+       }
+       before := loops()
+
+       winch := cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 
}{100, 40, 0, 0})
+       for i := 0; i < 10; i++ {
+               client := dialNoAuthServer(t, addr, hostKey)
+               session, err := client.NewSession()
+               require.NoError(t, err)
+               require.NoError(t, session.RequestPty("xterm", 24, 80, 
cryptossh.TerminalModes{}))
+               ok, err := session.SendRequest("subsystem", true, 
cryptossh.Marshal(struct{ Name string }{"sftp"}))
+               require.NoError(t, err)
+               require.True(t, ok, "the sftp subsystem must be accepted for 
the resize to matter")
+               // The sftp handler may already have ended the session, so a 
send can fail.
+               _, _ = session.SendRequest("window-change", false, winch)
+               _, _ = session.SendRequest("window-change", false, winch)
+               _ = session.Close()
+               _ = client.Close()
+       }
+
+       assert.Eventually(t, func() bool { return loops() <= before }, 
10*time.Second, 100*time.Millisecond,
+               "session request loops must exit after the clients disconnect")
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/client/ssh/server/session_limits.go 
new/netbird-0.81.5/client/ssh/server/session_limits.go
--- old/netbird-0.81.4/client/ssh/server/session_limits.go      1970-01-01 
01:00:00.000000000 +0100
+++ new/netbird-0.81.5/client/ssh/server/session_limits.go      2026-10-26 
17:02:31.000000000 +0100
@@ -0,0 +1,320 @@
+package server
+
+import (
+       "encoding/binary"
+       "math"
+       "sync/atomic"
+
+       "github.com/gliderlabs/ssh"
+       log "github.com/sirupsen/logrus"
+       cryptossh "golang.org/x/crypto/ssh"
+)
+
+const (
+       // maxSessionsPerConn bounds concurrent session channels on one SSH 
connection.
+       maxSessionsPerConn = 64
+
+       // maxDirectTCPIPPerConn bounds concurrent direct-tcpip channels, 
dialing or
+       // relaying, on one SSH connection.
+       maxDirectTCPIPPerConn = 512
+
+       // maxEnvRequests and maxEnvPayload bound the environment a single 
session can
+       // accumulate before its command starts.
+       maxEnvRequests = 128
+       maxEnvPayload  = 8 * 1024
+
+       // maxQueuedRequests and maxQueuedBytes bound the requests held for a 
session whose
+       // request loop is not consuming them. A client that exceeds either has 
its channel
+       // closed.
+       maxQueuedRequests = 128
+       maxQueuedBytes    = 256 * 1024
+)
+
+type (
+       sessionCountKey     struct{}
+       directTCPIPCountKey struct{}
+)
+
+// sessionChannelHandler serves "session" channels like 
ssh.DefaultSessionHandler, with
+// the per-connection session count capped, the request stream bounded, and 
any panic
+// from the session request loop contained to its channel.
+func (s *Server) sessionChannelHandler(srv *ssh.Server, conn 
*cryptossh.ServerConn, newChan cryptossh.NewChannel, ctx ssh.Context) {
+       release, ok := acquireConnSlot(ctx, sessionCountKey{}, 
maxSessionsPerConn)
+       if !ok {
+               if err := newChan.Reject(cryptossh.ResourceShortage, "too many 
sessions"); err != nil {
+                       log.Debugf("reject session channel: %v", err)
+               }
+               return
+       }
+       defer release()
+
+       guarded := &guardedSessionChannel{NewChannel: newChan}
+       defer func() {
+               r := recover()
+               if r == nil {
+                       return
+               }
+               log.Errorf("SSH session request loop panicked from %s: %v", 
ctx.RemoteAddr(), r)
+               guarded.closeChannel()
+       }()
+
+       ssh.DefaultSessionHandler(srv, conn, guarded, ctx)
+}
+
+// drainWindowChanges discards window changes until winCh is closed. It must 
run after
+// the session's command handler has returned.
+func drainWindowChanges(winCh <-chan ssh.Window) {
+       go func() {
+               for {
+                       if _, ok := <-winCh; !ok {
+                               return
+                       }
+               }
+       }()
+}
+
+// acquireConnSlot takes one of limit slots counted under key for the 
connection that
+// ctx belongs to. The returned func releases the slot.
+func acquireConnSlot(ctx ssh.Context, key any, limit int32) (func(), bool) {
+       counter := connCounter(ctx, key)
+       if counter.Add(1) > limit {
+               counter.Add(-1)
+               return nil, false
+       }
+       return func() { counter.Add(-1) }, true
+}
+
+func connCounter(ctx ssh.Context, key any) *atomic.Int32 {
+       ctx.Lock()
+       defer ctx.Unlock()
+
+       if c, ok := ctx.Value(key).(*atomic.Int32); ok {
+               return c
+       }
+       c := new(atomic.Int32)
+       ctx.SetValue(key, c)
+       return c
+}
+
+// guardedSessionChannel hands the session loop a filtered request stream.
+type guardedSessionChannel struct {
+       cryptossh.NewChannel
+       ch atomic.Pointer[cryptossh.Channel]
+}
+
+// Accept accepts the channel and returns its requests after filtering.
+func (g *guardedSessionChannel) Accept() (cryptossh.Channel, <-chan 
*cryptossh.Request, error) {
+       ch, reqs, err := g.NewChannel.Accept()
+       if err != nil {
+               return nil, nil, err
+       }
+       g.ch.Store(&ch)
+       return ch, filterSessionRequests(ch, reqs), nil
+}
+
+func (g *guardedSessionChannel) closeChannel() {
+       if ch := g.ch.Load(); ch != nil {
+               _ = (*ch).Close()
+       }
+}
+
+// refusedRequestType is the type given to a request the filter refuses. The 
session
+// loop answers unknown request types with a failure reply, so a refused 
request is
+// answered by the loop itself and in its position among the replies, which the
+// protocol requires since replies carry no request identifier.
+const refusedRequestType = "[email protected]"
+
+// requestFilter passes session channel requests to the session loop while 
keeping
+// what the loop can accumulate or block on bounded:
+//   - a malformed pty-req is refused instead of reaching the loop's parser
+//   - env requests beyond maxEnvRequests, or with an oversized payload, are 
refused
+//   - window-change requests are refused unless the session's command is a 
shell or
+//     exec, and coalesced after it starts, as the loop blocks sending on a 
one-slot
+//     channel that only a shell or exec handler drains. Subsystems never read 
it.
+//   - request types the loop does not act on are refused, which also drops 
their payload
+//   - the queue behind a stalled loop is capped by count and by payload 
bytes, after
+//     which the channel is closed
+type requestFilter struct {
+       ch          cryptossh.Channel
+       queue       []*queuedRequest
+       queuedBytes int
+       lastWinch   *queuedRequest
+       envCount    int
+
+       // commandSeen is set by the first shell, exec or subsystem request, 
which decides
+       // the session's command as far as the loop is concerned.
+       commandSeen bool
+       // started is set when that first command is a shell or exec.
+       started bool
+}
+
+// queuedRequest remembers the payload size counted against the queue, as a 
refusal
+// drops the payload and releases that size.
+type queuedRequest struct {
+       req  *cryptossh.Request
+       size int
+}
+
+// filterSessionRequests returns the requests of in as the session loop should 
see them.
+func filterSessionRequests(ch cryptossh.Channel, in <-chan *cryptossh.Request) 
<-chan *cryptossh.Request {
+       out := make(chan *cryptossh.Request)
+       f := &requestFilter{ch: ch}
+       go func() {
+               defer close(out)
+               f.run(in, out)
+       }()
+       return out
+}
+
+func (f *requestFilter) run(in <-chan *cryptossh.Request, out chan<- 
*cryptossh.Request) {
+       for {
+               var (
+                       sendCh chan<- *cryptossh.Request
+                       next   *cryptossh.Request
+               )
+               if len(f.queue) > 0 {
+                       sendCh = out
+                       next = f.queue[0].req
+               }
+
+               select {
+               case req, ok := <-in:
+                       if !ok {
+                               return
+                       }
+                       if !f.enqueue(req) {
+                               return
+                       }
+               case sendCh <- next:
+                       f.dropHead()
+               }
+       }
+}
+
+func (f *requestFilter) dropHead() {
+       head := f.queue[0]
+       if head == f.lastWinch {
+               f.lastWinch = nil
+       }
+       f.queuedBytes -= head.size
+       f.queue[0] = nil
+       f.queue = f.queue[1:]
+}
+
+// enqueue classifies req and queues it. It returns false after closing the 
channel
+// because the queue is full. Requests are classified first so a refused 
request's
+// dropped payload does not count against the limit.
+func (f *requestFilter) enqueue(req *cryptossh.Request) bool {
+       q := &queuedRequest{req: req}
+       f.classify(q)
+       q.size = len(req.Payload)
+
+       if len(f.queue) >= maxQueuedRequests || f.queuedBytes+q.size > 
maxQueuedBytes {
+               log.Warnf("SSH session request queue full, closing channel")
+               if f.ch != nil {
+                       _ = f.ch.Close()
+               }
+               return false
+       }
+
+       f.queue = append(f.queue, q)
+       f.queuedBytes += q.size
+       return true
+}
+
+func (f *requestFilter) classify(q *queuedRequest) {
+       req := q.req
+       switch req.Type {
+       case "pty-req":
+               if !validPtyReqPayload(req.Payload) {
+                       f.refuse(q)
+               }
+       case "env":
+               f.envCount++
+               if f.envCount > maxEnvRequests || len(req.Payload) > 
maxEnvPayload {
+                       f.refuse(q)
+               }
+       case "shell", "exec", "subsystem":
+               f.noteCommand(req.Type)
+       case "window-change":
+               f.coalesceWindowChange(q)
+       default:
+               if !loopActsOn(req.Type) {
+                       f.refuse(q)
+               }
+       }
+}
+
+// refuse drops the payload of q and releases the size it was charged against 
the queue.
+func (f *requestFilter) refuse(q *queuedRequest) {
+       f.queuedBytes -= q.size
+       q.size = 0
+       refuseRequest(q.req)
+}
+
+func (f *requestFilter) noteCommand(reqType string) {
+       if f.commandSeen {
+               return
+       }
+       f.commandSeen = true
+       f.started = reqType != "subsystem"
+}
+
+func (f *requestFilter) coalesceWindowChange(q *queuedRequest) {
+       if !f.started || !validWinchPayload(q.req.Payload) {
+               f.refuse(q)
+               return
+       }
+       if f.lastWinch != nil {
+               f.refuse(f.lastWinch)
+       }
+       f.lastWinch = q
+}
+
+// validWinchPayload reports whether p parses as a window-change payload whose 
columns and
+// rows are non-zero and fit the uint16 the PTY takes. A malformed or unusable 
replacement
+// must not displace a valid queued resize.
+func validWinchPayload(p []byte) bool {
+       var win struct{ Columns, Rows, Width, Height uint32 }
+       if err := cryptossh.Unmarshal(p, &win); err != nil {
+               return false
+       }
+       return usablePtyDimension(win.Columns) && usablePtyDimension(win.Rows)
+}
+
+func usablePtyDimension(v uint32) bool {
+       return v > 0 && v <= math.MaxUint16
+}
+
+// loopActsOn reports whether the session loop does anything with a request 
type other
+// than the ones the filter classifies itself. The loop answers every other 
type with a
+// failure, so its payload is not worth queueing.
+func loopActsOn(reqType string) bool {
+       switch reqType {
+       case "signal", "break", "[email protected]":
+               return true
+       }
+       return false
+}
+
+func refuseRequest(req *cryptossh.Request) {
+       req.Type = refusedRequestType
+       req.Payload = nil
+}
+
+// validPtyReqPayload reports whether p holds a term string followed by the two
+// uint32 window dimensions, without overflowing on a hostile string length. 
The
+// dimensions must fit the Windows int16 ConPty coordinate, which is narrower 
than the
+// Unix uint16 winsize; zero is allowed and defaulted by the Unix path.
+func validPtyReqPayload(p []byte) bool {
+       if len(p) < 4 {
+               return false
+       }
+       termLen := uint64(binary.BigEndian.Uint32(p))
+       if uint64(len(p)) < 4+termLen+8 {
+               return false
+       }
+       dims := p[4+termLen:]
+       return binary.BigEndian.Uint32(dims) <= math.MaxInt16 &&
+               binary.BigEndian.Uint32(dims[4:]) <= math.MaxInt16
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/netbird-0.81.4/client/ssh/server/session_limits_test.go 
new/netbird-0.81.5/client/ssh/server/session_limits_test.go
--- old/netbird-0.81.4/client/ssh/server/session_limits_test.go 1970-01-01 
01:00:00.000000000 +0100
+++ new/netbird-0.81.5/client/ssh/server/session_limits_test.go 2026-10-26 
17:02:31.000000000 +0100
@@ -0,0 +1,186 @@
+package server
+
+import (
+       "testing"
+       "time"
+
+       "github.com/gliderlabs/ssh"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+       cryptossh "golang.org/x/crypto/ssh"
+)
+
+// A malformed window-change arriving after a valid one must not cost the 
valid resize:
+// the valid request stays queued for the PTY, and only the malformed one is 
refused.
+func TestRequestFilter_MalformedWinchKeepsQueuedValidResize(t *testing.T) {
+       f := &requestFilter{}
+       require.True(t, f.enqueue(&cryptossh.Request{Type: "shell"}))
+
+       valid := &cryptossh.Request{Type: "window-change", Payload: 
cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{100, 40, 0, 0})}
+       require.True(t, f.enqueue(valid))
+
+       malformed := &cryptossh.Request{Type: "window-change", Payload: 
[]byte{1, 2, 3}}
+       require.True(t, f.enqueue(malformed))
+
+       assert.Equal(t, "window-change", valid.Type, "valid resize must still 
reach the PTY handler")
+       assert.Equal(t, refusedRequestType, malformed.Type, "malformed resize 
must be refused")
+}
+
+// A zero-row or zero-column resize parses but would size the PTY to nothing, 
so it must
+// not displace a valid queued resize either.
+func TestRequestFilter_ZeroSizeWinchKeepsQueuedValidResize(t *testing.T) {
+       f := &requestFilter{}
+       require.True(t, f.enqueue(&cryptossh.Request{Type: "shell"}))
+
+       valid := &cryptossh.Request{Type: "window-change", Payload: 
cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{100, 40, 0, 0})}
+       require.True(t, f.enqueue(valid))
+
+       zeroRows := &cryptossh.Request{Type: "window-change", Payload: 
cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{100, 0, 0, 0})}
+       require.True(t, f.enqueue(zeroRows))
+
+       assert.Equal(t, "window-change", valid.Type, "valid resize must still 
reach the PTY handler")
+       assert.Equal(t, refusedRequestType, zeroRows.Type, "zero-row resize 
must be refused")
+}
+
+// The initial pty-req size must fit both the Unix uint16 winsize and the 
Windows int16
+// ConPty coordinate, so larger values are refused instead of wrapping. Zero 
stays
+// accepted because the Unix path defaults it to 80x24.
+func TestValidPtyReqPayload_BoundsInitialSize(t *testing.T) {
+       ptyReq := func(columns, rows uint32) []byte {
+               return cryptossh.Marshal(struct {
+                       Term                         string
+                       Columns, Rows, Width, Height uint32
+                       Modes                        string
+               }{"xterm", columns, rows, 0, 0, ""})
+       }
+
+       assert.True(t, validPtyReqPayload(ptyReq(80, 24)), "ordinary size")
+       assert.True(t, validPtyReqPayload(ptyReq(0, 0)), "zero size is 
defaulted later")
+       assert.True(t, validPtyReqPayload(ptyReq(32767, 32767)), "largest size 
both platforms take")
+       assert.False(t, validPtyReqPayload(ptyReq(32768, 24)), "columns past 
the Windows int16 range")
+       assert.False(t, validPtyReqPayload(ptyReq(80, 32768)), "rows past the 
Windows int16 range")
+       assert.False(t, validPtyReqPayload(ptyReq(65536, 24)), "columns that 
wrap to zero as uint16")
+}
+
+// Sizes that are zero or do not fit the PTY's uint16 dimensions are refused, 
so they
+// cannot replace a valid queued resize.
+func TestValidWinchPayload_RejectsUnusableSizes(t *testing.T) {
+       tests := []struct {
+               name          string
+               columns, rows uint32
+       }{
+               {name: "zero columns", columns: 0, rows: 40},
+               {name: "zero rows", columns: 100, rows: 0},
+               {name: "columns wrap to zero as uint16", columns: 65536, rows: 
40},
+               {name: "rows wrap to zero as uint16", columns: 100, rows: 
65536},
+       }
+       for _, tc := range tests {
+               t.Run(tc.name, func(t *testing.T) {
+                       payload := cryptossh.Marshal(struct{ Columns, Rows, 
Width, Height uint32 }{tc.columns, tc.rows, 0, 0})
+                       assert.False(t, validWinchPayload(payload))
+               })
+       }
+       assert.True(t, validWinchPayload(cryptossh.Marshal(struct{ Columns, 
Rows, Width, Height uint32 }{65535, 65535, 0, 0})), "largest uint16 size is 
usable")
+}
+
+// A client that stops the loop from reading must not make the server buffer 
more than
+// maxQueuedBytes of payload, however few requests that takes.
+func TestRequestFilter_QueueIsBoundedByBytes(t *testing.T) {
+       f := &requestFilter{}
+       payload := make([]byte, 32*1024)
+
+       accepted := 0
+       for f.enqueue(&cryptossh.Request{Type: "exec", Payload: payload}) {
+               accepted++
+               require.LessOrEqual(t, accepted, maxQueuedRequests, "queue must 
stop growing")
+       }
+       assert.LessOrEqual(t, f.queuedBytes, maxQueuedBytes, "queued payload 
bytes must stay under the limit")
+       assert.Equal(t, maxQueuedBytes/len(payload), accepted, "queue must hold 
payloads up to the byte limit")
+}
+
+// Request types the session loop only answers with a failure carry no useful 
payload, so
+// they are refused and cost nothing against the queue.
+func TestRequestFilter_UnhandledTypesDropPayload(t *testing.T) {
+       f := &requestFilter{}
+       req := &cryptossh.Request{Type: "x11-req", Payload: make([]byte, 
32*1024)}
+       require.True(t, f.enqueue(req))
+
+       assert.Equal(t, refusedRequestType, req.Type, "unhandled type must be 
refused")
+       assert.Zero(t, f.queuedBytes, "refused request must not count its 
payload")
+}
+
+// Nothing drains window changes for a subsystem session, so a resize after 
one must be
+// refused for the rest of the session, even if a shell request follows.
+func TestRequestFilter_SubsystemSessionRefusesResize(t *testing.T) {
+       f := &requestFilter{}
+       winch := cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 
}{100, 40, 0, 0})
+
+       require.True(t, f.enqueue(&cryptossh.Request{Type: "subsystem", 
Payload: cryptossh.Marshal(struct{ Name string }{"sftp"})}))
+       require.True(t, f.enqueue(&cryptossh.Request{Type: "shell"}))
+       resize := &cryptossh.Request{Type: "window-change", Payload: winch}
+       require.True(t, f.enqueue(resize))
+       assert.Equal(t, refusedRequestType, resize.Type, "resize in a subsystem 
session must be refused")
+}
+
+// A loop blocked sending a resize that no handler reads is released by the 
drain, and the
+// drain ends when the loop closes the channel.
+func TestDrainWindowChanges_ReleasesBlockedSender(t *testing.T) {
+       winCh := make(chan ssh.Window, 1)
+       winCh <- ssh.Window{Width: 80, Height: 24}
+
+       sent := make(chan struct{})
+       go func() {
+               winCh <- ssh.Window{Width: 100, Height: 40}
+               close(sent)
+       }()
+       select {
+       case <-sent:
+               t.Fatal("sender should block while nothing reads the channel")
+       case <-time.After(50 * time.Millisecond):
+       }
+
+       drainWindowChanges(winCh)
+       select {
+       case <-sent:
+       case <-time.After(2 * time.Second):
+               t.Fatal("drain did not release the blocked sender")
+       }
+
+       // A drain that read one window change and exited would release the 
first sender but
+       // leave the loop blocked on the next resize.
+       for i := 0; i < 5; i++ {
+               select {
+               case winCh <- ssh.Window{Width: 120, Height: 48}:
+               case <-time.After(2 * time.Second):
+                       t.Fatal("drain stopped consuming after the first window 
change")
+               }
+       }
+       close(winCh)
+}
+
+// A superseded resize keeps its place in the queue so its refusal is answered 
in order,
+// but must stop counting against the byte limit.
+func TestRequestFilter_CoalescedResizeReleasesQueuedBytes(t *testing.T) {
+       f := &requestFilter{}
+       require.True(t, f.enqueue(&cryptossh.Request{Type: "shell"}))
+       winch := cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 
}{100, 40, 0, 0})
+
+       for i := 0; i < 50; i++ {
+               require.True(t, f.enqueue(&cryptossh.Request{Type: 
"window-change", Payload: winch}))
+       }
+
+       assert.Equal(t, len(winch), f.queuedBytes, "only the newest resize 
should be charged")
+}
+
+// A large request the loop ignores is refused and its payload dropped, so it 
cannot close
+// the channel by pushing the queue over the byte limit.
+func TestRequestFilter_IgnoredLargeRequestDoesNotCloseChannel(t *testing.T) {
+       f := &requestFilter{}
+       for i := 0; i < maxQueuedBytes/len(make([]byte, 32*1024)); i++ {
+               require.True(t, f.enqueue(&cryptossh.Request{Type: "exec", 
Payload: make([]byte, 32*1024)}))
+       }
+
+       ignored := &cryptossh.Request{Type: "x11-req", Payload: make([]byte, 
200*1024)}
+       assert.True(t, f.enqueue(ignored), "an ignored request must be refused 
rather than close the channel")
+       assert.Equal(t, refusedRequestType, ignored.Type)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/go.mod new/netbird-0.81.5/go.mod
--- old/netbird-0.81.4/go.mod   2026-10-24 10:56:12.000000000 +0200
+++ new/netbird-0.81.5/go.mod   2026-10-26 17:02:31.000000000 +0100
@@ -57,7 +57,7 @@
        github.com/eko/gocache/store/go_cache/v4 v4.2.2
        github.com/eko/gocache/store/redis/v4 v4.2.2
        github.com/fsnotify/fsnotify v1.9.0
-       github.com/gliderlabs/ssh v0.3.8
+       github.com/gliderlabs/ssh v0.3.9-0.20250127223818-909fa952d4e4
        github.com/go-jose/go-jose/v4 v4.1.4
        github.com/go-ole/go-ole v1.3.0
        github.com/gobwas/ws v1.4.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netbird-0.81.4/go.sum new/netbird-0.81.5/go.sum
--- old/netbird-0.81.4/go.sum   2026-10-24 10:56:12.000000000 +0200
+++ new/netbird-0.81.5/go.sum   2026-10-26 17:02:31.000000000 +0100
@@ -200,8 +200,8 @@
 github.com/fxamacker/cbor/v2 v2.9.1/go.mod 
h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
 github.com/gin-contrib/sse v0.1.0/go.mod 
h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 github.com/gin-gonic/gin v1.5.0/go.mod 
h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
-github.com/gliderlabs/ssh v0.3.8 
h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
-github.com/gliderlabs/ssh v0.3.8/go.mod 
h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
+github.com/gliderlabs/ssh v0.3.9-0.20250127223818-909fa952d4e4 
h1:iLDHIkpEUuk277c53BF7v9yCAzgQHOcaY8tOu5ZX9Ws=
+github.com/gliderlabs/ssh v0.3.9-0.20250127223818-909fa952d4e4/go.mod 
h1:1nh+SYql7iCMwTrbe9K8l/lah6xcgJTH7W4ZlVoq22c=
 github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 
h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
 github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod 
h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 
h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=

++++++ netbird.obsinfo ++++++
--- /var/tmp/diff_new_pack.7Wk9td/_old  2026-10-27 18:24:11.084715454 +0100
+++ /var/tmp/diff_new_pack.7Wk9td/_new  2026-10-27 18:24:11.093715831 +0100
@@ -1,5 +1,5 @@
 name: netbird
-version: 0.81.4
-mtime: 1792832172
-commit: 7e791d3e2972ddb763bb0e5c3bff4f9e14f70033
+version: 0.81.5
+mtime: 1793030551
+commit: 396976b8be081fbf0d3d959698352585e80618e3
 

++++++ update.sh ++++++
#!/bin/bash
#
QUILT_PATCHES=".quilt-patches"
set -x
set -e
VERSION="${1:?please give me a version}"

if ! test -d netbird ; then
  git clone https://github.com/netbirdio/netbird.git
fi

pushd netbird
git fetch --tags
git checkout ${VERSION}
ln -s .. ${QUILT_PATCHES}
quilt push -a
rm -rv client/ui
go mod vendor
tar --zstd -cvf ../vendor.tar.zst vendor/ go.sum
rm -rv vendor/ .pc/ ${QUILT_PATCHES}
git reset --hard
popd

rm *cpio
perl -p -i -e "s|refs/tags/[^<]+|refs/tags/${VERSION}|g" _service
osc service mr
osc ar

++++++ vendor.tar.zst ++++++
/work/SRC/openSUSE:Factory/netbird/vendor.tar.zst 
/work/SRC/openSUSE:Factory/.netbird.new.2252680/vendor.tar.zst differ: char 7, 
line 1

Reply via email to