07 апреля 2010, 12:59
Темы: ruby, ruby1.9, syntax, security

Введение
Совершенно не по работе заинтересовался переменной $SAFE и её ролью в жизни современного разработчика. Оказалось, что всё нужно проверять самому.
Нежная безопасность
Для тестирования возможностей на разных уровнях безопасности собрал небольшую программку. Она просит ввести имя файла, делая строковую переменную небезопасной, и пытается что-то с этим всем сделать.
print "child: "
child = gets.chomp
puts "child tainted: #{child.tainted?}"
(0..4).to_a.each do |i|
puts "SAFE: #{i}"
$a = "safe"
th = Thread.new do
$SAFE = i
child_copy = child.dup
Thread.current[:out] = ""
begin
load child_copy
Thread.current[:out] += "1. Child loaded\n"
rescue SecurityError => e
Thread.current[:out] += "1. Security error: #{e.to_s}\n"
begin
child_copy.untaint
load child_copy
Thread.current[:out] += "2. Child untainted and loaded\n"
rescue SecurityError => e
Thread.current[:out] += "2. Security error: #{e.to_s}\n"
begin
Thread.current[:out] += "3. Read from file '#{child_copy}': '#{File.read(child_copy)}'\n"
rescue SecurityError => e
Thread.current[:out] += "3. Security error: #{e.to_s}\n"
begin
Thread.current[:out] += "4. Read from untainted file: '#{File.read("child.rb")}'\n"
rescue SecurityError => e
Thread.current[:out] += "4. Security error: #{e.to_s}\n"
end
end
end
end
begin
$a = "modified"
Thread.current[:out] += "5. Global variable modified: $a = '#{$a}'\n"
rescue SecurityError => e
Thread.current[:out] += "5. Security error: #{e.to_s}\n"
end
begin
Dir.mkdir "test"
Thread.current[:out] += "6. Created directory 'test': #{File.exist?("test")}\n"
Dir.rmdir "test"
rescue SecurityError => e
Thread.current[:out] += "6. Security error: #{e.to_s}\n"
end
begin
Thread.current[:out] += "7. Dir glob: #{Dir.glob(File.join("..", "*")).inspect}\n"
rescue SecurityError => e
Thread.current[:out] += "7. Security error: #{e.to_s}\n"
end
begin
Thread.current[:out] += "8. System ls output: '#{`ls`.chomp}'"
rescue SecurityError => e
Thread.current[:out] += "8. Security error: #{e.to_s}\n"
end
end
th.join
puts "Global variable: $a = '#{$a}'"
puts th[:out] if th[:out]
end
Конструкция со Thread.current[:out] используется потому, что для $SAFE >= 4 нельзя ничего писать ни в какие устройства вывода.
Вроде бы всё логично. Первый уровень годится для умеренного карантина внешних данных. При желании их можно и расколдовать. Второй уровень запрещает изменения в файловой системе. Третий уровень похож на осаду с постоянным подозрением на шпионаж. Все созданные объекты считаются небезопасными. А четвёртый уровень это самое близкое к песочнице (sandbox) в руби, что что есть.
Кстати, когда ещё github работал как репозиторий библиотек, спецификация gemspec выполнялась там под $SAFE = 3. Для разработчиков это выливалось в то, что нужно было перечислять все файлы своей библиотеки вручную вместо использования какого-нибудь листинга.
Суровый гайдлайн
Конечно же, только использование $SAFE не убережёт от действительно настойчивой атаки или блокирующего кода. Например:
Thread.new do
$SAFE = 2
class String
def ==(other_string)
true
end
end
end.join
puts "string modified: #{'a' == 'b'}"
И это на втором уровне! А на третьем открыть класс тоже можно, но вызов перегруженного оператора будет вызывать SecurityError.
На сегодняшний момент эту концепцию безопасности можно считать сырой. Актуальное поведение руби 1.8 слегка отклоняется от описаний, что я нашёл. Поведение в 1.9 изменилось, но подробно нигде не описано (я не нашёл).
Это не значит, что этой переменной нет применения в жизни прогрессивного человечества. Адекватное текущему состоянию применение это гайдлайн при разработке. Руководство для программистов, которое само следит за своим исполнением. Жестковато, но зато действенно. :)
Материалы для самостоятельного изучения
- Код примеров в статье на github
- Старая, но самая подробная документация по $SAFE
- Просто дополнительно: шпаргалка по руби
24 марта 2010, 19:07
Темы: git, syntax

