tenter 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2cec3086122a566c922ffaeeda6e9d086c3dc78cd4900666e6e0ae2edefb15a3
4
- data.tar.gz: 9dacc38cf020d66efa977acdbf2dfab90527b926445b5f7aa5e1e57206a7c0b0
3
+ metadata.gz: e691f0744f22ab97bd7793c42c570328202deffe78aae13965ca528b1376c2ae
4
+ data.tar.gz: 1d79b962a619c625d8b812951087d913a3739ff7d8ca0d4e20f4b101ad820059
5
5
  SHA512:
6
- metadata.gz: b535883fe1cd070d1f8706a895c043db077950bb8e2c0d622e310e026831feed390a79980e5b0d39510c1abbed8f0f3b1072b8c4df0d92d390200724d2396193
7
- data.tar.gz: 660effd30f91a6d47745ee803d73b5f0082183c49268aca2bf7fa3cabb52dfd03bef29d683c282d12081ce6c3362ca80a3e6e4b2be319184972f69b616344b0e
6
+ metadata.gz: 8e5516a38863c5885008b5752c86ecd7f5c6ba300b266b4867240c92dfdafb47804cd38ccd6215f2367ec747051bf6c4baca7775f3a8e9550713c5bc1943e587
7
+ data.tar.gz: 7180c0c1bd2a01091089230f81b506b9d931543bf66ca7708213c06b888ea8ebf6125fab512beef261276b5096e0fcd98c58b5f080bc5d889ce75af805f32f0f
@@ -5,7 +5,68 @@ require "tenter/hooks"
5
5
  require "tenter/utils"
6
6
  require "tenter/version"
7
7
 
8
+ # A web app that provides webhooks for GitHub
9
+ #
10
+ # Tenter is a Sinatra-based web application that provides webhooks for use by
11
+ # GitHub. It is intended to be used as a gem in a Rack app.
12
+ #
13
+ # At its simplest, a user could write a `config.ru` file consisting of:
14
+ #
15
+ # require "tenter"
16
+ #
17
+ # run Tenter::Hooks
18
+ #
19
+ # Tenter comes with a series of sane defaults. A version of Tenter that uses
20
+ # these will expose endpoints in the form `/run/<action>/in/<dirname>/`. HTTP
21
+ # POST requests sent to such an endpoint will, if authenticated, result in
22
+ # Tenter executing the file on your the server's local file system at
23
+ # `/<doc_root>/<dirname>/commands/<action>`.
24
+ #
25
+ # As astute readers will have observed, this is a potentially massive security
26
+ # vulnerability. I am nevertheless able to sleep at night because Tenter
27
+ # will only execute a file matching the action in a given directory if:
28
+ #
29
+ # 1. the POST request includes an `HTTP_X_HUB_SIGNATURE` header; and
30
+ # 2. the value of this header matches the result of cryptographically
31
+ # signing the body of the HTTP request with a key defined by the user.
32
+ #
33
+ # By default, the key defined by the user is located in
34
+ # `/<doc_root>/<dirname>/hooks.yaml`.
35
+ #
36
+ # A user can override the default settings for their instance of Tenter by
37
+ # defining one or more of the following:
38
+ #
39
+ # - `:doc_root` (default: `"/var/www"`): The root directory in which each
40
+ # exposed directory will be located. It's recommended to specify this as an
41
+ # absolute path.
42
+ #
43
+ # - `:config_filename` (default: `"hooks.yaml"`): The filename of the
44
+ # configuration file in each exposed directory.
45
+ #
46
+ # - `:command_dir` (default: `"commands"`): The name of the subdirectory
47
+ # in which the files to execute are located.
48
+ #
49
+ # - `:log_file` (default `"log/commands.log"`): The path to the log file in each
50
+ # exposed directory where output from your commands will be logged. You can
51
+ # set this to `nil`to disable logging.
52
+ #
53
+ # The easiest way to do that is in the `config.ru` file:
54
+ #
55
+ # require "tenter"
56
+ #
57
+ # Tenter.settings = { log_file: nil } # disable logging
58
+ #
59
+ # run Tenter::Hooks
60
+ #
61
+ # @since 0.1.1
62
+ # @see https://github.com/pyrmont/tenter
8
63
  module Tenter
64
+
65
+ # Returns the default settings
66
+ #
67
+ # @return [Hash] the default settings
68
+ #
69
+ # @since 0.1.1
9
70
  def self.defaults
10
71
  { doc_root: "/var/www/",
11
72
  config_filename: "hooks.yaml",
@@ -14,15 +75,28 @@ module Tenter
14
75
  timestamp: true }
15
76
  end
16
77
 
78
+ # Resets Tenter's settings to their defaults
79
+ #
80
+ # @since 0.1.1
17
81
  def self.reset
18
82
  @settings = self.defaults
19
83
  end
20
84
 
