Path: blob/master/spec/lib/rex/proto/mssql/client_mixin_spec.rb
74576 views
# -*- coding: binary -*-12require 'spec_helper'3require 'rex/proto/mssql/client'45RSpec.describe Rex::Proto::MSSQL::ClientMixin do6let(:client) { Rex::Proto::MSSQL::Client.allocate }78describe '#mssql_parse_order' do9let(:info) { { errors: [] } }1011it 'consumes the ORDER token data and returns info unchanged' do12data = [4, 1, 2].pack('vvv')13result = client.mssql_parse_order(data, info)14expect(result[:errors]).to be_empty15expect(data).to be_empty16end1718it 'handles a single column ordinal' do19data = [2, 3].pack('vv')20result = client.mssql_parse_order(data, info)21expect(result[:errors]).to be_empty22expect(data).to be_empty23end2425it 'preserves remaining data after the ORDER token' do26trailing = "\xAB\xCD".b27data = [2, 1].pack('vv') + trailing28client.mssql_parse_order(data, info)29expect(data).to eq(trailing)30end3132it 'handles zero-length ORDER token' do33data = [0].pack('v')34result = client.mssql_parse_order(data, info)35expect(result[:errors]).to be_empty36expect(data).to be_empty37end38end3940describe '#mssql_parse_reply' do41context 'when response contains an ORDER token (0xA9)' do42it 'parses the ORDER token without error' do43colmeta = [0x81].pack('C') + [1].pack('v')44colmeta += [0, 0].pack('vv')45colmeta += [56].pack('C')46colmeta += [0].pack('C')4748order = [0xA9].pack('C') + [2, 1].pack('vv')49done = [0xFD].pack('C') + [0, 0, 0].pack('vvV')5051data = colmeta + order + done52result = client.mssql_parse_reply(data)53expect(result[:errors]).to be_empty54end5556it 'does not confuse ORDER token (0xA9) with NBCROW token (0xD2)' do57colmeta = [0x81].pack('C') + [1].pack('v')58colmeta += [0, 0].pack('vv')59colmeta += [56].pack('C')60colmeta += [0].pack('C')6162order = [0xA9].pack('C') + [4, 1, 2].pack('vvv')63row = [0xD1].pack('C') + [42].pack('V')64done = [0xFD].pack('C') + [0, 0, 1].pack('vvV')6566data = colmeta + order + row + done67result = client.mssql_parse_reply(data)68expect(result[:errors]).to be_empty69expect(result[:rows]).to eq([[42]])70end71end72end7374describe '#mssql_parse_nbcrow' do75let(:info) { { colinfos: [], colnames: [], errors: [] } }7677context 'when column info is missing' do78it 'returns early without errors' do79data = "\x00\x01\x02"80info[:colinfos] = nil81result = client.mssql_parse_nbcrow(data, info)82expect(result).to eq(info)83end84end8586context 'when data is empty' do87it 'returns info unchanged' do88info[:colinfos] = [{ id: :string, name: 'col1' }]89data = ''90result = client.mssql_parse_nbcrow(data, info)91expect(result[:errors]).to be_empty92end93end94end9596describe '#mssql_parse_tds_row' do97let(:info) { { colinfos: [], rows: [], errors: [] } }9899context 'when :int column has NULL sentinel' do100it 'returns nil for len == 0' do101info[:colinfos] = [{ id: :int, name: 'col1' }]102data = [0].pack('C')103client.mssql_parse_tds_row(data, info)104expect(info[:rows].last).to eq([nil])105expect(info[:errors]).to be_empty106end107108it 'returns nil for len == 255' do109info[:colinfos] = [{ id: :int, name: 'col1' }]110data = [255].pack('C')111client.mssql_parse_tds_row(data, info)112expect(info[:rows].last).to eq([nil])113expect(info[:errors]).to be_empty114end115116it 'parses a 4-byte integer' do117info[:colinfos] = [{ id: :int, name: 'col1' }]118data = [4].pack('C') + [42].pack('V')119client.mssql_parse_tds_row(data, info)120expect(info[:rows].last).to eq([42])121expect(info[:errors]).to be_empty122end123end124125context 'when :hex column has NULL sentinel (0xFFFF)' do126it 'returns nil for len == 65535' do127info[:colinfos] = [{ id: :hex, name: 'col1' }]128data = [65535].pack('v')129client.mssql_parse_tds_row(data, info)130expect(info[:rows].last).to eq([nil])131expect(info[:errors]).to be_empty132end133134it 'parses hex data' do135info[:colinfos] = [{ id: :hex, name: 'col1' }]136data = [2].pack('v') + "\x41\x42"137client.mssql_parse_tds_row(data, info)138expect(info[:rows].last).to eq(['4142'])139expect(info[:errors]).to be_empty140end141end142143context 'when :string column has NULL sentinel (0xFFFF)' do144it 'returns nil for len == 65535' do145info[:colinfos] = [{ id: :string, name: 'col1' }]146data = [65535].pack('v')147client.mssql_parse_tds_row(data, info)148expect(info[:rows].last).to eq([nil])149expect(info[:errors]).to be_empty150end151152it 'parses a string value' do153info[:colinfos] = [{ id: :string, name: 'col1' }]154data = [3].pack('v') + "foo"155client.mssql_parse_tds_row(data, info)156expect(info[:rows].last).to eq(['foo'])157expect(info[:errors]).to be_empty158end159end160161context 'when :guid column is NULL' do162it 'returns nil for read_length == 0' do163info[:colinfos] = [{ id: :guid, name: 'col1' }]164data = [0].pack('C')165client.mssql_parse_tds_row(data, info)166expect(info[:rows].last).to eq([nil])167expect(info[:errors]).to be_empty168end169end170171context 'when :int has invalid size' do172it 'logs error and consumes len bytes' do173info[:colinfos] = [{ id: :int, name: 'col1' }]174trailing = "AFTER".b175data = [3].pack('C') + "XXX" + trailing176client.mssql_parse_tds_row(data, info)177expect(info[:errors].length).to eq(1)178expect(info[:errors].first).to match(/invalid integer size: 3/)179expect(data).to eq(trailing)180end181end182end183end184185186