chronicle-shell 0.2.4 → 0.3.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 +4 -4
- data/.rubocop.yml +2 -0
- data/Gemfile +2 -2
- data/README.md +7 -10
- data/Rakefile +1 -1
- data/chronicle-shell.gemspec +23 -16
- data/lib/chronicle/shell/shell_history_extractor.rb +30 -17
- data/lib/chronicle/shell/shell_history_transformer.rb +42 -37
- data/lib/chronicle/shell/version.rb +1 -1
- data/lib/chronicle/shell.rb +3 -3
- metadata +78 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cba457d01ec6e7ee3719b80d980ee171797eabef20c7aaadc751e4e985f96f73
|
4
|
+
data.tar.gz: 8ec830dcd00de94eb7d9e9ff4a0942d3cc49f8975a88a792be4ad2be756630a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8f50a8c830a4687d9769d04d9a074f68feafb1ba9a07ac03dd2e4cc3e8794842436d2b968cc25cce91c41a555634ee5bf99a2bfcc5f70c37d306a08ce7108e0
|
7
|
+
data.tar.gz: 0ddbb6f0cb224a324a8b2150ca931e8f226b572b2e4a8fcb29df9616921a08c1b3f208c15737978e655ae3514a489ee234ce20378b973be37991e44a59ea427e
|
data/.rubocop.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -3,13 +3,6 @@
|
|
3
3
|
|
4
4
|
Shell importer for [chronicle-etl](https://github.com/chronicle-app/chronicle-etl)
|
5
5
|
|
6
|
-
## Available Connectors
|
7
|
-
### Extractors
|
8
|
-
- `shell:history` - Extract shell history from bash or zsh
|
9
|
-
|
10
|
-
### Transformers
|
11
|
-
- `shell:history` - Turn a shell command into Chronicle Schema
|
12
|
-
|
13
6
|
## Usage and examples
|
14
7
|
|
15
8
|
```bash
|
@@ -17,9 +10,13 @@ Shell importer for [chronicle-etl](https://github.com/chronicle-app/chronicle-et
|
|
17
10
|
gem install chronicle-etl
|
18
11
|
chronicle-etl plugins:install shell
|
19
12
|
|
20
|
-
# output commands since
|
21
|
-
chronicle-etl --extractor shell:
|
13
|
+
# output commands since 2 weeks ago
|
14
|
+
$ chronicle-etl --extractor shell:command --schema chronicle --since 2w --loader json
|
22
15
|
|
23
16
|
# Show recent commands sorted by frequency of use
|
24
|
-
chronicle-etl --extractor shell:
|
17
|
+
$ chronicle-etl --extractor shell:command --loader table --limit 500 --fields command --silent | sort | uniq -c | sort -nr
|
25
18
|
```
|
19
|
+
|
20
|
+
## Available Connectors
|
21
|
+
### Extractors
|
22
|
+
- `shell:command` - Extract shell history from bash or zsh
|
data/Rakefile
CHANGED
data/chronicle-shell.gemspec
CHANGED
@@ -1,24 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative 'lib/chronicle/shell/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name =
|
6
|
+
spec.name = 'chronicle-shell'
|
7
7
|
spec.version = Chronicle::Shell::VERSION
|
8
|
-
spec.authors = [
|
9
|
-
spec.email = [
|
8
|
+
spec.authors = ['Andrew Louis']
|
9
|
+
spec.email = ['andrew@hyfen.net']
|
10
10
|
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
-
spec.homepage =
|
14
|
-
spec.
|
15
|
-
spec.license = "MIT"
|
11
|
+
spec.summary = 'Shell connectors for Chronicle-ETL'
|
12
|
+
spec.description = 'Extract shell command history from Bash or Zsh'
|
13
|
+
spec.homepage = 'https://github.com/chronicle-app/chronicle-shell'
|
14
|
+
spec.license = 'MIT'
|
16
15
|
|
17
|
-
spec.metadata['allowed_push_host'] =
|
16
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
18
17
|
|
19
|
-
spec.metadata[
|
20
|
-
spec.metadata[
|
21
|
-
spec.metadata[
|
18
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
19
|
+
spec.metadata['source_code_uri'] = 'https://github.com/chronicle-app/chronicle-shell'
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/chronicle-app/chronicle-shell/releases'
|
22
21
|
|
23
22
|
# Specify which files should be added to the gem when it is released.
|
24
23
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
@@ -27,9 +26,17 @@ Gem::Specification.new do |spec|
|
|
27
26
|
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
28
27
|
end
|
29
28
|
end
|
30
|
-
spec.bindir =
|
29
|
+
spec.bindir = 'exe'
|
31
30
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
32
|
-
spec.require_paths = [
|
31
|
+
spec.require_paths = ['lib']
|
32
|
+
spec.required_ruby_version = '>= 3.1'
|
33
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
33
34
|
|
34
|
-
spec.add_dependency
|
35
|
+
spec.add_dependency 'chronicle-core', '~> 0.3'
|
36
|
+
spec.add_dependency 'chronicle-etl', '~> 0.6'
|
37
|
+
|
38
|
+
spec.add_development_dependency 'bundler', '~> 2.3'
|
39
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.10'
|
40
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
41
|
+
spec.add_development_dependency 'rubocop', '~> 1.63'
|
35
42
|
end
|
@@ -4,9 +4,10 @@ module Chronicle
|
|
4
4
|
module Shell
|
5
5
|
class ShellHistoryExtractor < Chronicle::ETL::Extractor
|
6
6
|
register_connector do |r|
|
7
|
-
r.
|
7
|
+
r.source = :shell
|
8
|
+
r.type = :command
|
9
|
+
r.strategy = :local
|
8
10
|
r.description = 'shell command history'
|
9
|
-
r.identifier = 'history'
|
10
11
|
end
|
11
12
|
|
12
13
|
setting :input
|
@@ -25,11 +26,11 @@ module Chronicle
|
|
25
26
|
def extract
|
26
27
|
@commands.each do |command|
|
27
28
|
meta = {
|
28
|
-
username
|
29
|
-
hostname
|
29
|
+
username:,
|
30
|
+
hostname:,
|
30
31
|
shell_name: @config.shell
|
31
32
|
}
|
32
|
-
yield
|
33
|
+
yield build_extraction(data: command, meta:)
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
@@ -42,11 +43,11 @@ module Chronicle
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def history_filename_default_bash
|
45
|
-
File.join(Dir.home,
|
46
|
+
File.join(Dir.home, '.bash_history')
|
46
47
|
end
|
47
48
|
|
48
49
|
def history_filename_default_zsh
|
49
|
-
File.join(Dir.home,
|
50
|
+
File.join(Dir.home, '.zsh_history')
|
50
51
|
end
|
51
52
|
|
52
53
|
def username
|
@@ -63,16 +64,17 @@ module Chronicle
|
|
63
64
|
def load_commands
|
64
65
|
commands = []
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
commands << command
|
67
|
+
case @config.shell.to_sym
|
68
|
+
when :bash
|
69
|
+
load_commands_from_bash do |command|
|
70
|
+
process_command(command, commands)
|
71
|
+
end
|
72
|
+
when :zsh
|
73
|
+
load_commands_from_zsh do |command|
|
74
|
+
process_command(command, commands)
|
75
75
|
end
|
76
|
+
else
|
77
|
+
raise "Unknown loader: #{@config.shell}"
|
76
78
|
end
|
77
79
|
|
78
80
|
commands = commands.first(@config.limit) if @config.limit
|
@@ -80,13 +82,24 @@ module Chronicle
|
|
80
82
|
commands
|
81
83
|
end
|
82
84
|
|
85
|
+
def process_command(command, commands)
|
86
|
+
return if @config.since && command[:timestamp] < @config.since
|
87
|
+
return if @config.until && command[:timestamp] > @config.until
|
88
|
+
|
89
|
+
if block_given?
|
90
|
+
yield command
|
91
|
+
else
|
92
|
+
commands << command
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
83
96
|
def load_commands_from_bash
|
84
97
|
command = nil
|
85
98
|
File.readlines(history_filename).reverse_each do |line|
|
86
99
|
timestamp_line = line.scrub.match(BASH_TIMESTAMP_REGEX)
|
87
100
|
if timestamp_line && command
|
88
101
|
timestamp = Time.at(timestamp_line[:timestamp].to_i)
|
89
|
-
command = { timestamp
|
102
|
+
command = { timestamp:, command: }
|
90
103
|
yield command
|
91
104
|
else
|
92
105
|
command = line.scrub.strip
|
@@ -1,59 +1,64 @@
|
|
1
1
|
require 'chronicle/etl'
|
2
|
+
require 'chronicle/models'
|
2
3
|
|
3
4
|
module Chronicle
|
4
|
-
module Shell
|
5
|
+
module Shell
|
5
6
|
class ShellHistoryTransformer < Chronicle::ETL::Transformer
|
6
7
|
register_connector do |r|
|
7
|
-
r.
|
8
|
+
r.source = :shell
|
9
|
+
r.type = :command
|
10
|
+
r.strategy = :local
|
8
11
|
r.description = 'a shell command'
|
9
|
-
r.
|
12
|
+
r.from_schema = :extraction
|
13
|
+
r.to_schema = :chronicle
|
10
14
|
end
|
11
15
|
|
12
|
-
def transform
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
16
|
+
def transform(record)
|
17
|
+
username = record.extraction.meta[:username]
|
18
|
+
hostname = record.extraction.meta[:hostname]
|
19
|
+
shell_name = record.extraction.meta[:shell_name]
|
20
|
+
timestamp = record.data[:timestamp]
|
21
|
+
command = record.data[:command]
|
19
22
|
|
20
|
-
|
21
|
-
|
23
|
+
build_command(username:, hostname:, command:, shell_name:,
|
24
|
+
timestamp:)
|
22
25
|
end
|
23
26
|
|
24
27
|
private
|
25
28
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
29
|
+
def build_command(username:, hostname:, command:, shell_name:, timestamp:)
|
30
|
+
Chronicle::Models::ControlAction.new do |r|
|
31
|
+
r.source = shell_name
|
32
|
+
r.result = build_command_result(command)
|
33
|
+
r.agent = build_agent(username, hostname)
|
34
|
+
r.end_time = timestamp
|
35
|
+
# r.object = build_host
|
36
|
+
r.dedupe_on << %i[source type end_time]
|
37
|
+
end
|
35
38
|
end
|
36
39
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
record
|
40
|
+
def build_command_result(text)
|
41
|
+
Chronicle::Models::ComputerCommand.new do |r|
|
42
|
+
r.text = text
|
43
|
+
r.source = 'system'
|
44
|
+
r.dedupe_on << %i[source text type]
|
45
|
+
end
|
44
46
|
end
|
45
47
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
record
|
48
|
+
def build_agent(username, hostname)
|
49
|
+
Chronicle::Models::Person.new do |r|
|
50
|
+
r.source = 'system'
|
51
|
+
r.slug = build_user_slug(username, hostname)
|
52
|
+
r.dedupe_on << %i[source slug type]
|
53
|
+
end
|
53
54
|
end
|
54
55
|
|
55
|
-
|
56
|
-
|
56
|
+
# TODO: implement this.
|
57
|
+
# TODO: figure out how to represent the host in schema
|
58
|
+
def build_host(hostname); end
|
59
|
+
|
60
|
+
def build_user_slug(username, hostname)
|
61
|
+
"#{username}@#{hostname}"
|
57
62
|
end
|
58
63
|
end
|
59
64
|
end
|
data/lib/chronicle/shell.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
3
|
+
require_relative 'shell/version'
|
4
|
+
require_relative 'shell/shell_history_extractor'
|
5
|
+
require_relative 'shell/shell_history_transformer'
|
6
6
|
|
7
7
|
module Chronicle
|
8
8
|
module Shell
|
metadata
CHANGED
@@ -1,29 +1,99 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chronicle-shell
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Louis
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-05-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: chronicle-core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.3'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: chronicle-etl
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - "~>"
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
33
|
+
version: '0.6'
|
20
34
|
type: :runtime
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
40
|
+
version: '0.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry-byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.10'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.10'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '13.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '13.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.63'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.63'
|
27
97
|
description: Extract shell command history from Bash or Zsh
|
28
98
|
email:
|
29
99
|
- andrew@hyfen.net
|
@@ -31,6 +101,7 @@ executables: []
|
|
31
101
|
extensions: []
|
32
102
|
extra_rdoc_files: []
|
33
103
|
files:
|
104
|
+
- ".rubocop.yml"
|
34
105
|
- CODE_OF_CONDUCT.md
|
35
106
|
- Gemfile
|
36
107
|
- LICENSE.txt
|
@@ -49,6 +120,7 @@ metadata:
|
|
49
120
|
homepage_uri: https://github.com/chronicle-app/chronicle-shell
|
50
121
|
source_code_uri: https://github.com/chronicle-app/chronicle-shell
|
51
122
|
changelog_uri: https://github.com/chronicle-app/chronicle-shell/releases
|
123
|
+
rubygems_mfa_required: 'true'
|
52
124
|
post_install_message:
|
53
125
|
rdoc_options: []
|
54
126
|
require_paths:
|
@@ -57,14 +129,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
129
|
requirements:
|
58
130
|
- - ">="
|
59
131
|
- !ruby/object:Gem::Version
|
60
|
-
version:
|
132
|
+
version: '3.1'
|
61
133
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
134
|
requirements:
|
63
135
|
- - ">="
|
64
136
|
- !ruby/object:Gem::Version
|
65
137
|
version: '0'
|
66
138
|
requirements: []
|
67
|
-
rubygems_version: 3.
|
139
|
+
rubygems_version: 3.4.10
|
68
140
|
signing_key:
|
69
141
|
specification_version: 4
|
70
142
|
summary: Shell connectors for Chronicle-ETL
|