clockker 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -0
- data/clockker.gemspec +1 -0
- data/lib/clockker/version.rb +1 -1
- data/lib/clockker/watcher.rb +86 -22
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7404b361d1f21b145d39ff872ed9901c19f7944
|
4
|
+
data.tar.gz: 7af9d8d18fdbfaf1779250f579a24fa8ef7e57c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66432b991bcbd96026f1761bd87211afad2b626e47df12b9627367cb767574ea2eb34f255b58fca4e17c369970e4bcbd7b1327d95f8b0eb981283317d68c7bf8
|
7
|
+
data.tar.gz: 69bd52efc272a2fa17ed05893e234b81a533d148548c5a1da5bb1afbd215d84b76fac85fb7adda1775cae0eefe5096ff38de45e084bff90cc106ea06b1e5ece6
|
data/README.md
CHANGED
@@ -8,6 +8,18 @@ Install it with:
|
|
8
8
|
|
9
9
|
$ gem install clockker
|
10
10
|
|
11
|
+
To access your Safari history when using macOS Mojave (10.14) and higher, you must give the Terminal app "Full Disk Access".
|
12
|
+
|
13
|
+
1. Settings
|
14
|
+
2. Security & Privacy
|
15
|
+
3. Privacy
|
16
|
+
4. Full Disk Access
|
17
|
+
5. <unlock>
|
18
|
+
6. + to add
|
19
|
+
7. Browse to find /Applications/Utilities/Terminal
|
20
|
+
8. Quit and restart Terminal
|
21
|
+
|
22
|
+
|
11
23
|
## Usage
|
12
24
|
|
13
25
|
Run it with:
|
@@ -68,6 +80,8 @@ After checking out the repo, run `bin/setup` to install dependencies. You can al
|
|
68
80
|
|
69
81
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
70
82
|
|
83
|
+
To run a development version run `bundle exec exe/clockker <params>`.
|
84
|
+
|
71
85
|
## Contributing
|
72
86
|
|
73
87
|
Bug reports and pull requests are welcome on GitHub at https://github.com/liquidmedia/clockker. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
data/clockker.gemspec
CHANGED
data/lib/clockker/version.rb
CHANGED
data/lib/clockker/watcher.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rb-fsevent'
|
2
2
|
require 'logger'
|
3
3
|
require 'net/http'
|
4
|
+
require 'sqlite3'
|
4
5
|
|
5
6
|
module Clockker
|
6
7
|
class Watcher
|
@@ -9,21 +10,33 @@ module Clockker
|
|
9
10
|
|
10
11
|
trap "INT" do
|
11
12
|
@fsevent.stop
|
12
|
-
|
13
|
-
@
|
13
|
+
@timer.kill # the timer can just go away; it doesn't need a graceful shutdown.
|
14
|
+
@safari_watcher_queue << :exit
|
15
|
+
@submitter_queue << :exit # this shuts down the submitter, first letting it submit its changeset
|
14
16
|
end
|
15
17
|
|
16
18
|
white_black_list = WhiteBlackList.new(config)
|
17
19
|
|
18
|
-
@
|
19
|
-
@
|
20
|
+
@submitter_queue = Queue.new
|
21
|
+
@safari_watcher_queue = Queue.new
|
22
|
+
@timer = Thread.new { create_timer(@safari_watcher_queue) }
|
23
|
+
@file_watcher = Thread.new { create_file_watcher('/', white_black_list, logger) }
|
24
|
+
@safari_watcher = Thread.new { create_safari_watcher(logger) }
|
20
25
|
@submitter = Thread.new { create_submitter(config.submit_frequency, config.region, config.token, config.identifier, logger, config.development) }
|
21
26
|
|
22
|
-
@
|
27
|
+
@file_watcher.join
|
28
|
+
@safari_watcher.join
|
23
29
|
@submitter.join
|
24
30
|
end
|
25
31
|
|
26
|
-
def
|
32
|
+
def create_timer(*queues)
|
33
|
+
loop do
|
34
|
+
sleep 5
|
35
|
+
queues.each {|q| q << :run}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_file_watcher(dir, white_black_list, logger)
|
27
40
|
@fsevent = FSEvent.new
|
28
41
|
@fsevent.watch dir, {file_events: true} do |directories, event_meta|
|
29
42
|
t = Time.now.utc
|
@@ -31,9 +44,9 @@ module Clockker
|
|
31
44
|
event_meta['events'].each do |event|
|
32
45
|
next if white_black_list.ignore?(event['path'])
|
33
46
|
count += 1
|
34
|
-
@
|
47
|
+
@submitter_queue << {touched_at: t, contents: event['path'], type: "file", metadata: {flags: event['flags'], id: event['id']}}
|
35
48
|
end
|
36
|
-
logger.debug "#{Time.now} checked #{event_meta['numEvents']} events and added #{count} to the
|
49
|
+
logger.debug "#{Time.now} checked #{event_meta['numEvents']} events and added #{count} to the submitter_queue"
|
37
50
|
end
|
38
51
|
logger.info "#{Time.now} now watching /"
|
39
52
|
logger.info "#{Time.now} whitelist paths: #{white_black_list.whitelist}"
|
@@ -42,8 +55,50 @@ module Clockker
|
|
42
55
|
logger.info "#{Time.now} fswatcher done"
|
43
56
|
end
|
44
57
|
|
45
|
-
def
|
46
|
-
|
58
|
+
def create_safari_watcher(logger)
|
59
|
+
safari_db = SQLite3::Database.new(File.expand_path("~/Library/Safari/History.db"))
|
60
|
+
logger.info "#{Time.now} now watching Safari"
|
61
|
+
last_time = Time.now.to_i - 978307200 # convert to macOS Core Data time
|
62
|
+
loop do
|
63
|
+
submit_now = false
|
64
|
+
exit_now = false
|
65
|
+
|
66
|
+
value = @safari_watcher_queue.pop
|
67
|
+
case value
|
68
|
+
when :exit
|
69
|
+
submit_now = true
|
70
|
+
exit_now = true
|
71
|
+
when :run
|
72
|
+
# we just move forward
|
73
|
+
else
|
74
|
+
# On the off chance the message isn't :run
|
75
|
+
end
|
76
|
+
|
77
|
+
safari_db.execute("
|
78
|
+
SELECT hv.id, datetime(hv.visit_time+978307200, \"unixepoch\") as visited_at, hv.visit_time, hv.title, hi.url
|
79
|
+
FROM history_visits hv, history_items hi
|
80
|
+
WHERE hv.history_item=hi.id
|
81
|
+
AND hv.visit_time > #{last_time}
|
82
|
+
ORDER BY hv.visit_time ASC
|
83
|
+
") do |row|
|
84
|
+
id, visited_at, visit_time, title, url = row
|
85
|
+
visited_at = Time.parse(visited_at+"Z") # the time is already UTC; let's make doubly sure!
|
86
|
+
# TODO check the whitelisted domain regexes to see if we can submit this one.
|
87
|
+
logger.info " - #{visited_at} - #{url} - #{title}"
|
88
|
+
@submitter_queue << {touched_at: visited_at, contents: url, type: "url", metadata: {title: title}}
|
89
|
+
last_time = visit_time
|
90
|
+
end
|
91
|
+
|
92
|
+
if exit_now
|
93
|
+
logger.info "#{Time.now} Safari watcher preparing to exit"
|
94
|
+
break
|
95
|
+
end
|
96
|
+
end
|
97
|
+
logger.info "#{Time.now} Safari watcher done"
|
98
|
+
end
|
99
|
+
|
100
|
+
def create_submitter(submit_frequency, region, token, identifier, logger, dev_mode)
|
101
|
+
logger.info "#{Time.now} submitting to clockk.com #{"and localhost:4000 " if dev_mode}every #{submit_frequency} seconds with token #{token} from identifier #{identifier} with version #{Clockker.version}"
|
47
102
|
t_start = Time.now
|
48
103
|
clockk_agent_uri = URI("https://#{region}.clockk.io/api/v1/agent/artifacts")
|
49
104
|
clockk_local_uri = URI("http://localhost:4000/api/v1/agent/artifacts")
|
@@ -52,7 +107,7 @@ module Clockker
|
|
52
107
|
submit_now = false
|
53
108
|
exit_now = false
|
54
109
|
|
55
|
-
value = @
|
110
|
+
value = @submitter_queue.pop
|
56
111
|
case value
|
57
112
|
when :submit
|
58
113
|
submit_now = true
|
@@ -68,29 +123,38 @@ module Clockker
|
|
68
123
|
end
|
69
124
|
|
70
125
|
if exit_now
|
71
|
-
logger.info "#{Time.now} preparing to exit"
|
126
|
+
logger.info "#{Time.now} Submitter preparing to exit"
|
72
127
|
end
|
73
128
|
|
74
|
-
if submit_now
|
129
|
+
if submit_now && @changeset.length > 0
|
75
130
|
logger.info "#{Time.now} submitting #{@changeset.length} items"
|
76
131
|
@changeset.each_with_index do |cs, idx|
|
77
|
-
logger.info " - #{idx} - #{cs[:
|
132
|
+
logger.info " - #{idx} - #{cs[:touched_at].strftime("%H:%M:%S")} #{cs[:contents]}"
|
78
133
|
end
|
79
134
|
|
80
135
|
begin
|
81
|
-
|
136
|
+
dev_data = {artifacts: @changeset.map{|c| {contents: c[:contents], touched_at: c[:touched_at].strftime("%Y-%m-%d %H:%M:%S"), type: c[:type], metadata: c[:metadata], identifier: identifier, agent: Clockker.version}}}.to_json
|
137
|
+
|
138
|
+
# temporarily we're keeping the old data structure, until all of our APIs are up to date.
|
139
|
+
prod_data = @changeset.find_all{|c| c[:type] == "file"}
|
140
|
+
prod_data = {artifacts: prod_data.map{|c| {file_path: c[:contents], touched_at: c[:touched_at].strftime("%Y-%m-%d %H:%M:%S"), identifier: identifier, agent: Clockker.version}}}.to_json
|
141
|
+
|
82
142
|
header = {"Authorization" => "Bearer #{token}", "Content-Type" => "application/json"}
|
83
143
|
|
144
|
+
# logger.info "PROD submission: #{prod_data.inspect}"
|
84
145
|
Net::HTTP.start(clockk_agent_uri.hostname, clockk_agent_uri.port, use_ssl: clockk_agent_uri.scheme == 'https') do |http|
|
85
|
-
http.post(clockk_agent_uri.path,
|
146
|
+
http.post(clockk_agent_uri.path, prod_data, header)
|
86
147
|
end
|
87
148
|
|
88
|
-
|
89
|
-
|
90
|
-
|
149
|
+
if dev_mode
|
150
|
+
begin
|
151
|
+
# logger.info "DEV submission: #{dev_data.inspect}"
|
152
|
+
Net::HTTP.start(clockk_local_uri.hostname, clockk_local_uri.port, use_ssl: clockk_local_uri.scheme == 'https') do |http|
|
153
|
+
http.post(clockk_local_uri.path, dev_data, header)
|
154
|
+
end
|
155
|
+
rescue Exception => q
|
156
|
+
logger.info "#{Time.now} Failed to reach localhost:4000; ignoring"
|
91
157
|
end
|
92
|
-
rescue Exception => q
|
93
|
-
logger.info "#{Time.now} Failed to reach localhost:4000; ignoring"
|
94
158
|
end
|
95
159
|
|
96
160
|
@changeset = []
|
@@ -102,7 +166,7 @@ module Clockker
|
|
102
166
|
end
|
103
167
|
|
104
168
|
if exit_now
|
105
|
-
logger.info "#{Time.now}
|
169
|
+
logger.info "#{Time.now} Submitter done"
|
106
170
|
break
|
107
171
|
end
|
108
172
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clockker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Doerwald
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-10-
|
11
|
+
date: 2018-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rb-fsevent
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sqlite3
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|