mirror of
https://github.com/projectdiscovery/nuclei
synced 2026-06-08 16:50:47 +00:00
593 lines
20 KiB
Go
593 lines
20 KiB
Go
//go:build integration
|
|
// +build integration
|
|
|
|
package integration_test
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/go-pg/pg/v10"
|
|
_ "github.com/go-sql-driver/mysql"
|
|
"github.com/ory/dockertest/v3"
|
|
dc "github.com/ory/dockertest/v3/docker"
|
|
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
|
osutils "github.com/projectdiscovery/utils/os"
|
|
"github.com/projectdiscovery/utils/reader"
|
|
"go.uber.org/multierr"
|
|
)
|
|
|
|
func javascriptDockerDisabled() bool {
|
|
return !osutils.IsLinux() || !hasAnyExecutable("docker", "podman")
|
|
}
|
|
|
|
var jsTestcases = []integrationCase{
|
|
{Path: "protocols/javascript/redis-pass-brute.yaml", TestCase: &javascriptRedisPassBrute{}, DisableOn: javascriptDockerDisabled, Serial: true},
|
|
{Path: "protocols/javascript/ssh-server-fingerprint.yaml", TestCase: &javascriptSSHServerFingerprint{}, DisableOn: javascriptDockerDisabled, Serial: true},
|
|
{Path: "protocols/javascript/net-multi-step.yaml", TestCase: &networkMultiStep{}},
|
|
{Path: "protocols/javascript/net-https.yaml", TestCase: &javascriptNetHttps{}},
|
|
{Path: "protocols/javascript/rsync-test.yaml", TestCase: &javascriptRsyncTest{}, DisableOn: javascriptDockerDisabled, Serial: true},
|
|
{Path: "protocols/javascript/vnc-pass-brute.yaml", TestCase: &javascriptVncPassBrute{}, DisableOn: javascriptDockerDisabled, Serial: true},
|
|
{Path: "protocols/javascript/postgres-pass-brute.yaml", TestCase: &javascriptPostgresPassBrute{}, DisableOn: javascriptDockerDisabled, Serial: true},
|
|
{Path: "protocols/javascript/mysql-connect.yaml", TestCase: &javascriptMySQLConnect{}, DisableOn: javascriptDockerDisabled, Serial: true},
|
|
{Path: "protocols/javascript/multi-ports.yaml", TestCase: &javascriptMultiPortsSSH{}},
|
|
{Path: "protocols/javascript/no-port-args.yaml", TestCase: &javascriptNoPortArgs{}},
|
|
{Path: "protocols/javascript/telnet-auth-test.yaml", TestCase: &javascriptTelnetAuthTest{}, DisableOn: javascriptDockerDisabled, Serial: true},
|
|
{Path: "protocols/javascript/asrep-roast.yaml", TestCase: &javascriptASRepRoast{}},
|
|
{Path: "protocols/javascript/wmi-command.yaml", TestCase: &javascriptWMICommand{}},
|
|
{Path: "protocols/javascript/goexec-redaction.yaml", TestCase: &javascriptGoExecRedaction{}},
|
|
{Path: "protocols/javascript/goexec-modules.yaml", TestCase: &javascriptGoExecModules{}},
|
|
{Path: "protocols/javascript/goexec-samba-ntlm.yaml", TestCase: &javascriptGoExecSambaNTLM{}, DisableOn: javascriptDockerDisabled, Serial: true},
|
|
}
|
|
|
|
var (
|
|
defaultRetry = 3
|
|
)
|
|
|
|
const (
|
|
javascriptContainerTTLSeconds = 300
|
|
javascriptDatabaseReadyTimeout = 3 * time.Minute
|
|
javascriptServiceReadyTimeout = 45 * time.Second
|
|
javascriptRetryDelay = 500 * time.Millisecond
|
|
javascriptDialTimeout = 500 * time.Millisecond
|
|
)
|
|
|
|
type javascriptDockerReadyCheck func(address string) error
|
|
|
|
type javascriptDockerSpec struct {
|
|
options *dockertest.RunOptions
|
|
port string
|
|
readyTimeout time.Duration
|
|
settleDelay time.Duration
|
|
readyCheck javascriptDockerReadyCheck
|
|
}
|
|
|
|
func newJavascriptDockerSpec(port string, options *dockertest.RunOptions, readyTimeout time.Duration, settleDelay time.Duration, readyCheck javascriptDockerReadyCheck) javascriptDockerSpec {
|
|
if options == nil {
|
|
options = &dockertest.RunOptions{}
|
|
}
|
|
if options.Platform == "" {
|
|
options.Platform = "linux/amd64"
|
|
}
|
|
if len(options.ExposedPorts) == 0 {
|
|
options.ExposedPorts = []string{port}
|
|
}
|
|
if readyTimeout <= 0 {
|
|
readyTimeout = javascriptServiceReadyTimeout
|
|
}
|
|
return javascriptDockerSpec{
|
|
options: options,
|
|
port: port,
|
|
readyTimeout: readyTimeout,
|
|
settleDelay: settleDelay,
|
|
readyCheck: readyCheck,
|
|
}
|
|
}
|
|
|
|
type javascriptNetHttps struct{}
|
|
|
|
func (j *javascriptNetHttps) Execute(filePath string) error {
|
|
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "scanme.sh", debug)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return expectResultsCount(results, 1)
|
|
}
|
|
|
|
type javascriptRedisPassBrute struct{}
|
|
|
|
func (j *javascriptRedisPassBrute) Execute(filePath string) error {
|
|
return runJavascriptDockerCase(filePath, newJavascriptDockerSpec("6379/tcp", &dockertest.RunOptions{
|
|
Repository: "redis",
|
|
Tag: "latest",
|
|
Cmd: []string{"redis-server", "--requirepass", "iamadmin"},
|
|
}, javascriptServiceReadyTimeout, 0, nil))
|
|
}
|
|
|
|
type javascriptSSHServerFingerprint struct{}
|
|
|
|
func (j *javascriptSSHServerFingerprint) Execute(filePath string) error {
|
|
return runJavascriptDockerCase(filePath, newJavascriptDockerSpec("22/tcp", &dockertest.RunOptions{
|
|
Repository: "alpine",
|
|
Tag: "latest",
|
|
Cmd: []string{
|
|
"sh",
|
|
"-c",
|
|
"apk add --no-cache openssh && ssh-keygen -A && mkdir -p /run/sshd && exec /usr/sbin/sshd -D -e",
|
|
},
|
|
}, javascriptServiceReadyTimeout, 2*time.Second, nil))
|
|
}
|
|
|
|
type javascriptVncPassBrute struct{}
|
|
|
|
func (j *javascriptVncPassBrute) Execute(filePath string) error {
|
|
return runJavascriptDockerCase(filePath, newJavascriptDockerSpec("5900/tcp", &dockertest.RunOptions{
|
|
Repository: "dorowu/ubuntu-desktop-lxde-vnc",
|
|
Tag: "latest",
|
|
Env: []string{
|
|
"VNC_PASSWORD=mysecret",
|
|
},
|
|
}, javascriptServiceReadyTimeout, 20*time.Second, nil))
|
|
}
|
|
|
|
type javascriptPostgresPassBrute struct{}
|
|
|
|
func (j *javascriptPostgresPassBrute) Execute(filePath string) error {
|
|
return runJavascriptDockerCase(filePath, newJavascriptDockerSpec("5432/tcp", &dockertest.RunOptions{
|
|
Repository: "postgres",
|
|
Tag: "13",
|
|
Env: []string{
|
|
"POSTGRES_INITDB_ARGS=--auth-host=md5 --auth-local=trust",
|
|
"POSTGRES_PASSWORD=postgres",
|
|
"POSTGRES_USER=postgres",
|
|
},
|
|
}, javascriptDatabaseReadyTimeout, 0, postgresReadyCheck("postgres", "postgres")), 1, 2)
|
|
}
|
|
|
|
type javascriptMySQLConnect struct{}
|
|
|
|
func (j *javascriptMySQLConnect) Execute(filePath string) error {
|
|
return runJavascriptDockerCase(filePath, newJavascriptDockerSpec("3306/tcp", &dockertest.RunOptions{
|
|
Repository: "mysql",
|
|
Tag: "5.7",
|
|
Env: []string{
|
|
"MYSQL_ROOT_PASSWORD=secret",
|
|
},
|
|
}, javascriptDatabaseReadyTimeout, 0, mysqlReadyCheck("root", "secret")))
|
|
}
|
|
|
|
type javascriptMultiPortsSSH struct{}
|
|
|
|
func (j *javascriptMultiPortsSSH) Execute(filePath string) error {
|
|
// use scanme.sh as target to ensure we match on the 2nd default port 22
|
|
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "scanme.sh", debug)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return expectResultsCount(results, 1)
|
|
}
|
|
|
|
type javascriptNoPortArgs struct{}
|
|
|
|
func (j *javascriptNoPortArgs) Execute(filePath string) error {
|
|
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "yo.dawg", debug)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return expectResultsCount(results, 1)
|
|
}
|
|
|
|
type javascriptWMICommand struct{}
|
|
|
|
func (j *javascriptWMICommand) Execute(filePath string) error {
|
|
// Exercises the protocolstate network-policy denial path: the helper sees
|
|
// 203.0.113.10 (RFC 5737 TEST-NET-3, hard-coded inside the template) on
|
|
// the exclude list and short-circuits before any dial, so the JSON result
|
|
// must contain "ok":false plus "network policy" in the error while not
|
|
// leaking the password.
|
|
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "127.0.0.1", debug, "-eh", "203.0.113.10")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return expectResultsCount(results, 1)
|
|
}
|
|
|
|
type javascriptGoExecRedaction struct{}
|
|
|
|
func (j *javascriptGoExecRedaction) Execute(filePath string) error {
|
|
listener := newGoExecCloseListener()
|
|
defer listener.Close()
|
|
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, listener.host, debug, "-V", "RPCEndpoint="+listener.binding)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return expectResultsCount(results, 1)
|
|
}
|
|
|
|
type javascriptGoExecModules struct{}
|
|
|
|
func (j *javascriptGoExecModules) Execute(filePath string) error {
|
|
listener := newGoExecCloseListener()
|
|
defer listener.Close()
|
|
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, listener.host, debug, "-V", "RPCEndpoint="+listener.binding)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return expectResultsCount(results, 1)
|
|
}
|
|
|
|
type javascriptGoExecSambaNTLM struct{}
|
|
|
|
// Execute spins up a Samba container with a known local user, binds it to
|
|
// host 127.0.0.1:445 (the goexec SMB dialer ignores any port carried in the
|
|
// target, so a randomised host port would never be reached), and runs the
|
|
// SCMR helper against it. NTLM authentication completes end-to-end against a
|
|
// real SMB server; the eventual failure happens at the SCMR layer because
|
|
// Samba does not implement remote service creation, which is exactly what
|
|
// the template asserts (structured Result + redaction).
|
|
func (j *javascriptGoExecSambaNTLM) Execute(filePath string) error {
|
|
pool, err := dockertest.NewPool("")
|
|
if err != nil {
|
|
return fmt.Errorf("could not create docker pool: %w", err)
|
|
}
|
|
if err := pool.Client.Ping(); err != nil {
|
|
return fmt.Errorf("could not connect to Docker: %w", err)
|
|
}
|
|
|
|
resource, err := pool.RunWithOptions(&dockertest.RunOptions{
|
|
Repository: "dperson/samba",
|
|
Tag: "latest",
|
|
Platform: "linux/amd64",
|
|
ExposedPorts: []string{"445/tcp"},
|
|
PortBindings: map[dc.Port][]dc.PortBinding{
|
|
"445/tcp": {{HostIP: "127.0.0.1", HostPort: "445"}},
|
|
},
|
|
Cmd: []string{
|
|
"-u", "auditor;samba-ntlm-secret",
|
|
"-s", "data;/share;yes;no;no;auditor;auditor;auditor",
|
|
"-w", "WORKGROUP",
|
|
"-p",
|
|
},
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("could not start samba: %w", err)
|
|
}
|
|
defer purge(pool, resource)
|
|
|
|
if err := resource.Expire(javascriptContainerTTLSeconds); err != nil {
|
|
return fmt.Errorf("could not expire samba: %w", err)
|
|
}
|
|
|
|
targetAddress := "127.0.0.1:445"
|
|
if err := waitForTCPService(targetAddress, javascriptServiceReadyTimeout); err != nil {
|
|
return err
|
|
}
|
|
// SMB ready-check: the port binds before smbd is actually accepting
|
|
// negotiate requests (especially under qemu emulation on arm64). Send a
|
|
// minimal SMB2 NEGOTIATE and require the response header to start with
|
|
// 0xFE 'S' 'M' 'B'.
|
|
if err := waitForServiceCheck(targetAddress, javascriptServiceReadyTimeout, smbReadyCheck()); err != nil {
|
|
return err
|
|
}
|
|
|
|
errS := make([]error, 0, defaultRetry)
|
|
for attempt := 1; attempt <= defaultRetry; attempt++ {
|
|
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "127.0.0.1", debug)
|
|
if err == nil {
|
|
if countErr := expectResultsCount(results, 1); countErr == nil {
|
|
return nil
|
|
} else {
|
|
err = countErr
|
|
}
|
|
}
|
|
errS = append(errS, err)
|
|
if attempt < defaultRetry {
|
|
time.Sleep(javascriptRetryDelay)
|
|
}
|
|
}
|
|
return multierr.Combine(errS...)
|
|
}
|
|
|
|
// goExecCloseListener is a loopback TCP listener that accepts incoming
|
|
// connections and immediately closes them. It is used to intercept the
|
|
// DCERPC dial issued by the GoExec helpers (via Options.Endpoint), so we
|
|
// exercise target normalization, host-allowed check, real dial, RPC bind
|
|
// failure, structured Result, and redaction without a Windows backend.
|
|
type goExecCloseListener struct {
|
|
listener net.Listener
|
|
host string
|
|
binding string
|
|
}
|
|
|
|
func newGoExecCloseListener() *goExecCloseListener {
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
panic(fmt.Errorf("goexec listener: %w", err))
|
|
}
|
|
host, port, err := net.SplitHostPort(ln.Addr().String())
|
|
if err != nil {
|
|
_ = ln.Close()
|
|
panic(fmt.Errorf("goexec listener addr: %w", err))
|
|
}
|
|
g := &goExecCloseListener{
|
|
listener: ln,
|
|
host: host,
|
|
binding: fmt.Sprintf("ncacn_ip_tcp:%s[%s]", host, port),
|
|
}
|
|
go g.serve()
|
|
return g
|
|
}
|
|
|
|
func (g *goExecCloseListener) serve() {
|
|
for {
|
|
conn, err := g.listener.Accept()
|
|
if err != nil {
|
|
return
|
|
}
|
|
_ = conn.Close()
|
|
}
|
|
}
|
|
|
|
func (g *goExecCloseListener) Close() {
|
|
_ = g.listener.Close()
|
|
}
|
|
|
|
type javascriptRsyncTest struct{}
|
|
|
|
func (j *javascriptRsyncTest) Execute(filePath string) error {
|
|
return runJavascriptDockerCase(filePath, newJavascriptDockerSpec("873/tcp", &dockertest.RunOptions{
|
|
Repository: "alpine",
|
|
Tag: "latest",
|
|
Cmd: []string{"sh", "-c", "apk add --no-cache rsync shadow && useradd -m rsyncuser && echo 'rsyncuser:mysecret' | chpasswd && echo 'rsyncuser:MySecret123' > /etc/rsyncd.secrets && chmod 600 /etc/rsyncd.secrets && echo -e '[data]\\n path = /data\\n comment = Local Rsync Share\\n read only = false\\n auth users = rsyncuser\\n secrets file = /etc/rsyncd.secrets' > /etc/rsyncd.conf && mkdir -p /data && exec rsync --daemon --no-detach --config=/etc/rsyncd.conf"},
|
|
}, javascriptServiceReadyTimeout, 0, nil))
|
|
}
|
|
|
|
type javascriptTelnetAuthTest struct{}
|
|
|
|
func (j *javascriptTelnetAuthTest) Execute(filePath string) error {
|
|
return runJavascriptDockerCase(filePath, newJavascriptDockerSpec("23/tcp", &dockertest.RunOptions{
|
|
Repository: "alpine",
|
|
Tag: "latest",
|
|
Cmd: []string{"sh", "-c", "apk add --no-cache busybox-extras shadow && useradd -m dev && echo 'dev:mysecret' | chpasswd && exec /usr/sbin/telnetd -F -p 23 -l /bin/login"},
|
|
}, javascriptServiceReadyTimeout, 0, nil))
|
|
}
|
|
|
|
type networkMultiStep struct{}
|
|
|
|
func (j *networkMultiStep) Execute(filePath string) error {
|
|
errState := &capturedError{}
|
|
server := testutils.NewTCPServer(nil, 0, func(conn net.Conn) {
|
|
defer func() { _ = conn.Close() }()
|
|
|
|
data, err := reader.ConnReadNWithTimeout(conn, 5, 5*time.Second)
|
|
if err != nil {
|
|
errState.Set(err)
|
|
return
|
|
}
|
|
if string(data) == "FIRST" {
|
|
_, _ = conn.Write([]byte("PING"))
|
|
}
|
|
|
|
data, err = reader.ConnReadNWithTimeout(conn, 6, 5*time.Second)
|
|
if err != nil {
|
|
errState.Set(err)
|
|
return
|
|
}
|
|
if string(data) == "SECOND" {
|
|
_, _ = conn.Write([]byte("PONG"))
|
|
}
|
|
_, _ = conn.Write([]byte("NUCLEI"))
|
|
})
|
|
defer server.Close()
|
|
|
|
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, server.URL, debug)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := errState.Err(); err != nil {
|
|
return err
|
|
}
|
|
if debug {
|
|
return expectResultsCount(results, 3)
|
|
}
|
|
return expectResultsCount(results, 1)
|
|
}
|
|
|
|
func runJavascriptDockerCase(filePath string, spec javascriptDockerSpec, expectedNumbers ...int) error {
|
|
if len(expectedNumbers) == 0 {
|
|
expectedNumbers = []int{1}
|
|
}
|
|
|
|
if spec.options == nil {
|
|
return fmt.Errorf("missing docker options for %s", filePath)
|
|
}
|
|
|
|
pool, err := dockertest.NewPool("")
|
|
if err != nil {
|
|
return fmt.Errorf("could not create docker pool: %w", err)
|
|
}
|
|
if err := pool.Client.Ping(); err != nil {
|
|
return fmt.Errorf("could not connect to Docker: %w", err)
|
|
}
|
|
|
|
resource, err := pool.RunWithOptions(spec.options)
|
|
if err != nil {
|
|
return fmt.Errorf("could not start resource for %s: %w", filePath, err)
|
|
}
|
|
defer purge(pool, resource)
|
|
|
|
if err := resource.Expire(javascriptContainerTTLSeconds); err != nil {
|
|
return fmt.Errorf("could not expire resource for %s: %w", filePath, err)
|
|
}
|
|
|
|
mappedPort := resource.GetPort(spec.port)
|
|
if mappedPort == "" {
|
|
return fmt.Errorf("missing mapped port for %s", spec.port)
|
|
}
|
|
|
|
targetAddress := net.JoinHostPort("127.0.0.1", mappedPort)
|
|
if err := waitForTCPService(targetAddress, spec.readyTimeout); err != nil {
|
|
return err
|
|
}
|
|
if spec.readyCheck != nil {
|
|
if err := waitForServiceCheck(targetAddress, spec.readyTimeout, spec.readyCheck); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if spec.settleDelay > 0 {
|
|
time.Sleep(spec.settleDelay)
|
|
}
|
|
|
|
errS := make([]error, 0, defaultRetry)
|
|
for attempt := 1; attempt <= defaultRetry; attempt++ {
|
|
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, targetAddress, debug)
|
|
if err == nil {
|
|
if countErr := expectResultsCount(results, expectedNumbers...); countErr == nil {
|
|
return nil
|
|
} else {
|
|
err = countErr
|
|
}
|
|
}
|
|
errS = append(errS, err)
|
|
if attempt < defaultRetry {
|
|
time.Sleep(javascriptRetryDelay)
|
|
}
|
|
}
|
|
|
|
return multierr.Combine(errS...)
|
|
}
|
|
|
|
func waitForTCPService(address string, timeout time.Duration) error {
|
|
deadline := time.Now().Add(timeout)
|
|
var lastErr error
|
|
for time.Now().Before(deadline) {
|
|
conn, err := net.DialTimeout("tcp", address, javascriptDialTimeout)
|
|
if err == nil {
|
|
_ = conn.Close()
|
|
return nil
|
|
}
|
|
lastErr = err
|
|
time.Sleep(javascriptRetryDelay)
|
|
}
|
|
if lastErr == nil {
|
|
lastErr = fmt.Errorf("timed out waiting for %s", address)
|
|
}
|
|
return fmt.Errorf("service %s did not become ready: %w", address, lastErr)
|
|
}
|
|
|
|
func waitForServiceCheck(address string, timeout time.Duration, check javascriptDockerReadyCheck) error {
|
|
deadline := time.Now().Add(timeout)
|
|
var lastErr error
|
|
for time.Now().Before(deadline) {
|
|
if err := check(address); err == nil {
|
|
return nil
|
|
} else {
|
|
lastErr = err
|
|
}
|
|
time.Sleep(javascriptRetryDelay)
|
|
}
|
|
if lastErr == nil {
|
|
lastErr = fmt.Errorf("timed out waiting for %s", address)
|
|
}
|
|
return fmt.Errorf("service %s did not become fully ready: %w", address, lastErr)
|
|
}
|
|
|
|
// smbReadyCheck sends a minimal SMB2 NEGOTIATE request and verifies the
|
|
// response carries the SMB2 protocol marker (0xFE 'S' 'M' 'B'). This lets us
|
|
// wait until samba's smbd is actually answering, not just until the kernel
|
|
// has bound port 445.
|
|
func smbReadyCheck() javascriptDockerReadyCheck {
|
|
return func(address string) error {
|
|
conn, err := net.DialTimeout("tcp", address, javascriptDialTimeout)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = conn.Close() }()
|
|
_ = conn.SetDeadline(time.Now().Add(2 * time.Second))
|
|
|
|
// minimal SMB2 NEGOTIATE: NetBIOS length-prefixed SMB2 header with
|
|
// command=0x0000 (NEGOTIATE) and a 36-byte structure that advertises
|
|
// support for dialect 0x0202 (SMB 2.0.2). Crafted byte-for-byte so we
|
|
// avoid pulling an SMB client library into the test binary.
|
|
req := []byte{
|
|
0x00, 0x00, 0x00, 0x66, // NetBIOS session length = 102 (64 header + 38 negotiate)
|
|
0xfe, 'S', 'M', 'B', // SMB2 protocol marker
|
|
0x40, 0x00, // Structure size
|
|
0x00, 0x00, // Credit charge
|
|
0x00, 0x00, 0x00, 0x00, // Status
|
|
0x00, 0x00, // Command = NEGOTIATE (0)
|
|
0x00, 0x00, // Credits requested
|
|
0x00, 0x00, 0x00, 0x00, // Flags
|
|
0x00, 0x00, 0x00, 0x00, // Next command
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Message ID
|
|
0x00, 0x00, 0x00, 0x00, // Reserved
|
|
0x00, 0x00, 0x00, 0x00, // Tree ID
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Session ID
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Signature (16 bytes)
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x24, 0x00, // NEGOTIATE structure size = 36
|
|
0x01, 0x00, // DialectCount = 1
|
|
0x00, 0x00, // SecurityMode
|
|
0x00, 0x00, // Reserved
|
|
0x00, 0x00, 0x00, 0x00, // Capabilities
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ClientGuid (16)
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ClientStartTime
|
|
0x02, 0x02, // Dialect = 0x0202 (SMB 2.0.2)
|
|
}
|
|
if _, err := conn.Write(req); err != nil {
|
|
return err
|
|
}
|
|
header := make([]byte, 8)
|
|
if _, err := io.ReadFull(conn, header); err != nil {
|
|
return fmt.Errorf("smb negotiate: read header: %w", err)
|
|
}
|
|
// header[4:8] should be the SMB2 protocol marker (skip the 4-byte NetBIOS prefix)
|
|
if header[4] != 0xFE || header[5] != 'S' || header[6] != 'M' || header[7] != 'B' {
|
|
return fmt.Errorf("smb negotiate: unexpected protocol marker %x", header[4:8])
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func mysqlReadyCheck(username, password string) javascriptDockerReadyCheck {
|
|
return func(address string) error {
|
|
dsn := fmt.Sprintf("%s:%s@tcp(%s)/information_schema?timeout=5s&readTimeout=5s&writeTimeout=5s", username, password, address)
|
|
db, err := sql.Open("mysql", dsn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = db.Close() }()
|
|
db.SetMaxOpenConns(1)
|
|
db.SetMaxIdleConns(0)
|
|
return db.Ping()
|
|
}
|
|
}
|
|
|
|
func postgresReadyCheck(username, password string) javascriptDockerReadyCheck {
|
|
return func(address string) error {
|
|
db := pg.Connect(&pg.Options{
|
|
Addr: address,
|
|
User: username,
|
|
Password: password,
|
|
Database: "postgres",
|
|
}).WithTimeout(5 * time.Second)
|
|
defer func() { _ = db.Close() }()
|
|
_, err := db.Exec("select 1")
|
|
return err
|
|
}
|
|
}
|
|
|
|
func purge(pool *dockertest.Pool, resource *dockertest.Resource) {
|
|
if resource == nil || pool == nil {
|
|
return
|
|
}
|
|
containerName := resource.Container.Name
|
|
_ = pool.Client.StopContainer(resource.Container.ID, 0)
|
|
_ = pool.Purge(resource)
|
|
_ = pool.RemoveContainerByName(containerName)
|
|
}
|