slack-messenger 2.3.4 → 2.3.5
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 +4 -4
- data/lib/slack-messenger/util/link_formatter.rb +39 -33
- data/lib/slack-messenger/util/untrusted_regexp.rb +73 -0
- data/lib/slack-messenger/version.rb +1 -1
- data/spec/lib/slack-messenger/untrusted_regexp_spec.rb +70 -0
- data/spec/lib/slack-messenger/util/link_formatter_spec.rb +10 -0
- metadata +32 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f4f125fd094ef63218f4ad86215076f640a95a0527e54ba99c44e5b603120d0
|
|
4
|
+
data.tar.gz: 2f8940ac0823981a3e82801917cfec3837c5d4fa86d869df8e95a1d83b8076c9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dcc73321d59bce0389b96aff9d6e0ce4bf6525f16388ab7afb0e7a81a1501a0feaee0b68786a017d1f0a5d4f2a7cec5e57aa123527c8b6e68745c586e8290575
|
|
7
|
+
data.tar.gz: e6bfd8a355b221a8fa673284e2c0a0475b55d3eaa7f99ea584b6556f3106c6c02ec1f136d889b5961433e54e9f4dc225caf798803586b107c78e46631f06326f
|
|
@@ -1,32 +1,38 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'untrusted_regexp'
|
|
4
|
+
|
|
3
5
|
module Slack
|
|
4
6
|
class Messenger
|
|
5
7
|
module Util
|
|
6
8
|
class LinkFormatter
|
|
7
|
-
#
|
|
8
|
-
HTML_PATTERN =
|
|
9
|
-
<a
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
(?:.*?)>
|
|
13
|
-
|
|
14
|
-
</a>
|
|
15
|
-
}x
|
|
9
|
+
# https://regex101.com/r/m7CTcW/1
|
|
10
|
+
HTML_PATTERN =
|
|
11
|
+
'<a' \
|
|
12
|
+
'(?:.*?)' \
|
|
13
|
+
'href=[\'"](?P<link>.+?)[\'"]' \
|
|
14
|
+
'(?:.*?)>' \
|
|
15
|
+
'(?P<text>.+?)' \
|
|
16
|
+
'</a>'
|
|
16
17
|
|
|
17
18
|
VALID_URI_CHARS = '\w\-\.\~\:\/\?\#\[\]\@\!\$\&\'\*\+\,\;\='
|
|
18
19
|
|
|
19
20
|
# Attempt at only matching pairs of parens per
|
|
20
21
|
# the markdown spec http://spec.commonmark.org/0.27/#links
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
# https://regex101.com/r/komyFe/1
|
|
23
|
+
MARKDOWN_PATTERN =
|
|
24
|
+
'\[' \
|
|
25
|
+
'(?P<text>[^\[\]]*?)' \
|
|
26
|
+
'\]' \
|
|
27
|
+
'\(' \
|
|
28
|
+
'(?P<link>' \
|
|
29
|
+
'(?:https?:\/\/|mailto:)' \
|
|
30
|
+
"(?:[#{VALID_URI_CHARS}]*?|[#{VALID_URI_CHARS}]*?\\([#{VALID_URI_CHARS}]*?\\)[#{VALID_URI_CHARS}]*?)" \
|
|
31
|
+
')' \
|
|
32
|
+
'\)'
|
|
33
|
+
|
|
34
|
+
HTML_REGEXP = UntrustedRegexp.new(HTML_PATTERN, multiline: true)
|
|
35
|
+
MARKDOWN_REGEXP = UntrustedRegexp.new(MARKDOWN_PATTERN, multiline: true)
|
|
30
36
|
|
|
31
37
|
class << self
|
|
32
38
|
def format string, **opts
|
|
@@ -54,27 +60,27 @@ module Slack
|
|
|
54
60
|
|
|
55
61
|
private
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
def sub_html_links string
|
|
64
|
+
return string unless formats.include?(:html)
|
|
59
65
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
end
|
|
66
|
+
HTML_REGEXP.replace_gsub(string) do |html_link|
|
|
67
|
+
slack_link(html_link[1], html_link[2])
|
|
63
68
|
end
|
|
69
|
+
end
|
|
64
70
|
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
def sub_markdown_links string
|
|
72
|
+
return string unless formats.include?(:markdown)
|
|
67
73
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
end
|
|
74
|
+
MARKDOWN_REGEXP.replace_gsub(string) do |markdown_link|
|
|
75
|
+
slack_link(markdown_link[2], markdown_link[1])
|
|
71
76
|
end
|
|
77
|
+
end
|
|
72
78
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
def slack_link link, text=nil
|
|
80
|
+
"<#{link}" \
|
|
81
|
+
"#{text && !text.empty? ? "|#{text}" : ''}" \
|
|
82
|
+
">"
|
|
83
|
+
end
|
|
78
84
|
end
|
|
79
85
|
end
|
|
80
86
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# An untrusted regular expression is any regexp containing patterns sourced
|
|
4
|
+
# from user input.
|
|
5
|
+
#
|
|
6
|
+
# Ruby's built-in regular expression library allows patterns which complete in
|
|
7
|
+
# exponential time, permitting denial-of-service attacks.
|
|
8
|
+
#
|
|
9
|
+
# Not all regular expression features are available in untrusted regexes, and
|
|
10
|
+
# there is a strict limit on total execution time. See the RE2 documentation
|
|
11
|
+
# at https://github.com/google/re2/wiki/Syntax for more details.
|
|
12
|
+
#
|
|
13
|
+
# This class doesn't change any instance variables, which allows it to be frozen
|
|
14
|
+
# and setup in constants.
|
|
15
|
+
#
|
|
16
|
+
# This class only provides support replacing matched token with a block (like `gsub`).
|
|
17
|
+
module Slack
|
|
18
|
+
class Messenger
|
|
19
|
+
module Util
|
|
20
|
+
class UntrustedRegexp
|
|
21
|
+
require 're2'
|
|
22
|
+
|
|
23
|
+
def initialize(pattern, multiline: false)
|
|
24
|
+
if multiline
|
|
25
|
+
pattern = "(?m)#{pattern}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
@regexp = RE2::Regexp.new(pattern, log_errors: false)
|
|
29
|
+
@scan_regexp = initialize_scan_regexp
|
|
30
|
+
|
|
31
|
+
raise RegexpError, regexp.error unless regexp.ok?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# There is no built-in replace with block support (like `gsub`). We can accomplish
|
|
35
|
+
# the same thing by parsing and rebuilding the string with the substitutions.
|
|
36
|
+
def replace_gsub(text)
|
|
37
|
+
new_text = +''
|
|
38
|
+
remainder = text
|
|
39
|
+
|
|
40
|
+
matched = match(remainder)
|
|
41
|
+
|
|
42
|
+
until matched.nil? || matched.to_a.compact.empty?
|
|
43
|
+
partitioned = remainder.partition(matched.to_s)
|
|
44
|
+
new_text << partitioned.first
|
|
45
|
+
remainder = partitioned.last
|
|
46
|
+
|
|
47
|
+
new_text << yield(matched)
|
|
48
|
+
|
|
49
|
+
matched = match(remainder)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
new_text << remainder
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def match(text)
|
|
56
|
+
scan_regexp.match(text)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
attr_reader :regexp, :scan_regexp
|
|
62
|
+
|
|
63
|
+
def initialize_scan_regexp
|
|
64
|
+
if regexp.number_of_capturing_groups == 0
|
|
65
|
+
RE2::Regexp.new('(' + regexp.source + ')')
|
|
66
|
+
else
|
|
67
|
+
regexp
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# require 'fast_spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Slack::Messenger::Util::UntrustedRegexp do
|
|
6
|
+
def create_regex(regex_str, multiline: false)
|
|
7
|
+
described_class.new(regex_str, multiline: multiline).freeze
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe '#initialize' do
|
|
11
|
+
subject { described_class.new(pattern) }
|
|
12
|
+
|
|
13
|
+
context 'invalid regexp' do
|
|
14
|
+
let(:pattern) { '[' }
|
|
15
|
+
|
|
16
|
+
it { expect { subject }.to raise_error(RegexpError) }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe '#replace_gsub' do
|
|
21
|
+
let(:regex_str) { '(?P<scheme>(ftp))' }
|
|
22
|
+
let(:regex) { create_regex(regex_str, multiline: true) }
|
|
23
|
+
|
|
24
|
+
def result(regex, text)
|
|
25
|
+
regex.replace_gsub(text) do |match|
|
|
26
|
+
if match[:scheme]
|
|
27
|
+
"http|#{match[:scheme]}|rss"
|
|
28
|
+
else
|
|
29
|
+
match.to_s
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'replaces all instances of the match in a string' do
|
|
35
|
+
text = 'Use only https instead of ftp'
|
|
36
|
+
|
|
37
|
+
expect(result(regex, text)).to eq('Use only https instead of http|ftp|rss')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'replaces nothing when no match' do
|
|
41
|
+
text = 'Use only https instead of gopher'
|
|
42
|
+
|
|
43
|
+
expect(result(regex, text)).to eq(text)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'handles empty text' do
|
|
47
|
+
text = ''
|
|
48
|
+
|
|
49
|
+
expect(result(regex, text)).to eq('')
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe '#match' do
|
|
54
|
+
context 'when there are matches' do
|
|
55
|
+
it 'returns a match object' do
|
|
56
|
+
result = create_regex('(?P<number>\d+)').match('hello 10')
|
|
57
|
+
|
|
58
|
+
expect(result[:number]).to eq('10')
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context 'when there are no matches' do
|
|
63
|
+
it 'returns nil' do
|
|
64
|
+
result = create_regex('(?P<number>\d+)').match('hello')
|
|
65
|
+
|
|
66
|
+
expect(result).to be_nil
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -151,5 +151,15 @@ RSpec.describe Slack::Messenger::Util::LinkFormatter do
|
|
|
151
151
|
expect(formatted).to eq "Hello World, enjoy [this](http://example.com)<a href='http://example2.com'>this2</a>."
|
|
152
152
|
end
|
|
153
153
|
end
|
|
154
|
+
|
|
155
|
+
context "handles malicious strings efficiently" do
|
|
156
|
+
let(:malicious_text) { "![" * 500000 }
|
|
157
|
+
|
|
158
|
+
subject(:format) { described_class.format(malicious_text) }
|
|
159
|
+
|
|
160
|
+
it "takes under a second" do
|
|
161
|
+
expect { Timeout.timeout(1) { subject } }.not_to raise_error
|
|
162
|
+
end
|
|
163
|
+
end
|
|
154
164
|
end
|
|
155
165
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: slack-messenger
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.3.
|
|
4
|
+
version: 2.3.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Steven Sloan
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
-
dependencies:
|
|
11
|
+
date: 2024-06-19 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: re2
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - '='
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 2.7.0
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - '='
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 2.7.0
|
|
13
27
|
description: " A slim ruby wrapper for posting to slack webhooks "
|
|
14
28
|
email:
|
|
15
29
|
- stevenosloan@gmail.com
|
|
@@ -29,6 +43,7 @@ files:
|
|
|
29
43
|
- lib/slack-messenger/util/escape.rb
|
|
30
44
|
- lib/slack-messenger/util/http_client.rb
|
|
31
45
|
- lib/slack-messenger/util/link_formatter.rb
|
|
46
|
+
- lib/slack-messenger/util/untrusted_regexp.rb
|
|
32
47
|
- lib/slack-messenger/version.rb
|
|
33
48
|
- spec/end_to_end_spec.rb
|
|
34
49
|
- spec/integration/ping_integration_test.rb
|
|
@@ -40,6 +55,7 @@ files:
|
|
|
40
55
|
- spec/lib/slack-messenger/payload_middleware/format_message_spec.rb
|
|
41
56
|
- spec/lib/slack-messenger/payload_middleware/stack_spec.rb
|
|
42
57
|
- spec/lib/slack-messenger/payload_middleware_spec.rb
|
|
58
|
+
- spec/lib/slack-messenger/untrusted_regexp_spec.rb
|
|
43
59
|
- spec/lib/slack-messenger/util/http_client_spec.rb
|
|
44
60
|
- spec/lib/slack-messenger/util/link_formatter_spec.rb
|
|
45
61
|
- spec/lib/slack-messenger_spec.rb
|
|
@@ -48,7 +64,7 @@ homepage: https://gitlab.com/gitlab-org/slack-notifier
|
|
|
48
64
|
licenses:
|
|
49
65
|
- MIT
|
|
50
66
|
metadata: {}
|
|
51
|
-
post_install_message:
|
|
67
|
+
post_install_message:
|
|
52
68
|
rdoc_options: []
|
|
53
69
|
require_paths:
|
|
54
70
|
- lib
|
|
@@ -63,22 +79,23 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
63
79
|
- !ruby/object:Gem::Version
|
|
64
80
|
version: '0'
|
|
65
81
|
requirements: []
|
|
66
|
-
rubygems_version: 3.
|
|
67
|
-
signing_key:
|
|
82
|
+
rubygems_version: 3.5.2
|
|
83
|
+
signing_key:
|
|
68
84
|
specification_version: 4
|
|
69
85
|
summary: A slim ruby wrapper for posting to slack webhooks
|
|
70
86
|
test_files:
|
|
71
|
-
- spec/
|
|
87
|
+
- spec/end_to_end_spec.rb
|
|
72
88
|
- spec/integration/ping_integration_test.rb
|
|
73
|
-
- spec/lib/slack-messenger_spec.rb
|
|
74
89
|
- spec/lib/slack-messenger/config_spec.rb
|
|
75
|
-
- spec/lib/slack-messenger/util/http_client_spec.rb
|
|
76
|
-
- spec/lib/slack-messenger/util/link_formatter_spec.rb
|
|
77
90
|
- spec/lib/slack-messenger/payload_middleware/at_spec.rb
|
|
78
|
-
- spec/lib/slack-messenger/payload_middleware/
|
|
91
|
+
- spec/lib/slack-messenger/payload_middleware/base_spec.rb
|
|
79
92
|
- spec/lib/slack-messenger/payload_middleware/channels_spec.rb
|
|
80
|
-
- spec/lib/slack-messenger/payload_middleware/
|
|
93
|
+
- spec/lib/slack-messenger/payload_middleware/format_attachments_spec.rb
|
|
81
94
|
- spec/lib/slack-messenger/payload_middleware/format_message_spec.rb
|
|
82
|
-
- spec/lib/slack-messenger/payload_middleware/
|
|
95
|
+
- spec/lib/slack-messenger/payload_middleware/stack_spec.rb
|
|
83
96
|
- spec/lib/slack-messenger/payload_middleware_spec.rb
|
|
84
|
-
- spec/
|
|
97
|
+
- spec/lib/slack-messenger/untrusted_regexp_spec.rb
|
|
98
|
+
- spec/lib/slack-messenger/util/http_client_spec.rb
|
|
99
|
+
- spec/lib/slack-messenger/util/link_formatter_spec.rb
|
|
100
|
+
- spec/lib/slack-messenger_spec.rb
|
|
101
|
+
- spec/spec_helper.rb
|