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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +44 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +114 -0
  5. data/Rakefile +170 -0
  6. data/codegen/ast.rb +67 -0
  7. data/codegen/emit_c.rb +68 -0
  8. data/codegen/emit_ruby.rb +128 -0
  9. data/codegen/ir.rb +193 -0
  10. data/codegen/overlay/availability.yml +158 -0
  11. data/codegen/overlay/version_map.yml +92 -0
  12. data/codegen/run.rb +80 -0
  13. data/codegen/snapshots/26.5.json +5828 -0
  14. data/examples/eslogger_clone.rb +5 -0
  15. data/examples/execblock.rb +16 -0
  16. data/examples/filemon.rb +10 -0
  17. data/examples/procmon.rb +13 -0
  18. data/ext/endpoint_security/client.c +795 -0
  19. data/ext/endpoint_security/client.h +88 -0
  20. data/ext/endpoint_security/endpoint_security.c +17 -0
  21. data/ext/endpoint_security/extconf.rb +37 -0
  22. data/ext/endpoint_security/field.c +719 -0
  23. data/ext/endpoint_security/field.h +42 -0
  24. data/ext/endpoint_security/generated/es_schema.c +1233 -0
  25. data/ext/endpoint_security/message.c +426 -0
  26. data/ext/endpoint_security/message.h +13 -0
  27. data/ext/endpoint_security/mute.c +291 -0
  28. data/ext/endpoint_security/mute.h +8 -0
  29. data/ext/endpoint_security/queue.c +124 -0
  30. data/ext/endpoint_security/queue.h +46 -0
  31. data/ext/endpoint_security/watchdog.c +243 -0
  32. data/lib/endpoint_security/availability.rb +30 -0
  33. data/lib/endpoint_security/client.rb +280 -0
  34. data/lib/endpoint_security/diagnostics.rb +26 -0
  35. data/lib/endpoint_security/doctor.rb +35 -0
  36. data/lib/endpoint_security/errors.rb +42 -0
  37. data/lib/endpoint_security/generated/availability.rb +169 -0
  38. data/lib/endpoint_security/generated/enums.rb +409 -0
  39. data/lib/endpoint_security/generated/event_types.rb +520 -0
  40. data/lib/endpoint_security/message.rb +29 -0
  41. data/lib/endpoint_security/object_model.rb +170 -0
  42. data/lib/endpoint_security/recorder.rb +26 -0
  43. data/lib/endpoint_security/version.rb +6 -0
  44. data/lib/endpoint_security.rb +38 -0
  45. data/support/entitlements.plist +8 -0
  46. data/support/esmock/esmock.c +482 -0
  47. data/support/esmock/esmock.h +21 -0
  48. data/support/sign_ruby.sh +13 -0
  49. 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,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EndpointSecurity
4
+ # Library version.
5
+ VERSION = "0.1.0"
6
+ 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
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>com.apple.developer.endpoint-security.client</key>
6
+ <true/>
7
+ </dict>
8
+ </plist>