1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Make specs pass on OpenBSD

Skip Process clockres specs that don't work on either FreeBSD or
Solaris/AIX in addition to OpenBSD.

Run most current String#crypt specs on non-OpenBSD, and add a new
set of crypt specs for OpenBSD, which support bcrypt but not DES
in crypt(3).

Use @server.connect_address instead of @server.getsockname in some
socket tests, as OpenBSD does not treat connection to all zero
IPv4 or IPv6 addresses as connection to localhost.

When trying to connect using UDP on an unsupported address family,
allow Errno::EPROTONOSUPPORT in addition to Errno::EAFNOSUPPORT,
as OpenBSD raises the former.
This commit is contained in:
Jeremy Evans 2019-06-06 21:10:21 -07:00
parent c55de95ff1
commit 119ca4343c
4 changed files with 117 additions and 66 deletions

View file

@ -4,7 +4,7 @@ require_relative 'fixtures/clocks'
describe "Process.clock_getres" do describe "Process.clock_getres" do
# clock_getres() seems completely buggy on FreeBSD: # clock_getres() seems completely buggy on FreeBSD:
# https://rubyci.org/logs/rubyci.s3.amazonaws.com/freebsd11zfs/ruby-trunk/log/20190428T093003Z.fail.html.gz # https://rubyci.org/logs/rubyci.s3.amazonaws.com/freebsd11zfs/ruby-trunk/log/20190428T093003Z.fail.html.gz
platform_is_not :freebsd do platform_is_not :freebsd, :openbsd do
# NOTE: Look at fixtures/clocks.rb for clock and OS-specific exclusions # NOTE: Look at fixtures/clocks.rb for clock and OS-specific exclusions
ProcessSpecs.clock_constants_for_resolution_checks.each do |name, value| ProcessSpecs.clock_constants_for_resolution_checks.each do |name, value|
it "matches the clock in practice for Process::#{name}" do it "matches the clock in practice for Process::#{name}" do
@ -50,13 +50,13 @@ describe "Process.clock_getres" do
# These are observed # These are observed
platform_is_not :solaris, :aix do platform_is_not :solaris, :aix, :openbsd do
it "with Process::CLOCK_REALTIME reports at least 1 microsecond" do it "with Process::CLOCK_REALTIME reports at least 1 microsecond" do
Process.clock_getres(Process::CLOCK_REALTIME, :nanosecond).should <= 1_000 Process.clock_getres(Process::CLOCK_REALTIME, :nanosecond).should <= 1_000
end end
end end
platform_is_not :aix do platform_is_not :aix, :openbsd do
it "with Process::CLOCK_MONOTONIC reports at least 1 microsecond" do it "with Process::CLOCK_MONOTONIC reports at least 1 microsecond" do
Process.clock_getres(Process::CLOCK_MONOTONIC, :nanosecond).should <= 1_000 Process.clock_getres(Process::CLOCK_MONOTONIC, :nanosecond).should <= 1_000
end end

View file

