auditea 0.1.0.beta.5 → 0.1.0.beta.6
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 +9 -1
- data/README.md +2 -2
- data/RELEASE.md +5 -5
- data/lib/auditea/host_inventory/windows_components.rb +12 -1
- data/lib/auditea/host_inventory/windows_services.rb +103 -0
- data/lib/auditea/inventory/components.rb +20 -0
- data/lib/auditea/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a53fe361e4064b6efcdc0102f09a27a356f76d116b88b531774e93e1ffaafeb6
|
|
4
|
+
data.tar.gz: be811c131585c9ba97cdf4cef22c57309d293af0709b1b65ef756a44602d50e3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 270fabedbb574108c32286ae01149d38e23ad17c3e28fd65abaf924d77f9244172807225e0e0c013a59551514ff82372ec1134ef75ad02b16c605cd87198e3dd
|
|
7
|
+
data.tar.gz: e42afa22d152260e21f3826075b2b2b1a07a0bedf78541bc320020b439dc23cb601f853e790606f847c124879c9331e1a59ef97e17593a387294cb2d10cc7bce
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.0.beta.
|
|
3
|
+
## 0.1.0.beta.6 (unreleased)
|
|
4
|
+
|
|
5
|
+
### Windows host inventory (additive)
|
|
6
|
+
|
|
7
|
+
- Conservative Windows OS edition detection for host inventory posture (`WindowsOs` consumers on the server)
|
|
8
|
+
- Conservative read-only Windows Apache service discovery (`AUD-SDK-APACHE-SERVICE01`) — service-name observation only; never claims Apache is absent when not observed
|
|
9
|
+
- Windows smoke helpers under `examples/` for controlled field validation (IRCM native smoke deferred to a separate scheduled run)
|
|
10
|
+
|
|
11
|
+
## 0.1.0.beta.5
|
|
4
12
|
|
|
5
13
|
### SDK readiness diagnostic
|
|
6
14
|
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Ruby / Rails SDK for [AudiTea](https://github.com/reznis/AudiTea) event collection.
|
|
4
4
|
|
|
5
|
-
Version: **0.1.0.beta.
|
|
5
|
+
Version: **0.1.0.beta.6** (published)
|
|
6
6
|
|
|
7
7
|
Captures durable technical and business evidence. AudiTea **server** owns taxonomy, dictionary, human labels, vulnerability matching, correlation, and reinterpretation.
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ The SDK does **not** perform vulnerability scanning, OS package/CVE inventory, d
|
|
|
12
12
|
|
|
13
13
|
```ruby
|
|
14
14
|
# Gemfile
|
|
15
|
-
gem "auditea", "0.1.0.beta.
|
|
15
|
+
gem "auditea", "0.1.0.beta.6"
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
```bash
|
data/RELEASE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Release: auditea 0.1.0.beta.
|
|
1
|
+
# Release: auditea 0.1.0.beta.6
|
|
2
2
|
|
|
3
3
|
Authoritative runbook for controlled beta publication of the Ruby SDK.
|
|
4
4
|
|
|
@@ -169,10 +169,10 @@ PR status may say `READY FOR HUMAN PUBLICATION REVIEW` while still listing exter
|
|
|
169
169
|
```bash
|
|
170
170
|
git checkout main
|
|
171
171
|
git pull
|
|
172
|
-
# confirm VERSION and CHANGELOG still say 0.1.0.beta.
|
|
172
|
+
# confirm VERSION and CHANGELOG still say 0.1.0.beta.6
|
|
173
173
|
gem build auditea.gemspec
|
|
174
174
|
# inspect package once more
|
|
175
|
-
gem push auditea-0.1.0.beta.
|
|
175
|
+
gem push auditea-0.1.0.beta.6.gem
|
|
176
176
|
```
|
|
177
177
|
|
|
178
178
|
Then mark CHANGELOG released with the publication date in a follow-up commit.
|
|
@@ -182,9 +182,9 @@ Then mark CHANGELOG released with the publication date in a follow-up commit.
|
|
|
182
182
|
## Post-publication verification
|
|
183
183
|
|
|
184
184
|
```bash
|
|
185
|
-
gem install auditea -v 0.1.0.beta.
|
|
185
|
+
gem install auditea -v 0.1.0.beta.6 --pre
|
|
186
186
|
ruby -rauditea -e 'puts Auditea::VERSION'
|
|
187
|
-
# expect: 0.1.0.beta.
|
|
187
|
+
# expect: 0.1.0.beta.6
|
|
188
188
|
```
|
|
189
189
|
|
|
190
190
|
Prefer installing the RubyGems artifact (not a git branch) into the controlled TEST app.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "timeout"
|
|
4
4
|
require_relative "../inventory/components"
|
|
5
|
+
require_relative "windows_services"
|
|
5
6
|
|
|
6
7
|
module Auditea
|
|
7
8
|
module HostInventory
|
|
@@ -97,7 +98,17 @@ module Auditea
|
|
|
97
98
|
)
|
|
98
99
|
end
|
|
99
100
|
end
|
|
100
|
-
|
|
101
|
+
|
|
102
|
+
components = seen.values
|
|
103
|
+
# Service discovery is a fallback for products the uninstall registry
|
|
104
|
+
# misses (manually unpacked ZIP, service-only install). When uninstall
|
|
105
|
+
# already proves an Apache HTTP Server installation (with a version), do
|
|
106
|
+
# not add a version-less service observation for the same installation.
|
|
107
|
+
unless components.any? { |c| c["product"] == "Apache HTTP Server" }
|
|
108
|
+
components += WindowsServices.collect(deadline: deadline)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
components
|
|
101
112
|
end
|
|
102
113
|
|
|
103
114
|
def parse_uninstall(output)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "windows_components"
|
|
4
|
+
|
|
5
|
+
module Auditea
|
|
6
|
+
module HostInventory
|
|
7
|
+
# Conservative read-only Windows service discovery. Proves Apache HTTP
|
|
8
|
+
# Server identity from trusted service metadata WITHOUT executing the
|
|
9
|
+
# binary, trusting caller paths, or inferring a version from a directory
|
|
10
|
+
# name. A proven-but-unversioned service is reported with version nil so
|
|
11
|
+
# the server can show "Not reported" rather than hiding a known install.
|
|
12
|
+
#
|
|
13
|
+
# Reads only HKLM\SYSTEM\CurrentControlSet\Services (a single, non-WOW64-
|
|
14
|
+
# redirected view). Reuses WindowsComponents.run_reg so the read remains
|
|
15
|
+
# bounded, timeout-protected, fail-open, and read-only.
|
|
16
|
+
module WindowsServices
|
|
17
|
+
SERVICES_KEY = 'HKLM\SYSTEM\CurrentControlSet\Services'
|
|
18
|
+
# Explicit generic Windows software channel (same as uninstall detection).
|
|
19
|
+
WINDOWS_PACKAGE = { "ecosystem" => "windows" }.freeze
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
# Single deterministic read-only services query.
|
|
24
|
+
def service_queries
|
|
25
|
+
[["query", SERVICES_KEY, "/s"]]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def collect(deadline:)
|
|
29
|
+
components = []
|
|
30
|
+
apache_emitted = false
|
|
31
|
+
|
|
32
|
+
service_queries.each do |argv|
|
|
33
|
+
remaining = deadline - WindowsComponents.monotonic_now
|
|
34
|
+
break if remaining <= 0
|
|
35
|
+
|
|
36
|
+
output = WindowsComponents.run_reg(
|
|
37
|
+
argv,
|
|
38
|
+
timeout: [WindowsComponents::PER_COMMAND_TIMEOUT_SECONDS, remaining].min
|
|
39
|
+
)
|
|
40
|
+
next if output.nil?
|
|
41
|
+
|
|
42
|
+
parse_services(output).each do |(display_name, image_path)|
|
|
43
|
+
next unless apache_http_server_service?(display_name, image_path)
|
|
44
|
+
next if apache_emitted
|
|
45
|
+
|
|
46
|
+
apache_emitted = true
|
|
47
|
+
components << Inventory::Components.build_without_version(
|
|
48
|
+
product: "Apache HTTP Server",
|
|
49
|
+
scope: "reverse_proxy",
|
|
50
|
+
observation_method: "windows.service_registry",
|
|
51
|
+
package: WINDOWS_PACKAGE,
|
|
52
|
+
evidence: { "command" => "reg.exe" }
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
components
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Parses `reg query ...\Services /s` output into [display_name, image_path]
|
|
61
|
+
# pairs, one per service subkey. Ignores values we do not need; never
|
|
62
|
+
# returns raw output or paths as evidence.
|
|
63
|
+
def parse_services(output)
|
|
64
|
+
services = []
|
|
65
|
+
display_name = nil
|
|
66
|
+
image_path = nil
|
|
67
|
+
output.to_s.each_line do |line|
|
|
68
|
+
stripped = line.strip
|
|
69
|
+
if stripped.start_with?("HKEY_")
|
|
70
|
+
services << [display_name, image_path] if display_name || image_path
|
|
71
|
+
display_name = nil
|
|
72
|
+
image_path = nil
|
|
73
|
+
next
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
if (match = stripped.match(/\ADisplayName\s+REG_\S+\s+(.+)\z/i))
|
|
77
|
+
display_name = match[1].to_s.strip
|
|
78
|
+
elsif (match = stripped.match(/\AImagePath\s+REG_\S+\s+(.+)\z/i))
|
|
79
|
+
image_path = match[1].to_s.strip
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
services << [display_name, image_path] if display_name || image_path
|
|
83
|
+
services
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Proves Apache HTTP Server identity from trusted service metadata. An
|
|
87
|
+
# `httpd.exe` ImagePath is the unambiguous Apache HTTP Server binary;
|
|
88
|
+
# a conservative DisplayName match covers the common "Apache2.4" /
|
|
89
|
+
# "Apache HTTP Server" names while rejecting unrelated Apache projects
|
|
90
|
+
# (Tomcat, Cassandra, DS, ...).
|
|
91
|
+
def apache_http_server_service?(display_name, image_path)
|
|
92
|
+
path = image_path.to_s.strip
|
|
93
|
+
return true if path.match?(%r{(?:\A|[\\/])httpd\.exe(?:["'\s]|\z)}i)
|
|
94
|
+
|
|
95
|
+
name = display_name.to_s.strip
|
|
96
|
+
return false if name.empty?
|
|
97
|
+
return false if name.match?(/\btomcat\b/i)
|
|
98
|
+
|
|
99
|
+
name.match?(/\Aapache(?:\s+http\s+server(?:\s+\d+(?:\.\d+)*)?|\s*\d+(?:\.\d+)*)?\s*\z/i)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -41,6 +41,26 @@ module Auditea
|
|
|
41
41
|
entry
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
# A component whose identity is proven but whose version is not available
|
|
45
|
+
# (e.g. an Apache HTTP Server proven as a Windows service, where no
|
|
46
|
+
# authoritative version metadata exists). Emits the component WITHOUT a
|
|
47
|
+
# `version` key so the server can truthfully render "Not reported" rather
|
|
48
|
+
# than inventing or silently dropping the known installed product.
|
|
49
|
+
def build_without_version(product:, scope:, observation_method:, package: nil, evidence: nil)
|
|
50
|
+
return nil unless SCOPES.include?(scope.to_s)
|
|
51
|
+
return nil if blank?(product)
|
|
52
|
+
|
|
53
|
+
entry = {
|
|
54
|
+
"product" => truncate(product, MAX_PRODUCT),
|
|
55
|
+
"scope" => scope.to_s,
|
|
56
|
+
"observation_method" => truncate(observation_method, MAX_METHOD)
|
|
57
|
+
}
|
|
58
|
+
entry["package"] = package if package.is_a?(Hash) && !package.empty?
|
|
59
|
+
sanitized = sanitize_evidence(evidence)
|
|
60
|
+
entry["evidence"] = sanitized if sanitized
|
|
61
|
+
entry
|
|
62
|
+
end
|
|
63
|
+
|
|
44
64
|
def sanitize_evidence(value)
|
|
45
65
|
return nil unless value.is_a?(Hash)
|
|
46
66
|
|
data/lib/auditea/version.rb
CHANGED
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.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- AudiTea
|
|
@@ -39,6 +39,7 @@ files:
|
|
|
39
39
|
- lib/auditea/host_inventory/component_detectors.rb
|
|
40
40
|
- lib/auditea/host_inventory/emitter.rb
|
|
41
41
|
- lib/auditea/host_inventory/windows_components.rb
|
|
42
|
+
- lib/auditea/host_inventory/windows_services.rb
|
|
42
43
|
- lib/auditea/instrumentation/active_job.rb
|
|
43
44
|
- lib/auditea/instrumentation/error_subscriber.rb
|
|
44
45
|
- lib/auditea/instrumentation/exception.rb
|