85
+ # Updates the provided settings
86
+ #
87
+ # @param opts [Hash] the keys and values to update
88
+ #
89
+ # @since 0.1.1
21
90
  def self.settings=(opts = {})
22
91
  @settings = self.settings.merge opts
23
92
  end
24
93
 
94
+ # Returns the settings for this instance
95
+ #
96
+ # @return [Hash] the settings for this instance
97
+ #
98
+ # @since 0.1.1
25
99
  def self.settings
26
- @settings ||= self.defaults
100
+ @settings ||= self.defaults
27
101
  end
28
102
  end
@@ -3,14 +3,28 @@
3
3
  require "openssl"
4
4
 
5
5
  module Tenter
6
+
7
+ # Sinatra helpers for Tenter
8
+ #
9
+ # These helpers provide idiomatic methods for use in Sinatra routes. This
10
+ # module is intended to be called by passing it to the `Sinatra::Base.helpers`
11
+ # method.
12
+ #
13
+ # @since 0.1.1
14
+ # @api private
6
15
  module Helpers
16
+
17
+ # Authenticates the request
18
+ #
19
+ # @since 0.1.1
20
+ # @api private
7
21
  def authenticate
8
22
  msg = "X-Hub-Signature header not set"
9
23
  halt 400, msg unless request.env['HTTP_X_HUB_SIGNATURE']
10
24
 
11
25
  msg = "X-Hub-Signature header did not match"
12
26
  halt 403, msg unless Tenter::Utils.dir_exists? params[:site_dir]
13
-
27
+
14
28
  secret = Tenter::Utils.secret params[:site_dir]
15
29
 
16
30
  request_sig = request.env['HTTP_X_HUB_SIGNATURE']
@@ -19,11 +33,15 @@ module Tenter
19
33
  OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'),
20
34
  secret,
21
35
  request_body)
22
-
36
+
23
37
  msg = "X-Hub-Signature header did not match"
24
38
  halt 403, msg unless Rack::Utils.secure_compare computed_sig, request_sig
25
39
  end
26
40
 
41
+ # Executes the command
42
+ #
43
+ # @since 0.1.1
44
+ # @api private
27
45
  def initiate
28
46
  command = Tenter::Utils.command params[:command_name], params[:site_dir]
29
47
 
@@ -34,14 +52,19 @@ module Tenter
34
52
  msg = ts + "Initiating: #{command["path"]}\n"
35
53
  Tenter::Utils.append_to_log command["log"], msg
36
54
 
37
- pid = if defined?(Bundler) && Bundler.respond_to?(:with_clean_env)
38
- Bundler.with_clean_env { command["proc"].call }
55
+ pid = if defined?(Bundler) && Bundler.respond_to?(:with_original_env)
56
+ Bundler.with_original_env { command["proc"].call }
39
57
  else
40
58
  command["proc"].call
41
59
  end
42
60
  (ENV["APP_ENV"] != "test") ? Process.detach(pid) : Process.wait(pid)
43
61
  end
44
62
 
63
+ # Generates the response's HTTP header status and body
64
+ #
65
+ # @param message [Symbol] the type of response
66
+ # @since 0.1.1
67
+ # @api private
45
68
  def notify(message)
46
69
  case message
47
70
  when :initiated
@@ -3,6 +3,13 @@
3
3
  require "sinatra/base"
4
4
 
5
5
  module Tenter
6
+
7
+ # The Sinatra application
8
+ #
9
+ # This sets out the routes for the Sinatra application.
10
+ #
11
+ # @since 0.1.1
12
+ # @api private
6
13
  class Hooks < Sinatra::Base
7
14
  helpers Tenter::Helpers
8
15
 
@@ -3,7 +3,22 @@
3
3
  require "yaml"
4
4
 
5
5
  module Tenter
6
+
7
+ # Utility functions for use by Tenter
8
+ #
9
+ # @since 0.1.1
10
+ # @api private
6
11
  module Utils
12
+
13
+ # Loads configuration data from a YAML file for a given directory
14
+ #
15
+ # @note The basename of the YAML file is specified in Tenter's configuration
16
+ # settings.
17
+ #
18
+ # @param site_dir [String] the directory containing the YAML file
19
+ #
20
+ # @since 0.1.1
21
+ # @api private
7
22
  def self.config(site_dir)
8
23
  @config ||= {}
9
24
  @config[site_dir] ||=
@@ -12,14 +27,40 @@ module Tenter
12
27
  Tenter.settings[:config_filename]))
13
28
  end
14
29
 
30
+ # Checks if the directory exists
31
+ #
32
+ # @note The directory provided must be given relative to the document root.
33
+ #
34
+ # @param site_dir [String] the directory to check
35
+ #
36
+ # @since 0.1.1
37
+ # @api private
15
38
  def self.dir_exists?(site_dir)
16
39
  File.directory? File.join(Tenter.settings[:doc_root], site_dir)
17
40
  end
18
41
 
