Ada sejumlah hal 'rapi' yang dapat dilakukan dalam bahasa dinamis yang dapat disembunyikan di bagian-bagian kode yang tidak segera jelas bagi programmer atau auditor lain mengenai fungsionalitas potongan kode yang diberikan.
Pertimbangkan urutan ini dalam irb (shell ruby interaktif):
irb(main):001:0> "bar".foo
NoMethodError: undefined method `foo' for "bar":String
from (irb):1
from /usr/bin/irb:12:in `<main>'
irb(main):002:0> class String
irb(main):003:1> def foo
irb(main):004:2> "foobar!"
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> "bar".foo
=> "foobar!"
Apa yang terjadi di sana adalah saya mencoba memanggil metode foo
dalam konstanta String. Ini gagal. Saya kemudian membuka kelas String dan mendefinisikan metode foo
o return "foobar!"
, dan kemudian menyebutnya. Ini berhasil.
Ini dikenal sebagai kelas terbuka dan memberi saya mimpi buruk setiap kali saya berpikir untuk menulis kode di ruby yang memiliki keamanan atau integritas. Tentu itu memungkinkan Anda melakukan beberapa hal dengan sangat cepat ... tapi saya bisa membuatnya jadi setiap kali seseorang menyimpan string, menyimpannya ke file, atau mengirimnya melalui jaringan. Dan ini sedikit mendefinisikan kembali String dapat terselip di mana saja dalam kode.
Banyak bahasa dinamis lainnya memiliki hal serupa yang dapat dilakukan. Perl memiliki Tie :: Scalar yang dapat di belakang layar mengubah cara kerja skalar yang diberikan (ini sedikit lebih jelas dan membutuhkan perintah khusus yang dapat Anda lihat, tetapi skalar yang dikirimkan dari tempat lain bisa menjadi masalah). Jika Anda memiliki akses ke Perl Cookbook, cari Resep 13.15 - Membuat Variabel Ajaib dengan dasi.
Karena hal-hal ini (dan yang lain sering menjadi bagian dari bahasa dinamis), banyak pendekatan untuk analisis statis keamanan dalam kode tidak berfungsi. Perl dan Undecidability menunjukkan hal ini sebagai kasus dan menunjukkan bahkan masalah sepele seperti itu dengan sintaksis menyoroti ( whatever / 25 ; # / ; die "this dies!";
menimbulkan tantangan karena whatever
dapat didefinisikan untuk mengambil argumen atau tidak pada saat runtime benar-benar mengalahkan stabilo sintaksis atau penganalisa statis).
Ini bisa menjadi lebih menarik di Ruby dengan kemampuan untuk mengakses lingkungan tempat penutupan ditentukan (lihat YouTube: Menjaga Ruby Reasonable dari RubyConf 2011 oleh Joshua Ballanco). Saya menyadari video ini dari komentar Ars Technica oleh MouseTheLuckyDog .
Pertimbangkan kode berikut:
def mal(&block)
puts ">:)"
block.call
t = block.binding.eval('(self.methods - Object.methods).sample')
block.binding.eval <<-END
def #{t.to_s}
raise 'MWHWAHAW!'
end
END
end
class Foo
def bar
puts "bar"
end
def qux
mal do
puts "qux"
end
end
end
f = Foo.new
f.bar
f.qux
f.bar
f.qux
Kode ini sepenuhnya terlihat, tetapi mal
metodenya bisa di tempat lain ... dan dengan kelas terbuka, tentu saja, dapat didefinisikan ulang di tempat lain.
Menjalankan kode ini:
~ / $ ruby foo.rb
batang
> :)
qux
batang
b.rb: 20: dalam `qux ': MWHWAHAW! (RuntimeError)
dari b.rb: 30: in `'
~ / $ ruby foo.rb
batang
> :)
qux
b.rb: 20: di `bar ': MWHWAHAW! (RuntimeError)
dari b.rb: 29: in `'
Dalam kode ini, penutupan dapat mengakses semua metode dan binding lainnya yang didefinisikan di kelas pada lingkup itu. Itu mengambil metode acak dan mendefinisikannya kembali untuk memunculkan pengecualian. (lihat kelas Binding di Ruby untuk mendapatkan gagasan tentang apa yang dapat diakses oleh objek ini)
Variabel, metode, nilai diri, dan mungkin blok iterator yang dapat diakses dalam konteks ini semuanya dipertahankan.
Versi lebih pendek yang menunjukkan definisi ulang suatu variabel:
def mal(&block)
block.call
block.binding.eval('a = 43')
end
a = 42
puts a
mal do
puts 1
end
puts a
Yang mana, ketika dijalankan menghasilkan:
42
1
43
Ini lebih dari kelas terbuka yang saya sebutkan di atas yang membuat analisis statis tidak mungkin. Apa yang ditunjukkan di atas adalah bahwa penutupan yang dilewatkan di tempat lain, disertai dengan lingkungan penuh yang telah didefinisikan. Ini dikenal sebagai lingkungan kelas satu (seperti ketika Anda dapat melewati fungsi, mereka adalah fungsi kelas satu, ini adalah lingkungan dan semua ikatan yang tersedia pada saat itu). Seseorang dapat mendefinisikan kembali variabel apa pun yang didefinisikan dalam ruang lingkup penutupan.
Baik atau buruk, mengeluh tentang ruby atau tidak (ada kegunaan di mana seseorang ingin bisa mendapatkan di lingkungan metode (lihat Aman di Perl)), pertanyaan "mengapa ruby akan dibatasi untuk proyek pemerintah "Benar-benar dijawab dalam video yang ditautkan di atas.
Mengingat bahwa:
- Ruby memungkinkan seseorang untuk mengekstrak lingkungan dari penutupan apa pun
- Ruby menangkap semua ikatan dalam lingkup penutupan
- Ruby mempertahankan semua binding sebagai hidup dan bisa berubah
- Ruby memiliki binding baru yang mengikat binding lama (daripada mengkloning lingkungan atau melarang rebinding)
Dengan implikasi dari empat pilihan desain ini, tidak mungkin untuk mengetahui apa yang dilakukan sedikit kode.
Lebih lanjut tentang ini dapat dibaca di blog Abstract Heresies . Pos khusus adalah tentang Skema di mana perdebatan semacam itu terjadi. (terkait pada SO: Mengapa Skema tidak mendukung lingkungan kelas satu? )
Namun, seiring berjalannya waktu, saya menyadari bahwa ada lebih banyak kesulitan dan lebih sedikit daya dengan lingkungan kelas satu daripada yang saya kira sebelumnya. Pada titik ini saya percaya bahwa lingkungan kelas satu tidak berguna yang terbaik, dan paling buruk berbahaya.
Saya harap bagian ini menunjukkan aspek bahaya dari lingkungan kelas satu dan mengapa Ruby diminta untuk menghapus dari solusi yang disediakan. Bukan hanya karena Ruby adalah bahasa dinamis (seperti yang disebutkan-jawab lain, bahasa dinamis lain telah diizinkan dalam proyek lain), tetapi ada masalah khusus yang membuat beberapa bahasa dinamis semakin sulit dipikirkan.