//go:build windows package starssh import ( "bytes" "errors" "io" "net" "os" "path/filepath" "strconv" "testing" "time" ) func TestParseGPGAssuanSocketInfo(t *testing.T) { info, ok := parseGPGAssuanSocketInfo([]byte("7247\n0123456789abcdef")) if !ok { t.Fatal("expected Assuan socket info to parse") } if info.port != 7247 || string(info.nonce) != "0123456789abcdef" || info.cygwin { t.Fatalf("info=%+v nonce=%x", info, info.nonce) } } func TestParseGPGCygwinSocketInfo(t *testing.T) { info, ok := parseGPGCygwinSocketInfo([]byte("!7247 s 00000001-02030405-06070809-0a0b0c0d\x00")) if !ok { t.Fatal("expected Cygwin socket info to parse") } want := []byte{1, 0, 0, 0, 5, 4, 3, 2, 9, 8, 7, 6, 13, 12, 11, 10} if info.port != 7247 || string(info.nonce) != string(want) || !info.cygwin { t.Fatalf("info=%+v nonce=%x", info, info.nonce) } } func TestParseGPGAssuanSocketRedirect(t *testing.T) { t.Setenv("STARSSH_TEST_PIPE", `\\.\pipe\openssh-ssh-agent`) target, ok := parseGPGAssuanSocketRedirect([]byte("%Assuan%\r\nsocket=${STARSSH_TEST_PIPE}\r\n")) if !ok { t.Fatal("expected Assuan redirect to parse") } if target != `\\.\pipe\openssh-ssh-agent` { t.Fatalf("target=%q", target) } } func TestReadInvalidAgentSSHSocketReturnsGPGSocketError(t *testing.T) { path := t.TempDir() + "/S.gpg-agent.ssh" if err := os.WriteFile(path, []byte("not a socket info file"), 0o600); err != nil { t.Fatalf("write socket file: %v", err) } _, err := dialResolvedSSHAgent(resolvedSSHAgentEndpoint{ Endpoint: path, Source: "SSH_AUTH_SOCK", Network: defaultSSHAgentNetwork(path), }, 0) if !errors.Is(err, errInvalidGPGSocketInfo) { t.Fatalf("err=%v want errInvalidGPGSocketInfo", err) } } func TestMissingAgentSSHSocketReturnsReadError(t *testing.T) { path := filepath.Join(t.TempDir(), "S.gpg-agent.ssh") _, err := dialResolvedSSHAgent(resolvedSSHAgentEndpoint{ Endpoint: path, Source: "identity-agent", Network: defaultSSHAgentNetwork(path), }, 0) if err == nil { t.Fatal("expected missing GPG socket file error") } if !errors.Is(err, os.ErrNotExist) { t.Fatalf("err=%v want os.ErrNotExist", err) } } func TestUnreadableAgentSSHSocketReturnsReadError(t *testing.T) { path := filepath.Join(t.TempDir(), "S.gpg-agent.ssh") if err := os.Mkdir(path, 0o700); err != nil { t.Fatalf("mkdir socket path: %v", err) } _, err := dialResolvedSSHAgent(resolvedSSHAgentEndpoint{ Endpoint: path, Source: "identity-agent", Network: defaultSSHAgentNetwork(path), }, 0) if err == nil { t.Fatal("expected unreadable GPG socket file error") } if errors.Is(err, errInvalidGPGSocketInfo) { t.Fatalf("err=%v should expose read failure before parse", err) } } func TestDialWindowsGPGSocketFilePerformsNonceHandshake(t *testing.T) { listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("listen tcp: %v", err) } defer listener.Close() type handshakeResult struct { nonce []byte err error } resultCh := make(chan handshakeResult, 1) go func() { conn, err := listener.Accept() if err != nil { resultCh <- handshakeResult{err: err} return } defer conn.Close() nonce := make([]byte, 16) if _, err := io.ReadFull(conn, nonce); err != nil { resultCh <- handshakeResult{err: err} return } resultCh <- handshakeResult{nonce: append([]byte(nil), nonce...)} }() socketPath := filepath.Join(t.TempDir(), "S.gpg-agent.ssh") if err := os.WriteFile(socketPath, []byte(strconv.Itoa(listener.Addr().(*net.TCPAddr).Port)+"\n0123456789abcdef"), 0o600); err != nil { t.Fatalf("write socket file: %v", err) } conn, err := dialWindowsGPGSocketFile(socketPath, time.Second) if err != nil { t.Fatalf("dialWindowsGPGSocketFile: %v", err) } _ = conn.Close() var result handshakeResult select { case result = <-resultCh: case <-time.After(time.Second): t.Fatal("listener did not accept GPG socket connection") } if result.err != nil { t.Fatalf("listener handshake error: %v", result.err) } if !bytes.Equal(result.nonce, []byte("0123456789abcdef")) { t.Fatalf("nonce=%q", result.nonce) } }