╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ────────╮ │src/serve_ssh.zig│ ╰─────────────────╯ ╭─────────────────────────────────────────────────────────────────╮ │512 io: std.Io, │ │513 allocator: std.mem.Allocator, │ │514 service: GitService, │ │515 + protocol_version: xit.net_server_common.ProtocolVersion,│ │516 error_writer: *std.Io.Writer, │ │517 ) !void { │ │518 var session_reader_buf: [4096]u8 = undefined; │ ╰─────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │521 var session_writer = ssh.SessionWriter.init(sess, &session_writer_buf); │ │522 │ │523 switch (service) { │ │521 - .upload_pack => try repo.uploadPack(io, allocator, &session_reader.interface, &session_writer.interface, .{}), │ │522 - .receive_pack => try push.receivePackAndConsume(repo_opts, io, allocator, repo, &session_reader.interface, &session_writer.interface, .{}, repo_root_path, error_writer), │ │524 + .upload_pack => try repo.uploadPack(io, allocator, &session_reader.interface, &session_writer.interface, .{ .protocol_version = protocol_version }), │ │525 + .receive_pack => try push.receivePackAndConsume(repo_opts, io, allocator, repo, &session_reader.interface, &session_writer.interface, .{ .protocol_version = protocol_version }, repo_root_path, error_writer),│ │526 } │ │527 │ │528 // flush whatever the pack op buffered into our writer adapter │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────────╮ │src/serve_ssh_protocol.zig│ ╰──────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │169 /// what the client wants to do on this channel. │ │170 pub const Request = union(enum) { │ │171 shell: ?PtySize, // pty info if a pty-req arrived before the shell│ │172 - exec: []const u8, // command string from the exec request │ │172 + exec: Exec, │ │173 + │ │174 + pub const Exec = struct { │ │175 + command: []const u8, │ │176 + /// the GIT_PROTOCOL env value, if one arrived before the exec│ │177 + git_protocol: ?[]const u8, │ │178 + }; │ │179 }; │ │180 │ │181 /// next thing that arrived from the client. │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │1798 remote_window: u32, // bytes we may still send to peer before they adjust │ │1799 max_packet: u32, // max CHANNEL_DATA payload the peer accepts in one packet│ │1800 pty: ?PtySize = null, │ │1801 + // value of GIT_PROTOCOL, the one env variable we keep │ │1802 + git_protocol_buf: [64]u8 = undefined, │ │1803 + git_protocol_len: ?usize = null, │ │1804 }; │ │1805 │ │1806 pub const PtySize = struct { width_cells: u16, height_cells: u16 }; │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │2003 } │ │2004 │ │2005 if (std.mem.eql(u8, req_type, "env")) { │ │1997 - // ignore environment variables silently (we don't pass them anywhere) │ │2006 + // string name, string value. the rest are ignored silently. │ │2007 + const name = try takeString(&r, max_packet_len); │ │2008 + const value = try takeString(&r, max_packet_len); │ │2009 + if (std.mem.eql(u8, name, "GIT_PROTOCOL") and value.len <= ch.git_protocol_buf.len) {│ │2010 + @memcpy(ch.git_protocol_buf[0..value.len], value); │ │2011 + ch.git_protocol_len = value.len; │ │2012 + } │ │2013 try replyChannelRequest(conn, ch, want_reply, true); │ │2014 return null; │ │2015 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2032 // capture the command string for the handler to inspect │ │2033 const cmd = try takeString(&r, 4096); │ │2034 try replyChannelRequest(conn, ch, want_reply, true); │ │2020 - return .{ .exec = cmd }; │ │2035 + return .{ .exec = .{ │ │2036 + .command = cmd, │ │2037 + .git_protocol = if (ch.git_protocol_len) |len| ch.git_protocol_buf[0..len] else null,│ │2038 + } }; │ │2039 } │ │2040 │ │2041 // unknown request kind — fail it │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───────╮ │src/test/ssh.zig│ ╰────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │962 pub fn handleSession(self: *ServerTask, sess: *proto.SessionCtx, request: proto.Request) !void {│ │963 @memcpy(&self.captured_fp, &sess.fingerprint); │ │964 switch (request) { │ │965 - .exec => |cmd| { │ │965 + .exec => |exec| { │ │966 // copy out of the protocol's stack-scoped buffer so the │ │967 // test can assert on it after the handler returns. │ │968 - @memcpy(self.captured_exec_buf[0..cmd.len], cmd); │ │969 - self.captured_exec_len = cmd.len; │ │968 + @memcpy(self.captured_exec_buf[0..exec.command.len], exec.command); │ │969 + self.captured_exec_len = exec.command.len; │ │970 }, │ │971 .shell => {}, │ │972 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