Введение
Для работы в проекте с открытыми исходниками весьма удобна распределённая система контроля версий. Я использую git. Понятно, что есть процесс с использованием патча, высылаемого по почте, но этот процесс не является эксклюзивным для распределённой системы контроля версий. Поэтому я опишу процесс с так называемым pull request.
Постановка задачи
Пишет мне некто Tallak Tveide, сообщая, что он сделал копию моего проекта у себя на github и внёс несколько правок, которые ему были необходимы, и от которых другие ребята, пользующиеся этой библиотекой только выиграют. Ветка, в которой находятся нужные мне правки, называется eos_40D_bugs. Это довольно кстати, что нашёлся человек с Кэноном, потому что я испытываю всё на Никонах :)
Каковы же мои действия?
Решение
Заходим в наш локальный рабочий репозиторий и добавляем новый источник правок:
git remote add tallakt git://github.com/tallakt/gphoto4ruby
Теперь рассмотрим правки:
git fetch tallakt eos_40D_bugs:develop
Эта команда заберёт из репозитория tallakt с ветки eos_40D_bugs исправления и создаст локальную версию в локальной ветке develop. Чтобы увидеть исправления:
git diff develop
Что выдаст нам исправления относительно текущей ветки.
git checkout develop
Чтобы работать с правками и тестировать то, что получилось.
Если я пока не готов сливать исправления с основной веткой master, но хочу ещё поработать с этим из разных мест, то мне нужно создать ветку develop в моём центральном репозитории на github, который относительно локальной копии у меня обычно называется origin.
git push origin develop
Это создаст ветку develop на удалённом репозитории, с которой я потом смогу работать из другого локального репозитория, выполнив:
git pull origin develop
После того, как я доволен изменениями и хочу сделать официальный релиз:
git merge master
git branch -d develop
git push origin master
git push origin :develop
Первая команда, предполагая, что текущая ветка develop, сливает её в master. Вторая команда удаляет локальную ветку develop. Третья команда отправляет изменения в ветку master на центральном репозитории. Четвёртая команда удаляет ветку develop на центральном репозитории.
Материалы для самостоятельного изучения
- Несколько шпаргалок от github
- Дельная документация по git
Послесловие
Как вы заметили, в этом году мои статьи сопровождаются прекраснейшими тематическими картинками авторства Ирины Троицкой моей прекрасной супруги. Её перу также принадлежит дизайн сайта и логотипа.
26 февраля 2010, 10:47
Темы: notice

