endpoint_security 0.1.0-universal-darwin
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/.rubocop.yml +44 -0
- data/LICENSE.txt +21 -0
- data/README.md +114 -0
- data/Rakefile +170 -0
- data/codegen/ast.rb +67 -0
- data/codegen/emit_c.rb +68 -0
- data/codegen/emit_ruby.rb +128 -0
- data/codegen/ir.rb +193 -0
- data/codegen/overlay/availability.yml +158 -0
- data/codegen/overlay/version_map.yml +92 -0
- data/codegen/run.rb +80 -0
- data/codegen/snapshots/26.5.json +5828 -0
- data/examples/eslogger_clone.rb +5 -0
- data/examples/execblock.rb +16 -0
- data/examples/filemon.rb +10 -0
- data/examples/procmon.rb +13 -0
- data/ext/endpoint_security/client.c +795 -0
- data/ext/endpoint_security/client.h +88 -0
- data/ext/endpoint_security/endpoint_security.c +17 -0
- data/ext/endpoint_security/extconf.rb +37 -0
- data/ext/endpoint_security/field.c +719 -0
- data/ext/endpoint_security/field.h +42 -0
- data/ext/endpoint_security/generated/es_schema.c +1233 -0
- data/ext/endpoint_security/message.c +426 -0
- data/ext/endpoint_security/message.h +13 -0
- data/ext/endpoint_security/mute.c +291 -0
- data/ext/endpoint_security/mute.h +8 -0
- data/ext/endpoint_security/queue.c +124 -0
- data/ext/endpoint_security/queue.h +46 -0
- data/ext/endpoint_security/watchdog.c +243 -0
- data/lib/endpoint_security/availability.rb +30 -0
- data/lib/endpoint_security/client.rb +280 -0
- data/lib/endpoint_security/diagnostics.rb +26 -0
- data/lib/endpoint_security/doctor.rb +35 -0
- data/lib/endpoint_security/errors.rb +42 -0
- data/lib/endpoint_security/generated/availability.rb +169 -0
- data/lib/endpoint_security/generated/enums.rb +409 -0
- data/lib/endpoint_security/generated/event_types.rb +520 -0
- data/lib/endpoint_security/message.rb +29 -0
- data/lib/endpoint_security/object_model.rb +170 -0
- data/lib/endpoint_security/recorder.rb +26 -0
- data/lib/endpoint_security/version.rb +6 -0
- data/lib/endpoint_security.rb +38 -0
- data/support/entitlements.plist +8 -0
- data/support/esmock/esmock.c +482 -0
- data/support/esmock/esmock.h +21 -0
- data/support/sign_ruby.sh +13 -0
- metadata +90 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EndpointSecurity
|
|
4
|
+
# Immutable copy of an Endpoint Security audit token.
|
|
5
|
+
AuditToken = Data.define(:pid, :pidversion, :ruid, :euid, :rgid, :egid, :asid, :auid)
|
|
6
|
+
# Immutable copy of a native +stat+ structure.
|
|
7
|
+
Stat = Data.define(
|
|
8
|
+
:dev, :ino, :mode, :nlink, :uid, :gid, :rdev, :size, :blocks, :block_size, :atime, :mtime, :ctime, :birthtime
|
|
9
|
+
)
|
|
10
|
+
# Immutable copy of a native +statfs+ structure.
|
|
11
|
+
Statfs = Data.define(
|
|
12
|
+
:block_size, :io_size, :blocks, :blocks_free, :blocks_available, :files, :files_free, :fsid, :owner,
|
|
13
|
+
:type, :flags, :subtype, :fs_type_name, :mount_path, :mount_source, :extended_flags
|
|
14
|
+
)
|
|
15
|
+
# Immutable copy of a native +attrlist+ structure.
|
|
16
|
+
Attrlist = Data.define(:bitmap_count, :reserved, :common, :volume, :directory, :file, :fork)
|
|
17
|
+
|
|
18
|
+
# Code-signing flag masks from +kern/cs_blobs.h+.
|
|
19
|
+
CS_FLAG_MASKS = {
|
|
20
|
+
valid: 0x00000001, adhoc: 0x00000002, get_task_allow: 0x00000004, installer: 0x00000008,
|
|
21
|
+
hard: 0x00000100, kill: 0x00000200, restrict: 0x00000800, enforcement: 0x00001000,
|
|
22
|
+
require_lv: 0x00002000, runtime: 0x00010000, platform_binary: 0x04000000, signed: 0x20000000
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
# Code-signing status flags for a process.
|
|
26
|
+
CSFlags = Data.define(:value) do
|
|
27
|
+
# @return [Boolean] whether +flag+ is present
|
|
28
|
+
def include?(flag) = value.anybits?(CS_FLAG_MASKS.fetch(flag.to_sym))
|
|
29
|
+
|
|
30
|
+
CS_FLAG_MASKS.each_key { |flag| define_method("#{flag}?") { include?(flag) } }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Selected member of a tagged native union.
|
|
34
|
+
TaggedUnion = Data.define(:kind, :value) do
|
|
35
|
+
# @api private
|
|
36
|
+
# @return [TaggedUnion]
|
|
37
|
+
def self.__native_new(kind, value) = new(kind: kind, value: value)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Lazily loaded native array field.
|
|
41
|
+
class FieldArray
|
|
42
|
+
include Enumerable
|
|
43
|
+
|
|
44
|
+
# @api private
|
|
45
|
+
# @yieldreturn [Array] copied field values
|
|
46
|
+
def initialize(&loader)
|
|
47
|
+
@loader = loader
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Iterates over field values.
|
|
51
|
+
# @return [Enumerator, FieldArray]
|
|
52
|
+
def each(&)
|
|
53
|
+
return enum_for(__method__) unless block_given?
|
|
54
|
+
|
|
55
|
+
values.each(&)
|
|
56
|
+
self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @return [Object, nil] value at +index+
|
|
60
|
+
def [](index) = values[index]
|
|
61
|
+
|
|
62
|
+
# @return [Integer] number of values
|
|
63
|
+
def length = values.length
|
|
64
|
+
alias size length
|
|
65
|
+
|
|
66
|
+
# @return [Array] copied values
|
|
67
|
+
def to_a = values.dup
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def values = (@values ||= Array(@loader.call).freeze)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Lazy enumerable for exec arguments and environment entries.
|
|
75
|
+
class ExecArgs < FieldArray; end
|
|
76
|
+
|
|
77
|
+
[AuditToken, Stat, Statfs, Attrlist].each do |value_class|
|
|
78
|
+
value_class.define_singleton_method(:__native_new) do |*values|
|
|
79
|
+
new(**members.zip(values).to_h)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Base for lazy views backed by a native message.
|
|
84
|
+
class NativeView
|
|
85
|
+
# @return [Hash]
|
|
86
|
+
def to_h
|
|
87
|
+
__field_names.to_h { |name| [name, deep_copy(public_send(name))] }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Resolves generated native fields lazily.
|
|
91
|
+
# @api private
|
|
92
|
+
def method_missing(name, ...)
|
|
93
|
+
field = name.to_s.delete_suffix("?")
|
|
94
|
+
predicate = name.to_s.end_with?("?")
|
|
95
|
+
field = "is_#{field}" if predicate && __field_names.include?(:"is_#{field}")
|
|
96
|
+
return super unless __field_names.include?(field.to_sym)
|
|
97
|
+
|
|
98
|
+
value = __read_field(field)
|
|
99
|
+
value.is_a?(Array) ? FieldArray.new { value } : value
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def respond_to_missing?(name, include_private = false)
|
|
103
|
+
field = name.to_s.delete_suffix("?")
|
|
104
|
+
__field_names.include?(field.to_sym) || __field_names.include?(:"is_#{field}") || super
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def deep_copy(value)
|
|
110
|
+
case value
|
|
111
|
+
when NativeView then value.to_h
|
|
112
|
+
when FieldArray, Array then value.map { |item| deep_copy(item) }
|
|
113
|
+
when Data then deep_copy(value.to_h)
|
|
114
|
+
when Hash then value.to_h { |key, item| [key, deep_copy(item)] }
|
|
115
|
+
when String
|
|
116
|
+
return value.dup if value.encoding != Encoding::BINARY && value.valid_encoding?
|
|
117
|
+
|
|
118
|
+
{ encoding: :base64, data: [value].pack("m0") }
|
|
119
|
+
else value
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Lazy view of an +es_process_t+.
|
|
125
|
+
class Process
|
|
126
|
+
# @return [Integer]
|
|
127
|
+
def pid = audit_token.pid
|
|
128
|
+
|
|
129
|
+
# @return [String]
|
|
130
|
+
def cdhash_hex = cdhash.unpack1("H*")
|
|
131
|
+
|
|
132
|
+
# @return [CSFlags] code-signing status flags
|
|
133
|
+
def codesigning_flags = CSFlags.new(value: __read_field("codesigning_flags"))
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Lazy view of an +es_file_t+.
|
|
137
|
+
class File
|
|
138
|
+
# Returns the possibly truncated path supplied by Endpoint Security.
|
|
139
|
+
# @return [String]
|
|
140
|
+
def path
|
|
141
|
+
if path_truncated? && __warn_on_truncated_path?
|
|
142
|
+
warn "Endpoint Security path is truncated; do not use it for authorization"
|
|
143
|
+
end
|
|
144
|
+
__read_field("path")
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Lazy view of an event-specific Endpoint Security structure.
|
|
149
|
+
class Event
|
|
150
|
+
# @return [ExecArgs] exec arguments
|
|
151
|
+
def args = (@args ||= ExecArgs.new { __exec_values(:args) })
|
|
152
|
+
|
|
153
|
+
# @return [ExecArgs] exec environment
|
|
154
|
+
def env = (@env ||= ExecArgs.new { __exec_values(:env) })
|
|
155
|
+
|
|
156
|
+
# @return [FieldArray] exec file descriptors
|
|
157
|
+
def fds = (@fds ||= FieldArray.new { __exec_values(:fds) })
|
|
158
|
+
|
|
159
|
+
# @return [Hash] deep copy of the event fields
|
|
160
|
+
def to_h
|
|
161
|
+
super.tap do |hash|
|
|
162
|
+
next unless __schema_name == "es_event_exec_t"
|
|
163
|
+
|
|
164
|
+
hash[:args] = args.to_a
|
|
165
|
+
hash[:env] = env.to_a
|
|
166
|
+
hash[:fds] = fds.map(&:to_h)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module EndpointSecurity
|
|
6
|
+
# Writes subscribed events as newline-delimited JSON.
|
|
7
|
+
class Recorder
|
|
8
|
+
# @param events [Array<Symbol>] events to record
|
|
9
|
+
# @param out [IO] destination for JSON lines
|
|
10
|
+
def initialize(events:, out: $stdout, **client_options)
|
|
11
|
+
@events = events
|
|
12
|
+
@out = out
|
|
13
|
+
@client_options = client_options
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Starts recording and blocks until the client stops.
|
|
17
|
+
# @return [Client]
|
|
18
|
+
def run
|
|
19
|
+
Client.open(**@client_options) do |client|
|
|
20
|
+
client.subscribe(@events)
|
|
21
|
+
@events.each { |event| client.on(event) { |message| @out.puts(JSON.generate(message.to_h)) } }
|
|
22
|
+
client.run
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "endpoint_security/version"
|
|
4
|
+
require_relative "endpoint_security/errors"
|
|
5
|
+
require_relative "endpoint_security/diagnostics"
|
|
6
|
+
require_relative "endpoint_security/generated/event_types"
|
|
7
|
+
require_relative "endpoint_security/generated/enums"
|
|
8
|
+
require_relative "endpoint_security/generated/availability"
|
|
9
|
+
require_relative "endpoint_security/availability"
|
|
10
|
+
require_relative "endpoint_security/endpoint_security"
|
|
11
|
+
require_relative "endpoint_security/object_model"
|
|
12
|
+
require_relative "endpoint_security/message"
|
|
13
|
+
require_relative "endpoint_security/client"
|
|
14
|
+
require_relative "endpoint_security/recorder"
|
|
15
|
+
|
|
16
|
+
# Ruby bindings for Apple's Endpoint Security API.
|
|
17
|
+
module EndpointSecurity
|
|
18
|
+
class << self
|
|
19
|
+
# Encoding used for +es_string_token_t+ values.
|
|
20
|
+
# @return [Symbol] +:utf8+ or +:binary+
|
|
21
|
+
attr_reader :string_encoding
|
|
22
|
+
|
|
23
|
+
# Selects the encoding used for native string tokens.
|
|
24
|
+
# @param value [Symbol] +:utf8+ or +:binary+
|
|
25
|
+
# @return [Symbol]
|
|
26
|
+
def string_encoding=(value)
|
|
27
|
+
value = value.to_sym
|
|
28
|
+
raise ArgumentError, "string_encoding must be :utf8 or :binary" unless %i[utf8 binary].include?(value)
|
|
29
|
+
|
|
30
|
+
@string_encoding = value
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
self.string_encoding = :utf8
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Short alias for {EndpointSecurity}.
|
|
38
|
+
ES = EndpointSecurity
|