safe_regexp 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c1ef6c0df7c6e214380ee04d7cb031024464a50d49ad12e0cdf825a1a9304531
4
+ data.tar.gz: afa6f06caa7c0378cdaba0d69e0eb5a210c4afce6631f352d4c7e917c406e9c0
5
+ SHA512:
6
+ metadata.gz: 928559f2fb198ceb875de832efa8389faaec72f56e30c69889e37c2631c366304358a54a79d5bfe24efae56c271ee48330e6417b7d6d840a6b9a93367149469e
7
+ data.tar.gz: 4dd3585d1f00f8a9cf6188310cd432702ae37615a4ee937780919e5ae7d8b4cc51a86edcb3c7f7a77ebd78a918829d109c289be75275745033ffb3cc04d07ac8
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (C) 2013 Michael Grosser <michael@grosser.it>
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.
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module SafeRegexp
3
+ VERSION = "0.1.0"
4
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+ require "timeout"
3
+
4
+ module SafeRegexp
5
+ class SubprocessTimeout < Timeout::Error
6
+ end
7
+
8
+ class RegexpTimeout < Timeout::Error
9
+ end
10
+
11
+ class << self
12
+ def execute(regex, method, string, timeout: 1, keepalive: 10)
13
+ retried = false
14
+ begin
15
+ read, write, pid = executor
16
+ write.puts Marshal.dump([regex, method, string, keepalive])
17
+ rescue Errno::EPIPE # keepalive killed the process
18
+ raise if retried
19
+ retried = true
20
+ discard_executor
21
+ retry
22
+ end
23
+
24
+ begin
25
+ Timeout.timeout(timeout, SubprocessTimeout) { Marshal.load(read.gets) }
26
+ rescue SubprocessTimeout
27
+ kill_executor pid
28
+ raise RegexpTimeout
29
+ end
30
+ end
31
+
32
+ def shutdown
33
+ return unless (pid = Thread.current[:safe_regexp_executor]&.last)
34
+ kill_executor pid
35
+ end
36
+
37
+ private
38
+
39
+ def kill_executor(pid)
40
+ begin
41
+ Process.kill :KILL, pid # kill -9
42
+ begin
43
+ Process.wait pid # reap child
44
+ rescue Errno::ECHILD
45
+ nil # already reaped
46
+ end
47
+ rescue Errno::ESRCH
48
+ nil # already dead
49
+ end
50
+ discard_executor
51
+ end
52
+
53
+ # faster than kill if we know it's dead
54
+ def discard_executor
55
+ Thread.current[:safe_regexp_executor] = nil
56
+ end
57
+
58
+ # - keepalive gets extended by whatever time the matching takes, but that should not be too bad
59
+ # we could fix it, but that means extra overhead that I'd rather avoid
60
+ # - using select to avoid having extra threads
61
+ def executor
62
+ Thread.current[:safe_regexp_executor] ||= begin
63
+ in_read, in_write = IO.pipe
64
+ out_read, out_write = IO.pipe
65
+ pid = fork do
66
+ in_write.close
67
+ out_read.close
68
+ keepalive = 1
69
+ loop do
70
+ break unless IO.select([in_read], nil, nil, keepalive)
71
+ break unless (instructions = in_read.gets)
72
+ regexp, method, string, keepalive = Marshal.load(instructions)
73
+ result = regexp.public_send(method, string)
74
+ out_write.puts Marshal.dump(result)
75
+ end
76
+ exit! # to not run any kind of at_exit hook the parent might have configured
77
+ end
78
+ Process.detach(pid)
79
+ in_read.close
80
+ out_write.close
81
+ [out_read, in_write, pid]
82
+ end
83
+ end
84
+ end
85
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: safe_regexp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Grosser
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-03-28 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: michael@grosser.it
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - MIT-LICENSE
20
+ - lib/safe_regexp.rb
21
+ - lib/safe_regexp/version.rb
22
+ homepage: https://github.com/grosser/safe_regexp
23
+ licenses:
24
+ - MIT
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 2.3.0
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 2.7.6
43
+ signing_key:
44
+ specification_version: 4
45
+ summary: Backtracking bomb safety / timeouts for regular expressions
46
+ test_files: []