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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41159ac77b2c985abcaa10646004e458f34b0542a68c907b93fcb9bde162944e
4
- data.tar.gz: fc6b0fbc4d3c2d6764a18b6aa06e1d8002328e1653e18e94d64656655f35df04
3
+ metadata.gz: 4f4f125fd094ef63218f4ad86215076f640a95a0527e54ba99c44e5b603120d0
4
+ data.tar.gz: 2f8940ac0823981a3e82801917cfec3837c5d4fa86d869df8e95a1d83b8076c9
5
5
  SHA512:
6
- metadata.gz: 0af8745e8984db10f54f1fb373a6eccab3d5bf106655d5189b1281147be8a0fb0d0d1ed3b57a16c11a555f7bf5f9dbf103cfd38684c0e0bd1d4c6b0a6330a636
7
- data.tar.gz: 129722ffe887ddc8434086ef159ed14068bd8d8045c2c3651d2d37eff25770ad3d6fca34a5939b7b82aa482f023904b00495222e3b98913a99566a50027834b2
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
- # http://rubular.com/r/19cNXW5qbH
8
- HTML_PATTERN = %r{
9
- <a
10
- (?:.*?)
11
- href=['"](.+?)['"]
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
- # https://rubular.com/r/WfdZ1arvF6PNWO
23
- MARKDOWN_PATTERN = %r{
24
- \[ ([^\[\]]*?) \]
25
- \(
26
- ( (?:https?:\/\/|mailto:)
27
- (?:[#{VALID_URI_CHARS}]*?|[#{VALID_URI_CHARS}]*?\([#{VALID_URI_CHARS}]*?\)[#{VALID_URI_CHARS}]*?) )
28
- \)
29
- }x
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
- def sub_html_links string
58
- return string unless formats.include?(:html)
63
+ def sub_html_links string
64
+ return string unless formats.include?(:html)
59
65
 
60
- string.gsub(HTML_PATTERN) do
61
- slack_link Regexp.last_match[1], Regexp.last_match[2]
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
- def sub_markdown_links string
66
- return string unless formats.include?(:markdown)
71
+ def sub_markdown_links string
72
+ return string unless formats.include?(:markdown)
67
73
 
68
- string.gsub(MARKDOWN_PATTERN) do
69
- slack_link Regexp.last_match[2], Regexp.last_match[1]
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
- def slack_link link, text=nil
74
- "<#{link}" \
75
- "#{text && !text.empty? ? "|#{text}" : ''}" \
76
- ">"
77
- end
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Slack
4
4
  class Messenger
5
- VERSION = "2.3.4".freeze # rubocop:disable Style/RedundantFreeze
5
+ VERSION = "2.3.5".freeze # rubocop:disable Style/RedundantFreeze
6
6
  end
7
7
  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
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: 2020-10-02 00:00:00.000000000 Z
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.1.2
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/spec_helper.rb
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/format_attachments_spec.rb
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/stack_spec.rb
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/base_spec.rb
95
+ - spec/lib/slack-messenger/payload_middleware/stack_spec.rb
83
96
  - spec/lib/slack-messenger/payload_middleware_spec.rb
84
- - spec/end_to_end_spec.rb
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