42
+ # Returns the secret value for a particular directory
43
+ #
44
+ # @param site_dir [String} the directory
45
+ #
46
+ # @since 0.1.1
47
+ # @api private
19
48
  def self.secret(site_dir)
20
49
  self.config(site_dir).fetch("secret", nil)
21
50
  end
22
51
 
52
+ # Returns the details of the command to execute
53
+ #
54
+ # @note The directory provided must be given relative to the document root.
55
+ #
56
+ # @param command_name [String] the name of the file representing the
57
+ # command
58
+ # @param site_dir [String} the directory containing the command
59
+ #
60
+ # @return [Hash] the details of the command to execute
61
+ #
62
+ # @since 0.1.1
63
+ # @api private
23
64
  def self.command(command_name, site_dir)
24
65
  site_path = File.join(Tenter.settings[:doc_root], site_dir)
25
66
  command = {}
@@ -43,6 +84,13 @@ module Tenter
43
84
  return command
44
85
  end
45
86
 
87
+ # Appends a statement to the log
88
+ #
89
+ # @param log [String] the file representing the log
90
+ # @param statement [String] the statement to append to the log
91
+ #
92
+ # @since 0.1.0
93
+ # @api private
46
94
  def self.append_to_log(log, statement)
47
95
  File.write(log, statement, mode: "a")
48
96
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tenter
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -3,28 +3,30 @@
3
3
  require "./lib/tenter/version"
4
4
 
5
5
  Gem::Specification.new do |s|
6
- s.name = "tenter"
7
- s.version = Tenter::VERSION
8
- s.authors = ["Michael Camilleri"]
9
- s.email = ["mike@inqk.net"]
10
- s.summary = "A web app for running user-defined commands"
6
+ s.name = "tenter"
7
+ s.version = Tenter::VERSION
8
+ s.authors = ["Michael Camilleri"]
9
+ s.email = ["mike@inqk.net"]
10
+ s.summary = "A web app for running user-defined commands"
11
11
  s.description = <<-desc.strip.gsub(/\s+/, " ")
12
12
  Tenter is a Sinatra-based application that provides webhooks for use by
13
13
  GitHub.
14
14
  desc
15
- s.homepage = "https://github.com/pyrmont/tenter/"
16
- s.licenses = "Unlicense"
17
- s.required_ruby_version = ">= 2.5"
15
+ s.homepage = "https://github.com/pyrmont/tenter/"
16
+ s.licenses = "Unlicense"
17
+ s.files = Dir["Gemfile", "LICENSE", "tenter.gemspec", "lib/tenter.rb",
18
+ "lib/**/*.rb"]
18
19
 
19
- s.files = Dir["Gemfile", "LICENSE", "tenter.gemspec", "lib/tenter.rb",
20
- "lib/**/*.rb"]
21
- s.require_paths = ["lib"]
22
-
20
+ s.required_ruby_version = ">= 2.5"
21
+ s.require_paths = ["lib"]
23
22
  s.metadata["allowed_push_host"] = "https://rubygems.org"
24
23
 
25
24
  s.add_runtime_dependency "sinatra", "~> 2.0"
25
+ s.add_runtime_dependency "thin", "~> 1.0"
26
26
 
27
- s.add_development_dependency "minitest", "~> 5.11"
28
- s.add_development_dependency "rack-test", "~> 1.1"
29
- s.add_development_dependency "rake", "~> 12.3"
27
+ s.add_development_dependency "minitest", "~> 5.0"
28
+ s.add_development_dependency "rack-test", "~> 1.0"
29
+ s.add_development_dependency "rake", "~> 13.0"
30
+ s.add_development_dependency "redcarpet", "~> 3.0"
31
+ s.add_development_dependency "yard", "~> 0.0"
30
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Camilleri
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-08 00:00:00.000000000 Z
11
+ date: 2020-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sinatra
@@ -24,48 +24,90 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thin
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: minitest
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: '5.11'
47
+ version: '5.0'
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: '5.11'
54
+ version: '5.0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rack-test
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: '1.1'
61
+ version: '1.0'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '1.1'
68
+ version: '1.0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rake
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - "~>"
60
74
  - !ruby/object:Gem::Version
61
- version: '12.3'
75
+ version: '13.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '13.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: redcarpet
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.0'
62
104
  type: :development
63
105
  prerelease: false
64
106
  version_requirements: !ruby/object:Gem::Requirement
65
107
  requirements:
66
108
  - - "~>"
67
109
  - !ruby/object:Gem::Version
68
- version: '12.3'
110
+ version: '0.0'
69
111
  description: Tenter is a Sinatra-based application that provides webhooks for use
70
112
  by GitHub.
71
113
  email:
@@ -102,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
144
  - !ruby/object:Gem::Version
103
145
  version: '0'
104
146
  requirements: []
105
- rubygems_version: 3.0.3
147
+ rubygems_version: 3.1.2
106
148
  signing_key:
107
149
  specification_version: 4
108
150
  summary: A web app for running user-defined commands