auditea 0.1.0.beta.1 → 0.1.0.beta.3
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 +4 -4
- data/CHANGELOG.md +60 -1
- data/DESIGN.md +13 -0
- data/README.md +136 -16
- data/RELEASE.md +184 -22
- data/auditea.gemspec +2 -2
- data/lib/auditea/configuration.rb +22 -1
- data/lib/auditea/host_inventory/background_refresh.rb +64 -0
- data/lib/auditea/host_inventory/collector.rb +246 -0
- data/lib/auditea/host_inventory/emitter.rb +105 -0
- data/lib/auditea/host_inventory.rb +37 -0
- data/lib/auditea/instrumentation/active_job.rb +3 -0
- data/lib/auditea/instrumentation/error_subscriber.rb +93 -0
- data/lib/auditea/instrumentation/exception.rb +134 -16
- data/lib/auditea/instrumentation/structural_request.rb +136 -0
- data/lib/auditea/inventory/background_refresh.rb +64 -0
- data/lib/auditea/inventory/collector.rb +301 -0
- data/lib/auditea/inventory/digest.rb +59 -0
- data/lib/auditea/inventory/emitter.rb +105 -0
- data/lib/auditea/inventory/string_bound.rb +43 -0
- data/lib/auditea/inventory.rb +36 -0
- data/lib/auditea/middleware/context.rb +27 -0
- data/lib/auditea/railtie.rb +8 -0
- data/lib/auditea/sanitizer.rb +14 -1
- data/lib/auditea/version.rb +1 -1
- data/lib/auditea.rb +45 -0
- metadata +15 -3
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
module Auditea
|
|
7
|
+
module Inventory
|
|
8
|
+
# Stable digest over normalized inventory (no timestamps / PIDs / hostnames).
|
|
9
|
+
module DigestComputer
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def compute(inventory)
|
|
13
|
+
payload = canonicalize(inventory_without_digest(inventory))
|
|
14
|
+
Digest::SHA256.hexdigest(JSON.generate(payload))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def inventory_without_digest(inventory)
|
|
18
|
+
case inventory
|
|
19
|
+
when Hash
|
|
20
|
+
inventory.each_with_object({}) do |(key, value), memo|
|
|
21
|
+
next if key.to_s == "digest"
|
|
22
|
+
|
|
23
|
+
memo[key.to_s] = inventory_without_digest(value)
|
|
24
|
+
end
|
|
25
|
+
when Array
|
|
26
|
+
inventory.map { |value| inventory_without_digest(value) }
|
|
27
|
+
else
|
|
28
|
+
inventory
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def canonicalize(value)
|
|
33
|
+
case value
|
|
34
|
+
when Hash
|
|
35
|
+
value.keys.map(&:to_s).sort.to_h do |key|
|
|
36
|
+
[key, canonicalize(hash_fetch(value, key))]
|
|
37
|
+
end
|
|
38
|
+
when Array
|
|
39
|
+
value.map { |child| canonicalize(child) }
|
|
40
|
+
when String, Numeric, TrueClass, FalseClass, NilClass
|
|
41
|
+
value
|
|
42
|
+
else
|
|
43
|
+
value.to_s
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Prefer key existence over truthiness so false/nil are preserved.
|
|
48
|
+
def hash_fetch(hash, key)
|
|
49
|
+
return hash[key] if hash.key?(key)
|
|
50
|
+
|
|
51
|
+
sym = key.to_sym
|
|
52
|
+
return hash[sym] if hash.key?(sym)
|
|
53
|
+
|
|
54
|
+
nil
|
|
55
|
+
end
|
|
56
|
+
private_class_method :hash_fetch
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Auditea
|
|
4
|
+
module Inventory
|
|
5
|
+
# Process-local emission gate: ~daily refresh, immediate emit on digest change.
|
|
6
|
+
module Emitter
|
|
7
|
+
ACTION = "runtime.inventory"
|
|
8
|
+
CATEGORY = "security"
|
|
9
|
+
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def capture!(force: true)
|
|
13
|
+
return skipped("disabled") unless Auditea.configuration.capture_runtime_inventory?
|
|
14
|
+
|
|
15
|
+
return skipped("not_due") if !force && !collection_due?
|
|
16
|
+
|
|
17
|
+
mark_collection_attempt!
|
|
18
|
+
inventory = Collector.collect
|
|
19
|
+
emit(inventory, force: force)
|
|
20
|
+
rescue StandardError => error
|
|
21
|
+
Auditea.handle_error(error)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def maybe_capture!
|
|
25
|
+
capture!(force: false)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Cheap gate for request/lifecycle hooks — does not collect until due.
|
|
29
|
+
def collection_due?(now: monotonic_now)
|
|
30
|
+
state_mutex.synchronize do
|
|
31
|
+
return true if @last_collection_attempt_at.nil?
|
|
32
|
+
|
|
33
|
+
(now - @last_collection_attempt_at) >= Auditea.configuration.inventory_refresh_interval
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def reset!
|
|
38
|
+
state_mutex.synchronize do
|
|
39
|
+
@last_digest = nil
|
|
40
|
+
@last_emitted_at = nil
|
|
41
|
+
@last_collection_attempt_at = nil
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def emit(inventory, force:)
|
|
46
|
+
digest = inventory["digest"].to_s
|
|
47
|
+
unless force || should_emit?(digest)
|
|
48
|
+
return { ok: true, skipped: true, reason: "unchanged_within_interval", digest: digest }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
result = Auditea.capture(
|
|
52
|
+
ACTION,
|
|
53
|
+
category: CATEGORY,
|
|
54
|
+
evidence: { "inventory" => inventory },
|
|
55
|
+
metadata: { "sdk_event_kind" => force ? "runtime_inventory_explicit" : "runtime_inventory_auto" }
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
mark_emitted!(digest) if result.is_a?(Hash) && (result[:ok] || result["ok"])
|
|
59
|
+
result.is_a?(Hash) ? result.merge(digest: digest) : result
|
|
60
|
+
end
|
|
61
|
+
private_class_method :emit
|
|
62
|
+
|
|
63
|
+
def should_emit?(digest)
|
|
64
|
+
state_mutex.synchronize do
|
|
65
|
+
return true if @last_digest.nil?
|
|
66
|
+
return true if digest != @last_digest
|
|
67
|
+
return true if @last_emitted_at.nil?
|
|
68
|
+
|
|
69
|
+
(monotonic_now - @last_emitted_at) >= Auditea.configuration.inventory_refresh_interval
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
private_class_method :should_emit?
|
|
73
|
+
|
|
74
|
+
def mark_emitted!(digest)
|
|
75
|
+
state_mutex.synchronize do
|
|
76
|
+
@last_digest = digest
|
|
77
|
+
@last_emitted_at = monotonic_now
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
private_class_method :mark_emitted!
|
|
81
|
+
|
|
82
|
+
def mark_collection_attempt!
|
|
83
|
+
state_mutex.synchronize do
|
|
84
|
+
@last_collection_attempt_at = monotonic_now
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
private_class_method :mark_collection_attempt!
|
|
88
|
+
|
|
89
|
+
def skipped(reason)
|
|
90
|
+
{ ok: false, skipped: true, reason: reason }
|
|
91
|
+
end
|
|
92
|
+
private_class_method :skipped
|
|
93
|
+
|
|
94
|
+
def state_mutex
|
|
95
|
+
@state_mutex ||= Mutex.new
|
|
96
|
+
end
|
|
97
|
+
private_class_method :state_mutex
|
|
98
|
+
|
|
99
|
+
def monotonic_now
|
|
100
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
101
|
+
end
|
|
102
|
+
private_class_method :monotonic_now
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Auditea
|
|
4
|
+
module Inventory
|
|
5
|
+
# UTF-8-safe byte bounds for inventory string fields.
|
|
6
|
+
module StringBound
|
|
7
|
+
ELLIPSIS = "…"
|
|
8
|
+
ELLIPSIS_BYTES = ELLIPSIS.bytesize
|
|
9
|
+
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
# Returns a string with bytesize <= max_bytes (valid UTF-8), or nil for nil input.
|
|
13
|
+
# When truncating, reserves ELLIPSIS_BYTES from the budget when max_bytes allows;
|
|
14
|
+
# otherwise truncates without an ellipsis. max_bytes <= 0 yields "".
|
|
15
|
+
def truncate(value, max_bytes)
|
|
16
|
+
return nil if value.nil?
|
|
17
|
+
return "" if max_bytes <= 0
|
|
18
|
+
|
|
19
|
+
string = value.to_s
|
|
20
|
+
return string if string.bytesize <= max_bytes
|
|
21
|
+
|
|
22
|
+
if max_bytes < ELLIPSIS_BYTES
|
|
23
|
+
truncate_bytes(string, max_bytes)
|
|
24
|
+
else
|
|
25
|
+
"#{truncate_bytes(string, max_bytes - ELLIPSIS_BYTES)}#{ELLIPSIS}"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def truncate_bytes(string, budget)
|
|
30
|
+
return "" if budget <= 0
|
|
31
|
+
|
|
32
|
+
truncated = string.b.byteslice(0, budget)
|
|
33
|
+
truncated.force_encoding(Encoding::UTF_8)
|
|
34
|
+
until truncated.empty? || truncated.valid_encoding?
|
|
35
|
+
truncated = truncated.byteslice(0, truncated.bytesize - 1)
|
|
36
|
+
truncated.force_encoding(Encoding::UTF_8)
|
|
37
|
+
end
|
|
38
|
+
truncated
|
|
39
|
+
end
|
|
40
|
+
private_class_method :truncate_bytes
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "inventory/string_bound"
|
|
4
|
+
require_relative "inventory/digest"
|
|
5
|
+
require_relative "inventory/collector"
|
|
6
|
+
require_relative "inventory/emitter"
|
|
7
|
+
require_relative "inventory/background_refresh"
|
|
8
|
+
|
|
9
|
+
module Auditea
|
|
10
|
+
# Runtime and dependency inventory for AudiTea (opt-in).
|
|
11
|
+
module Inventory
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
def capture!(force: true)
|
|
15
|
+
Emitter.capture!(force: force)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def maybe_capture!
|
|
19
|
+
Emitter.maybe_capture!
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Cheap request-path entry: schedule background capture when due (never inline).
|
|
23
|
+
def schedule_refresh!
|
|
24
|
+
BackgroundRefresh.schedule!
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def collection_due?
|
|
28
|
+
Emitter.collection_due?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def reset!
|
|
32
|
+
Emitter.reset!
|
|
33
|
+
BackgroundRefresh.reset!
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -4,6 +4,7 @@ module Auditea
|
|
|
4
4
|
module Middleware
|
|
5
5
|
# Rack middleware: sets CurrentContext with safe structural request fields.
|
|
6
6
|
# Does NOT store session_id, cookies, Authorization, or query strings.
|
|
7
|
+
# Opportunistic inventory refresh uses a cheap due-gate only; collection is rare.
|
|
7
8
|
class Context
|
|
8
9
|
def initialize(app)
|
|
9
10
|
@app = app
|
|
@@ -11,6 +12,7 @@ module Auditea
|
|
|
11
12
|
|
|
12
13
|
def call(env)
|
|
13
14
|
CurrentContext.clear!
|
|
15
|
+
Instrumentation::Exception::Deduper.clear!
|
|
14
16
|
request = ::Rack::Request.new(env)
|
|
15
17
|
CurrentContext.set(
|
|
16
18
|
"request_id" => env["action_dispatch.request_id"] || env["HTTP_X_REQUEST_ID"],
|
|
@@ -21,7 +23,9 @@ module Auditea
|
|
|
21
23
|
)
|
|
22
24
|
@app.call(env)
|
|
23
25
|
ensure
|
|
26
|
+
opportunistically_refresh_inventory
|
|
24
27
|
CurrentContext.clear!
|
|
28
|
+
Instrumentation::Exception::Deduper.clear!
|
|
25
29
|
end
|
|
26
30
|
|
|
27
31
|
private
|
|
@@ -30,6 +34,29 @@ module Auditea
|
|
|
30
34
|
path = request.path.to_s
|
|
31
35
|
path.split("?", 2).first
|
|
32
36
|
end
|
|
37
|
+
|
|
38
|
+
def opportunistically_refresh_inventory
|
|
39
|
+
refresh_runtime_inventory
|
|
40
|
+
refresh_host_inventory
|
|
41
|
+
rescue StandardError => error
|
|
42
|
+
Auditea.handle_error(error)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def refresh_runtime_inventory
|
|
46
|
+
return unless Auditea.configuration.capture_runtime_inventory?
|
|
47
|
+
return unless Auditea::Inventory.collection_due?
|
|
48
|
+
|
|
49
|
+
# Never collect/deliver inline on the request path (even with sync transport).
|
|
50
|
+
Auditea::Inventory.schedule_refresh!
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def refresh_host_inventory
|
|
54
|
+
return unless Auditea.configuration.capture_host_inventory?
|
|
55
|
+
return unless Auditea::HostInventory.collection_due?
|
|
56
|
+
|
|
57
|
+
# Never collect/deliver inline on the request path (even with sync transport).
|
|
58
|
+
Auditea::HostInventory.schedule_refresh!
|
|
59
|
+
end
|
|
33
60
|
end
|
|
34
61
|
end
|
|
35
62
|
end
|
data/lib/auditea/railtie.rb
CHANGED
|
@@ -18,10 +18,18 @@ module Auditea
|
|
|
18
18
|
initializer "auditea.instrumentation" do
|
|
19
19
|
Auditea::Instrumentation::Request.install!
|
|
20
20
|
Auditea::Instrumentation::ActiveJob.install!
|
|
21
|
+
Auditea::Instrumentation::ErrorSubscriber.install!
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
config.after_initialize do
|
|
24
25
|
at_exit { Auditea.shutdown }
|
|
26
|
+
begin
|
|
27
|
+
# Startup capture is explicit (force) so long-lived refresh can stay gated.
|
|
28
|
+
Auditea.capture_runtime_inventory! if Auditea.configuration.capture_runtime_inventory?
|
|
29
|
+
Auditea.capture_host_inventory! if Auditea.configuration.capture_host_inventory?
|
|
30
|
+
rescue StandardError => error
|
|
31
|
+
Auditea.handle_error(error)
|
|
32
|
+
end
|
|
25
33
|
end
|
|
26
34
|
end
|
|
27
35
|
end
|
data/lib/auditea/sanitizer.rb
CHANGED
|
@@ -6,6 +6,10 @@ module Auditea
|
|
|
6
6
|
# Exact normalized key matching only (no substring matches).
|
|
7
7
|
module Sanitizer
|
|
8
8
|
FILTERED = "[FILTERED]"
|
|
9
|
+
DEFAULT_ARRAY_MAX = 100
|
|
10
|
+
# Inventory packages are intentionally larger than ordinary evidence arrays.
|
|
11
|
+
# Keep in sync with Inventory::Collector::MAX_PACKAGES (literal avoids load-order coupling).
|
|
12
|
+
INVENTORY_PACKAGES_ARRAY_MAX = 400
|
|
9
13
|
SENSITIVE_KEYS = %w[
|
|
10
14
|
password password_confirmation password_digest authorization cookie set_cookie
|
|
11
15
|
api_key access_token refresh_token secret secret_key client_secret private_key token
|
|
@@ -42,6 +46,14 @@ module Auditea
|
|
|
42
46
|
"#{string.byteslice(0, max_bytes)}…"
|
|
43
47
|
end
|
|
44
48
|
|
|
49
|
+
def inventory_packages_path?(path)
|
|
50
|
+
path.to_s == "evidence.inventory.packages"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def array_limit_for(path)
|
|
54
|
+
inventory_packages_path?(path) ? INVENTORY_PACKAGES_ARRAY_MAX : DEFAULT_ARRAY_MAX
|
|
55
|
+
end
|
|
56
|
+
|
|
45
57
|
def deep_sanitize(value, depth:, path:, redacted:)
|
|
46
58
|
cfg = Auditea.configuration
|
|
47
59
|
return { "truncated" => true, "reason" => "max_depth" } if depth > cfg.nesting_max_depth
|
|
@@ -59,7 +71,8 @@ module Auditea
|
|
|
59
71
|
end
|
|
60
72
|
end
|
|
61
73
|
when Array
|
|
62
|
-
|
|
74
|
+
limit = array_limit_for(path)
|
|
75
|
+
value.first(limit).each_with_index.map do |child, index|
|
|
63
76
|
child_path = path ? "#{path}[#{index}]" : "[#{index}]"
|
|
64
77
|
deep_sanitize(child, depth: depth + 1, path: child_path, redacted: redacted)
|
|
65
78
|
end
|
data/lib/auditea/version.rb
CHANGED
data/lib/auditea.rb
CHANGED
|
@@ -28,8 +28,12 @@ require_relative "auditea/transport/http"
|
|
|
28
28
|
require_relative "auditea/transport/queue"
|
|
29
29
|
require_relative "auditea/client"
|
|
30
30
|
require_relative "auditea/log"
|
|
31
|
+
require_relative "auditea/inventory"
|
|
32
|
+
require_relative "auditea/host_inventory"
|
|
31
33
|
require_relative "auditea/instrumentation/request"
|
|
32
34
|
require_relative "auditea/instrumentation/exception"
|
|
35
|
+
require_relative "auditea/instrumentation/structural_request"
|
|
36
|
+
require_relative "auditea/instrumentation/error_subscriber"
|
|
33
37
|
require_relative "auditea/instrumentation/logger_adapter"
|
|
34
38
|
require_relative "auditea/instrumentation/active_job"
|
|
35
39
|
require_relative "auditea/middleware/context"
|
|
@@ -54,6 +58,8 @@ module Auditea
|
|
|
54
58
|
@configuration = Configuration.new
|
|
55
59
|
@client&.shutdown
|
|
56
60
|
@client = nil
|
|
61
|
+
Inventory.reset!
|
|
62
|
+
HostInventory.reset!
|
|
57
63
|
end
|
|
58
64
|
|
|
59
65
|
def client
|
|
@@ -90,6 +96,25 @@ module Auditea
|
|
|
90
96
|
# Alias kept for legacy familiarity (KEEP CONCEPT).
|
|
91
97
|
alias track capture
|
|
92
98
|
|
|
99
|
+
# First-class exception capture → action `exception.raised` / category `error`.
|
|
100
|
+
# Rebuilds structured exception evidence (type, message, frames, handled, mechanism).
|
|
101
|
+
# Uses CurrentContext when present; sanitizes like normal events; fail-open by default.
|
|
102
|
+
def capture_exception(error, handled: false, mechanism: nil, severity: "error",
|
|
103
|
+
context: nil, metadata: nil, evidence: nil)
|
|
104
|
+
Instrumentation::Exception.capture(
|
|
105
|
+
error,
|
|
106
|
+
handled: handled,
|
|
107
|
+
mechanism: mechanism || "auditea.capture_exception",
|
|
108
|
+
severity: severity,
|
|
109
|
+
context: context,
|
|
110
|
+
metadata: (metadata || {}).merge("sdk_event_kind" => "explicit"),
|
|
111
|
+
evidence: evidence,
|
|
112
|
+
claim: true
|
|
113
|
+
)
|
|
114
|
+
rescue StandardError => error
|
|
115
|
+
handle_error(error)
|
|
116
|
+
end
|
|
117
|
+
|
|
93
118
|
# Structured application log as a first-class event (not Rails.logger auto-forward).
|
|
94
119
|
def log(level, message, context: {}, metadata: {})
|
|
95
120
|
Log.record(level, message, context: context, metadata: metadata)
|
|
@@ -97,6 +122,26 @@ module Auditea
|
|
|
97
122
|
handle_error(error)
|
|
98
123
|
end
|
|
99
124
|
|
|
125
|
+
# Opt-in runtime + dependency inventory (`runtime.inventory`). Default off.
|
|
126
|
+
# Does not raise on collection failure unless raise_errors is enabled.
|
|
127
|
+
def capture_runtime_inventory!(force: true)
|
|
128
|
+
Inventory.capture!(force: force)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Bounded refresh for long-lived processes (~daily when unchanged).
|
|
132
|
+
def maybe_capture_runtime_inventory!
|
|
133
|
+
Inventory.maybe_capture!
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Opt-in host/platform inventory (`host.inventory`). Default off.
|
|
137
|
+
def capture_host_inventory!(force: true)
|
|
138
|
+
HostInventory.capture!(force: force)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def maybe_capture_host_inventory!
|
|
142
|
+
HostInventory.maybe_capture!
|
|
143
|
+
end
|
|
144
|
+
|
|
100
145
|
def flush(timeout: 5)
|
|
101
146
|
client.flush(timeout: timeout)
|
|
102
147
|
rescue StandardError => error
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: auditea
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.0.beta.
|
|
4
|
+
version: 0.1.0.beta.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- AudiTea
|
|
@@ -9,8 +9,8 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
|
-
description: Capture business events, Rails request evidence, exceptions,
|
|
13
|
-
logs for AudiTea /api/v1.
|
|
12
|
+
description: Capture business events, Rails request evidence, exceptions, structured
|
|
13
|
+
logs, and optional runtime/host inventory for AudiTea /api/v1.
|
|
14
14
|
email:
|
|
15
15
|
- opensource@auditea.app
|
|
16
16
|
executables: []
|
|
@@ -30,10 +30,22 @@ files:
|
|
|
30
30
|
- lib/auditea/configuration.rb
|
|
31
31
|
- lib/auditea/current_context.rb
|
|
32
32
|
- lib/auditea/event_builder.rb
|
|
33
|
+
- lib/auditea/host_inventory.rb
|
|
34
|
+
- lib/auditea/host_inventory/background_refresh.rb
|
|
35
|
+
- lib/auditea/host_inventory/collector.rb
|
|
36
|
+
- lib/auditea/host_inventory/emitter.rb
|
|
33
37
|
- lib/auditea/instrumentation/active_job.rb
|
|
38
|
+
- lib/auditea/instrumentation/error_subscriber.rb
|
|
34
39
|
- lib/auditea/instrumentation/exception.rb
|
|
35
40
|
- lib/auditea/instrumentation/logger_adapter.rb
|
|
36
41
|
- lib/auditea/instrumentation/request.rb
|
|
42
|
+
- lib/auditea/instrumentation/structural_request.rb
|
|
43
|
+
- lib/auditea/inventory.rb
|
|
44
|
+
- lib/auditea/inventory/background_refresh.rb
|
|
45
|
+
- lib/auditea/inventory/collector.rb
|
|
46
|
+
- lib/auditea/inventory/digest.rb
|
|
47
|
+
- lib/auditea/inventory/emitter.rb
|
|
48
|
+
- lib/auditea/inventory/string_bound.rb
|
|
37
49
|
- lib/auditea/log.rb
|
|
38
50
|
- lib/auditea/middleware/context.rb
|
|
39
51
|
- lib/auditea/railtie.rb
|