chronicle-shell 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 83dc9bab0e1b65930d8243a16ba6debe3d92cb4424b50e80d8511dc13dc6f3e4
4
- data.tar.gz: 57d4a5d1f434dedacb3071c3f96ac211990f42b92b75db1416b852b3b5f93806
3
+ metadata.gz: cba457d01ec6e7ee3719b80d980ee171797eabef20c7aaadc751e4e985f96f73
4
+ data.tar.gz: 8ec830dcd00de94eb7d9e9ff4a0942d3cc49f8975a88a792be4ad2be756630a4
5
5
  SHA512:
6
- metadata.gz: de0dba35c9ea5e3612c52f2adcf543c4922485e69e44e650113a89cb03ad24737bf7a1a271cdb3febd409ab02a481ed1d436a9374fe50a181c634dc6d69c2b93
7
- data.tar.gz: 5cbfef0e14a2d6e1ee7ffabdc99627ab5af6bad3d30368e082dad3057a701bc5dfef3da38e5ce7c8aadcf8689b35910060fe36486e060f1f7ffd358599a414c2
6
+ metadata.gz: f8f50a8c830a4687d9769d04d9a074f68feafb1ba9a07ac03dd2e4cc3e8794842436d2b968cc25cce91c41a555634ee5bf99a2bfcc5f70c37d306a08ce7108e0
7
+ data.tar.gz: 0ddbb6f0cb224a324a8b2150ca931e8f226b572b2e4a8fcb29df9616921a08c1b3f208c15737978e655ae3514a489ee234ce20378b973be37991e44a59ea427e
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ inherit_gem:
2
+ chronicle-core: .rubocop-plugin.yml
data/Gemfile CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source "https://rubygems.org"
3
+ source 'https://rubygems.org'
4
4
 
5
5
  # Specify your gem's dependencies in chronicle-shell.gemspec
6
6
  gemspec
7
7
 
8
- gem "rake", "~> 13.0"
8
+ gem 'rake', '~> 13.0'
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 Feb 7 as json
21
- chronicle-etl --extractor shell:history --transformer shell:history --since "2022-02-07" --loader json
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:history --limit 500 --fields command --silent | sort | uniq -c | sort -nr
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
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
3
+ require 'bundler/gem_tasks'
4
4
  task default: %i[]
@@ -1,24 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "lib/chronicle/shell/version"
3
+ require_relative 'lib/chronicle/shell/version'
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = "chronicle-shell"
6
+ spec.name = 'chronicle-shell'
7
7
  spec.version = Chronicle::Shell::VERSION
8
- spec.authors = ["Andrew Louis"]
9
- spec.email = ["andrew@hyfen.net"]
8
+ spec.authors = ['Andrew Louis']
9
+ spec.email = ['andrew@hyfen.net']
10
10
 
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.required_ruby_version = ">= 2.7.0"
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'] = "https://rubygems.org"
16
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
18
17
 
19
- spec.metadata["homepage_uri"] = spec.homepage
20
- spec.metadata["source_code_uri"] = "https://github.com/chronicle-app/chronicle-shell"
21
- spec.metadata["changelog_uri"] = "https://github.com/chronicle-app/chronicle-shell/releases"
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 = "exe"
29
+ spec.bindir = 'exe'
31
30
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
- spec.require_paths = ["lib"]
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 "chronicle-etl", "~> 0.5"
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.provider = 'shell'
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: username,
29
- hostname: hostname,
29
+ username:,
30
+ hostname:,
30
31
  shell_name: @config.shell
31
32
  }
32
- yield Chronicle::ETL::Extraction.new(data: command, meta: meta)
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, ".bash_history")
46
+ File.join(Dir.home, '.bash_history')
46
47
  end
47
48
 
48
49
  def history_filename_default_zsh
49
- File.join(Dir.home, ".zsh_history")
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
- loader = "load_commands_from_#{@config.shell}"
67
- __send__(loader) do |command|
68
- next if @config.since && command[:timestamp] < @config.since
69
- next if @config.until && command[:timestamp] > @config.until
70
-
71
- if block_given?
72
- yield command
73
- else
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: timestamp, command: command }
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.provider = 'shell'
8
+ r.source = :shell
9
+ r.type = :command
10
+ r.strategy = :local
8
11
  r.description = 'a shell command'
9
- r.identifier = 'history'
12
+ r.from_schema = :extraction
13
+ r.to_schema = :chronicle
10
14
  end
11
15
 
12
- def transform
13
- @command = @extraction.data
14
- build_commanded
15
- end
16
-
17
- def id
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
- def timestamp
21
- @command[:timestamp]
23
+ build_command(username:, hostname:, command:, shell_name:,
24
+ timestamp:)
22
25
  end
23
26
 
24
27
  private
25
28
 
26
- def build_commanded
27
- record = ::Chronicle::ETL::Models::Activity.new
28
- record.verb = 'commanded'
29
- record.end_at = timestamp
30
- record.provider = @extraction.meta[:shell_name]
31
- record.dedupe_on << [:verb, :end_at, :provider]
32
- record.involved = build_command
33
- record.actor = build_actor
34
- record
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 build_command
38
- record = ::Chronicle::ETL::Models::Entity.new
39
- record.represents = 'command'
40
- record.provider = @extraction.meta[:shell_name]
41
- record.body = @command[:command]
42
- record.dedupe_on << [:body, :provider, :represents]
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 build_actor
47
- record = ::Chronicle::ETL::Models::Entity.new
48
- record.represents = 'identity'
49
- record.provider = 'system'
50
- record.slug = build_user_slug
51
- record.dedupe_on << [:represents, :provider, :slug]
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
- def build_user_slug
56
- "#{@extraction.meta[:username]}@#{@extraction.meta[:hostname]}"
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Chronicle
4
4
  module Shell
5
- VERSION = "0.2.4"
5
+ VERSION = '0.3.0'
6
6
  end
7
7
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "shell/version"
4
- require_relative "shell/shell_history_extractor"
5
- require_relative "shell/shell_history_transformer"
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.2.4
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: 2022-03-25 00:00:00.000000000 Z
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.5'
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.5'
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: 2.7.0
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.3.3
139
+ rubygems_version: 3.4.10
68
140
  signing_key:
69
141
  specification_version: 4
70
142
  summary: Shell connectors for Chronicle-ETL