Compare commits

..
Author SHA1 Message Date
dependabot[bot] 9e6e2e2397 chore(deps): bump go.opentelemetry.io/otel/sdk (#8135)
Bumps the go_modules group with 1 update in the / directory: [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go).


Updates `go.opentelemetry.io/otel/sdk` from 1.45.0 to 1.47.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.45.0...v1.47.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-version: 1.47.0
  dependency-type: indirect
  dependency-group: go_modules
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-14 04:23:28 +00:00
7 changed files with 22 additions and 398 deletions

No files matched your search

+6 -5
View File
@@ -443,11 +443,12 @@ require (
go.opentelemetry.io/contrib/detectors/gcp v1.44.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
go.opentelemetry.io/otel v1.45.0 // indirect
go.opentelemetry.io/otel/metric v1.45.0 // indirect
go.opentelemetry.io/otel/sdk v1.45.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.45.0 // indirect
go.opentelemetry.io/otel/trace v1.45.0 // indirect
go.opentelemetry.io/otel v1.47.0 // indirect
go.opentelemetry.io/otel/log v1.47.0 // indirect
go.opentelemetry.io/otel/metric v1.47.0 // indirect
go.opentelemetry.io/otel/sdk v1.47.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.47.0 // indirect
go.opentelemetry.io/otel/trace v1.47.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/zap v1.27.1 // indirect
go.uber.org/zap/exp v0.3.0 // indirect
+14 -12
View File
@@ -1280,20 +1280,22 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM=
go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw=
go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
go.opentelemetry.io/otel/metric/x v0.67.0 h1:PcicCNZFkZ4bXfSooXdo3WN7RBOVOtjVdo1wD358Uns=
go.opentelemetry.io/otel/metric/x v0.67.0/go.mod h1:FBjCWZe6wgcqxcMtjdGiClDKXb2YxxXii0CXftE4QtI=
go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o=
go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA=
go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI=
go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw=
go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY=
go.opentelemetry.io/otel/metric v1.47.0/go.mod h1:ADGSXxRrXM6bjbvLo535EstVFlPpPYZm4LBKixjDHwU=
go.opentelemetry.io/otel/metric/x v0.69.0 h1:DjRLr15H83v+hCW7JA9NoJvOkYTtmq5YoDRbe9deYpM=
go.opentelemetry.io/otel/metric/x v0.69.0/go.mod h1:uVvsMPMFFyj/HUQfrUnH3JjnOQ1dwFDorgFLRBasM0k=
go.opentelemetry.io/otel/sdk v1.47.0 h1:zWXEr4j2lFefG87TU6Yg8a7ngfohIKFZHKp0Hf5hC6I=
go.opentelemetry.io/otel/sdk v1.47.0/go.mod h1:VUc24kiOeoGsxG8G9ULx3fWKvB7jMhnGE8Oi607lgR0=
go.opentelemetry.io/otel/sdk/metric v1.47.0 h1:lfISg2j93VT6yqdk9OfUaZmw/GfcZqCCV3jdXtsPnKw=
go.opentelemetry.io/otel/sdk/metric v1.47.0/go.mod h1:ypLp+mW1Nt2x+Szt3b5/i1syodyts49lMOwxpDI3VGw=
go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM=
go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -1,130 +0,0 @@
package kerberos
import (
"strings"
"testing"
"github.com/jcmturner/gokrb5/v8/messages"
"github.com/jcmturner/gokrb5/v8/types"
)
// buildTicketWithCipher creates a messages.Ticket whose EncPart.Cipher has the
// given length. The TktVNO, Realm and SName fields are filled with minimal
// valid values so the function does not panic before it reaches Cipher access.
func buildTicketWithCipher(cipherLen int) messages.Ticket {
cipher := make([]byte, cipherLen)
for i := range cipher {
cipher[i] = byte(i & 0xff)
}
return messages.Ticket{
TktVNO: 5,
Realm: "TEST.REALM",
SName: types.PrincipalName{
NameType: 1,
NameString: []string{"host", "server.test.realm"},
},
EncPart: types.EncryptedData{
EType: 17,
Cipher: cipher,
},
}
}
// buildASRepWithCipher creates a messages.ASRep whose EncPart.Cipher has the
// given length.
func buildASRepWithCipher(cipherLen int) messages.ASRep {
cipher := make([]byte, cipherLen)
for i := range cipher {
cipher[i] = byte(i & 0xff)
}
rep := messages.ASRep{}
rep.EncPart = types.EncryptedData{
EType: 17,
Cipher: cipher,
}
rep.CName = types.PrincipalName{
NameType: 1,
NameString: []string{"testuser"},
}
rep.CRealm = "TEST.REALM"
return rep
}
// --- TGStoHashcat ---
// TestTGStoHashcat_NoPanicOnShortCipher is the regression test.
// Without the bounds check, TGStoHashcat panics with:
//
// runtime error: slice bounds out of range [:16] with length N (N < 16)
//
// With the fix it must return an error instead.
func TestTGStoHashcat_NoPanicOnShortCipher(t *testing.T) {
for cipherLen := 0; cipherLen < 16; cipherLen++ {
cipherLen := cipherLen // capture
t.Run("", func(t *testing.T) {
tgs := buildTicketWithCipher(cipherLen)
result, err := TGStoHashcat(tgs, "user")
if err == nil {
t.Errorf("cipher length %d: expected error, got hashcat string %q", cipherLen, result)
}
if !strings.Contains(err.Error(), "too short") {
t.Errorf("cipher length %d: unexpected error message %q", cipherLen, err.Error())
}
})
}
}
// TestTGStoHashcat_Valid_16Bytes confirms that a cipher of exactly 16 bytes succeeds.
func TestTGStoHashcat_Valid_16Bytes(t *testing.T) {
tgs := buildTicketWithCipher(16)
result, err := TGStoHashcat(tgs, "user")
if err != nil {
t.Fatalf("unexpected error for 16-byte cipher: %v", err)
}
if !strings.HasPrefix(result, "$krb5tgs$") {
t.Errorf("unexpected hash format: %q", result)
}
}
// TestTGStoHashcat_Valid_Large confirms that a larger cipher also succeeds.
func TestTGStoHashcat_Valid_Large(t *testing.T) {
tgs := buildTicketWithCipher(256)
result, err := TGStoHashcat(tgs, "user")
if err != nil {
t.Fatalf("unexpected error for 256-byte cipher: %v", err)
}
if !strings.HasPrefix(result, "$krb5tgs$") {
t.Errorf("unexpected hash format: %q", result)
}
}
// --- ASRepToHashcat ---
// TestASRepToHashcat_NoPanicOnShortCipher is the regression test for ASRep.
func TestASRepToHashcat_NoPanicOnShortCipher(t *testing.T) {
for cipherLen := 0; cipherLen < 16; cipherLen++ {
cipherLen := cipherLen
t.Run("", func(t *testing.T) {
asrep := buildASRepWithCipher(cipherLen)
result, err := ASRepToHashcat(asrep)
if err == nil {
t.Errorf("cipher length %d: expected error, got hashcat string %q", cipherLen, result)
}
if !strings.Contains(err.Error(), "too short") {
t.Errorf("cipher length %d: unexpected error message %q", cipherLen, err.Error())
}
})
}
}
// TestASRepToHashcat_Valid_16Bytes confirms that a cipher of exactly 16 bytes succeeds.
func TestASRepToHashcat_Valid_16Bytes(t *testing.T) {
asrep := buildASRepWithCipher(16)
result, err := ASRepToHashcat(asrep)
if err != nil {
t.Fatalf("unexpected error for 16-byte cipher: %v", err)
}
if !strings.HasPrefix(result, "$krb5asrep$") {
t.Errorf("unexpected hash format: %q", result)
}
}
+2 -24
View File
@@ -167,11 +167,6 @@ func sendUDP(conn *net.UDPConn, b []byte) ([]byte, error) {
return r, nil
}
// maxKDCResponseSize caps the per-response allocation for a KDC TCP reply.
// RFC 4120 §7.2.2 uses a 4-byte big-endian length which could otherwise
// be set to 4 GB by a malicious or spoofed peer causing OOM.
const maxKDCResponseSize = 1 << 20 // 1 MiB
// sendTCP sends bytes to connection over TCP.
func sendTCP(conn *net.TCPConn, b []byte) ([]byte, error) {
defer func() {
@@ -189,18 +184,12 @@ func sendTCP(conn *net.TCPConn, b []byte) ([]byte, error) {
}
sh := make([]byte, 4)
// Use io.ReadFull to guarantee all 4 header bytes are read.
_, err = io.ReadFull(conn, sh)
_, err = conn.Read(sh)
if err != nil {
return r, fmt.Errorf("error reading response size header: %v", err)
}
s := binary.BigEndian.Uint32(sh)
// Reject unreasonably large responses before allocating.
if s > maxKDCResponseSize {
return r, fmt.Errorf("kdc response too large: %d bytes (max %d)", s, maxKDCResponseSize)
}
rb := make([]byte, s)
_, err = io.ReadFull(conn, rb)
if err != nil {
@@ -222,13 +211,7 @@ func CheckKrbError(b []byte) ([]byte, error) {
}
// TGStoHashcat converts a TGS to a hashcat format.
// The hashcat $krb5tgs$ format splits EncPart.Cipher into a 16-byte HMAC
// prefix and the remaining ciphertext. A cipher shorter than 16 bytes
// would cause a slice-bounds panic; guard against truncated KDC replies.
func TGStoHashcat(tgs messages.Ticket, username string) (string, error) {
if len(tgs.EncPart.Cipher) < 16 {
return "", fmt.Errorf("tgs cipher too short: need at least 16 bytes, got %d", len(tgs.EncPart.Cipher))
}
return fmt.Sprintf("$krb5tgs$%d$*%s$%s$%s*$%s$%s",
tgs.EncPart.EType,
username,
@@ -239,13 +222,8 @@ func TGStoHashcat(tgs messages.Ticket, username string) (string, error) {
), nil
}
// ASRepToHashcat converts an AS-REP message to a hashcat format.
// A cipher shorter than 16 bytes from a truncated KDC reply would cause
// a slice-bounds panic on Cipher[:16]; check before indexing.
// ASRepToHashcat converts an AS-REP message to a hashcat format
func ASRepToHashcat(asrep messages.ASRep) (string, error) {
if len(asrep.EncPart.Cipher) < 16 {
return "", fmt.Errorf("as-rep cipher too short: need at least 16 bytes, got %d", len(asrep.EncPart.Cipher))
}
return fmt.Sprintf("$krb5asrep$%d$%s@%s:%s$%s",
asrep.EncPart.EType,
asrep.CName.PrincipalNameString(),
-120
View File
@@ -1,120 +0,0 @@
package kerberos
import (
"encoding/binary"
"io"
"net"
"testing"
"time"
)
// serveKDCTCP is a minimal fake KDC that:
// 1. Reads the full framed Kerberos request from the client.
// 2. Writes a 4-byte big-endian length header containing bodyLen.
// 3. If bodyLen <= maxKDCResponseSize, writes exactly bodyLen bytes of body.
// For oversized values, only the header is sent (simulating a rogue peer).
func serveKDCTCP(ln net.Listener, bodyLen uint32) {
conn, err := ln.Accept()
if err != nil {
return
}
defer func() { _ = conn.Close() }()
_ = conn.SetDeadline(time.Now().Add(5 * time.Second))
// Drain the client's framed Kerberos request (4-byte length + body).
hdr := make([]byte, 4)
if _, err := io.ReadFull(conn, hdr); err != nil {
return
}
msgLen := binary.BigEndian.Uint32(hdr)
if msgLen > 0 && msgLen < 65536 {
body := make([]byte, msgLen)
_, _ = io.ReadFull(conn, body)
}
// Send the response length header.
rhdr := make([]byte, 4)
binary.BigEndian.PutUint32(rhdr, bodyLen)
_, _ = conn.Write(rhdr)
// Only write the body for valid sizes; for oversized we expect the client
// to reject the size before reading anything.
if bodyLen > 0 && bodyLen <= maxKDCResponseSize {
respBody := make([]byte, bodyLen)
respBody[0] = 0x01 // non-empty sentinel
_, _ = conn.Write(respBody)
}
}
// TestSendTCP_OversizedResponseRejected verifies that sendTCP returns an error
// when a spoofed KDC advertises a response body larger than maxKDCResponseSize,
// rather than allocating that many bytes and causing an OOM.
//
// Without the bounds check the code does:
//
// rb := make([]byte, s) // s == bodyLen, up to 2^32-1 bytes
//
// which would allocate up to 4 GiB per call.
func TestSendTCP_OversizedResponseRejected(t *testing.T) {
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
defer func() { _ = ln.Close() }()
oversized := uint32(maxKDCResponseSize + 1)
go serveKDCTCP(ln, oversized)
conn, err := net.DialTimeout("tcp", ln.Addr().String(), 3*time.Second)
if err != nil {
t.Fatalf("dial failed: %v", err)
}
tcpConn, ok := conn.(*net.TCPConn)
if !ok {
_ = conn.Close()
t.Fatalf("expected *net.TCPConn, got %T", conn)
}
// Set a generous timeout so the test does not hang.
_ = tcpConn.SetDeadline(time.Now().Add(10 * time.Second))
_, err = sendTCP(tcpConn, []byte{0x01})
if err == nil {
t.Fatal("expected error for oversized KDC response, got nil")
}
t.Logf("correctly rejected oversized response: %v", err)
}
// TestSendTCP_SmallValidResponseSucceeds confirms that sendTCP succeeds when
// the KDC returns a small, valid body.
func TestSendTCP_SmallValidResponseSucceeds(t *testing.T) {
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
defer func() { _ = ln.Close() }()
validLen := uint32(32)
go serveKDCTCP(ln, validLen)
conn, err := net.DialTimeout("tcp", ln.Addr().String(), 3*time.Second)
if err != nil {
t.Fatalf("dial failed: %v", err)
}
tcpConn, ok := conn.(*net.TCPConn)
if !ok {
_ = conn.Close()
t.Fatalf("expected *net.TCPConn, got %T", conn)
}
_ = tcpConn.SetDeadline(time.Now().Add(10 * time.Second))
resp, err := sendTCP(tcpConn, []byte{0x01})
if err != nil {
t.Fatalf("unexpected error for valid %d-byte KDC response: %v", validLen, err)
}
if len(resp) != int(validLen) {
t.Errorf("expected %d bytes, got %d", validLen, len(resp))
}
}
-11
View File
@@ -16,12 +16,6 @@ import (
const (
pkt = "\x00\x00\x00\xc0\xfeSMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x08\x00\x01\x00\x00\x00\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00x\x00\x00\x00\x02\x00\x00\x00\x02\x02\x10\x02\"\x02$\x02\x00\x03\x02\x03\x10\x03\x11\x03\x00\x00\x00\x00\x01\x00&\x00\x00\x00\x00\x00\x01\x00 \x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\n\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00"
// maxSMBGhostBodySize caps the per-connection allocation when reading the
// SMBv3 negotiate response body. The 4-byte NetBIOS length field is
// controlled by the remote peer; without this cap a malicious or spoofed
// server can claim a giant body and trigger an OOM/DoS.
maxSMBGhostBodySize = 10 * 1024 * 1024 // 10 MiB
)
// DetectSMBGhost tries to detect SMBGhost vulnerability
@@ -71,11 +65,6 @@ func detectSMBGhost(ctx context.Context, executionId string, host string, port i
}
length := args[0].(int)
// Guard against a malicious or spoofed server advertising a giant body:
// ConnReadNWithTimeout allocates `length` bytes before reading.
if length <= 0 || length > maxSMBGhostBodySize {
return false, fmt.Errorf("unexpected smbghost response body length %d (max %d)", length, maxSMBGhostBodySize)
}
_ = conn.SetReadDeadline(time.Now().Add(2 * time.Second))
data, err := reader.ConnReadNWithTimeout(conn, int64(length), time.Duration(5)*time.Second)
if err != nil {
-96
View File
@@ -1,96 +0,0 @@
package smb
import (
"encoding/binary"
"fmt"
"io"
"net"
"testing"
"time"
)
// serveGhostPacket is a minimal fake SMB server that:
// 1. Reads the probe packet from the client (and discards it).
// 2. Writes a 4-byte NetBIOS length header containing bodyLen.
// 3. If bodyLen > 0 and <= maxSMBGhostBodySize, writes exactly bodyLen bytes of body.
// For oversized values, only the header is sent (simulating a rogue peer).
func serveGhostPacket(ln net.Listener, bodyLen uint32) {
conn, err := ln.Accept()
if err != nil {
return
}
defer func() { _ = conn.Close() }()
_ = conn.SetDeadline(time.Now().Add(5 * time.Second))
// Drain the probe packet the client sends.
buf := make([]byte, 512)
_, _ = conn.Read(buf)
// Send the 4-byte length header.
hdr := make([]byte, 4)
binary.BigEndian.PutUint32(hdr, bodyLen)
_, _ = conn.Write(hdr)
if bodyLen > 0 && bodyLen <= maxSMBGhostBodySize {
// Send a valid-length body (all zeros).
body := make([]byte, bodyLen)
_, _ = conn.Write(body)
}
// For the giant bodyLen case we intentionally do NOT write a body;
// the fix should reject it before attempting to read.
}
// TestDetectSMBGhost_OversizedLengthIsRejected verifies that a spoofed server
// advertising a body length larger than maxSMBGhostBodySize is rejected with
// an error instead of causing an OOM allocation.
//
// Without the bounds check, detectSMBGhost would call
// reader.ConnReadNWithTimeout(conn, int64(length), ...) with a 2 GB value,
// allocating a ~2 GB byte slice on each request.
func TestDetectSMBGhost_OversizedLengthIsRejected(t *testing.T) {
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to start listener: %v", err)
}
defer func() { _ = ln.Close() }()
oversizedLen := uint32(maxSMBGhostBodySize + 1)
go serveGhostPacket(ln, oversizedLen)
addr := ln.Addr().String()
_, portStr, _ := net.SplitHostPort(addr)
var port int
fmt.Sscanf(portStr, "%d", &port)
// Use a raw TCP connection to call detectSMBGhost so we don't need a
// real executionId / network-policy setup.
conn, err := net.DialTimeout("tcp", addr, 3*time.Second)
if err != nil {
t.Fatalf("dial failed: %v", err)
}
defer func() { _ = conn.Close() }()
// Reproduce detectSMBGhost's NetBIOS-length read logic so we can call
// the same guard path.
_ = conn.SetDeadline(time.Now().Add(3 * time.Second))
_, _ = conn.Write([]byte(pkt))
hdr := make([]byte, 4)
_, err = io.ReadFull(conn, hdr)
if err != nil {
t.Fatalf("failed to read header: %v", err)
}
length := int(binary.BigEndian.Uint32(hdr))
// Apply the same guard as the fix.
if length <= 0 || length > maxSMBGhostBodySize {
// This is the correct path — the fix catches it before allocating.
t.Logf("oversized length %d correctly rejected (max %d)", length, maxSMBGhostBodySize)
return
}
t.Errorf("length %d should have been rejected but was not (max %d)", length, maxSMBGhostBodySize)
}