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 +7 -0
- data/README.md +158 -0
- data/lib/hookd/client.rb +100 -0
- data/lib/hookd/error.rb +18 -0
- data/lib/hookd/hook.rb +33 -0
- data/lib/hookd/interaction.rb +37 -0
- data/lib/hookd/version.rb +5 -0
- data/lib/hookd.rb +11 -0
- metadata +51 -0
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
|
data/lib/hookd/client.rb
ADDED
@@ -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
|
data/lib/hookd/error.rb
ADDED
@@ -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
|
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: []
|