macos-artifacts 0.6.0
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/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: []
|