@ -2,72 +2,117 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
describe "String#crypt" do describe "String#crypt" do
# Note: MRI's documentation just says that the C stdlib function crypt() is platform_is :openbsd do
# called. it "returns a cryptographic hash of self by applying the bcrypt algorithm with the specified salt" do
# "mypassword".crypt("$2a$04$0WVaz0pV3jzfZ5G5tpmHWu").should == "$2a$04$0WVaz0pV3jzfZ5G5tpmHWuBQGbkjzgtSc3gJbmdy0GAGMa45MFM2."
# I'm not sure if crypt() is guaranteed to produce the same result across
# different platforms. It seems that there is one standard UNIX implementation
# of crypt(), but that alternative implementations are possible. See
# http://www.unix.org.ua/orelly/networking/puis/ch08_06.htm
it "returns a cryptographic hash of self by applying the UNIX crypt algorithm with the specified salt" do
"".crypt("aa").should == "aaQSqAReePlq6"
"nutmeg".crypt("Mi").should == "MiqkFWCm1fNJI"
"ellen1".crypt("ri").should == "ri79kNd7V6.Sk"
"Sharon".crypt("./").should == "./UY9Q7TvYJDg"
"norahs".crypt("am").should == "amfIADT2iqjA."
"norahs".crypt("7a").should == "7azfT5tIdyh0I"
# Only uses first 8 chars of string # Only uses first 72 characters of string
"01234567".crypt("aa").should == "aa4c4gpuvCkSE" ("12345678"*9).crypt("$2a$04$0WVaz0pV3jzfZ5G5tpmHWu").should == "$2a$04$0WVaz0pV3jzfZ5G5tpmHWukj/ORBnsMjCGpST/zCJnAypc7eAbutK"
"012345678".crypt("aa").should == "aa4c4gpuvCkSE" ("12345678"*10).crypt("$2a$04$0WVaz0pV3jzfZ5G5tpmHWu").should == "$2a$04$0WVaz0pV3jzfZ5G5tpmHWukj/ORBnsMjCGpST/zCJnAypc7eAbutK"
"0123456789".crypt("aa").should == "aa4c4gpuvCkSE"
# Only uses first 2 chars of salt # Only uses first 29 characters of salt
"hello world".crypt("aa").should == "aayPz4hyPS1wI" "mypassword".crypt("$2a$04$0WVaz0pV3jzfZ5G5tpmHWuB").should == "$2a$04$0WVaz0pV3jzfZ5G5tpmHWuBQGbkjzgtSc3gJbmdy0GAGMa45MFM2."
"hello world".crypt("aab").should == "aayPz4hyPS1wI" end
"hello world".crypt("aabc").should == "aayPz4hyPS1wI"
it "raises Errno::EINVAL when the salt is shorter than 29 characters" do
lambda { "mypassword".crypt("$2a$04$0WVaz0pV3jzfZ5G5tpmHW") }.should raise_error(Errno::EINVAL)
end
it "calls #to_str to converts the salt arg to a String" do
obj = mock('$2a$04$0WVaz0pV3jzfZ5G5tpmHWu')
obj.should_receive(:to_str).and_return("$2a$04$0WVaz0pV3jzfZ5G5tpmHWu")
"mypassword".crypt(obj).should == "$2a$04$0WVaz0pV3jzfZ5G5tpmHWuBQGbkjzgtSc3gJbmdy0GAGMa45MFM2."
end
it "taints the result if either salt or self is tainted" do
tainted_salt = "$2a$04$0WVaz0pV3jzfZ5G5tpmHWu"
tainted_str = "mypassword"
tainted_salt.taint
tainted_str.taint
"mypassword".crypt("$2a$04$0WVaz0pV3jzfZ5G5tpmHWu").tainted?.should == false
tainted_str.crypt("$2a$04$0WVaz0pV3jzfZ5G5tpmHWu").tainted?.should == true
"mypassword".crypt(tainted_salt).tainted?.should == true
tainted_str.crypt(tainted_salt).tainted?.should == true
end
it "doesn't return subclass instances" do
StringSpecs::MyString.new("mypassword").crypt("$2a$04$0WVaz0pV3jzfZ5G5tpmHWu").should be_an_instance_of(String)
"mypassword".crypt(StringSpecs::MyString.new("$2a$04$0WVaz0pV3jzfZ5G5tpmHWu")).should be_an_instance_of(String)
StringSpecs::MyString.new("mypassword").crypt(StringSpecs::MyString.new("$2a$04$0WVaz0pV3jzfZ5G5tpmHWu")).should be_an_instance_of(String)
end
end end
it "raises an ArgumentError when the salt is shorter than two characters" do platform_is_not :openbsd do
lambda { "hello".crypt("") }.should raise_error(ArgumentError) # Note: MRI's documentation just says that the C stdlib function crypt() is
lambda { "hello".crypt("f") }.should raise_error(ArgumentError) # called.
lambda { "hello".crypt("\x00\x00") }.should raise_error(ArgumentError) #
lambda { "hello".crypt("\x00a") }.should raise_error(ArgumentError) # I'm not sure if crypt() is guaranteed to produce the same result across
lambda { "hello".crypt("a\x00") }.should raise_error(ArgumentError) # different platforms. It seems that there is one standard UNIX implementation
end # of crypt(), but that alternative implementations are possible. See
# http://www.unix.org.ua/orelly/networking/puis/ch08_06.htm
it "returns a cryptographic hash of self by applying the UNIX crypt algorithm with the specified salt" do
"".crypt("aa").should == "aaQSqAReePlq6"
"nutmeg".crypt("Mi").should == "MiqkFWCm1fNJI"
"ellen1".crypt("ri").should == "ri79kNd7V6.Sk"
"Sharon".crypt("./").should == "./UY9Q7TvYJDg"
"norahs".crypt("am").should == "amfIADT2iqjA."
"norahs".crypt("7a").should == "7azfT5tIdyh0I"
it "raises an ArgumentError when the string contains NUL character" do # Only uses first 8 chars of string
lambda { "poison\0null".crypt("aa") }.should raise_error(ArgumentError) "01234567".crypt("aa").should == "aa4c4gpuvCkSE"
end "012345678".crypt("aa").should == "aa4c4gpuvCkSE"
"0123456789".crypt("aa").should == "aa4c4gpuvCkSE"
it "calls #to_str to converts the salt arg to a String" do # Only uses first 2 chars of salt
obj = mock('aa') "hello world".crypt("aa").should == "aayPz4hyPS1wI"
obj.should_receive(:to_str).and_return("aa") "hello world".crypt("aab").should == "aayPz4hyPS1wI"
"hello world".crypt("aabc").should == "aayPz4hyPS1wI"
end
"".crypt(obj).should == "aaQSqAReePlq6" it "raises an ArgumentError when the string contains NUL character" do
lambda { "poison\0null".crypt("aa") }.should raise_error(ArgumentError)
end
it "calls #to_str to converts the salt arg to a String" do
obj = mock('aa')
obj.should_receive(:to_str).and_return("aa")
"".crypt(obj).should == "aaQSqAReePlq6"
end
it "taints the result if either salt or self is tainted" do
tainted_salt = "aa"
tainted_str = "hello"
tainted_salt.taint
tainted_str.taint
"hello".crypt("aa").tainted?.should == false
tainted_str.crypt("aa").tainted?.should == true
"hello".crypt(tainted_salt).tainted?.should == true
tainted_str.crypt(tainted_salt).tainted?.should == true
end
it "doesn't return subclass instances" do
StringSpecs::MyString.new("hello").crypt("aa").should be_an_instance_of(String)
"hello".crypt(StringSpecs::MyString.new("aa")).should be_an_instance_of(String)
StringSpecs::MyString.new("hello").crypt(StringSpecs::MyString.new("aa")).should be_an_instance_of(String)
end
it "raises an ArgumentError when the salt is shorter than two characters" do
lambda { "hello".crypt("") }.should raise_error(ArgumentError)
lambda { "hello".crypt("f") }.should raise_error(ArgumentError)
lambda { "hello".crypt("\x00\x00") }.should raise_error(ArgumentError)
lambda { "hello".crypt("\x00a") }.should raise_error(ArgumentError)
lambda { "hello".crypt("a\x00") }.should raise_error(ArgumentError)
end
end end
it "raises a type error when the salt arg can't be converted to a string" do it "raises a type error when the salt arg can't be converted to a string" do
lambda { "".crypt(5) }.should raise_error(TypeError) lambda { "".crypt(5) }.should raise_error(TypeError)
lambda { "".crypt(mock('x')) }.should raise_error(TypeError) lambda { "".crypt(mock('x')) }.should raise_error(TypeError)
end end
it "taints the result if either salt or self is tainted" do
tainted_salt = "aa"
tainted_str = "hello"
tainted_salt.taint
tainted_str.taint
"hello".crypt("aa").tainted?.should == false
tainted_str.crypt("aa").tainted?.should == true
"hello".crypt(tainted_salt).tainted?.should == true
tainted_str.crypt(tainted_salt).tainted?.should == true
end
it "doesn't return subclass instances" do
StringSpecs::MyString.new("hello").crypt("aa").should be_an_instance_of(String)
"hello".crypt(StringSpecs::MyString.new("aa")).should be_an_instance_of(String)
StringSpecs::MyString.new("hello").crypt(StringSpecs::MyString.new("aa")).should be_an_instance_of(String)
end
end end

