[Перевод] Веская причина для проверки ваших зависимостей: AGPL-edition

Вот вы берёте код под лицензиями BSD, MIT и Apache2 и в ус не дуете, а потом — бац! — вторая смена, и в транзитивной зависимости рисуется код под AGPL. Мы стараемся следить за этим и предпочитаем скорее перебдеть, чем недобдеть.

mb_z57j18zi54isv5iw5brf_kui.png
Прежде чем добавить в любой из своих проектов новые зависимости, я всегда делаю базовую проверку. Что я проверяю (стандартный чеклист):

  • Под какой лицензией выпущен код?
  • Кто автор?
  • Есть ли какие-то серьёзные нерешённые проблемы в трекере ошибок?
  • Ведётся ли история серьёзных багов в трекере ошибок?
  • Каким образом происходит код-ревью для пулреквестов?


После этого я бегло просматриваю сам код в поисках чего-то явно небезопасного или вредоносного. Это нужно, чтобы почувствовать качество самой кодовой базы. Далее я пытаюсь найти «коричневые M&Ms» — незначительные детали, которые могут указывать на большие проблемы. И рекурсивно повторяю всё вышесказанное с транзитивными зависимостями. Кроме того, я ещё раз бегло просматриваю код каждый раз, когда обновляю зависимость.

Это довольно большой объём работы, но она необходима, чтобы не стать жертвой атак типа event-stream. А недавно мне напомнили ещё об одной веской причине для проверки зависимостей. В тот момент я делал ревью активно рекламируемой библиотеки от Duo для WebAuthn на Go, она лежит тут: github.com/duo-labs/webauthn.

Всё началось с того, что я заметил несколько «коричневых M&M’s»:

  • Данные логировались в stdout, несмотря на то, что это библиотека.
  • Код был с запашком.


Конечно же, эти мелкие проблемы были лишь предвестниками чего-то большего: когда я начал ревью одной из транзитивных зависимостей (github.com/katzenpost/core/crypto/eddsa), меня встретил заголовок лицензии AGPLv3.

Это плохая новость для тех, кто хочет использовать библиотеку WebAuthn от Duo. Несмотря на то, что Duo лицензировал свою библиотеку под BSD, выбирая её, вы также связываете своё приложение с библиотекой AGPL. А, по мнению (A)GPL, таким образом вы создаёте «модифицированный» продукт, попадающий под правила раздела 13 AGPL:

Если вы вносите изменения в программу, ваша изменённая версия должна явно предлагать всем пользователям, взаимодействующим с ней удалённо через сеть (если ваша версия поддерживает такое взаимодействие), возможность получить бесплатный свободный доступ к исходному коду вашей версии с помощью стандартных средств копирования программного обеспечения (несмотря на любые другие положения настоящей Лицензии).

Другими словами, если вы использовали github.com/duo-labs/webauthn в общедоступном web-приложении, теперь ваше web-приложение должно быть с открытым исходным кодом.

Самая возмутительная вещь об этой зависимости заключается в том, что она дублирует golang.org/x/crypto/ed25519 — одну из квазистандартных «х» библиотек Go.

По факту github.com/duo-labs/webauthn — это то же самое, что и первоначально используемая golang.org/x/crypto/ed25519. Подмена произошли во время пулреквеста внешней коллаборации под названием «Consolidate COSE things to their own area». В процессе переноса части кода из одного файла в другой этот пулреквест незаметно изменил реализацию OKPPublicKeyData.Verify.

Вот OKPPublicKeyData.Verify, который использует golang.org/x/crypto/ed25519:

// Verify Octet Key Pair (OKP) Public Key Signature
func (k *OKPPublicKeyData) Verify(data []byte, sig []byte) (bool, error) {
	f := HasherFromCOSEAlg(COSEAlgorithmIdentifier(k.PublicKeyData.Algorithm))
	h := f()
	h.Write(data)
	return ed25519.Verify(k.XCoord, h.Sum(nil), sig), nil
}


А вот OKPPublicKeyData.Verify, который использует AGPL-лицензированный github.com/katzenpost/core/crypto/eddsa:

// Verify Octet Key Pair (OKP) Public Key Signature
func (k *OKPPublicKeyData) Verify(data []byte, sig []byte) (bool, error) {
	f := HasherFromCOSEAlg(COSEAlgorithmIdentifier(k.PublicKeyData.Algorithm))
	h := f()
	h.Write(data)
	var oKey eddsa.PublicKey
	err := oKey.FromBytes(k.XCoord)
	if err != nil {
		return false, err
	}
	return oKey.Verify(h.Sum(nil), sig), nil
}


Этому изменению не было дано никакого объяснения. Ревью пулреквеста провели два сотрудника Duo, одобрили его и смержили.

Именно поэтому мне не нравится принимать запросы на пулреквесты, которые перемещают код. Даже если новая организация кода лучше прежнего, затраченное время на проверку «не делает ли новый пулреквест чего-нибудь лишнего» съедает слишком много времени.

Я разместил предупреждение о зависимости библиотеки с лицензией AGPL, и разработчики вернули обратно golang.org/x/crypto/ed25519. Несмотря на это, я решил не использовать github.com/duo-labs/webauthn. Основная часть библиотеки и её зависимостей предназначена для поддержки WebAuthn misfeature под названием attestation, которую у меня есть меньше, чем нулевое желание использовать. Я только что закончил писать значительно более простую, свободную от attestation библиотеку, и по размеру она меньше, чем одна десятая от вышеупомянутой. Скоро я открою её исходный код. Разработка этой библиотеки вышла дешевле, чем ответственность за использование существующей WebAuthn Go библиотеки.

Этот случай напомнил мне, почему мне нравится программирование на Go. Благодаря обширным стандартным и квазистандартным «х» библиотекам Go, в моих проектах обычно мало дополнительных зависимостей. А хорошая репутация и операционные процедуры Go позволяют мне не париться и не заниматься перепроверкой исходного кода компилятора и стандартных библиотек. И, несмотря на то, что я люблю Rust, я прихожу в ужас каждый раз, когда смотрю на дерево зависимостей их типичных библиотек: обычно я вижу десятки транзитивных зависимостей, написанных непонятными рандомными чуваками из интернета, которым у меня нет никаких оснований доверять. Проверка всех этих зависимостей занимает слишком много времени, поэтому я гораздо менее продуктивен в Rust, чем Go.

Последнее замечание: как поклонник проверяемых структур данных, таких как Прозрачность сертификатов (Certificate Transparency), я должен любить новую базу данных Go checksum database. Однако checksum database не принесёт вам никакой пользы, если вы не потратите время на проверку своих зависимостей. К сожалению, я уже видел одного чрезмерно увлечённого пользователя Go, утверждающего, что checksum database решает все проблемы с управлением зависимостями. Но это не так. Нет никаких простых способов, чтобы от этого избавиться, и вам нужно смириться с фактом: время от времени нужно делать ревью зависимостей в ваших проектах.

© Habrahabr.ru