Давненько я не обновлял этот блог :)
Всё дело в том, что где-то там был Новый Год, в который я торжественно посетил родителей. До и после Нового Года была сдача панорам Санкт-Петербурга и ещё одного пока не скажу какого города.
Когда съёмки 2009 года были сданы, выяснилось, что полным ходом идёт зима. А у нас не так много городов, которые можно красиво снимать зимой. Тогда мы отправились всем составом в тёплые края, в месячный отпуск.
Теперь, когда я снова в Москве, этот блог вернётся к своему обычному режиму. Спасибо всем, кто был со мной в прошлом году. Этот год обещает быть очень интересным.
15 декабря 2009, 13:46
Темы: ruby, ruby1.9, bash
Введение
Как молодой язык с неутверждённой спецификацией, руби переживает подростковую болезнь, через которую большинство известных языков уже прошли. Есть новая более быстрая версия, на которую уже стоит переходить, но уже много написано на предыдущей, и так боязно всё ломать...
Поэтому необходимо найти удобный для себя способ (а лучше несколько) чтобы начать использовать руби 1.9.
Постановка задачи
Сейчас практически панацеей для использования более одной версии руби является rvm. Очень удобно в использовании, полностью прозрачно, и позволяет иметь разные версии руби в разных окнах терминала.
Но недавно мне понадобилось скомпилировать wxRuby под свою систему (kubuntu 9.10 amd64), и rvm не справилась с этой задачей. По какой-то причине в момент компилляции были недоступны заголовки руби. Поэтому я решил поставить две версии руби более явно: одна системная (1.8.7) и одна в папке /opt (1.9.1). Причем все команды, связанные с руби 1.9 будут вызываться с суффиксом: ruby1.9, irb1.9, gem1.9, rake1.9.
Возможно, подобных инструкций уже полно, но мне будет удобнее, если я точно буду знать, где находится одна из них :) При всём этом, конечно, rvm продолжает работать. Мы никак ему не помешаем.
Решение
Сначала нужно поставить новый readline. Без него, когда мы будем использовать irb1.9, мы не сможем наслаждаться доступом к истории с помощью стрелок вверх-вниз и перемещаться по введенному тексту с помощью стрелок в стороны.
sudo apt-get install libreadline5-dev
Теперь хорошо бы вписать пути в наше окружение. В конце ~/.bashrc добавим:
export PATH=$PATH:/opt/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/lib
Теперь следует скачать и разархивировать последнюю версию руби. Зайдя в папку скомпилировать и установить:
./configure --prefix=/opt --enable-shared --program-suffix=1.9
make
sudo make install
Теперь у нас есть две отдельных установки руби и сопутствующих инструментов. Единственное, что обе установки используют общие конфигурационные файлы: ~/.gemrc, ~/.irbrc и т.п., что вполне удобно. Также для обеих систем общей директорией джемов будет ~/.gem, куда будут устанавливаться библиотеки, запусти мы их установку без sudo (в случае с sudo, конечно же, директории установки различаются).
Так же я не нашёл быстрого способа добавить /opt/bin в переменную PATH для sudo. Поэтому в таких случаях пока использую полный путь. Например, первая команда, которую следует выполнить:
sudo /opt/bin/gem1.9 update --system
Потому что в пакете с руби идёт версия 1.3.1, а настоящие пацаны уже во всю используют 1.3.5.
Материалы для самостоятельного изучения
- Всё об установке нескольких версий руби на одной системе
- Проект «Используй руби 1.9 или вали!»
09 декабря 2009, 16:43
Темы: ruby, ruby1.9, syntax, extension
Введение
Сегодня будет блиц-молния, которой место, скорее в твиттере. Однако, мне необходимо развеять тучи, которые я сам же и нагнал. :)
Постановка задачи
Не так давно я писал о том, что при создании руби-оболочки вокруг библиотеки на си, легко получить код, который блокирует все потоки приложения. Можно ли с этим что-то сделать?
Решение
Если мы имеем чуть больше контроля над участками кода на си, которые выполняются долго (обычно в цикле), то можно добавить спасительный код в тело цикла:
if (!rb_thread_alone()) rb_thread_schedule();
Это позволит каждый раз при очередном витке передавать управление соседним потокам, если они есть, и сделает приложение более отзывчивым.
То есть, если мы сделаем точную копию функции из той статьи и добавим наш спасительный код внутрь for, то в выводе такой программы:
# coding: utf-8
require "block_thread.so"
t1 = Thread.new do
10.times { |i| puts i; sleep 0.1 }
end
t2 = Thread.new do
puts "Блокируем"
BlockThread.cycle
puts "Разблокируем"
end
t3 = Thread.new do
puts "Стараемся не блокировать"
BlockThread.cycle_with_schedule
puts "Закончили стараться"
end
t1.join
t2.join
t3.join
Будет картина гораздо приятнее:
Блокируем
0
Стараемся не блокировать
Разблокируем
1
2
3
4
5
Закончили стараться
6
7
8
9
Заметьте, кстати, что два лишних вывода между «Блокируем» и «Разблокируем» благодаря передаче управления соседнимпотокам между cycle и puts.
Понятно, что соседние потоки тормозятся. Но теперь не на всё время, а только на исполнение одного витка цикла.
Материалы для самостоятельного изучения
Полный код статьи на github