View file

@ -114,24 +114,24 @@ describe 'Socket#connect_nonblock' do
platform_is_not :windows do platform_is_not :windows do
it 'raises Errno::EISCONN when already connected' do it 'raises Errno::EISCONN when already connected' do
@server.listen(1) @server.listen(1)
@client.connect(@server.getsockname).should == 0 @client.connect(@server.connect_address).should == 0
lambda { lambda {
@client.connect_nonblock(@server.getsockname) @client.connect_nonblock(@server.connect_address)
# A second call needed if non-blocking sockets become default # A second call needed if non-blocking sockets become default
# XXX honestly I don't expect any real code to care about this spec # XXX honestly I don't expect any real code to care about this spec
# as it's too implementation-dependent and checking for connect() # as it's too implementation-dependent and checking for connect()
# errors is futile anyways because of TOCTOU # errors is futile anyways because of TOCTOU
@client.connect_nonblock(@server.getsockname) @client.connect_nonblock(@server.connect_address)
}.should raise_error(Errno::EISCONN) }.should raise_error(Errno::EISCONN)
end end
it 'returns 0 when already connected in exceptionless mode' do it 'returns 0 when already connected in exceptionless mode' do
@server.listen(1) @server.listen(1)
@client.connect(@server.getsockname).should == 0 @client.connect(@server.connect_address).should == 0
@client.connect_nonblock(@server.getsockname, exception: false).should == 0 @client.connect_nonblock(@server.connect_address, exception: false).should == 0
end end
end end
@ -140,7 +140,7 @@ describe 'Socket#connect_nonblock' do
@server.bind(@sockaddr) @server.bind(@sockaddr)
lambda { lambda {
@client.connect_nonblock(@server.getsockname) @client.connect_nonblock(@server.connect_address)
}.should raise_error(IO::EINPROGRESSWaitWritable) }.should raise_error(IO::EINPROGRESSWaitWritable)
end end
end end

View file

@ -30,7 +30,13 @@ describe 'UDPSocket#initialize' do
@socket.binmode?.should be_true @socket.binmode?.should be_true
end end
it 'raises Errno::EAFNOSUPPORT when given an invalid address family' do it 'raises Errno::EAFNOSUPPORT or Errno::EPROTONOSUPPORT when given an invalid address family' do
lambda { UDPSocket.new(666) }.should raise_error(Errno::EAFNOSUPPORT) begin
UDPSocket.new(666)
rescue Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT => e
[Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT].should include(e.class)
else
raise "expected Errno::EAFNOSUPPORT or Errno::EPROTONOSUPPORT exception raised"
end
end end
end end