hookd-client 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b373613f2ef4194aafacdb65fc585e39163d8673d619aa720834ac46083506e9
4
+ data.tar.gz: a432227574294bbcb7f80c15be7d97a901dc7b33f79e450fcd13f9b9ef7b5f66
5
+ SHA512:
6
+ metadata.gz: 4f3e6702167c4a92e5110d6da8d946c5f73029dd64df94bd49db1716b053e923a525e83e97ef5c6c3473f5d68ddcdd634ec98d3b50ae4d494a514391cab1902e
7
+ data.tar.gz: bc2629120bf46c57d13627406cbf886daba9ab2569a5d54cbc9808475311cb5ec6eae3c66cd2c0e442f5ecb8d331f4a2792fd13f24a8b9c4c051b3ace3920201
data/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # Hookd Ruby Client
2
+
3
+ Ruby client library for [Hookd](https://github.com/JoshuaMart/hookd/server), a DNS/HTTP interaction server for security testing and debugging.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'hookd-client'
11
+ ```
12
+
13
+ Or install it yourself as:
14
+
15
+ ```bash
16
+ gem install hookd-client
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Basic Example
22
+
23
+ ```ruby
24
+ require 'hookd'
25
+ require 'typhoeus'
26
+
27
+ # Initialize the client
28
+ client = Hookd::Client.new(
29
+ server: "https://hookd.example.com",
30
+ token: ENV['HOOKD_TOKEN']
31
+ )
32
+
33
+ # Register a new hook
34
+ hook = client.register
35
+ puts "DNS endpoint: #{hook.dns}"
36
+ puts "HTTP endpoint: #{hook.http}"
37
+ puts "HTTPS endpoint: #{hook.https}"
38
+
39
+ # Make a request to the HTTP endpoint to simulate an interaction
40
+ Typhoeus.get(hook.http)
41
+
42
+ # Poll for interactions
43
+ interactions = client.poll(hook.id)
44
+ interactions.each do |interaction|
45
+ if interaction.dns?
46
+ puts "DNS query: #{interaction.data}"
47
+ elsif interaction.http?
48
+ puts "HTTP request: #{interaction.data}"
49
+ end
50
+ end
51
+ ```
52
+
53
+ ### Configuration
54
+
55
+ The client requires two configuration parameters:
56
+
57
+ - `server`: The Hookd server URL (e.g., `https://hookd.example.com`)
58
+ - `token`: Authentication token for API access
59
+
60
+ ### API Reference
61
+
62
+ #### `Hookd::Client`
63
+
64
+ Main client class for interacting with the Hookd server.
65
+
66
+ ##### `#register`
67
+
68
+ Register a new hook and get DNS/HTTP endpoints.
69
+
70
+ ```ruby
71
+ hook = client.register
72
+ # => #<Hookd::Hook id="abc123" dns="abc123.hookd.example.com" ...>
73
+ ```
74
+
75
+ Returns: `Hookd::Hook` object
76
+
77
+ Raises:
78
+ - `Hookd::AuthenticationError` - Authentication failed
79
+ - `Hookd::ServerError` - Server error (5xx)
80
+ - `Hookd::ConnectionError` - Connection failed
81
+
82
+ ##### `#poll(hook_id)`
83
+
84
+ Poll for interactions captured by a hook.
85
+
86
+ ```ruby
87
+ interactions = client.poll("abc123")
88
+ # => [#<Hookd::Interaction type="dns" ...>, ...]
89
+ ```
90
+
91
+ Parameters:
92
+ - `hook_id` (String) - The hook ID to poll
93
+
94
+ Returns: Array of `Hookd::Interaction` objects
95
+
96
+ Raises:
97
+ - `Hookd::AuthenticationError` - Authentication failed
98
+ - `Hookd::NotFoundError` - Hook not found
99
+ - `Hookd::ServerError` - Server error (5xx)
100
+ - `Hookd::ConnectionError` - Connection failed
101
+
102
+ ##### `#metrics`
103
+
104
+ Get server metrics (requires authentication).
105
+
106
+ ```ruby
107
+ metrics = client.metrics
108
+ # => {"total_hooks" => 42, "total_interactions" => 1337, ...}
109
+ ```
110
+
111
+ Returns: Hash with metrics data
112
+
113
+ #### `Hookd::Hook`
114
+
115
+ Represents a registered hook with endpoints.
116
+
117
+ Attributes:
118
+ - `id` (String) - Unique hook identifier
119
+ - `dns` (String) - DNS endpoint
120
+ - `http` (String) - HTTP endpoint
121
+ - `https` (String) - HTTPS endpoint
122
+ - `created_at` (String) - Creation timestamp
123
+
124
+ #### `Hookd::Interaction`
125
+
126
+ Represents a captured DNS or HTTP interaction.
127
+
128
+ Attributes:
129
+ - `type` (String) - Interaction type ("dns" or "http")
130
+ - `timestamp` (String) - When the interaction was captured
131
+ - `data` (Hash) - Interaction details
132
+
133
+ Methods:
134
+ - `#dns?` - Returns true if this is a DNS interaction
135
+ - `#http?` - Returns true if this is an HTTP interaction
136
+
137
+ ### Error Handling
138
+
139
+ The client raises specific exceptions for different error conditions:
140
+
141
+ ```ruby
142
+ begin
143
+ hook = client.register
144
+ rescue Hookd::AuthenticationError
145
+ puts "Invalid token"
146
+ rescue Hookd::ConnectionError => e
147
+ puts "Connection failed: #{e.message}"
148
+ rescue Hookd::ServerError => e
149
+ puts "Server error: #{e.message}"
150
+ end
151
+ ```
152
+
153
+ Exception hierarchy:
154
+ - `Hookd::Error` (base class)
155
+ - `Hookd::AuthenticationError` - 401 Unauthorized
156
+ - `Hookd::NotFoundError` - 404 Not Found
157
+ - `Hookd::ServerError` - 5xx Server Error
158
+ - `Hookd::ConnectionError` - Network/connection errors
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'uri'
6
+
7
+ module Hookd
8
+ # HTTP client for interacting with Hookd server
9
+ class Client
10
+ attr_reader :server, :token
11
+
12
+ def initialize(server:, token:)
13
+ @server = server
14
+ @token = token
15
+ @uri = URI.parse(server)
16
+ end
17
+
18
+ # Register a new hook
19
+ # @return [Hookd::Hook] the newly registered hook
20
+ # @raise [Hookd::AuthenticationError] if authentication fails
21
+ # @raise [Hookd::ServerError] if server returns 5xx
22
+ # @raise [Hookd::ConnectionError] if connection fails
23
+ def register
24
+ response = post('/register')
25
+ Hook.from_hash(response)
26
+ end
27
+
28
+ # Poll for interactions on a hook
29
+ # @param hook_id [String] the hook ID to poll
30
+ # @return [Array<Hookd::Interaction>] array of interactions (may be empty)
31
+ # @raise [Hookd::AuthenticationError] if authentication fails
32
+ # @raise [Hookd::NotFoundError] if hook not found
33
+ # @raise [Hookd::ServerError] if server returns 5xx
34
+ # @raise [Hookd::ConnectionError] if connection fails
35
+ def poll(hook_id)
36
+ response = get("/poll/#{hook_id}")
37
+
38
+ # Response is {"interactions": [...]}
39
+ interactions = response['interactions']
40
+ return [] if interactions.nil? || interactions.empty? || !interactions.is_a?(Array)
41
+
42
+ interactions.map { |i| Interaction.from_hash(i) }
43
+ rescue NoMethodError => e
44
+ raise Error, "Invalid response format: #{e.message}"
45
+ end
46
+
47
+ # Get server metrics (requires authentication)
48
+ # @return [Hash] metrics data
49
+ # @raise [Hookd::AuthenticationError] if authentication fails
50
+ # @raise [Hookd::ServerError] if server returns 5xx
51
+ # @raise [Hookd::ConnectionError] if connection fails
52
+ def metrics
53
+ get('/metrics')
54
+ end
55
+
56
+ private
57
+
58
+ def get(path)
59
+ request = Net::HTTP::Get.new(path)
60
+ request['Authorization'] = "Bearer #{token}"
61
+ execute_request(request)
62
+ end
63
+
64
+ def post(path, body = nil)
65
+ request = Net::HTTP::Post.new(path)
66
+ request['Authorization'] = "Bearer #{token}"
67
+ request['Content-Type'] = 'application/json'
68
+ request.body = body.to_json if body
69
+ execute_request(request)
70
+ end
71
+
72
+ def execute_request(request)
73
+ http = Net::HTTP.new(@uri.host, @uri.port)
74
+ http.use_ssl = @uri.scheme == 'https'
75
+ http.open_timeout = 10
76
+ http.read_timeout = 30
77
+
78
+ response = http.request(request)
79
+
80
+ case response.code.to_i
81
+ when 200, 201
82
+ raise Error, 'Empty response body from server' if response.body.nil? || response.body.empty?
83
+
84
+ JSON.parse(response.body)
85
+ when 401
86
+ raise AuthenticationError, "Authentication failed: #{response.body}"
87
+ when 404
88
+ raise NotFoundError, "Resource not found: #{response.body}"
89
+ when 500..599
90
+ raise ServerError, "Server error (#{response.code}): #{response.body}"
91
+ else
92
+ raise Error, "Unexpected response (#{response.code}): #{response.body}"
93
+ end
94
+ rescue SocketError, Errno::ECONNREFUSED, Net::OpenTimeout, Net::ReadTimeout => e
95
+ raise ConnectionError, "Connection failed: #{e.message}"
96
+ rescue JSON::ParserError => e
97
+ raise Error, "Invalid JSON response: #{e.message}"
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hookd
4
+ # Base error class for all Hookd errors
5
+ class Error < StandardError; end
6
+
7
+ # Raised when authentication fails (401)
8
+ class AuthenticationError < Error; end
9
+
10
+ # Raised when a resource is not found (404)
11
+ class NotFoundError < Error; end
12
+
13
+ # Raised when there's a connection error
14
+ class ConnectionError < Error; end
15
+
16
+ # Raised when the server returns a 5xx error
17
+ class ServerError < Error; end
18
+ end
data/lib/hookd/hook.rb ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hookd
4
+ # Represents a registered hook with DNS and HTTP endpoints
5
+ class Hook
6
+ attr_reader :id, :dns, :http, :https, :created_at
7
+
8
+ def initialize(id:, dns:, http:, https:, created_at:)
9
+ @id = id
10
+ @dns = dns
11
+ @http = http
12
+ @https = https
13
+ @created_at = created_at
14
+ end
15
+
16
+ # Create a Hook from API response hash
17
+ def self.from_hash(hash)
18
+ raise ArgumentError, "Invalid hash: expected Hash, got #{hash.class}" unless hash.is_a?(Hash)
19
+
20
+ new(
21
+ id: hash['id'],
22
+ dns: hash['dns'],
23
+ http: hash['http'],
24
+ https: hash['https'],
25
+ created_at: hash['created_at']
26
+ )
27
+ end
28
+
29
+ def to_s
30
+ "#<Hookd::Hook id=#{id} dns=#{dns}>"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hookd
4
+ # Represents a captured DNS or HTTP interaction
5
+ class Interaction
6
+ attr_reader :type, :timestamp, :data
7
+
8
+ def initialize(type:, timestamp:, data:)
9
+ @type = type
10
+ @timestamp = timestamp
11
+ @data = data
12
+ end
13
+
14
+ # Create an Interaction from API response hash
15
+ def self.from_hash(hash)
16
+ new(
17
+ type: hash['type'],
18
+ timestamp: hash['timestamp'],
19
+ data: hash['data']
20
+ )
21
+ end
22
+
23
+ # Check if this is a DNS interaction
24
+ def dns?
25
+ type == 'dns'
26
+ end
27
+
28
+ # Check if this is an HTTP interaction
29
+ def http?
30
+ type == 'http'
31
+ end
32
+
33
+ def to_s
34
+ "#<Hookd::Interaction type=#{type} timestamp=#{timestamp}>"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hookd
4
+ VERSION = '0.1.0'
5
+ end
data/lib/hookd.rb ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'hookd/version'
4
+ require_relative 'hookd/error'
5
+ require_relative 'hookd/hook'
6
+ require_relative 'hookd/interaction'
7
+ require_relative 'hookd/client'
8
+
9
+ # Hookd client library for interacting with Hookd DNS/HTTP interaction server
10
+ module Hookd
11
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hookd-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jomar
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Ruby client library for Hookd, a DNS/HTTP interaction server for security
13
+ testing and debugging
14
+ email:
15
+ - contact@jomar.fr
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - README.md
21
+ - lib/hookd.rb
22
+ - lib/hookd/client.rb
23
+ - lib/hookd/error.rb
24
+ - lib/hookd/hook.rb
25
+ - lib/hookd/interaction.rb
26
+ - lib/hookd/version.rb
27
+ homepage: https://github.com/JoshuaMart/hookd
28
+ licenses:
29
+ - MIT
30
+ metadata:
31
+ homepage_uri: https://github.com/JoshuaMart/hookd
32
+ source_code_uri: https://github.com/jomar/JoshuaMart
33
+ rubygems_mfa_required: 'true'
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 3.1.0
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubygems_version: 3.6.9
49
+ specification_version: 4
50
+ summary: Ruby client for Hookd interaction server
51
+ test_files: []