macos-artifacts 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +62 -0
- data/LICENSE.txt +21 -0
- data/README.md +90 -0
- data/Rakefile +4 -0
- data/lib/macos/artifacts/apps.rb +87 -0
- data/lib/macos/artifacts/files.rb +279 -0
- data/lib/macos/artifacts/help.rb +69 -0
- data/lib/macos/artifacts/state.rb +187 -0
- data/lib/macos/artifacts/version.rb +7 -0
- data/lib/macos/artifacts.rb +145 -0
- data/macos-artifacts.gemspec +35 -0
- data/sig/macos/artifacts.rbs +6 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bef9e063059ed8e1aa054b732e4fa670b438794331fd84c136088ddb3744ed79
|
4
|
+
data.tar.gz: 064d53b36446e2101a2d59479240d10c462ab22840627f6818ef9832d02d0948
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3187009f4306d5ad8c41e496e0ce76c76a0e74741a66fb8e1cd9910a98b9c34fd7160a82f6d24e019d9d3d0a28844cc6e76af0a3c206ba65b57447a7d89130c9
|
7
|
+
data.tar.gz: 89d75dfe2fae0b58649ccc28fd27088ee3b37d6dd5f531c38fa568f95872730347dc41dad650c61c0909879effc118930f5b762d4bddf9ef2d1462e24a66df93
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
|
4
|
+
---
|
5
|
+
## version 0.6.0
|
6
|
+
- Add: `help` to output options. User `Macos::Artifacts::Help::options`
|
7
|
+
|
8
|
+
---
|
9
|
+
## version 0.5.2
|
10
|
+
- Add: `state` list of open network connections
|
11
|
+
- Add: `files` directory output for /private/tmp
|
12
|
+
- Add: `files` directory output for /Users/Shared
|
13
|
+
|
14
|
+
---
|
15
|
+
## version 0.5.0
|
16
|
+
- Add: `apps` module
|
17
|
+
|
18
|
+
|
19
|
+
---
|
20
|
+
## version 0.4.3
|
21
|
+
- Add: processCPU to `state` module
|
22
|
+
- Add: processMemoery to `state` module
|
23
|
+
|
24
|
+
|
25
|
+
---
|
26
|
+
## version 0.4.2
|
27
|
+
- Add: crontab to `files` module
|
28
|
+
- Add: hosts file to `files` module
|
29
|
+
|
30
|
+
|
31
|
+
---
|
32
|
+
## version 0.4.1
|
33
|
+
- Add: Preference files for system and users
|
34
|
+
|
35
|
+
---
|
36
|
+
## version 0.4.0
|
37
|
+
- UPDATE: refactor
|
38
|
+
|
39
|
+
|
40
|
+
---
|
41
|
+
## version 0.3.0
|
42
|
+
- UPDATE: break `logs` module out to it's own file
|
43
|
+
|
44
|
+
|
45
|
+
---
|
46
|
+
## version 0.2.2
|
47
|
+
- UPDATE: break `state` module out to it's own file
|
48
|
+
|
49
|
+
|
50
|
+
---
|
51
|
+
## version 0.2.0
|
52
|
+
- ADD: `state` module
|
53
|
+
|
54
|
+
|
55
|
+
---
|
56
|
+
## version 0.1.5
|
57
|
+
- ADD: `logs` module
|
58
|
+
|
59
|
+
|
60
|
+
---
|
61
|
+
## version 0.1.0
|
62
|
+
- Initial release
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2023 nic scott
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# Macos::Artifacts
|
2
|
+
|
3
|
+
## Summary:
|
4
|
+
macOS Artifacts outpus a lot of information, from static facts about a machine (serial number, os verion), to outputs of file directories that may be usefull for discovery or reconnaissance (launchagenets and launchdaemons), to the current state of installed system extesnions.
|
5
|
+
|
6
|
+
Output is simple text making it able to be scraped up by an MDM or EDR solution to aid in an investiagtion. You can copy and save to a .yaml format if you want syntax highlighting, though output format is not strictly yaml.
|
7
|
+
|
8
|
+
---
|
9
|
+
## Installation:
|
10
|
+
|
11
|
+
`sudp gem install macos-artifacts`
|
12
|
+
|
13
|
+
---
|
14
|
+
## Usage:
|
15
|
+
|
16
|
+
`require 'macos/artifacts'`
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
For options:
|
21
|
+
|
22
|
+
Macos::Artifacts::Help::options
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
Macos::Artifacts::computerName
|
26
|
+
Macos::Artifacts::serial
|
27
|
+
Macos::Artifacts::version
|
28
|
+
Macos::Artifacts::build
|
29
|
+
Macos::Artifacts::kernel
|
30
|
+
Macos::Artifacts::modelName
|
31
|
+
Macos::Artifacts::modelID
|
32
|
+
Macos::Artifacts::chip
|
33
|
+
Macos::Artifacts::architecture
|
34
|
+
Macos::Artifacts::memory
|
35
|
+
Macos::Artifacts::hardwareUID
|
36
|
+
Macos::Artifacts::publicIP
|
37
|
+
Macos::Artifacts::privateIP
|
38
|
+
Macos::Artifacts::sipStatus
|
39
|
+
Macos::Artifacts::filevaultStatus
|
40
|
+
Macos::Artifacts::firewallStatus
|
41
|
+
Macos::Artifacts::screenlockStatus
|
42
|
+
Macos::Artifacts::lockStatus
|
43
|
+
Macos::Artifacts::softwareUpdates
|
44
|
+
Macos::Artifacts::Files::systemLaunchAgents
|
45
|
+
Macos::Artifacts::Files::systemLaunchDaemons
|
46
|
+
Macos::Artifacts::Files::userLaunchAgents
|
47
|
+
Macos::Artifacts::Files::listUsersAccountDirectory
|
48
|
+
Macos::Artifacts::Files::systemApplicationSupport
|
49
|
+
Macos::Artifacts::Files::userApplicationSupport
|
50
|
+
Macos::Artifacts::Files::libraryPreferences
|
51
|
+
Macos::Artifacts::Files::userLibraryPreferences
|
52
|
+
Macos::Artifacts::Files::cronTabs
|
53
|
+
Macos::Artifacts::Files::etcHosts
|
54
|
+
Macos::Artifacts::State::users
|
55
|
+
Macos::Artifacts::State::adminUsers
|
56
|
+
Macos::Artifacts::State::systemExtensions
|
57
|
+
Macos::Artifacts::State::processCPU
|
58
|
+
Macos::Artifacts::State::processMemory
|
59
|
+
```
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
```yaml
|
64
|
+
#Example output of all Macos::Artifacts commands
|
65
|
+
Host Name: nics-mac
|
66
|
+
Serial: X57CLJT7CV
|
67
|
+
Version: 13.5.1
|
68
|
+
Build: 22F82
|
69
|
+
Kernel: 22.5.0
|
70
|
+
Model Name: MacBook Pro
|
71
|
+
Model ID: MacBookPro18,3
|
72
|
+
Chip: Apple M1 Pro
|
73
|
+
Architecture: arm64
|
74
|
+
Memory: 32 GB
|
75
|
+
Hardware UID: 6AE03961-13A4-5418-BA2A-87FF9075FA91
|
76
|
+
Public IP: 172.76.37.139
|
77
|
+
Private IP: 192.68.68.68
|
78
|
+
SIP Status: enabled
|
79
|
+
FireVault Status: On
|
80
|
+
Firewall Status: Off
|
81
|
+
Screen Lock Status: screenLock delay is immediate
|
82
|
+
Activation Lock Status: Disabled
|
83
|
+
Software Updates:
|
84
|
+
Auto Updates: 1
|
85
|
+
Auto Download: 1
|
86
|
+
Auto Install: 1
|
87
|
+
Install Config Data: 1
|
88
|
+
Install Critical Updates: 1
|
89
|
+
```
|
90
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Macos
|
4
|
+
module Artifacts
|
5
|
+
module Apps
|
6
|
+
def self.applications
|
7
|
+
$applicationsPath = "/Applications"
|
8
|
+
$applicationsDirectory = Dir.entries("#{$applicationsPath}")
|
9
|
+
puts "Applications Folder:"
|
10
|
+
$applicationsDirectory.sort!.each do | filename |
|
11
|
+
if ! filename.start_with?(".")
|
12
|
+
if File.extname(filename) == ".app"
|
13
|
+
plist = CFPropertyList::List.new(:file => "#{$applicationsPath}/#{filename}/Contents/Info.plist")
|
14
|
+
data = CFPropertyList.native_types(plist.value)
|
15
|
+
data.each do |k,v|
|
16
|
+
if k == "CFBundleShortVersionString"
|
17
|
+
puts " #{$applicationsPath}/#{filename}: #{v}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
else
|
21
|
+
if File.directory?("#{$applicationsPath}/#{filename}")
|
22
|
+
puts " #{$applicationsPath}/#{filename}:"
|
23
|
+
subpath = Dir.entries("#{$applicationsPath}/#{filename}")
|
24
|
+
subpath.each do |subdirapp|
|
25
|
+
if ! subdirapp.start_with?(".")
|
26
|
+
if File.extname(subdirapp) == ".app"
|
27
|
+
plist = CFPropertyList::List.new(:file => "#{$applicationsPath}/#{filename}/#{subdirapp}/Contents/Info.plist")
|
28
|
+
data = CFPropertyList.native_types(plist.value)
|
29
|
+
data.each do |k,v|
|
30
|
+
if k == "CFBundleShortVersionString"
|
31
|
+
puts " #{$applicationsPath}/#{filename}/#{subdirapp}: #{v}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.packagesReceipts
|
44
|
+
pkgReceipts = `pkgutil --packages`.split("\n")
|
45
|
+
pkgReceipts.sort!
|
46
|
+
puts "Package Receipts: "
|
47
|
+
|
48
|
+
pkgReceipts.each do |pkg|
|
49
|
+
puts " #{pkg.strip}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.installHistory
|
54
|
+
history = `system_profiler SPInstallHistoryDataType `.split("\n")
|
55
|
+
history.shift
|
56
|
+
history.shift
|
57
|
+
puts "Application Install History:"
|
58
|
+
history.each do |item|
|
59
|
+
item = item.strip
|
60
|
+
if ! item.empty?
|
61
|
+
if item.start_with?(/^Version:/)
|
62
|
+
puts " #{item}"
|
63
|
+
elsif item.start_with?(/^Source:/)
|
64
|
+
puts " #{item}"
|
65
|
+
elsif item.start_with?(/^Install Date:/)
|
66
|
+
puts " #{item}"
|
67
|
+
else
|
68
|
+
puts " #{item}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.appInstallLocations
|
75
|
+
history = `mdfind "kMDItemKind == Application"`.split("\n")
|
76
|
+
puts "Application Install Locations:"
|
77
|
+
history.each do |item|
|
78
|
+
if ! item.start_with?("/System")
|
79
|
+
puts " #{item.strip}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,279 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$currentUser = ENV['USER']
|
4
|
+
|
5
|
+
module Macos
|
6
|
+
module Artifacts
|
7
|
+
module Files
|
8
|
+
|
9
|
+
def self.systemLaunchAgents
|
10
|
+
$systemLaunchAgentsPath = "/Library/LaunchAgents"
|
11
|
+
$launchAgentDir = Dir.entries("#{$systemLaunchAgentsPath}")
|
12
|
+
puts "System Launchagents:"
|
13
|
+
$launchAgentDir.each do | filename |
|
14
|
+
if filename != "." && filename != ".."
|
15
|
+
puts " #{$systemLaunchAgentsPath}/#{filename}"
|
16
|
+
plist = CFPropertyList::List.new(:file => "#{$systemLaunchAgentsPath}/#{filename}")
|
17
|
+
data = CFPropertyList.native_types(plist.value)
|
18
|
+
data.each do |k,v|
|
19
|
+
puts " #{k}: #{v}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.systemLaunchDaemons
|
26
|
+
$systemLaunchAgentsPath = "/Library/LaunchDaemons"
|
27
|
+
$launchAgentDir = Dir.entries("#{$systemLaunchAgentsPath}")
|
28
|
+
puts "System LaunchDaemons:"
|
29
|
+
$launchAgentDir.each do | filename |
|
30
|
+
if filename != "." && filename != ".."
|
31
|
+
puts " #{$systemLaunchAgentsPath}/#{filename}"
|
32
|
+
plist = CFPropertyList::List.new(:file => "#{$systemLaunchAgentsPath}/#{filename}")
|
33
|
+
data = CFPropertyList.native_types(plist.value)
|
34
|
+
data.each do |k,v|
|
35
|
+
puts " #{k}: #{v}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.userLaunchAgents
|
42
|
+
userArray = []
|
43
|
+
Dir.entries("/Users").each do |username|
|
44
|
+
if !username.start_with?(".")
|
45
|
+
if username != "Shared" and username != "Guest"
|
46
|
+
userArray.push("#{username}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
userArray.each do |username|
|
52
|
+
userLaunchAgentsPath = "/Users/#{username}/Library/LaunchAgents"
|
53
|
+
if Dir.exist?("#{userLaunchAgentsPath}")
|
54
|
+
launchAgentDir = Dir.entries("#{userLaunchAgentsPath}")
|
55
|
+
puts "#{username} LaunchAgents:"
|
56
|
+
launchAgentDir.each do | filename |
|
57
|
+
if filename != "." && filename != ".." && filename != ".DS_Store"
|
58
|
+
puts " #{userLaunchAgentsPath}/#{filename}"
|
59
|
+
plist = CFPropertyList::List.new(:file => "#{userLaunchAgentsPath}/#{filename}")
|
60
|
+
data = CFPropertyList.native_types(plist.value)
|
61
|
+
data.each do |k,v|
|
62
|
+
puts " #{k}: #{v}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.listUsersAccountDirectory
|
72
|
+
$userHomeFolder = Dir.entries("/Users/#{$currentUser}")
|
73
|
+
fileArray = []
|
74
|
+
$userHomeFolder.each do | filename |
|
75
|
+
if filename != "." && filename != ".."
|
76
|
+
filePath = "/Users/#{$currentUser}/#{filename}"
|
77
|
+
fileArray.push("#{filePath}")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
fileArray = fileArray.sort
|
82
|
+
|
83
|
+
puts "Home Directory for #{$currentUser}:"
|
84
|
+
fileArray.each do |item|
|
85
|
+
if ! Dir.exist?("#{item}")
|
86
|
+
puts " #{item}"
|
87
|
+
elsif Dir.exist?("#{item}")
|
88
|
+
puts " #{item}:"
|
89
|
+
$subDir = Dir.entries("#{item}")
|
90
|
+
$subDir.each do |subfile|
|
91
|
+
if subfile != "." && subfile != ".."
|
92
|
+
puts " #{item}/#{subfile}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
else
|
96
|
+
puts "ERROR"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.userApplicationSupport
|
102
|
+
userApplicationSupport = "/Users/#{$currentUser}/Library/Application Support"
|
103
|
+
if Dir.exist?("#{userApplicationSupport}")
|
104
|
+
puts "#{$currentUser} Application Support:"
|
105
|
+
Dir.entries("#{userApplicationSupport}").each do | filename |
|
106
|
+
if filename != "." && filename != ".." && filename != ".DS_Store"
|
107
|
+
puts " #{userApplicationSupport}/#{filename}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.systemApplicationSupport
|
114
|
+
systemApplicationSupport = "/Library/Application Support"
|
115
|
+
if Dir.exist?("#{systemApplicationSupport}")
|
116
|
+
puts "System Application Support:"
|
117
|
+
Dir.entries("#{systemApplicationSupport}").each do | filename |
|
118
|
+
if filename != "." && filename != ".." && filename != ".DS_Store"
|
119
|
+
puts " #{systemApplicationSupport}/#{filename}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.libraryPreferences
|
126
|
+
systemApplicationSupport = "/Library/Preferences"
|
127
|
+
if Dir.exist?("#{systemApplicationSupport}")
|
128
|
+
puts "Library Preferences:"
|
129
|
+
Dir.entries("#{systemApplicationSupport}").each do | filename |
|
130
|
+
if filename != "." && filename != ".." && filename != ".DS_Store"
|
131
|
+
puts " #{systemApplicationSupport}/#{filename}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.userLibraryPreferences
|
138
|
+
userArray = []
|
139
|
+
Dir.entries("/Users").each do |username|
|
140
|
+
if !username.start_with?(".")
|
141
|
+
if username != "Shared" and username != "Guest"
|
142
|
+
userArray.push("#{username}")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
userArray.each do |username|
|
147
|
+
filesArray = []
|
148
|
+
userPreferences = "/Users/#{username}/Library/Preferences"
|
149
|
+
if Dir.exist?("#{userPreferences}")
|
150
|
+
puts "#{username} Preferences:"
|
151
|
+
Dir.entries("#{userPreferences}").each do | filename |
|
152
|
+
if filename != "." && filename != ".." && filename != ".DS_Store"
|
153
|
+
filesArray.push("#{userPreferences}/#{filename}")
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
filesArray.sort.each do |filename|
|
158
|
+
puts " #{filename}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.cronTabs
|
164
|
+
cronTab = `/usr/bin/crontab -l`.strip
|
165
|
+
puts "crontabs:"
|
166
|
+
if cronTab.empty?
|
167
|
+
puts " No current crontabs"
|
168
|
+
else
|
169
|
+
puts " #{cronTab}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.etcHosts
|
174
|
+
hostfiles = `cat /etc/hosts`.split("\n")
|
175
|
+
puts "Hosts File:"
|
176
|
+
hostfiles.each do |line|
|
177
|
+
puts " #{line}"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.usrLocal
|
182
|
+
path = "/usr/local"
|
183
|
+
|
184
|
+
puts "#{path} Directory:"
|
185
|
+
if File.exists?("#{path}")
|
186
|
+
output = `ls -al #{path}`.split("\n")
|
187
|
+
output.shift
|
188
|
+
output.each do |item|
|
189
|
+
puts " #{item}"
|
190
|
+
end
|
191
|
+
else
|
192
|
+
puts " No such file or directory"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def self.usrLocalBin
|
197
|
+
path = "/usr/local/bin"
|
198
|
+
|
199
|
+
puts "#{path} Directory:"
|
200
|
+
if File.exists?("#{path}")
|
201
|
+
output = `ls -al #{path}`.split("\n")
|
202
|
+
output.shift
|
203
|
+
output.each do |item|
|
204
|
+
puts " #{item}"
|
205
|
+
end
|
206
|
+
else
|
207
|
+
puts " No such file or directory"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def self.usrLocalSbin
|
212
|
+
path = "/usr/local/sbin"
|
213
|
+
|
214
|
+
puts "#{path} Directory:"
|
215
|
+
if File.exists?("#{path}")
|
216
|
+
output = `ls -al #{path}`.split("\n")
|
217
|
+
output.shift
|
218
|
+
output.each do |item|
|
219
|
+
puts " #{item}"
|
220
|
+
end
|
221
|
+
else
|
222
|
+
puts " No such file or directory"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.usersShared
|
227
|
+
path = "/Users/Shared"
|
228
|
+
|
229
|
+
puts "#{path} Directory:"
|
230
|
+
if File.exists?("#{path}")
|
231
|
+
output = `ls -al #{path}`.split("\n")
|
232
|
+
output.shift
|
233
|
+
output.each do |item|
|
234
|
+
puts " #{item}"
|
235
|
+
end
|
236
|
+
else
|
237
|
+
puts " No such file or directory"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.privateTmp
|
242
|
+
path = "/private/tmp"
|
243
|
+
|
244
|
+
puts "#{path} Directory:"
|
245
|
+
if File.exists?("#{path}")
|
246
|
+
output = `ls -al #{path}`.split("\n")
|
247
|
+
output.shift
|
248
|
+
output.each do |item|
|
249
|
+
puts " #{item}"
|
250
|
+
end
|
251
|
+
else
|
252
|
+
puts " No such file or directory"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def self.scriptInstallLocations
|
257
|
+
history1 = `mdfind "kMDItemKind == 'Shell Script'"`.split("\n")
|
258
|
+
puts "Script Install Locations:"
|
259
|
+
history1.each do |item|
|
260
|
+
if ! item.start_with?("/System", "/Library/Developer", "/usr/share", "/usr/bin", "/Library/Ruby")
|
261
|
+
if ! item.include? "/Library/Application Support/Code/User/History"
|
262
|
+
puts " #{item.strip}"
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
history = `mdfind "kMDItemKind == '* Source'"`.split("\n")
|
268
|
+
history.each do |item|
|
269
|
+
if ! item.start_with?("/System", "/Library/Developer", "/usr/share", "/usr/bin", "/Library/Ruby")
|
270
|
+
if ! item.include? "/Library/Application Support/Code/User/History"
|
271
|
+
puts " #{item.strip}"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Macos
|
4
|
+
module Artifacts
|
5
|
+
module Help
|
6
|
+
def self.options
|
7
|
+
puts ""
|
8
|
+
puts "Summary:
|
9
|
+
Artifact is an easy way to output a lot of facts and states about a macOS machine.
|
10
|
+
It's meant to be quick text output so it can be returned via an MDM or EDR solution to aid in investigations"
|
11
|
+
puts ""
|
12
|
+
puts "Macos::Artifact Usage:"
|
13
|
+
puts " Macos::Artifacts::computerName uses scutil to return computername"
|
14
|
+
puts " Macos::Artifacts::serial returns serial number from system_profiler"
|
15
|
+
puts " Macos::Artifacts::version uses sw_vers to return os version"
|
16
|
+
puts " Macos::Artifacts::build uses sw_vers to return buld number"
|
17
|
+
puts " Macos::Artifacts::kernel uses uname to return kernel"
|
18
|
+
puts " Macos::Artifacts::modelName users system_profiler to return model name"
|
19
|
+
puts " Macos::Artifacts::modelID users system_profiler to return model id"
|
20
|
+
puts " Macos::Artifacts::chip users system_profiler to return hardware chip"
|
21
|
+
puts " Macos::Artifacts::architecture users uanme to return hardware architecture"
|
22
|
+
puts " Macos::Artifacts::memory users system_profiler to return memory installed"
|
23
|
+
puts " Macos::Artifacts::hardwareUID users system_profiler to return hardware id"
|
24
|
+
puts " Macos::Artifacts::publicIP public ip returned by dig"
|
25
|
+
puts " Macos::Artifacts::privateIP private ip returned by ipconfig"
|
26
|
+
puts " Macos::Artifacts::sipStatus returns sip status from csrutil"
|
27
|
+
puts " Macos::Artifacts::filevaultStatus returns filevault status from fdesetup"
|
28
|
+
puts " Macos::Artifacts::firewallStatus read firewall status from com.apple.alf"
|
29
|
+
puts " Macos::Artifacts::screenlockStatus checks screenlock status and time"
|
30
|
+
puts " Macos::Artifacts::lockStatus returns Activation Lock Status"
|
31
|
+
puts " Macos::Artifacts::softwareUpdates returns machines softwareupate settings"
|
32
|
+
puts ""
|
33
|
+
puts "Macos::Artifacts::Files Usage:"
|
34
|
+
puts " Macos::Artifacts::Files::systemLaunchAgents list output of installed /Library/LaunchAgents"
|
35
|
+
puts " Macos::Artifacts::Files::systemLaunchDaemons list output of installed /Library/LaunchDaemons"
|
36
|
+
puts " Macos::Artifacts::Files::userLaunchAgents list output of Users ~/Library/LaunchAgents"
|
37
|
+
puts " Macos::Artifacts::Files::listUsersAccountDirectory list output of users home directory"
|
38
|
+
puts " Macos::Artifacts::Files::systemApplicationSupport list output of /Library/Application Support"
|
39
|
+
puts " Macos::Artifacts::Files::userApplicationSupport list output of ~/Library/Application Support"
|
40
|
+
puts " Macos::Artifacts::Files::libraryPreferences list output of /Library/Preferences"
|
41
|
+
puts " Macos::Artifacts::Files::userLibraryPreferences list output of ~/Library/Preferences"
|
42
|
+
puts " Macos::Artifacts::Files::cronTabs list output crontabs"
|
43
|
+
puts " Macos::Artifacts::Files::etcHosts list output of /etc/hosts file"
|
44
|
+
puts " Macos::Artifacts::Files::usrLocal list output of /usr/local"
|
45
|
+
puts " Macos::Artifacts::Files::usrLocalBin list output of /usr/local/bin"
|
46
|
+
puts " Macos::Artifacts::Files::usrLocalSbin list output of /usr/local/sbin"
|
47
|
+
puts " Macos::Artifacts::Files::usersShared list output of /User/Shared"
|
48
|
+
puts " Macos::Artifacts::Files::privateTmp list ooutput of /private/tmp"
|
49
|
+
puts " Macos::Artifacts::Files::scriptInstallLocations list output paths for shell, python, ruby scripts"
|
50
|
+
puts ""
|
51
|
+
puts "Macos::Artifacts::State Usage:"
|
52
|
+
puts " Macos::Artifacts::State::users list of local users with UIDs"
|
53
|
+
puts " Macos::Artifacts::State::adminUsers list of users in admin gropu"
|
54
|
+
puts " Macos::Artifacts::State::systemExtensions output of systemextensionctl"
|
55
|
+
puts " Macos::Artifacts::State::processCPU top 10 CPU Processes"
|
56
|
+
puts " Macos::Artifacts::State::processMemory top 10 Memory Processes"
|
57
|
+
puts " Macos::Artifacts::State::openNetworkConnections open network connections"
|
58
|
+
puts " Macos::Artifacts::State::networkInterfaces returns network interfaces"
|
59
|
+
puts ""
|
60
|
+
puts "Macos::Artifacts::Apps Usage:"
|
61
|
+
puts " Macos::Artifacts::Apps::applications outputs main applicaitons folder with version"
|
62
|
+
puts " Macos::Artifacts::Apps::packagesReceipts outputs list of installed packages"
|
63
|
+
puts " Macos::Artifacts::Apps::installHistory outputs history of installed apps"
|
64
|
+
puts " Macos::Artifacts::Apps::appInstallLocations outputs list of appliction install paths"
|
65
|
+
puts ""
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Macos
|
4
|
+
module Artifacts
|
5
|
+
module State
|
6
|
+
def self.users
|
7
|
+
$userHash={}
|
8
|
+
Dir.entries("/Users").each do |username|
|
9
|
+
if !username.start_with?(".")
|
10
|
+
if username != "Shared" and username != "Guest"
|
11
|
+
uid = `id -u #{username}`.strip
|
12
|
+
$userHash["#{username}"] = uid
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
puts "Local Users:"
|
18
|
+
$userHash.each do |key, value|
|
19
|
+
puts " #{key}: #{value}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.adminUsers
|
24
|
+
admins=`dscl . -read /Groups/admin GroupMembership`.split(" ")
|
25
|
+
admins.shift()
|
26
|
+
|
27
|
+
puts "Admins:"
|
28
|
+
admins.each do |name|
|
29
|
+
puts " #{name}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.systemExtensions
|
34
|
+
sysext = `systemextensionsctl list`.split("\n")
|
35
|
+
sysext.shift()
|
36
|
+
|
37
|
+
puts "System Extensions:"
|
38
|
+
sysext.each do |line|
|
39
|
+
|
40
|
+
if line.start_with?('---')
|
41
|
+
line = line.split(" ")
|
42
|
+
puts " Type: #{line[1]}"
|
43
|
+
elsif !line.start_with?("enabled")
|
44
|
+
line = line.split(" ")
|
45
|
+
if line[0] = "*"
|
46
|
+
puts " Enabled: true"
|
47
|
+
else
|
48
|
+
puts " Enabled: false"
|
49
|
+
end
|
50
|
+
if line[1] = "*"
|
51
|
+
puts " Active: true"
|
52
|
+
else
|
53
|
+
puts " Active: false"
|
54
|
+
end
|
55
|
+
puts " TeamID: #{line[2]}"
|
56
|
+
puts " BundleID: #{line[3]}"
|
57
|
+
puts " Version: #{line[4]}"
|
58
|
+
|
59
|
+
if line[5] != "[activated"
|
60
|
+
if line[6] != "[activated"
|
61
|
+
if line[7] != "[activated"
|
62
|
+
if line[8] != "[activated"
|
63
|
+
puts " Name: #{line[5]} #{line[6]} #{line[7]} #{line[8]}"
|
64
|
+
else
|
65
|
+
puts " Name: #{line[5]} #{line[6]} #{line[7]}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
else
|
69
|
+
puts " Name: #{line[5]}"
|
70
|
+
end
|
71
|
+
else
|
72
|
+
puts " Name: #{line[5]}"
|
73
|
+
end
|
74
|
+
|
75
|
+
if line[6] == "[activated"
|
76
|
+
puts " State: #{line[6]} #{line[7]}"
|
77
|
+
elsif line[7] == "[activated"
|
78
|
+
puts " State: #{line[7]} #{line[8]}"
|
79
|
+
elsif line[8] == "[activated"
|
80
|
+
puts " State: #{line[8]} #{line[9]}"
|
81
|
+
elsif line[9] == "[activated"
|
82
|
+
puts " State: #{line[9]} #{line[10]}"
|
83
|
+
else
|
84
|
+
puts " State: #{line[6]} #{line[7]}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.processCPU
|
91
|
+
$psArray = []
|
92
|
+
$psHash = {}
|
93
|
+
processes = `ps axc -o user,pid,%cpu,%mem,start,time,command`.split("\n")
|
94
|
+
processes.shift()
|
95
|
+
|
96
|
+
processes.each do |item|
|
97
|
+
data = item.split(" ")
|
98
|
+
psHash = {
|
99
|
+
:user => "#{data[0].strip}",
|
100
|
+
:pid => "#{data[1].strip}",
|
101
|
+
:cpu => "#{data[2].to_f}",
|
102
|
+
:mem => "#{data[3].strip}",
|
103
|
+
:start => "#{data[4].strip}",
|
104
|
+
:time => "#{data[5].strip}",
|
105
|
+
:command => "#{data[6].strip}"
|
106
|
+
}
|
107
|
+
$psArray.push(psHash)
|
108
|
+
end
|
109
|
+
|
110
|
+
$psArray.sort_by! { |hash| hash[:cpu] }
|
111
|
+
puts "Top 10 CPU Processes:"
|
112
|
+
|
113
|
+
$psArray.last(10).reverse.each do |ps|
|
114
|
+
ps.each do |key,value|
|
115
|
+
if key.to_s == "user"
|
116
|
+
puts " #{key}: #{value}"
|
117
|
+
else
|
118
|
+
puts " #{key}: #{value}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.processMemory
|
125
|
+
$psArray = []
|
126
|
+
$psHash = {}
|
127
|
+
processes = `ps axc -o user,pid,%cpu,%mem,start,time,command`.split("\n")
|
128
|
+
processes.shift()
|
129
|
+
|
130
|
+
processes.each do |item|
|
131
|
+
data = item.split(" ")
|
132
|
+
psHash = {
|
133
|
+
:user => "#{data[0].strip.to_s}",
|
134
|
+
:pid => "#{data[1].strip}",
|
135
|
+
:cpu => "#{data[2].to_f}",
|
136
|
+
:mem => "#{data[3].strip}",
|
137
|
+
:start => "#{data[4].strip}",
|
138
|
+
:time => "#{data[5].strip}",
|
139
|
+
:command => "#{data[6].strip}"
|
140
|
+
}
|
141
|
+
$psArray.push(psHash)
|
142
|
+
end
|
143
|
+
|
144
|
+
$psArray.sort_by! { |hash| hash[:mem] }
|
145
|
+
puts "Top 10 Memory Processes:"
|
146
|
+
|
147
|
+
$psArray.last(10).reverse.each do |ps|
|
148
|
+
ps.each do |key,value|
|
149
|
+
if key.to_s == "user"
|
150
|
+
puts " #{key}: #{value}"
|
151
|
+
else
|
152
|
+
puts " #{key}: #{value}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.openNetworkConnections
|
159
|
+
listOfOpenConnections = `lsof -i`.split("\n")
|
160
|
+
listOfOpenConnections.shift
|
161
|
+
puts "Open Network Connections:"
|
162
|
+
listOfOpenConnections.each do |line|
|
163
|
+
line = line.split(" ")
|
164
|
+
puts " Command: #{line[0]}"
|
165
|
+
puts " PID: #{line[1]}"
|
166
|
+
puts " USER: #{line[2]}"
|
167
|
+
puts " TYPE: #{line[4]}"
|
168
|
+
puts " NODE: #{line[7]}"
|
169
|
+
puts " NAME: #{line[8]}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.networkInterfaces
|
174
|
+
listOfNetworkInterfaces = `ifconfig`.split("\n")
|
175
|
+
puts "Network Interfaces:"
|
176
|
+
listOfNetworkInterfaces.each do |item|
|
177
|
+
if item.start_with?(/^*:/)
|
178
|
+
puts item.strip
|
179
|
+
else
|
180
|
+
puts " #{item}"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "artifacts/version"
|
4
|
+
require_relative "artifacts/state"
|
5
|
+
require_relative "artifacts/files"
|
6
|
+
require_relative "artifacts/apps"
|
7
|
+
require_relative "artifacts/help"
|
8
|
+
require 'cfpropertylist'
|
9
|
+
|
10
|
+
$currentUser = ENV['USER']
|
11
|
+
|
12
|
+
module Macos
|
13
|
+
module Artifacts
|
14
|
+
def self.computerName
|
15
|
+
computerName = `scutil --get ComputerName`.strip
|
16
|
+
puts "Host Name: #{computerName}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.serial
|
20
|
+
serialNumber = `system_profiler SPHardwareDataType | grep Serial | cut -d ":" -f2 | xargs`.strip
|
21
|
+
puts "Serial: #{serialNumber}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.version
|
25
|
+
osVersion = `sw_vers -ProductVersion`.strip
|
26
|
+
puts "Version: #{osVersion}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.build
|
30
|
+
osBuild = `sw_vers -BuildVersion`.strip
|
31
|
+
puts "Build: #{osBuild}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.kernel
|
35
|
+
arch = `uname -r`.strip
|
36
|
+
puts "Kernel: #{arch}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.modelName
|
40
|
+
modelName = `system_profiler SPHardwareDataType | grep "Model Name" | cut -d ":" -f2 | xargs`.strip
|
41
|
+
puts "Model Name: #{modelName}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.modelID
|
45
|
+
modelID = `system_profiler SPHardwareDataType | grep "Model Identifier" | cut -d ":" -f2 | xargs`.strip
|
46
|
+
puts "Model ID: #{modelID}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.chip
|
50
|
+
chipArch = `system_profiler SPHardwareDataType | grep "Chip" | cut -d ":" -f2 | xargs`.strip
|
51
|
+
puts "Chip: #{chipArch}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.architecture
|
55
|
+
arch = `uname -m`.strip
|
56
|
+
puts "Architecture: #{arch}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.memory
|
60
|
+
memory = `system_profiler SPHardwareDataType | grep "Memory" | cut -d ":" -f2 | xargs`.strip
|
61
|
+
puts "Memory: #{memory}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.hardwareUID
|
65
|
+
uuid = `system_profiler SPHardwareDataType | grep "Hardware" | cut -d ":" -f2 | xargs`.strip
|
66
|
+
puts "Hardware UID: #{uuid}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.publicIP
|
70
|
+
ip = `dig -4 TXT +short o-o.myaddr.l.google.com ns1.google.com | tr -d '"'`.strip
|
71
|
+
puts "Public IP: #{ip}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.privateIP
|
75
|
+
ip = `ipconfig getifaddr en0`.strip
|
76
|
+
puts "Private IP: #{ip}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.sipStatus
|
80
|
+
command = `csrutil status | cut -d ":" -f2 | xargs | head -c7 | xargs`.strip
|
81
|
+
puts "SIP Status: #{command}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.filevaultStatus
|
85
|
+
command = `fdesetup status | awk '{print $3}' | head -c2 | xargs`.strip
|
86
|
+
puts "FireVault Status: #{command}"
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.firewallStatus
|
90
|
+
command = `defaults read /Library/Preferences/com.apple.alf globalstate`.strip
|
91
|
+
|
92
|
+
if command == "0"
|
93
|
+
puts "Firewall Status: Off"
|
94
|
+
else
|
95
|
+
puts "Firewall Status: On"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.lockStatus
|
100
|
+
status = `system_profiler SPHardwareDataType | grep "Activation Lock Status" | cut -d ":" -f2 | xargs`.strip
|
101
|
+
puts "Activation Lock Status: #{status}"
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.screenlockStatus
|
105
|
+
screensaverPlist = "/Library/Managed Preferences/com.apple.screensaver.plist"
|
106
|
+
|
107
|
+
if File.exists?("#{screensaverPlist}")
|
108
|
+
screenSaverTime = `defaults read '#{screensaverPlist}' askForPasswordDelay`.strip
|
109
|
+
puts "found"
|
110
|
+
else
|
111
|
+
$askForPasswordStatus = `sysadminctl -screenLock status 2>&1 >/dev/null`.split("]")
|
112
|
+
puts "Screen Lock Status: #{$askForPasswordStatus[1].strip}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.softwareUpdates
|
117
|
+
managedUpdates = "/Library/Managed Preferences/com.apple.SoftwareUpdate.plist"
|
118
|
+
|
119
|
+
puts "Software Updates:"
|
120
|
+
if File.exists?("#{managedUpdates}")
|
121
|
+
autoCheck = `defaults read "#{managedUpdates}" AutomaticCheckEnabled`.strip
|
122
|
+
automaticDownload = `defaults read "#{managedUpdates}" AutomaticDownload`.strip
|
123
|
+
automaticallyInstallMacOSUpdates = `defaults read "#{managedUpdates}" AutomaticallyInstallMacOSUpdates`.strip
|
124
|
+
configDataInstall = `defaults read "#{managedUpdates}" ConfigDataInstall`.strip
|
125
|
+
criticalUpdateInstall = `defaults read "#{managedUpdates}" CriticalUpdateInstall`.strip
|
126
|
+
puts " Auto Updates: #{autoCheck}"
|
127
|
+
puts " Auto Download: #{automaticDownload}"
|
128
|
+
puts " Auto Install: #{automaticallyInstallMacOSUpdates}"
|
129
|
+
puts " Install Config Data: #{configDataInstall}"
|
130
|
+
puts " Install Critical Updates: #{criticalUpdateInstall}"
|
131
|
+
else
|
132
|
+
autoCheck = `defaults read /Library/Preferences/com.apple.SoftwareUpdate.plist AutomaticCheckEnabled`.strip
|
133
|
+
automaticDownload = `defaults read /Library/Preferences/com.apple.SoftwareUpdate.plist AutomaticDownload`.strip
|
134
|
+
automaticallyInstallMacOSUpdates = `defaults read /Library/Preferences/com.apple.SoftwareUpdate.plist AutomaticallyInstallMacOSUpdates`.strip
|
135
|
+
configDataInstall = `defaults read /Library/Preferences/com.apple.SoftwareUpdate.plist ConfigDataInstall`.strip
|
136
|
+
criticalUpdateInstall = `defaults read /Library/Preferences/com.apple.SoftwareUpdate.plist CriticalUpdateInstall`.strip
|
137
|
+
puts " Auto Updates: #{autoCheck}"
|
138
|
+
puts " Auto Download: #{automaticDownload}"
|
139
|
+
puts " Auto Install: #{automaticallyInstallMacOSUpdates}"
|
140
|
+
puts " Install Config Data: #{configDataInstall}"
|
141
|
+
puts " Install Critical Updates: #{criticalUpdateInstall}"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/macos/artifacts/version"
|
4
|
+
require_relative "lib/macos/artifacts/state"
|
5
|
+
require_relative "lib/macos/artifacts/files"
|
6
|
+
require_relative "lib/macos/artifacts/apps"
|
7
|
+
require_relative "lib/macos/artifacts/help"
|
8
|
+
|
9
|
+
Gem::Specification.new do |spec|
|
10
|
+
spec.name = "macos-artifacts"
|
11
|
+
spec.version = Macos::Artifacts::VERSION
|
12
|
+
spec.authors = ["nic scott"]
|
13
|
+
spec.email = ["nls.inbox@gmail.com"]
|
14
|
+
spec.summary = %q{A collection of macOS artifacts}
|
15
|
+
spec.homepage = "https://github.com/nlscott/macos-artifacts"
|
16
|
+
spec.license = "MIT"
|
17
|
+
spec.required_ruby_version = ">= 2.6.0"
|
18
|
+
|
19
|
+
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
+
spec.files = Dir.chdir(__dir__) do
|
23
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
24
|
+
(File.expand_path(f) == __FILE__) ||
|
25
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
spec.bindir = "exe"
|
29
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
# Uncomment to register a new dependency of your gem
|
33
|
+
spec.add_development_dependency "bundler", ">= 2.2.33"
|
34
|
+
spec.add_development_dependency "CFPropertyList", ">= 3.0.6"
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: macos-artifacts
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- nic scott
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-09-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.2.33
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.2.33
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: CFPropertyList
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.0.6
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.0.6
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- nls.inbox@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- CHANGELOG.md
|
49
|
+
- LICENSE.txt
|
50
|
+
- README.md
|
51
|
+
- Rakefile
|
52
|
+
- lib/macos/artifacts.rb
|
53
|
+
- lib/macos/artifacts/apps.rb
|
54
|
+
- lib/macos/artifacts/files.rb
|
55
|
+
- lib/macos/artifacts/help.rb
|
56
|
+
- lib/macos/artifacts/state.rb
|
57
|
+
- lib/macos/artifacts/version.rb
|
58
|
+
- macos-artifacts.gemspec
|
59
|
+
- sig/macos/artifacts.rbs
|
60
|
+
homepage: https://github.com/nlscott/macos-artifacts
|
61
|
+
licenses:
|
62
|
+
- MIT
|
63
|
+
metadata: {}
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 2.6.0
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubygems_version: 3.2.3
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: A collection of macOS artifacts
|
83
|
+
test_files: []
|