...
 
Commits (38)
......@@ -33,6 +33,8 @@ cache:
>> /etc/apt/apt.conf.d/99custom
# Ensure the custom APT directory does exist
- mkdir -p {${APT_ARCHIVES_DIR},${APT_LISTS_DIR}}/partial
# Remove jessie-updates repo which doesn't exist anymore
- sed -i -e '/jessie-updates/s/^#*/#/' /etc/apt/sources.list
- apt-get update -qq
# To keep things DRY, use an env var to handle packages to be installed via APT
- apt-get install -qq -y $APT_INSTALL_PACKAGES
......@@ -52,7 +54,8 @@ cache:
CHECK_CODE_COVERAGE: "true"
<<: *setup_prerequisites
script:
- eatmydata gem install bundler --no-ri --no-rdoc
- bundler_args="$(ruby -e 'puts %{-v 1.17.3} if RUBY_VERSION[2].to_i < 3')"
- eatmydata gem install bundler --no-document $bundler_args
- eatmydata bundle install --jobs $(nproc) --path vendor
- SCHLEUDER_ENV=test SCHLEUDER_CONFIG=spec/schleuder.yml eatmydata bundle exec rake db:init
- eatmydata bundle exec rspec
......@@ -60,14 +63,15 @@ cache:
changelog:
image: debian:unstable
variables:
APT_INSTALL_PACKAGES: ca-certificates git
APT_INSTALL_PACKAGES: ca-certificates git ruby
<<: *setup_prerequisites
script:
# Ensure we work with the latest master
- git fetch origin master:master
# Compare the master and current branch using their common ancestors
- source <(./utils/ci/get-target-branch.rb)
# Ensure we work with the latest state
- git fetch origin $target_branch:$target_branch
# Compare the target and current branch using their common ancestors
# to check if the changelog was edited
- if git diff --exit-code --quiet master...HEAD -- CHANGELOG.md; then
- if git diff --exit-code --quiet $target_branch...HEAD -- CHANGELOG.md; then
echo "No CHANGELOG edit found, please verify manually";
exit 1;
fi
......@@ -111,7 +115,7 @@ bundler:audit:
only:
- schedules
script:
- gem install bundler-audit --no-ri --no-rdoc
- gem install bundler-audit --no-document
- bundle install --jobs $(nproc) --path vendor
- bundle-audit update
- bundle-audit check
......
......@@ -5,6 +5,42 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## unreleased
## [3.5.0] / ???
### Added
* `deliver_selfsent` per-list option to control whether subscribers get a copy of mail they sent themselves. (#365)
## [3.4.1] / 2019-09-16
### Fixed
* Do not crash on protected header emails generated by mutt (#430)
* Show an error message if `refresh_keys` is called with an email address for which no list exists.
* Fix recognizing keywords with "protected headers" and empty subject. Previously, if the subject was unset, keywords were not recognized and the original "protected headers" could leak. (#431)
### Changed
* Filter third party signatures on user-IDs when fetching or refreshing keys to mitigate against signature flooding. This works only if the version of gpg is 2.1.15 or newer. If the version is older, an email is being sent to the superadmin each time a key is fetched or keys are refreshed. See <https://dkg.fifthhorseman.net/blog/openpgp-certificate-flooding.html> for background information.
## [3.4.0] / 2019-02-14
### Fixed
* Stop leaking keywords to third parties by stripping HTML from multipart/alternative messages if they contain keywords. (#399)
* Avoid shelling out in a test-case to avoid an occasional error occurring in CI runs that complains about invalid data in ASCII-8BIT strings.
### Changed
* Update the dependency 'mail' to version 2.7.x., and allow carriage returns (CR) in test-cases as mail-2.7 puts those out.
* Update the dependency 'sqlite3' to version 1.3.x.
* Adapt fixtures and factories for factorybot version 5.x.
* Let schleuder-code load the filter files in test-mode, avoid explicit path names (which make headaches when running tests on installed packages).
## [3.3.0] / 2018-09-04
### Fixed
* Handle missing arguments for several keywords and reply with a helpful error-message.
......@@ -17,7 +53,6 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Added
* To remove a fingerprint from a subscription one can use the new keyword X-UNSET-FINGERPRINT (#360).
* `deliver_selfsent` per-list opton to control whether subscribers get a copy of mail they sent themselves. (#365)
### Changed
......
......@@ -47,15 +47,15 @@ Additionally these **rubygems** are required (will be installed automatically un
Installing Schleuder
------------
1. Download [the gem](https://schleuder.org/download/schleuder-3.2.3.gem) and [the OpenPGP-signature](https://schleuder.org/download/schleuder-3.2.3.gem.sig) and verify:
1. Download [the gem](https://schleuder.org/download/schleuder-3.4.1.gem) and [the OpenPGP-signature](https://schleuder.org/download/schleuder-3.4.1.gem.sig) and verify:
```
gpg --recv-key 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3
gpg --verify schleuder-3.2.3.gem.sig
gpg --verify schleuder-3.4.1.gem.sig
```
2. If all went well install the gem:
```
gem install schleuder-3.2.3.gem
gem install schleuder-3.4.1.gem
```
3. Set up schleuder:
......@@ -65,7 +65,7 @@ Installing Schleuder
This creates necessary directories, copies example configs, etc. If you see errors about missing write permissions please follow the advice given.
For further information on setup and configuration please read <https://schleuder.org/docs/#setup>.
For further information on setup and configuration please read <https://schleuder.org/schleuder/docs/server-admins.html>.
Command line usage
......@@ -145,4 +145,4 @@ GNU GPL 3.0. Please see [LICENSE.txt](LICENSE.txt).
Alternative Download
--------------------
Alternatively to the gem-files you can download the latest release as [a tarball](https://schleuder.org/download/schleuder-3.2.3.tar.gz) and [its OpenPGP-signature](https://schleuder.org/download/schleuder-3.2.3.tar.gz.sig).
Alternatively to the gem-files you can download the latest release as [a tarball](https://schleuder.org/download/schleuder-3.4.1.tar.gz) and [its OpenPGP-signature](https://schleuder.org/download/schleuder-3.4.1.tar.gz.sig).
......@@ -110,7 +110,7 @@ end
desc 'Publish gem-file to rubygems.org'
task :publish_gem do
puts "Really push #{@filename_gem} to rubygems.org? [yN]"
if gets.match(/^y/i)
if $stdin.gets.match(/^y/i)
puts "Pushing..."
`gem push #{@filename_gem}`
else
......
......@@ -67,6 +67,7 @@ module Schleuder
desc 'refresh_keys [list1@example.com]', "Refresh all keys of all list from the keyservers sequentially (one by one or on the passed list). (This is supposed to be run from cron weekly.)"
def refresh_keys(list=nil)
GPGME::Ctx.send_notice_if_gpg_does_not_know_import_filter
work_on_lists(:refresh_keys,list)
permission_notice
end
......@@ -319,11 +320,15 @@ Please notify the users and admins of this list of these changes.
private
def work_on_lists(subj, list=nil)
selected_lists = if list.nil?
List.all
if list.nil?
selected_lists = List.all
else
List.where(email: list)
selected_lists = List.where(email: list)
if selected_lists.blank?
error("No list with this address exists: #{list.inspect}")
end
end
selected_lists.each do |list|
I18n.locale = list.language
output = list.send(subj)
......
module Schleuder
module Filters
def self.strip_html_from_alternative_if_keywords_present(list, mail)
if mail[:content_type].blank? ||
mail[:content_type].content_type != 'multipart/alternative' ||
mail.keywords.blank?
return false
end
Schleuder.logger.debug 'Stripping html-part from multipart/alternative-message because it contains keywords'
mail.parts.delete_if do |part|
part[:content_type].content_type == 'text/html'
end
mail.content_type = 'multipart/mixed'
mail.add_pseudoheader(:note, I18n.t('pseudoheaders.stripped_html_from_multialt_with_keywords'))
end
end
end
......@@ -103,7 +103,7 @@ module GPGME
end
def refresh_key(fingerprint)
args = "#{keyserver_arg} --refresh-keys #{fingerprint}"
args = "#{keyserver_arg} #{import_filter_arg} --refresh-keys #{fingerprint}"
gpgerr, gpgout, exitcode = self.class.gpgcli(args)
if exitcode > 0
......@@ -136,7 +136,8 @@ module GPGME
arguments, error = fetch_key_gpg_arguments_for(input)
return error if error
gpgerr, gpgout, exitcode = self.class.gpgcli(arguments)
self.class.send_notice_if_gpg_does_not_know_import_filter
gpgerr, gpgout, exitcode = self.class.gpgcli("#{import_filter_arg} #{arguments}")
# Unfortunately gpg doesn't exit with code > 0 if `--fetch-key` fails.
if exitcode > 0 || gpgerr.grep(/ unable to fetch /).presence
......@@ -270,5 +271,25 @@ module GPGME
""
end
end
def self.gpg_knows_import_filter?
sufficient_gpg_version?('2.1.15')
end
def import_filter_arg
if self.class.gpg_knows_import_filter?
%{ --import-filter drop-sig='sig_created_d > 0000-00-00'}
end
end
def self.send_notice_if_gpg_does_not_know_import_filter
if ! gpg_knows_import_filter?
Schleuder.logger.notify_superadmin(
subject: 'Schleuder installation problem',
message: "Your version of GnuPG is very old, please update!\n\nWith your version of GnuPG we can not protect your setup against signature flooding. Please update to at least version 2.1.15 to fix this problem. See <https://dkg.fifthhorseman.net/blog/openpgp-certificate-flooding.html> for details on the background."
)
''
end
end
end
end
......@@ -18,9 +18,14 @@ module Schleuder
notify_admin(string, original_message)
end
def notify_admin(thing, original_message=nil, subject='Error')
def notify_superadmin(message:, original_message: nil, subject: 'Error')
notify_admin(message, original_message, subject, superadmin)
end
def notify_admin(thing, original_message=nil, subject='Error', recipients=nil)
# Minimize using other classes here, we don't know what caused the error.
msg_parts = convert_to_msg_parts(thing, original_message)
recipients ||= adminaddresses
Array(adminaddresses).each do |address, key|
mail = Mail.new
mail.from = @from
......
......@@ -53,13 +53,12 @@ module Mail
# headers, which reveals protected subjects.
if self.subject != new.subject
new.protected_headers_subject = self.subject.dup
# Delete the protected headers which might leak information.
if new.parts.first.content_type == "text/rfc822-headers; protected-headers=v1"
new.parts.shift
end
end
# Delete the protected headers which might leak information.
if new.parts.first && new.parts.first.content_type == "text/rfc822-headers; protected-headers=v1"
new.parts.shift
end
new
end
......
module Schleuder
VERSION = '3.2.3'
VERSION = '3.4.1'
end
......@@ -251,6 +251,7 @@ de:
invalid_input: "Ungültige Angabe. Gültig sind: URLs, OpenPGP-Fingerabdrücke, oder Emailadressen."
pseudoheaders:
stripped_html_from_multialt: Diese Email enthielt einen alternativen HTML-Teil, der PGP-Daten beinhaltete. Der HTML-Teil wurde entfernt, um die Email sauberer analysieren zu können.
stripped_html_from_multialt_with_keywords: Diese Email enthielt Schlüsselwörter und einen alternativen HTML-Teil. Der HTML-Teil wurde entfernt, um zu verhindern dass diese Schlüsselwörter Aussenstehenden bekannt werden.
signature_states:
unknown: "Unbekannte Signatur von unbekanntem Schlüssel 0x%{fingerprint}"
unsigned: "Unsigniert"
......
......@@ -255,6 +255,7 @@ en:
invalid_input: "Invalid input. Allowed are: URLs, OpenPGP-fingerprints, or email-addresses."
pseudoheaders:
stripped_html_from_multialt: This message included an alternating HTML-part that contained PGP-data. The HTML-part was removed to enable parsing the message more properly.
stripped_html_from_multialt_with_keywords: This message included keywords and an alternating HTML-part. The HTML-part was removed to prevent the disclosure of these keywords to third parties.
signature_states:
unknown: "Unknown signature by unknown key 0x%{fingerprint}"
unsigned: "Unsigned"
......
......@@ -30,13 +30,13 @@ Gem::Specification.new do |s|
}
s.required_ruby_version = ">= 2.1.0"
s.add_runtime_dependency 'gpgme', '~> 2.0', '>= 2.0.13' # Explicitly include to force a version.
s.add_runtime_dependency 'mail', '~> 2.6.0'
s.add_runtime_dependency 'mail', '~> 2.7.1'
s.add_runtime_dependency 'mail-gpg', '~> 0.3', '>= 0.3.3'
s.add_runtime_dependency 'activerecord', '~> 4.2'
# TODO: Drop this once we cease to support ruby 2.1, see #310
s.add_runtime_dependency 'rack-test', '~> 0.7.0'
s.add_runtime_dependency 'rake', '>= 10.5.0'
s.add_runtime_dependency 'sqlite3', '~> 1'
s.add_runtime_dependency 'sqlite3', '~> 1.3.6'
s.add_runtime_dependency 'sinatra', '~> 1'
s.add_runtime_dependency 'sinatra-contrib', '~> 1'
s.add_runtime_dependency 'thor', '~> 0'
......
FactoryBot.define do
factory :list do
sequence(:email) {|n| "list#{n}@example.org" }
fingerprint "59C71FB38AEE22E091C78259D06350440F759BD3"
log_level "warn"
subject_prefix nil
subject_prefix_in nil
subject_prefix_out nil
openpgp_header_preference "signencrypt"
internal_footer nil
public_footer nil
headers_to_meta ["from", "to", "cc", "date"]
bounces_drop_on_headers "x-spam-flag" => true
keywords_admin_only ["subscribe", "unsubscribe", "delete-key"]
keywords_admin_notify ["add-key"]
send_encrypted_only true
receive_encrypted_only false
receive_signed_only false
receive_authenticated_only false
receive_from_subscribed_emailaddresses_only false
receive_admin_only false
keep_msgid true
bounces_drop_all false
bounces_notify_admins true
deliver_selfsent true
include_list_headers true
include_openpgp_header true
max_message_size_kb 10240
language "en"
forward_all_incoming_to_admins false
logfiles_to_keep 2
fingerprint { "59C71FB38AEE22E091C78259D06350440F759BD3" }
log_level { "warn" }
subject_prefix { nil }
subject_prefix_in { nil }
subject_prefix_out { nil }
openpgp_header_preference { "signencrypt" }
internal_footer { nil }
public_footer { nil }
headers_to_meta { ["from", "to", "cc", "date", "sig", "enc"] }
bounces_drop_on_headers { { "x-spam-flag" => true } }
keywords_admin_only { ["subscribe", "unsubscribe", "delete-key"] }
keywords_admin_notify { ["add-key"] }
send_encrypted_only { true }
receive_encrypted_only { false }
receive_signed_only { false }
receive_authenticated_only { false }
receive_from_subscribed_emailaddresses_only { false }
receive_admin_only { false }
keep_msgid { true }
bounces_drop_all { false }
bounces_notify_admins { true }
deliver_selfsent { true }
include_list_headers { true }
include_openpgp_header { true }
max_message_size_kb { 10240 }
language { "en" }
forward_all_incoming_to_admins { false }
logfiles_to_keep { 2 }
after(:build) do |list|
FileUtils.mkdir_p(list.listdir)
gpghome_upstream = File.join "spec", "gnupg"
......
......@@ -33,18 +33,9 @@ Content-Type: text/plain;
charset="windows-1258"
Content-Transfer-Encoding: quoted-printable
Hello
=20
=20
Goodbye
------=MailPart0000_0010_0A37C499--
From schleuder@example.org Thu Jun 13 15:19:33 2019
Received: from 127.0.0.1 (helo=localhost.localdomain)
by mail.example.com with esmtpsa (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256)
(Exim 4.92)
id 1hbPdc-0007GN-6b
for schleuder@example.org; Thu, 13 Jun 2019 15:19:32 +0200
Date: Thu, 13 Jun 2019 15:19:30 +0200
From: dev <schleuder@example.org>
To: schleuder@example.org
Subject: ...
Message-ID: <20190613131930.ABC@xyz>
MIME-Version: 1.0
Content-Type: multipart/encrypted; protocol="application/pgp-encrypted";
boundary="z6Eq5LdranGa6ru8"
Content-Disposition: inline
--z6Eq5LdranGa6ru8
Content-Type: application/pgp-encrypted
Content-Disposition: attachment
Version: 1
--z6Eq5LdranGa6ru8
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="msg.asc"
-----BEGIN PGP MESSAGE-----
hQIMA691X8Gl2MArAQ//SFZyc/TD/9PYMddJcUIp4F85wsoCUZUaVLpKBzUZdrLv
rln9bgaou4MiUXF8ZTSqq2ET6A3X7+wpDjs79KiDJnILUmguGDT2KTkyD8lxP9nd
oIKtqKdf95AYGmItYkaQqdZf1No2q4ZBQNWXp8+LZgxINn5AW+9wuOo8F9w+tyZJ
8r9jlj5TJ0YnVp5FieKMMyxiSOCGX8lAaqi4TbML35OWrnL8Decsz5tTX4jfqr8L
cvNuIpa863WkbZxMxLEEn4/yC6upmOnU3eSZ9M/UoXiqgBsd01KEoOvmIIPOgGce
IaCxO4zuoPvtcQsuinlLCI2oX9mpex6iTMGmD1J0G9FNGI3OHkwZcahw+4/3dv9K
jfUjm6XwndtYi6ifAPAf8M8RT84hFlZKqR7IpGmpqWnLZx6BcFV0RDu8GCIPD6Fr
UeLu1hGLD3SMbKy9zSR4lDSkMRvCUumXAebtEvfp7dfQ9Z8I866J5/9EZIDH88M1
Rb9agaBlwwr8Oy0hzC3rwvLyqXi1KD79f+YmGL0yatYPTm37qCE+QdfXCkesN6jg
SV3zjtpBalP0KMCtAhouFf6xDz615nWvC5NRh2yzYOhSVfmZEVrB9Zz7GZx8rsMi
2U0ALYJIc6EI0uc/sLZ9dYu6hBa72VmSe90zS5IE2ZYB24GnzXV95iMsvH35/4vS
igFWNQmHxWc9GfwgXN2/P2k4zokCxywLLhoa5xoy+SGmipz2pU8IS4chGT6H5edU
/CdBAAdHw7eNgXkuctH9eAM7WE/yu+1tmAPUvzr3ojFyXF+L8d8gcCbOJA4scHb6
/L8B0J+CZG/dkMDxU/xjyc7zz8not13HTr6xklMUIam/OzM95YFmleAN4g==
=l547
-----END PGP MESSAGE-----
--z6Eq5LdranGa6ru8--
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
QKfPPOk=
=EqIC
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBEyKRU4BEADHgQONUfSJzP6fTDN+YkQFHvYSewcyZeOfbnjjiFkroy30lCks
OfQaULGmQJwPF6sofvdLHcCm491PbK6IcmfpGhsQIroIfkDh305CqM/vZFcuzd0M
4Mf3aZTlsqns9ah4uO2/Pu3rjLXvPH4rhwKNcQZ2BceGICM0TJhR7J+e2ScqgJ3e
PpZpN7LNwQFzr7kn0a0oMC4vdJjZKmEWp5FL4mYfMDbwuB9vxPglYNP7Mxmi2/yB
LhNm1h1u4bAC3pcv/GwDkcLevYmRqX/DWJd9fOQF6bgO3UppEG+DHMmuB/oLR5aq
t/ZOH7PvsVWbYbiZ3aCnyRlK6EOONjKXScut0EI/QirUzWwfxZnFYMc+ZbR1mfNU
cEWro5N/jc1uuQCKdU6nYsCzSN7jJz3yaEisLlB13hBvpLkQ8JuqWV8iQKNlfSo4
TDlrITHjYtDKKJjvef1GR1iCuYnLbGClgraB+ukB9pJN29vyhbkCVRzyGUeD+dUD
NUbgQoYBjTkQwAfNLDC28Kitefp/Mai+flH+bPoXWpzc+pM/E4FMEFNzDtpT8xXQ
veSj1oGtrhic2OpQGUeMj1Obj9GQwYKlIulTKtMarO5oCL4FN8ezynt4976devo8
8AcgPPnfjqxddugTj6KWzU2UfTna0fPAc/KX5D92bttNo2g7Am/qSjqy4QARAQAB
tBNwYXogPHBhekBuYWRpci5vcmc+iQI3BBMBCAAhBQJMikVOAhsDBQsJCAcDBRUI
CQoLBRYCAwEAAh4BAheAAAoJELGjbwgGnlXebjMQAKiu5mPpvhnW7/va1btPIdkb
AJDW2Ia/Qu8DJTDAmVSVcvrC4WMx+yFdqloLGY26bLzG0+25CuHwzhUtEfa3wc/W
OLGAbeHcCEBru3lHkCnaQ0Ec/En60Sbt8Gu42X4gO/br/quQDwOZmJamEZ6N+xoJ
s7tDYCdbquaOoa5GVDIBdCifSeiAFQRbWzNc8ofkjLyu+QQ8i1V0b3Jura3WD2rn
H/tMeWoGB35kAgLcuhL3s1l3046HRbVsWyZq6GmYp+b3LRYLjd8sY92DYBA9hzLV
FtOMsUcJR+qVrJ3Sp0m3kajWBIRNFc3OEMvveRRR+joUhzfec8jsOYXWeKerRs6x
diitfpHAgnUbcjPs5DSyy6+cNGNGHuFqa9Xtawm9sL+KwN4OPFXaLWHTH682+Uh9
cKUYYBBCi8yydpxuey6QuwzBOekGKWeLlUTZLNmwUvBotCFjq6ma+iDi/cjnZaoB
QPS9YCLYp34ouutAM674o5J7dveHc17554BiIA2K6HBKr8hW0AQCOsJAtfyZbu+2
CMkW4SDTCDdFcBX6TOtzlt/yDMzLv18uCmM1b8VSpt+IaYmwHCHzxOfH6rI6POXb
t42vckQIU2/lKEt43Hbsk6FluvoZ+GoOtLPaKy+UYlmCIdXCD4N3fMXrEZ6Uft4f
b8PCtvKu+hfCkwO1epAYiEYEEBEIAAYFAkyKRsgACgkQCYrIOkwAKOlgJwCgm7L+
ouDS+/EBsKm99/OjyIxn7nsAn2gN7CsFYhzKNfiDOMt1oYmrQrmauQINBEyKRU4B
EAC4Hxbh3QDfj2QUEB/NX0qKdA1KiYPb7bQnZgpekhxrZKhnBZ+vMn2cH7OQSiQK
fohv2yXCF3ba+b4VS5dK0nP23Z4qcPqOkagqtEjK1hjbNDaIX2flC8MUD9Y30wqX
6hPIKZYuRqkmqptlvVZCowqqWUYc/VS5cqiDJZESMGj+oaFdJ7E4OObuJob6x6+i
2wnTjYnp6boujgmWlR/TA3URv9OMxxk3SFF7sZmtmxOQd9kaWFRdnya38iZnoziP
ka1phZYEDT8gy7TMx0ZRgPLbH+SBkr2lDlO78ZOZZJxVrnX/QAz8duzOko7LobvK
1ltJp0VyHYtprkocTb2qIMdUNX82y/P0DQiqwioU0+Pzk1kBFILn3S51GAfVMXAl
VBlCS8xyS0bvevzo6dInnKiQU5tnF7AQbSt0raOXn7Ef1gPIXi7sHUpluDJnYMtU
1tv+Dx6NbmDM04qJrZkezn/trieMtscJMRKVX6CEWzEMoLwjxshyZ/TZrNoOfcsv
EwPyDx9xmumcIWh2LchN+fjLi965Vwqip46gzB1Lzv4E870BsYcvmB9iMPEPkGKb
ZP9OxHY/laCJOTlgicVCDGw8b6TIJKlV4mPo41Xs4pqvJe6FDWotoAttUk1hJVyn
MbsY9JtASWBiPEHUlhU0fdrzWH5b+/MBnABfLL0uxGvPJQARAQABiQIfBBgBCAAJ
BQJMikVOAhsMAAoJELGjbwgGnlXeBZwP/0UGK9Kunjhe+dWJhWbjt0g4h3uy0UUQ
tySCkatkdNNfNXwdMfTmZsm1cRWq8oho1wxFvNQ+n9fKU2PUB4ck0NVKg4I7skuf
mbb4SxFDqohq/69v+g20tjhl/7LYj/sd/e63RCkmz15ijRBGD5hRQztCAwRYaN7P
LxCYbGqsvbwjVg+wtBAPXWF5a0Zzyb5QEUNU0afs7YjegbQEyPH4B9hUyTZYhm/R
3KFNm8xK5Sp8rkvwQadYrJ80wOPGI773l2mBXflETr0zSWChMWOpVT+lKynxfn+s
+hBxYBFagPRwrpcLYD4fT00uewmDKNUZ0eUk/E5v3Kmy1e4xHdPTdQgg2vqI1XQ5
79zQruhmKprHpcVgDfKw8uVzeuhHGlLZIReYVhu9wZR4kITbtNGL91T+KtqVkTK+
sTxOa6tIDhMffkW6t/1MAjEy5BdsRe65qxa9eyhweZGkuoL5I33VNVwY5n7vkH+o
u/WUm70Ix9hUejA6Pn8Gn5UFOTcBYX34/yzdXk3+XMZAg+qGwPfZ+D0V/cPOoxkC
/QYGLdjp8dZP921NTPLRZiWviN0cYXY0IBV0+TBSCGo2kvOQgFk0qbQjQ57ePwTt
u8qeiNGrv6iwIjNsLJpf4tpwWrYGL/tczYAUxD8wnC7vDVY0kjfEX3Al+Mpou2Qi
nyRAUuvGT7Lx
=wHia
-----END PGP PUBLIC KEY BLOCK-----
......@@ -160,9 +160,8 @@ describe 'cli' do
dirmngr_pid = `pgrep -a dirmngr | grep #{list.listdir}`.split(' ',2).first
expect(dirmngr_pid).to be_nil
end
mail = Mail::TestMailer.deliveries.first
mail = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] }
expect(Mail::TestMailer.deliveries.length).to eq 1
b = mail.first_plaintext_part.body.to_s
expect(b).to match(/Refreshing all keys from the keyring of list #{list.email} resulted in this:\n\n/)
expect(b).to match(/\nThis key was updated \(new signatures\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]\n/)
......@@ -182,9 +181,8 @@ describe 'cli' do
with_sks_mock do
Cli.new.refresh_keys list1.email
end
mail = Mail::TestMailer.deliveries.first
mail = Mail::TestMailer.deliveries.find { |message| message.to == [list1.admins.first.email] }
expect(Mail::TestMailer.deliveries.length).to eq 1
b = mail.first_plaintext_part.body.to_s
expect(b).to match(/Refreshing all keys from the keyring of list #{list1.email} resulted in this:\n\n/)
expect(b).to match(/\nThis key was updated \(new signatures\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]\n/)
......@@ -200,9 +198,8 @@ describe 'cli' do
list.import_key(File.read("spec/fixtures/expired_key.txt"))
Cli.new.refresh_keys
mail = Mail::TestMailer.deliveries.first
mail = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] }
expect(Mail::TestMailer.deliveries.length).to eq 1
expect(mail.to_s).to include("Refreshing all keys from the keyring of list #{list.email} resulted in this")
if GPGME::Ctx.sufficient_gpg_version?('2.1')
expect(mail.to_s).to include("keyserver refresh failed: No keyserver available")
......
......@@ -1427,7 +1427,7 @@ describe "user sends keyword" do
rescue SystemExit
end
end
raw = Mail::TestMailer.deliveries.first
raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] }
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(list.keys.size).to eql(list_keys_num + 1)
......@@ -1463,7 +1463,7 @@ describe "user sends keyword" do
rescue SystemExit
end
end
raw = Mail::TestMailer.deliveries.first
raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] }
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(list.keys.size).to eql(list_keys_num)
......@@ -1499,7 +1499,7 @@ describe "user sends keyword" do
rescue SystemExit
end
end
raw = Mail::TestMailer.deliveries.first
raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] }
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(list.keys.size).to eql(list_keys_num + 1)
......@@ -1536,7 +1536,7 @@ describe "user sends keyword" do
rescue SystemExit
end
end
raw = Mail::TestMailer.deliveries.first
raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] }
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(list.keys.size).to eql(list_keys_num)
......@@ -1572,7 +1572,7 @@ describe "user sends keyword" do
rescue SystemExit
end
end
raw = Mail::TestMailer.deliveries.first
raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] }
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(list.keys.size).to eql(list_keys_num)
......@@ -1608,7 +1608,7 @@ describe "user sends keyword" do
rescue SystemExit
end
end
raw = Mail::TestMailer.deliveries.first
raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] }
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(list.keys.size).to eql(list_keys_num + 1)
......@@ -1644,7 +1644,7 @@ describe "user sends keyword" do
rescue SystemExit
end
end
raw = Mail::TestMailer.deliveries.first
raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] }
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(list.keys.size).to eql(list_keys_num)
......@@ -1680,7 +1680,7 @@ describe "user sends keyword" do
rescue SystemExit
end
end
raw = Mail::TestMailer.deliveries.first
raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] }
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(list.keys.size).to eql(list_keys_num)
......@@ -1838,7 +1838,7 @@ EOS
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
content_body = "Hello again! ¡Hola!\n"
content_body = "Hello again! ¡Hola!\r\n"
mail.charset = 'iso-8859-1'
mail.body = "x-list-name: #{list.email}\nX-resend: someone@example.org\n#{content_body}".encode('iso-8859-1')
mail.deliver
......@@ -1880,7 +1880,7 @@ EOS
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
content_body = "This is a test\nAnd here are some umlauts:ÄäÖöÜüß"
content_body = "This is a test\r\nAnd here are some umlauts:ÄäÖöÜüß"
mail.charset = 'utf-8'
mail.body = "x-list-name: #{list.email}\nX-resend: someone@example.org\n#{content_body}".encode('utf-8')
mail.deliver
......
......@@ -85,4 +85,65 @@ describe "protected subject" do
teardown_list_and_mailer(list)
end
it "works with mutt protected headers" do
list = create(:list)
list.subscribe("schleuder@example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
mail = Mail.read("spec/fixtures/mutt_protected_headers.txt")
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
Mail::TestMailer.deliveries.clear
begin
Schleuder::Runner.new().run(encrypted_mail.to_s, list.email)
rescue SystemExit
end
raw = Mail::TestMailer.deliveries.first
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(message.parts[1].body.to_s).to eql("Subject: x\n")
expect(message.parts[2].body.to_s).to eql("test\n")
teardown_list_and_mailer(list)
end
it "recognizes keywords in mails with protected headers and empty subject" do
list = create(:list)
list.subscribe("schleuder@example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
ENV['GNUPGHOME'] = list.listdir
mail = Mail.new
mail.to = list.request_address
mail.from = list.admins.first.email
gpg_opts = {
encrypt: true,
keys: {list.request_address => list.fingerprint},
sign: true,
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
mail.body = "x-list-name: #{list.email}\nx-list-keys"
protected_headers = Mail::Part.new do
body "Subject: protected"
content_type "text/rfc822-headers; protected-headers=v1"
end
mail.add_part protected_headers
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
Mail::TestMailer.deliveries.clear
begin
Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address)
rescue SystemExit
end
raw = Mail::TestMailer.deliveries.first
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(message.first_plaintext_part.body.to_s).to include("59C71FB38AEE22E091C78259D06350440F759BD3")
expect(message.first_plaintext_part.body.to_s).to_not include("Your message didn't contain any keywords, thus there was nothing to do.")
teardown_list_and_mailer(list)
end
end
......@@ -7,18 +7,23 @@ describe "user sends a plain text message" do
'signed-mime',
].each do |t|
it "from thunderbird being #{t}" do
start_smtp_daemon
list = create(:list)
list = create(:list, send_encrypted_only: false)
list.subscribe("admin@example.org", nil, true)
message_path = "spec/fixtures/mails/#{t}/thunderbird.eml"
list.import_key(File.read('spec/fixtures/openpgpkey_52507B0163A8D9F0094FFE03B1A36F08069E55DE.asc'))
mail = Mail.read("spec/fixtures/mails/#{t}/thunderbird.eml")
error = nil
error = run_schleuder(:work, list.email, message_path)
mails = Dir.glob("#{smtp_daemon_outputdir}/mail-*")
begin
Schleuder::Runner.new().run(mail.to_s, list.email)
rescue SystemExit => exc
error = exc
end
mails = Mail::TestMailer.deliveries
expect(error).to be_empty
expect(error).to be_nil
expect(mails.size).to eq 1
stop_smtp_daemon
content = mails.first.parts[0].parts[1].to_s
expect(content).not_to include("-----BEGIN PGP SIGNATURE-----")
end
end
end
......
......@@ -79,6 +79,7 @@ describe Schleuder::Filters::Runner do
'receive_signed_only',
'receive_encrypted_only',
'receive_from_subscribed_emailaddresses_only',
'strip_html_from_alternative_if_keywords_present'
]
end
it 'loads custom filters from filters_dir and sorts them in, ignores filter not following convention' do
......@@ -101,6 +102,7 @@ describe Schleuder::Filters::Runner do
'receive_encrypted_only',
'post_example',
'receive_from_subscribed_emailaddresses_only',
'strip_html_from_alternative_if_keywords_present'
]
end
it 'loads custom filters from filters_dir and sorts them in with missing dir' do
......@@ -122,6 +124,7 @@ describe Schleuder::Filters::Runner do
'receive_signed_only',
'receive_encrypted_only',
'receive_from_subscribed_emailaddresses_only',
'strip_html_from_alternative_if_keywords_present'
]
end
it 'loads custom filters from filters_dir even with non-2-digit priority' do
......@@ -145,6 +148,7 @@ describe Schleuder::Filters::Runner do
'receive_signed_only',
'receive_encrypted_only',
'receive_from_subscribed_emailaddresses_only',
'strip_html_from_alternative_if_keywords_present',
]
end
end
......
require "spec_helper"
# make sure we have the filters loaded, as they will be loaded lazily within the code
Dir[File.join(File.dirname(__FILE__),'../../../lib/schleuder/filters/*/*.rb')].each do |file|
require file
end
describe Schleuder::Filters do
before do
# Make sure we have the filters loaded, as they will be loaded lazily within the code.
list = create(:list)
Schleuder::Filters::Runner.new(list, 'pre').filters
Schleuder::Filters::Runner.new(list, 'post').filters
end
context '.fix_exchange_messages' do
it "fixes pgp/mime-messages that were mangled by Exchange" do
......@@ -73,4 +75,55 @@ describe Schleuder::Filters do
end
end
context '.strip_html_from_alternative_if_keywords_present' do
it 'strips HTML-part from multipart/alternative-message that contains keywords' do
list = create(:list)
mail = Mail.new
mail.to = list.email
mail.from = 'outside@example.org'
mail.text_part = content = 'x-resend: someone@example.org\n\nblabla'
mail.html_part = '<p>x-resend: someone@example.org</p><p>blabla</p>'
mail.subject = 'test'
mail.to_s
Schleuder::Filters.strip_html_from_alternative_if_keywords_present(list, mail)
expect(mail[:content_type].content_type).to eql('multipart/mixed')
expect(mail.parts.size).to be(1)
expect(mail.parts.first[:content_type].content_type).to eql('text/plain')
expect(mail.dynamic_pseudoheaders).to include('Note: This message included keywords and an alternating HTML-part. The HTML-part was removed to prevent the disclosure of these keywords to third parties.')
end
it 'does NOT strip HTML-part from multipart/alternative-message that does NOT contain keywords' do
list = create(:list)
mail = Mail.new
mail.to = 'schleuder@example.org'
mail.from = 'outside@example.org'
mail.text_part = content = 'Hello someone@example.org,\n\nblabla'
mail.html_part = '<p>Hello someone@example.org,</p><p>blabla</p>'
mail.subject = 'test'
Schleuder::Filters.strip_html_from_alternative_if_keywords_present(list, mail)
expect(mail[:content_type].content_type).to eql('multipart/alternative')
expect(mail.parts.size).to be(2)
expect(mail.parts.first[:content_type].content_type).to eql('text/plain')
expect(mail.parts.last[:content_type].content_type).to eql('text/html')
expect(mail.dynamic_pseudoheaders).to be_blank
end
it 'does not choke on nor change a message without Content-Type-header' do
mail = Mail.new
mail.to = 'schleuder@example.org'
mail.from = 'outside@example.org'
mail.body = 'blabla'
mail.subject = 'test'
Schleuder::Filters.strip_html_from_alternative_if_keywords_present(nil, mail)
expect(mail[:content_type]).to be_nil
expect(mail.parts.size).to be(0)
expect(mail.dynamic_pseudoheaders).to be_blank
end
end
end
......@@ -227,5 +227,31 @@ describe GPGME::Ctx do
expect(mail.to_s).to match(/gpgkeys: .* error .* connect/)
end
end
it 'does not import non-self-signatures if gpg >= 2.1.15; or else sends a warning' do
list = create(:list)
list.delete_key('87E65ED2081AE3D16BE4F0A5EBDBE899251F2412')
list.subscribe('admin@example.org', nil, true)
list.import_key(File.read('spec/fixtures/bla_foo_key.txt'))
res = ''
with_sks_mock do
res = list.gpg.refresh_keys(list.keys)
end
# GPGME apparently does not show signatures correctly in some cases, so we better use gpgcli.
signature_output = list.gpg.class.gpgcli(['--list-sigs', '87E65ED2081AE3D16BE4F0A5EBDBE899251F2412'])[1].grep(/0F759BD3.*schleuder@example.org/)
if GPGME::Ctx.sufficient_gpg_version?('2.1.15')
expect(res).to be_empty
expect(signature_output).to be_empty
else
message = Mail::TestMailer.deliveries.first
expect(message.to).to eql([Conf.superadmin])
expect(message.subject).to eql('Schleuder installation problem')
expect(res).not_to be_empty
expect(signature_output).not_to be_empty
end
end
end
end
......@@ -526,6 +526,32 @@ describe Schleuder::List do
teardown_list_and_mailer(list)
end
it 'does not import non-self-signatures if gpg >= 2.1.15; or else sends a warning' do
list = create(:list)
list.delete_key('87E65ED2081AE3D16BE4F0A5EBDBE899251F2412')
list.subscribe('admin@example.org', nil, true)
output = ''
with_sks_mock do
output = list.fetch_keys('87E65ED2081AE3D16BE4F0A5EBDBE899251F2412')
end
# GPGME apparently does not show signatures correctly in some cases, so we better use gpgcli.
signature_output = list.gpg.class.gpgcli(['--list-sigs', '87E65ED2081AE3D16BE4F0A5EBDBE899251F2412'])[1].grep(/0F759BD3.*schleuder@example.org/)
expect(output).to include("This key was fetched (new key):\n0x87E65ED2081AE3D16BE4F0A5EBDBE899251F2412 bla@foo")
if GPGME::Ctx.gpg_knows_import_filter?
expect(signature_output).to be_empty
else
message = Mail::TestMailer.deliveries.first
expect(message.to).to eql([Conf.superadmin])
expect(message.subject).to eql('Schleuder installation problem')
expect(signature_output).not_to be_empty
end
teardown_list_and_mailer(list)
end
end
describe "send_list_key_to_subscriptions" do
......
......@@ -27,42 +27,48 @@ describe Schleuder::Subscription do
end
it "is invalid when email is nil" do
subscription = build(:subscription, email: nil)
list = create(:list)
subscription = build(:subscription, list_id: list.id, email: nil)
expect(subscription).not_to be_valid
expect(subscription.errors.messages[:email]).to include("can't be blank")
end
it "is invalid when email is blank" do
subscription = build(:subscription, email: "")
list = create(:list)
subscription = build(:subscription, list_id: list.id, email: "")
expect(subscription).not_to be_valid
expect(subscription.errors.messages[:email]).to include("can't be blank")
end
it "is invalid when email does not contain an @" do
subscription = build(:subscription, email: "fooatbar.org")
list = create(:list)
subscription = build(:subscription, list_id: list.id, email: "fooatbar.org")
expect(subscription).not_to be_valid
expect(subscription.errors.messages[:email]).to include("is not a valid email address")
end
it "is valid when fingerprint is empty" do
subscription = build(:subscription, fingerprint: "")
list = create(:list)
subscription = build(:subscription, list_id: list.id, fingerprint: "")
expect(subscription).to be_valid
expect(subscription.errors.messages[:fingerprint]).to be_blank
end
it "is valid when fingerprint is nil" do
subscription = build(:subscription, fingerprint: nil)
list = create(:list)
subscription = build(:subscription, list_id: list.id, fingerprint: nil)
expect(subscription).to be_valid
expect(subscription.errors.messages[:fingerprint]).to be_blank
end
it "is invalid when fingerprint contains invalid characters" do
subscription = build(:subscription, fingerprint: "&$$$$123AAA")
list = create(:list)
subscription = build(:subscription, list_id: list.id, fingerprint: "&$$$$123AAA")
expect(subscription).not_to be_valid
expect(subscription.errors.messages[:fingerprint]).to include("is not a valid OpenPGP-fingerprint")
......@@ -70,7 +76,8 @@ describe Schleuder::Subscription do
BOOLEAN_SUBSCRIPTION_ATTRIBUTES.each do |subscription_attribute|
it "is invalid if #{subscription_attribute} is nil" do
subscription = build(:subscription)
list = create(:list)
subscription = build(:subscription, list_id: list.id)
subscription[subscription_attribute] = nil
expect(subscription).not_to be_valid
......@@ -78,7 +85,8 @@ describe Schleuder::Subscription do
end
it "is invalid if #{subscription_attribute} is blank" do
subscription = build(:subscription)
list = create(:list)
subscription = build(:subscription, list_id: list.id)
subscription[subscription_attribute] = ""
expect(subscription).not_to be_valid
......@@ -87,8 +95,10 @@ describe Schleuder::Subscription do
end
it "is invalid if the given email is already subscribed for the list" do
subscription1 = create(:subscription)
subscription2 = build(:subscription, email: subscription1.email)
list1 = create(:list)
list2 = create(:list)
subscription1 = create(:subscription, list_id: list1.id)
subscription2 = create(:subscription, list_id: list2.id, email: subscription1.email)
subscription3 = build(:subscription, email: subscription1.email, list_id: subscription1.list_id)
expect(subscription1).to be_valid
......
......@@ -23,6 +23,8 @@ class SksMock < Sinatra::Base
File.read('spec/fixtures/olduid_key_with_newuid.txt')
when '0x59C71FB38AEE22E091C78259D06350440F759BD3'
File.read('spec/fixtures/default_list_key.txt')
when '0x87E65ED2081AE3D16BE4F0A5EBDBE899251F2412'
File.read('spec/fixtures/openpgp-keys/public-key-with-third-party-signature.txt')
else
404
end
......
......@@ -3,3 +3,6 @@
# That's actually \nto, which codespell is not able to parse correctly
nto
keyserver
keyservers
fpr
#!/usr/bin/env ruby
# This script gets the target branch of a specific merge request. This
# is useful for example to check for a CHANGELOG edit.
#
# To enable the "export" of the env var to the parent (calling) shell,
# this script needs to be called in the following way:
# source <(./get-target-branch.rb)
require 'net/http'
require 'uri'
require 'json'
uri = URI.parse('https://0xacab.org/api/v4/projects/schleuder%2Fschleuder/merge_requests?state=opened')
response = Net::HTTP.get(uri)
json_data = JSON.parse(response)
if ! json_data.is_a?(Array)
puts json_data
exit 1
end
json_data.each do |merge_request|
if merge_request['sha'] == ENV['CI_COMMIT_SHA']
puts "export target_branch=#{merge_request['target_branch']}"
break
end
end