rspamd-ruby 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +27 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +98 -0
- data/MIT-LICENSE +20 -0
- data/README.md +74 -0
- data/Rakefile +9 -0
- data/bin/release +14 -0
- data/lib/rspamd/check/result.rb +59 -0
- data/lib/rspamd/client.rb +69 -0
- data/lib/rspamd/configuration.rb +39 -0
- data/lib/rspamd/errors.rb +4 -0
- data/lib/rspamd/service.rb +34 -0
- data/lib/rspamd/version.rb +3 -0
- data/lib/rspamd-ruby.rb +2 -0
- data/rspamd-ruby.gemspec +20 -0
- data/test/client_test.rb +161 -0
- data/test/fixtures/mail/ham.eml +19 -0
- data/test/fixtures/responses/already_reported.json +1 -0
- data/test/fixtures/responses/ham.json +12 -0
- data/test/fixtures/responses/spam.json +7 -0
- data/test/test_helper.rb +4 -0
- metadata +128 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1fbf843187c61d00b8bdfc92261d43df0cacd691ea0e028427acb5b9493b81df
|
4
|
+
data.tar.gz: aa65860dd58db51d418d3eae722f27b8518dba360f42ff4a8fe371b58ed0f08b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 514f0dadd0c2bcc3ff8c68b1f65da421a8f5b5dd988188e28db72d1ca2899d3a22718a4479d34106e5124b8bf74c5074f1c8279f631e0767b39878ea958dc044
|
7
|
+
data.tar.gz: bd141fd2320ea5ea328e24fb09b434cacdff04e65256de6549e9304cf9d85d5ea44c2adea95c3236cc667479546b9aec515d8bf6bd61442890d71d1824ee4e97
|
@@ -0,0 +1,27 @@
|
|
1
|
+
name: CI
|
2
|
+
on: [push, pull_request]
|
3
|
+
jobs:
|
4
|
+
tests:
|
5
|
+
runs-on: ubuntu-latest
|
6
|
+
|
7
|
+
steps:
|
8
|
+
- uses: actions/checkout@v1
|
9
|
+
|
10
|
+
- name: Set up Ruby
|
11
|
+
uses: ruby/setup-ruby@v1
|
12
|
+
with:
|
13
|
+
ruby-version: 2.7.8
|
14
|
+
rubygems: latest
|
15
|
+
|
16
|
+
- name: Cache gem dependencies
|
17
|
+
uses: actions/cache@v1
|
18
|
+
with:
|
19
|
+
path: vendor/bundle
|
20
|
+
key: ${{ runner.os }}-bundler-${{ hashFiles('**/Gemfile.lock') }}
|
21
|
+
restore-keys: ${{ runner.os }}-bundler-
|
22
|
+
|
23
|
+
- name: Install dependencies
|
24
|
+
run: gem install bundler && bundle install --jobs 4 --retry 3 --path vendor/bundle
|
25
|
+
|
26
|
+
- name: Run tests
|
27
|
+
run: bundle exec rake test
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
inherit_gem: { rubocop-37signals: rubocop.yml }
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.7.8
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
GIT
|
2
|
+
remote: https://github.com/basecamp/house-style.git
|
3
|
+
revision: b3ad65254828e8e8019a0d9a6205aff9ad206a77
|
4
|
+
branch: main
|
5
|
+
specs:
|
6
|
+
rubocop-37signals (1.0.0)
|
7
|
+
rubocop
|
8
|
+
rubocop-minitest
|
9
|
+
rubocop-performance
|
10
|
+
rubocop-rails
|
11
|
+
|
12
|
+
PATH
|
13
|
+
remote: .
|
14
|
+
specs:
|
15
|
+
rspamd-ruby (1.0.0)
|
16
|
+
|
17
|
+
GEM
|
18
|
+
remote: https://rubygems.org/
|
19
|
+
specs:
|
20
|
+
activesupport (6.1.7.4)
|
21
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
22
|
+
i18n (>= 1.6, < 2)
|
23
|
+
minitest (>= 5.1)
|
24
|
+
tzinfo (~> 2.0)
|
25
|
+
zeitwerk (~> 2.3)
|
26
|
+
addressable (2.8.1)
|
27
|
+
public_suffix (>= 2.0.2, < 6.0)
|
28
|
+
ast (2.4.2)
|
29
|
+
concurrent-ruby (1.2.0)
|
30
|
+
crack (0.4.3)
|
31
|
+
safe_yaml (~> 1.0.0)
|
32
|
+
debug (1.7.1)
|
33
|
+
irb (>= 1.5.0)
|
34
|
+
reline (>= 0.3.1)
|
35
|
+
hashdiff (1.0.0)
|
36
|
+
i18n (1.12.0)
|
37
|
+
concurrent-ruby (~> 1.0)
|
38
|
+
io-console (0.6.0)
|
39
|
+
irb (1.6.2)
|
40
|
+
reline (>= 0.3.0)
|
41
|
+
json (2.6.3)
|
42
|
+
minitest (5.17.0)
|
43
|
+
parallel (1.22.1)
|
44
|
+
parser (3.2.1.0)
|
45
|
+
ast (~> 2.4.1)
|
46
|
+
public_suffix (5.0.0)
|
47
|
+
rack (3.0.4.1)
|
48
|
+
rainbow (3.1.1)
|
49
|
+
rake (13.0.1)
|
50
|
+
regexp_parser (2.7.0)
|
51
|
+
reline (0.3.2)
|
52
|
+
io-console (~> 0.5)
|
53
|
+
rexml (3.2.5)
|
54
|
+
rubocop (1.45.1)
|
55
|
+
json (~> 2.3)
|
56
|
+
parallel (~> 1.10)
|
57
|
+
parser (>= 3.2.0.0)
|
58
|
+
rainbow (>= 2.2.2, < 4.0)
|
59
|
+
regexp_parser (>= 1.8, < 3.0)
|
60
|
+
rexml (>= 3.2.5, < 4.0)
|
61
|
+
rubocop-ast (>= 1.24.1, < 2.0)
|
62
|
+
ruby-progressbar (~> 1.7)
|
63
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
64
|
+
rubocop-ast (1.26.0)
|
65
|
+
parser (>= 3.2.1.0)
|
66
|
+
rubocop-minitest (0.27.0)
|
67
|
+
rubocop (>= 0.90, < 2.0)
|
68
|
+
rubocop-performance (1.16.0)
|
69
|
+
rubocop (>= 1.7.0, < 2.0)
|
70
|
+
rubocop-ast (>= 0.4.0)
|
71
|
+
rubocop-rails (2.19.1)
|
72
|
+
activesupport (>= 4.2.0)
|
73
|
+
rack (>= 1.1)
|
74
|
+
rubocop (>= 1.33.0, < 2.0)
|
75
|
+
ruby-progressbar (1.11.0)
|
76
|
+
safe_yaml (1.0.5)
|
77
|
+
tzinfo (2.0.6)
|
78
|
+
concurrent-ruby (~> 1.0)
|
79
|
+
unicode-display_width (2.4.2)
|
80
|
+
webmock (3.8.0)
|
81
|
+
addressable (>= 2.3.6)
|
82
|
+
crack (>= 0.3.2)
|
83
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
84
|
+
zeitwerk (2.6.8)
|
85
|
+
|
86
|
+
PLATFORMS
|
87
|
+
ruby
|
88
|
+
|
89
|
+
DEPENDENCIES
|
90
|
+
debug
|
91
|
+
minitest (> 5.11)
|
92
|
+
rake (~> 13.0)
|
93
|
+
rspamd-ruby!
|
94
|
+
rubocop-37signals!
|
95
|
+
webmock (~> 3.0)
|
96
|
+
|
97
|
+
BUNDLED WITH
|
98
|
+
2.4.17
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2023 37signals
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Ruby client for Rspamd’s HTTP API
|
2
|
+
|
3
|
+
## Usage
|
4
|
+
|
5
|
+
Initialize a client with the host and port of an Rspamd controller process:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
client = Rspamd::Client.new(host: "localhost", port: 11334)
|
9
|
+
```
|
10
|
+
|
11
|
+
Check a message:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
result = client.check(<<~MIME)
|
15
|
+
Date: Tue, 21 Jan 2020 21:04:42 +0000
|
16
|
+
From: Alice <alice@example.com>
|
17
|
+
To: Bob <bob@example.com>
|
18
|
+
Message-ID: <975bad33-2e76-40c3-89aa-7fe1edcbe7ce@example.com>
|
19
|
+
Subject: Hello
|
20
|
+
Mime-Version: 1.0
|
21
|
+
Content-Type: text/plain; charset=UTF-8
|
22
|
+
Content-Transfer-Encoding: quoted-printable
|
23
|
+
Delivered-To: bob@example.com
|
24
|
+
|
25
|
+
Hi Bob!
|
26
|
+
|
27
|
+
-Alice
|
28
|
+
MIME
|
29
|
+
|
30
|
+
result.spam? # => false
|
31
|
+
result.ham? # => true
|
32
|
+
|
33
|
+
result.score # => 1.2
|
34
|
+
result.required_score # => 15
|
35
|
+
result.action # => "no action"
|
36
|
+
```
|
37
|
+
|
38
|
+
Report a message as spam:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
client.spam!(<<~MIME)
|
42
|
+
Date: Tue, 21 Jan 2020 21:04:42 +0000
|
43
|
+
From: Spammer <spammer@example.com>
|
44
|
+
To: Bob <bob@example.com>
|
45
|
+
Message-ID: <975bad33-2e76-40c3-89aa-7fe1edcbe7ce@example.com>
|
46
|
+
Subject: Hello
|
47
|
+
Mime-Version: 1.0
|
48
|
+
Content-Type: text/plain; charset=UTF-8
|
49
|
+
Content-Transfer-Encoding: quoted-printable
|
50
|
+
Delivered-To: bob@example.com
|
51
|
+
|
52
|
+
Buy some stuff?
|
53
|
+
MIME
|
54
|
+
```
|
55
|
+
|
56
|
+
Report a message as ham:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
client.ham!(<<~MIME)
|
60
|
+
Date: Tue, 21 Jan 2020 21:04:42 +0000
|
61
|
+
From: Alice <alice@example.com>
|
62
|
+
To: Bob <bob@example.com>
|
63
|
+
Message-ID: <975bad33-2e76-40c3-89aa-7fe1edcbe7ce@example.com>
|
64
|
+
Subject: Hello
|
65
|
+
Mime-Version: 1.0
|
66
|
+
Content-Type: text/plain; charset=UTF-8
|
67
|
+
Content-Transfer-Encoding: quoted-printable
|
68
|
+
Delivered-To: bob@example.com
|
69
|
+
|
70
|
+
Hi Bob!
|
71
|
+
|
72
|
+
-Alice
|
73
|
+
MIME
|
74
|
+
```
|
data/Rakefile
ADDED
data/bin/release
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
VERSION=$1
|
4
|
+
|
5
|
+
printf "module Rspamd\n VERSION = \"$VERSION\"\nend\n" > ./lib/rspamd/version.rb
|
6
|
+
bundle
|
7
|
+
git add Gemfile.lock lib/rspamd/version.rb
|
8
|
+
git commit -m "Bump version for $VERSION"
|
9
|
+
git push
|
10
|
+
git tag v$VERSION
|
11
|
+
git push --tags
|
12
|
+
gem build rspamd-ruby.gemspec
|
13
|
+
gem push "rspamd-ruby-$VERSION.gem" --host https://rubygems.org
|
14
|
+
rm "rspamd-ruby-$VERSION.gem"
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Rspamd
|
2
|
+
module Check
|
3
|
+
class Result
|
4
|
+
attr_reader :data
|
5
|
+
|
6
|
+
def self.parse(source)
|
7
|
+
new JSON.parse(source)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(data = {})
|
11
|
+
@data = data
|
12
|
+
end
|
13
|
+
|
14
|
+
def spam?
|
15
|
+
score >= required_score
|
16
|
+
end
|
17
|
+
|
18
|
+
def ham?
|
19
|
+
!spam?
|
20
|
+
end
|
21
|
+
|
22
|
+
def skipped?
|
23
|
+
data.fetch("is_skipped")
|
24
|
+
end
|
25
|
+
|
26
|
+
def score
|
27
|
+
data.fetch("score")
|
28
|
+
end
|
29
|
+
|
30
|
+
def required_score
|
31
|
+
data.fetch("required_score")
|
32
|
+
end
|
33
|
+
|
34
|
+
def action
|
35
|
+
data.fetch("action")
|
36
|
+
end
|
37
|
+
|
38
|
+
def symbols
|
39
|
+
data.fetch("symbols")
|
40
|
+
end
|
41
|
+
|
42
|
+
def subject
|
43
|
+
data["subject"]
|
44
|
+
end
|
45
|
+
|
46
|
+
def urls
|
47
|
+
data["urls"] || []
|
48
|
+
end
|
49
|
+
|
50
|
+
def emails
|
51
|
+
data["emails"] || []
|
52
|
+
end
|
53
|
+
|
54
|
+
def message_id
|
55
|
+
data["message_id"]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "rspamd/configuration"
|
2
|
+
require "rspamd/service"
|
3
|
+
require "rspamd/check/result"
|
4
|
+
require "rspamd/errors"
|
5
|
+
|
6
|
+
module Rspamd
|
7
|
+
class Client
|
8
|
+
attr_reader :configuration
|
9
|
+
|
10
|
+
def initialize(**options)
|
11
|
+
@configuration = Configuration.new(**options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def ping
|
15
|
+
service.get("/ping")
|
16
|
+
.then { |response| response.is_a?(Net::HTTPOK) && response.body.match?(/\Apong(\r?\n)?\z/) }
|
17
|
+
rescue Net::OpenTimeout, Net::ReadTimeout, IOError, SystemCallError
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def check(message, headers: {})
|
22
|
+
service.post("/checkv2", headers: headers, body: message).then do |response|
|
23
|
+
if response.is_a?(Net::HTTPOK)
|
24
|
+
Check::Result.parse(response.body)
|
25
|
+
else
|
26
|
+
raise InvalidResponse, "Received invalid response from Rspamd: expected 200 OK, got #{response.code} #{response.message}".strip
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def spam!(message)
|
32
|
+
learn :spam, message
|
33
|
+
end
|
34
|
+
|
35
|
+
def ham!(message)
|
36
|
+
learn :ham, message
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_fuzzy(message, flag: 1, weight: 1)
|
40
|
+
post("/fuzzyadd", message, "Flag" => flag, "Weight" => weight)
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete_fuzzy(message, flag: 1)
|
44
|
+
post("/fuzzydel", message, "Flag" => flag)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def service
|
49
|
+
@service ||= Service.new(configuration)
|
50
|
+
end
|
51
|
+
|
52
|
+
def learn(classification, message)
|
53
|
+
post("/learn#{classification}", message)
|
54
|
+
end
|
55
|
+
|
56
|
+
def post(endpoint, body, **headers)
|
57
|
+
service.post(endpoint, body: body, headers: headers).then do |response|
|
58
|
+
case response
|
59
|
+
when Net::HTTPOK
|
60
|
+
JSON.parse(response.body).fetch("success")
|
61
|
+
when Net::HTTPNoContent, Net::HTTPAlreadyReported
|
62
|
+
false
|
63
|
+
else
|
64
|
+
raise Rspamd::Error, JSON.parse(response.body)["error"] || "Received unspecified error from Rspamd"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Rspamd
|
2
|
+
class Configuration
|
3
|
+
attr_reader :options
|
4
|
+
|
5
|
+
def initialize(**options)
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def scheme
|
10
|
+
options[:scheme] || "http"
|
11
|
+
end
|
12
|
+
|
13
|
+
def host
|
14
|
+
options[:host] || "localhost"
|
15
|
+
end
|
16
|
+
|
17
|
+
def port
|
18
|
+
options[:port] || 11333
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def open_timeout
|
23
|
+
options[:open_timeout] || 1
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_timeout
|
27
|
+
options[:read_timeout] || 10
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def user_agent
|
32
|
+
options[:user_agent] || "rspamd-ruby"
|
33
|
+
end
|
34
|
+
|
35
|
+
def password
|
36
|
+
options[:password]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Rspamd
|
2
|
+
class Service
|
3
|
+
attr_reader :configuration
|
4
|
+
|
5
|
+
def initialize(configuration)
|
6
|
+
@configuration = configuration
|
7
|
+
end
|
8
|
+
|
9
|
+
def get(path)
|
10
|
+
client.get path, default_headers
|
11
|
+
end
|
12
|
+
|
13
|
+
def post(path, body: nil, headers: {})
|
14
|
+
client.post path, body, default_headers.merge(headers.compact.transform_values(&:to_s))
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def client
|
19
|
+
@client ||= Net::HTTP.start \
|
20
|
+
configuration.host,
|
21
|
+
configuration.port,
|
22
|
+
use_ssl: configuration.scheme == "https",
|
23
|
+
open_timeout: configuration.open_timeout,
|
24
|
+
read_timeout: configuration.read_timeout
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_headers
|
28
|
+
{
|
29
|
+
"User-Agent" => configuration.user_agent,
|
30
|
+
"Password" => configuration.password
|
31
|
+
}.compact
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/rspamd-ruby.rb
ADDED
data/rspamd-ruby.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative "lib/rspamd/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "rspamd-ruby"
|
5
|
+
s.version = Rspamd::VERSION
|
6
|
+
s.authors = [ "George Claghorn", "Lewis Buckley" ]
|
7
|
+
s.email = "lewis@37signals.com"
|
8
|
+
s.summary = "Client for Rspamd's HTTP API"
|
9
|
+
s.homepage = "https://github.com/basecamp/rspamd-ruby"
|
10
|
+
|
11
|
+
s.required_ruby_version = ">= 2.7.8"
|
12
|
+
|
13
|
+
s.add_development_dependency "rake", "~> 13.0"
|
14
|
+
s.add_development_dependency "minitest", "> 5.11"
|
15
|
+
s.add_development_dependency "webmock", "~> 3.0"
|
16
|
+
s.add_development_dependency "debug"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
20
|
+
end
|
data/test/client_test.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class Rspamd::ClientTest < Minitest::Test
|
4
|
+
def setup
|
5
|
+
@client = Rspamd::Client.new(host: "localhost", port: 11333)
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_successfully_pinging
|
9
|
+
stub_request(:get, "http://localhost:11333/ping").to_return(status: 200, body: "pong\r\n")
|
10
|
+
assert @client.ping
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_unsuccessfully_pinging_due_to_a_server_error
|
14
|
+
stub_request(:get, "http://localhost:11333/ping").to_return(status: 500)
|
15
|
+
assert !@client.ping
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_unsuccessfully_pinging_due_to_a_timeout
|
19
|
+
stub_request(:get, "http://localhost:11333/ping").to_timeout
|
20
|
+
assert !@client.ping
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_successfully_checking_a_ham_message
|
24
|
+
stub_request(:post, "http://localhost:11333/checkv2")
|
25
|
+
.with(body: mail(:ham))
|
26
|
+
.to_return(status: 200, body: response("ham.json"))
|
27
|
+
|
28
|
+
result = @client.check(mail(:ham))
|
29
|
+
assert !result.spam?
|
30
|
+
assert result.ham?
|
31
|
+
assert_equal 1.0, result.score
|
32
|
+
assert_equal 15.0, result.required_score
|
33
|
+
assert_equal "no action", result.action
|
34
|
+
assert_equal %w[ foo.example.com bar.example.com baz.example.com ], result.urls
|
35
|
+
assert_equal %w[ alice@example.com ], result.emails
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_successfully_checking_a_spam_message
|
39
|
+
stub_request(:post, "http://localhost:11333/checkv2")
|
40
|
+
.with(body: mail(:ham))
|
41
|
+
.to_return(status: 200, body: response("spam.json"))
|
42
|
+
|
43
|
+
result = @client.check(mail(:ham))
|
44
|
+
assert result.spam?
|
45
|
+
assert !result.ham?
|
46
|
+
assert_equal 17.8, result.score
|
47
|
+
assert_equal 6.0, result.required_score
|
48
|
+
assert_equal "add header", result.action
|
49
|
+
assert_equal [], result.urls
|
50
|
+
assert_equal [], result.emails
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_unsuccessfully_checking_a_message_due_to_a_server_error
|
54
|
+
stub_request(:post, "http://localhost:11333/checkv2")
|
55
|
+
.with(body: mail(:ham))
|
56
|
+
.to_return(status: [ 500, "Internal Server Error" ])
|
57
|
+
|
58
|
+
error = assert_raises(Rspamd::InvalidResponse) { @client.check(mail(:ham)) }
|
59
|
+
assert_equal "Received invalid response from Rspamd: expected 200 OK, got 500 Internal Server Error", error.message
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_providing_headers_on_check
|
63
|
+
request = stub_request(:post, "http://localhost:11333/checkv2")
|
64
|
+
.with(body: mail(:ham), headers: { "Settings-Id" => "outbound" })
|
65
|
+
.to_return(status: 200, body: response("spam.json"))
|
66
|
+
|
67
|
+
@client.check(mail(:ham), headers: { "Settings-Id" => "outbound" })
|
68
|
+
assert_requested request
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_successfully_reporting_a_message_as_spam
|
72
|
+
request = stub_request(:post, "http://localhost:11333/learnspam")
|
73
|
+
.with(body: mail(:ham))
|
74
|
+
.to_return(status: 200, body: '{"success": true}')
|
75
|
+
|
76
|
+
assert @client.spam!(mail(:ham))
|
77
|
+
assert_requested request
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_unsuccessfully_reporting_a_message_as_spam
|
81
|
+
stub_request(:post, "http://localhost:11333/learnspam")
|
82
|
+
.with(body: mail(:ham))
|
83
|
+
.to_return(status: 500, body: '{"error": "Unknown statistics error, found when storing data on backend"}')
|
84
|
+
|
85
|
+
error = assert_raises(Rspamd::Error) { @client.spam!(mail(:ham)) }
|
86
|
+
assert_equal "Unknown statistics error, found when storing data on backend", error.message
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_successfully_reporting_a_message_as_ham
|
90
|
+
request = stub_request(:post, "http://localhost:11333/learnham")
|
91
|
+
.with(body: mail(:ham))
|
92
|
+
.to_return(status: 200, body: '{"success": true}')
|
93
|
+
|
94
|
+
assert @client.ham!(mail(:ham))
|
95
|
+
assert_requested request
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_successfully_adding_a_message_to_fuzzy_storage
|
99
|
+
request = stub_request(:post, "http://localhost:11333/fuzzyadd")
|
100
|
+
.with(body: mail(:ham))
|
101
|
+
.to_return(status: 200, body: '{"success": true}')
|
102
|
+
|
103
|
+
assert @client.add_fuzzy(mail(:ham))
|
104
|
+
assert_requested request
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_successfully_deleting_a_message_from_fuzzy_storage
|
108
|
+
request = stub_request(:post, "http://localhost:11333/fuzzydel")
|
109
|
+
.with(body: mail(:ham))
|
110
|
+
.to_return(status: 200, body: '{"success": true}')
|
111
|
+
|
112
|
+
assert @client.delete_fuzzy(mail(:ham))
|
113
|
+
assert_requested request
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_unsuccessfully_reporting_a_message_as_ham
|
117
|
+
stub_request(:post, "http://localhost:11333/learnham")
|
118
|
+
.with(body: mail(:ham))
|
119
|
+
.to_return(status: 500, body: '{"error": "Unknown statistics error, found when storing data on backend"}')
|
120
|
+
|
121
|
+
error = assert_raises(Rspamd::Error) { @client.ham!(mail(:ham)) }
|
122
|
+
assert_equal "Unknown statistics error, found when storing data on backend", error.message
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_reporting_a_previously_reported_message_as_spam
|
126
|
+
request = stub_request(:post, "http://localhost:11333/learnspam")
|
127
|
+
.with(body: mail(:ham))
|
128
|
+
.to_return(status: 208, body: response("already_reported.json"))
|
129
|
+
|
130
|
+
assert !@client.spam!(mail(:ham))
|
131
|
+
assert_requested request
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_reporting_a_message_with_too_few_tokens_as_spam
|
135
|
+
request = stub_request(:post, "http://localhost:11333/learnspam")
|
136
|
+
.with(body: mail(:ham))
|
137
|
+
.to_return(status: [ 204, "<undef> contains less tokens than required for bayes classifier: 3 < 11" ])
|
138
|
+
|
139
|
+
assert !@client.spam!(mail(:ham))
|
140
|
+
assert_requested request
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_customizing_user_agent
|
144
|
+
stub_request(:get, "http://localhost:11333/ping").to_return(status: 200, body: "pong\r\n")
|
145
|
+
assert Rspamd::Client.new(host: "localhost", port: "11333", user_agent: "Rspamd tests").ping
|
146
|
+
assert_requested :get, "http://localhost:11333/ping", headers: { "User-Agent" => "Rspamd tests" }
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
def mail(name)
|
151
|
+
fixture "mail/#{name}.eml"
|
152
|
+
end
|
153
|
+
|
154
|
+
def response(path)
|
155
|
+
fixture "responses/#{path}"
|
156
|
+
end
|
157
|
+
|
158
|
+
def fixture(path)
|
159
|
+
File.read File.expand_path("fixtures/#{path}", __dir__)
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Date: Tue, 21 Jan 2020 21:04:42 +0000
|
2
|
+
From: Alice <alice@example.com>
|
3
|
+
To: Bob <bob@example.com>
|
4
|
+
Message-ID: <975bad33-2e76-40c3-89aa-7fe1edcbe7ce@example.com>
|
5
|
+
Subject: Hello
|
6
|
+
Mime-Version: 1.0
|
7
|
+
Content-Type: text/plain; charset=UTF-8
|
8
|
+
Content-Transfer-Encoding: quoted-printable
|
9
|
+
Delivered-To: bob@example.com
|
10
|
+
|
11
|
+
Hi Bob! Check these out:
|
12
|
+
|
13
|
+
foo.example.com/bar
|
14
|
+
bar.example.com/baz
|
15
|
+
baz.example.com/quux
|
16
|
+
|
17
|
+
Best,
|
18
|
+
Alice
|
19
|
+
alice@example.com
|
@@ -0,0 +1 @@
|
|
1
|
+
{"error": "<975bad33-2e76-40c3-89aa-7fe1edcbe7ce@example.com> already learned as spam, ignore it"}
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rspamd-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- George Claghorn
|
8
|
+
- Lewis Buckley
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2023-07-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '13.0'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '13.0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: minitest
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '5.11'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '5.11'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: webmock
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '3.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3.0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: debug
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
description:
|
71
|
+
email: lewis@37signals.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".github/workflows/ci.yml"
|
77
|
+
- ".gitignore"
|
78
|
+
- ".rubocop.yml"
|
79
|
+
- ".ruby-version"
|
80
|
+
- Gemfile
|
81
|
+
- Gemfile.lock
|
82
|
+
- MIT-LICENSE
|
83
|
+
- README.md
|
84
|
+
- Rakefile
|
85
|
+
- bin/release
|
86
|
+
- lib/rspamd-ruby.rb
|
87
|
+
- lib/rspamd/check/result.rb
|
88
|
+
- lib/rspamd/client.rb
|
89
|
+
- lib/rspamd/configuration.rb
|
90
|
+
- lib/rspamd/errors.rb
|
91
|
+
- lib/rspamd/service.rb
|
92
|
+
- lib/rspamd/version.rb
|
93
|
+
- rspamd-ruby.gemspec
|
94
|
+
- test/client_test.rb
|
95
|
+
- test/fixtures/mail/ham.eml
|
96
|
+
- test/fixtures/responses/already_reported.json
|
97
|
+
- test/fixtures/responses/ham.json
|
98
|
+
- test/fixtures/responses/spam.json
|
99
|
+
- test/test_helper.rb
|
100
|
+
homepage: https://github.com/basecamp/rspamd-ruby
|
101
|
+
licenses: []
|
102
|
+
metadata: {}
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 2.7.8
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubygems_version: 3.4.10
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: Client for Rspamd's HTTP API
|
122
|
+
test_files:
|
123
|
+
- test/client_test.rb
|
124
|
+
- test/fixtures/mail/ham.eml
|
125
|
+
- test/fixtures/responses/already_reported.json
|
126
|
+
- test/fixtures/responses/ham.json
|
127
|
+
- test/fixtures/responses/spam.json
|
128
|
+
- test/test_helper.rb
|