vtk 0.9.5 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d1dcc8ae6e6afb5c4c1c924795a50653a6e017e471898f5c200b56c5a7b6b65c
4
- data.tar.gz: 909287a71eec23d871a149a3cd44c5be0b1336fa7b5850c08eefb417a9842516
3
+ metadata.gz: c41b577facc94e32eb15aeea7f5817a49f54c31a568654c5dfe5da082d0775f9
4
+ data.tar.gz: 87820698519528ff97594bf9e148a0a20ca700026079a0fc2b65ede3bee84b19
5
5
  SHA512:
6
- metadata.gz: f2d233defa6fe42e20f5627d214a3d3ef6737cd7ea85ee33b65680d1028762087d38926a5b3d6ed9297256501ad18931d405a6e0403f7d81fb420f375bd2b345
7
- data.tar.gz: 85a0fd85fbc7c6c69bdc01714069a68bb2f0225180b89950d02c32e9cdbdcf7998d260925e637966665771cf57d4dc8b00d6002a995b42f5aca5a1495e77543e
6
+ metadata.gz: 729f7b3a93dd61661ad557d471796a059ada830970afe16bbeef7bdf84e0e92887a321249c5ffc025db051fe71dfffb00fdec836382e3041392ae3ddcc9e5b59
7
+ data.tar.gz: eb5411ac4f245210470d310767fabd2e877759a5dcb023b00ae4925e8dcb4dec16f7637441433ca978d809d807734b18e4e51d901f69107e8291d186309d2ba3
@@ -4,7 +4,7 @@ on: [push,pull_request]
4
4
 
5
5
  jobs:
6
6
  build:
7
- runs-on: ubuntu-20.04
7
+ runs-on: ubuntu-22.04
8
8
  steps:
9
9
  - uses: actions/checkout@v2
10
10
  - uses: ruby/setup-ruby@v1
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2021-08-06 20:18:41 UTC using RuboCop version 1.8.1.
3
+ # on 2023-12-15 17:05:02 UTC using RuboCop version 1.59.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -9,4 +9,9 @@
9
9
  # Offense count: 1
10
10
  # Configuration parameters: CountComments, CountAsOne.
11
11
  Metrics/ClassLength:
12
- Max: 405
12
+ Max: 403
13
+
14
+ # Offense count: 2
15
+ Style/OpenStructUse:
16
+ Exclude:
17
+ - 'lib/vtk/commands/socks/setup.rb'
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 3.2.2
1
+ ruby 3.4.7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## [v1.1.0](https://github.com/department-of-veterans-affairs/vtk/tree/v1.1.0) (2025-12-15)
4
+
5
+ [Full Changelog](https://github.com/department-of-veterans-affairs/vtk/compare/v1.0.0...v1.1.0)
6
+
7
+ **Merged pull requests:**
8
+
9
+ - feat(scan): add vtk scan machine for Shai-Hulud detection [\#64](https://github.com/department-of-veterans-affairs/vtk/pull/64) ([ericboehs](https://github.com/ericboehs))
10
+ - fix: resolve rubocop offenses in socks/setup.rb [\#66](https://github.com/department-of-veterans-affairs/vtk/pull/66) ([ericboehs](https://github.com/ericboehs))
11
+ - Fix rubocop exceptions and update GH to run on Ubuntu Latest [\#63](https://github.com/department-of-veterans-affairs/vtk/pull/63) ([ericboehs](https://github.com/ericboehs))
12
+
13
+ ## [v1.0.0](https://github.com/department-of-veterans-affairs/vtk/tree/v1.0.0) (2024-09-18)
14
+
15
+ 🎉
16
+
17
+ [Full Changelog](https://github.com/department-of-veterans-affairs/vtk/compare/v0.9.5...v1.0.0)
18
+
19
+ **Merged pull requests:**
20
+
21
+ - fix: OpenStruct is no longer auto required in Ruby 3.2 [\#62](https://github.com/department-of-veterans-affairs/vtk/pull/62) ([ericboehs](https://github.com/ericboehs))
22
+ - Update RuboCop w/ corrections [\#53](https://github.com/department-of-veterans-affairs/vtk/pull/53) ([ericboehs](https://github.com/ericboehs))
23
+
24
+ ## [v0.9.5](https://github.com/department-of-veterans-affairs/vtk/tree/v0.9.5) (2023-12-15)
25
+
26
+ [Full Changelog](https://github.com/department-of-veterans-affairs/vtk/compare/v0.9.4...v0.9.5)
27
+
28
+ **Merged pull requests:**
29
+
30
+ - Add sudo to proxy setup command for MacOS. [\#52](https://github.com/department-of-veterans-affairs/vtk/pull/52) ([omahane](https://github.com/omahane))
31
+
3
32
  ## [v0.9.4](https://github.com/department-of-veterans-affairs/vtk/tree/v0.9.4) (2022-04-11)
4
33
 
5
34
  [Full Changelog](https://github.com/department-of-veterans-affairs/vtk/compare/v0.9.3...v0.9.4)
data/Gemfile CHANGED
@@ -4,3 +4,13 @@ source 'https://rubygems.org'
4
4
 
5
5
  # Specify your gem's dependencies in vtk.gemspec
6
6
  gemspec
7
+
8
+ group :development do
9
+ gem 'github_changelog_generator', '~> 1.15.0'
10
+ gem 'pry', '~> 0.13.0'
11
+ gem 'rake', '~> 13.0.0'
12
+ gem 'rspec', '~> 3.10.0'
13
+ gem 'rubocop', '~> 1.59'
14
+ gem 'rubocop-rake', '~> 0.5.0'
15
+ gem 'rubocop-rspec', '~> 2.0.0'
16
+ end
data/README.md CHANGED
@@ -67,6 +67,39 @@ $ vtk socks off
67
67
  ----> Disconnected from SOCKS.
68
68
  ```
69
69
 
70
+ ### Scan
71
+
72
+ Security scanning commands for detecting malware and vulnerabilities.
73
+
74
+ ---
75
+
76
+ ```
77
+ $ vtk scan machine
78
+ ```
79
+
80
+ The **machine subcommand** checks your local machine for signs of active Shai-Hulud malware infection. This is a fast (~5 second) check that looks for:
81
+
82
+ - **Critical indicators**: `~/.dev-env/` persistence folder, malicious processes (Runner.Listener, SHA1HULUD), malware payload files
83
+ - **High-risk indicators**: Exfiltration artifacts, unexpected Bun/Trufflehog installations
84
+ - **Credential inventory**: Lists credential files that should be rotated if infected
85
+
86
+ **Exit codes:**
87
+ - `0` - Clean (no infection indicators)
88
+ - `1` - Infected (critical indicators found)
89
+ - `2` - Warning (needs investigation)
90
+
91
+ **Options:**
92
+ - `--verbose` / `-v` - Detailed output with all checks
93
+ - `--quiet` / `-q` - Exit code only, no output
94
+ - `--json` / `-j` - JSON output format
95
+
96
+ Example:
97
+ ```
98
+ $ vtk scan machine --quiet && echo "Clean" || echo "Check machine!"
99
+ ```
100
+
101
+ ---
102
+
70
103
  ### Help
71
104
 
72
105
  For helpful information about commands and subcommands run the following:
@@ -74,6 +107,7 @@ For helpful information about commands and subcommands run the following:
74
107
  $ vtk -h
75
108
  $ vtk module -h
76
109
  $ vtk socks -h
110
+ $ vtk scan -h
77
111
 
78
112
  ### Docker
79
113
 
data/lib/vtk/cli.rb CHANGED
@@ -23,5 +23,8 @@ module Vtk
23
23
 
24
24
  require_relative 'commands/module'
25
25
  register Vtk::Commands::Module, 'module', 'module [SUBCOMMAND]', 'Command description...'
26
+
27
+ require_relative 'commands/scan'
28
+ register Vtk::Commands::Scan, 'scan', 'scan [SUBCOMMAND]', 'Security scanning for malware and vulnerabilities'
26
29
  end
27
30
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require_relative '../../command'
5
+
6
+ module Vtk
7
+ module Commands
8
+ class Scan
9
+ # Check for active malware infection indicators (Shai-Hulud)
10
+ class Machine < Vtk::Command
11
+ attr_reader :options
12
+
13
+ def initialize(options)
14
+ @options = options
15
+ super()
16
+ end
17
+
18
+ def execute(output: $stdout)
19
+ @output = output
20
+
21
+ script_path, gem_root = find_script
22
+ return script_not_found(output, gem_root) unless script_path
23
+
24
+ run_script(script_path)
25
+ end
26
+
27
+ private
28
+
29
+ def script_not_found(output, gem_root)
30
+ output.puts 'ERROR: Could not find shai-hulud-machine-check.sh script'
31
+ output.puts "Expected at: #{gem_root}/scripts/shai-hulud-machine-check.sh"
32
+ 1
33
+ end
34
+
35
+ def run_script(script_path)
36
+ cmd = ['bash', script_path]
37
+ cmd << '--verbose' if options[:verbose]
38
+ cmd << '--json' if options[:json]
39
+ cmd << '--quiet' if options[:quiet]
40
+ cmd << "--scan-dirs=#{options[:scan_dirs]}" if options[:scan_dirs]
41
+
42
+ system(*cmd)
43
+ $CHILD_STATUS.exitstatus
44
+ end
45
+
46
+ def find_script
47
+ # Look for script relative to this gem's location
48
+ # __dir__ = lib/vtk/commands/scan, so go up 4 levels to get to vtk root
49
+ gem_root = File.expand_path('../../../..', __dir__)
50
+ script_path = File.join(gem_root, 'scripts', 'shai-hulud-machine-check.sh')
51
+
52
+ # Use explicit bash interpreter, so executable bit not required
53
+ return [script_path, gem_root] if File.exist?(script_path)
54
+
55
+ [nil, gem_root]
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Vtk
6
+ module Commands
7
+ # Security scanning commands for developer machines and repositories
8
+ class Scan < Thor
9
+ namespace :scan
10
+
11
+ desc 'machine', 'Check for active malware infection indicators (Shai-Hulud)'
12
+ method_option :help, aliases: '-h', type: :boolean,
13
+ desc: 'Display usage information'
14
+ method_option :verbose, aliases: '-v', type: :boolean,
15
+ desc: 'Detailed output with all checks'
16
+ method_option :json, aliases: '-j', type: :boolean,
17
+ desc: 'Output results as JSON'
18
+ method_option :quiet, aliases: '-q', type: :boolean,
19
+ desc: 'Exit code only, no output'
20
+ method_option :scan_dirs, type: :string,
21
+ desc: 'Additional directories to scan for backdoor workflows (comma-separated)'
22
+ def machine
23
+ if options[:help]
24
+ invoke :help, ['machine']
25
+ else
26
+ require_relative 'scan/machine'
27
+ exit_status = Vtk::Commands::Scan::Machine.new(options).execute
28
+ exit exit_status
29
+ end
30
+ end
31
+
32
+ # Future subcommands:
33
+ # desc 'repos', 'Scan lockfiles for compromised packages'
34
+ # desc 'credentials', 'Inventory credentials that may need rotation'
35
+ end
36
+ end
37
+ end
@@ -30,14 +30,14 @@ module Vtk
30
30
 
31
31
  def connected?
32
32
  system "curl --max-time 2 -sL --proxy socks5h://127.0.0.1:#{port} sentry.vfs.va.gov 2> /dev/null | " \
33
- 'grep -q sentry-logo'
33
+ 'grep -q sentry-logo'
34
34
  end
35
35
 
36
36
  def connect_to_socks
37
37
  exit if system "lsof -Pi :#{port} -sTCP:LISTEN -t > /dev/null"
38
38
 
39
39
  Process.spawn "nohup ssh -vvv -o ServerAliveInterval=60 -o ConnectTimeout=5 socks -D #{port} -N" \
40
- '> /tmp/socks.log 2>&1 &'
40
+ '> /tmp/socks.log 2>&1 &'
41
41
  end
42
42
 
43
43
  def ensure_connection
@@ -4,6 +4,7 @@ require_relative '../../command'
4
4
  require 'tty-prompt'
5
5
  require 'fileutils'
6
6
  require 'erb'
7
+ require 'ostruct'
7
8
 
8
9
  module Vtk
9
10
  module Commands
@@ -11,7 +12,7 @@ module Vtk
11
12
  # Sets up socks access to the VA network
12
13
  class Setup < Vtk::Command
13
14
  PROXY_URL = 'https://raw.githubusercontent.com/department-of-veterans-affairs/va.gov-team/master/' \
14
- 'scripts/socks/proxy.pac'
15
+ 'scripts/socks/proxy.pac'
15
16
 
16
17
  attr_reader :ssh_config_path, :input, :output, :boot_script_path, :ssh_key_path, :prompt, :port, :skip_test
17
18
 
@@ -19,9 +20,9 @@ module Vtk
19
20
  @options = options
20
21
  @prompt = TTY::Prompt.new interrupt: :exit
21
22
  @port = options['port'] || '2001'
22
- @boot_script_path = options['boot_script_path'] || "#{ENV['HOME']}/Library"
23
- @ssh_key_path = options['ssh_key_path'] || "#{ENV['HOME']}/.ssh/id_rsa_vagov"
24
- @ssh_config_path = options['ssh_config_path'] || "#{ENV['HOME']}/.ssh/config"
23
+ @boot_script_path = options['boot_script_path'] || "#{Dir.home}/Library"
24
+ @ssh_key_path = options['ssh_key_path'] || "#{Dir.home}/.ssh/id_rsa_vagov"
25
+ @ssh_config_path = options['ssh_config_path'] || "#{Dir.home}/.ssh/config"
25
26
  @skip_test = options['skip_test'] || false
26
27
 
27
28
  super()
@@ -170,7 +171,7 @@ module Vtk
170
171
 
171
172
  ssh_agent_add
172
173
  system 'git config --global credential.helper > /dev/null || ' \
173
- "git config --global credential.helper 'cache --timeout=600'"
174
+ "git config --global credential.helper 'cache --timeout=600'"
174
175
  cloned = system(
175
176
  "git clone --quiet#{' --depth 1' if macos?} --no-checkout --filter=blob:none #{repo_url} '/tmp/dova-devops'"
176
177
  )
@@ -207,7 +208,7 @@ module Vtk
207
208
  @repo_url ||= begin
208
209
  keyscan_github_com
209
210
 
210
- if github_ssh_configured
211
+ if github_ssh_configured?
211
212
  'git@github.com:department-of-veterans-affairs/devops.git'
212
213
  else
213
214
  'https://github.com/department-of-veterans-affairs/devops.git'
@@ -221,7 +222,7 @@ module Vtk
221
222
  `ssh-keyscan -H github.com >> ~/.ssh/known_hosts 2> /dev/null`
222
223
  end
223
224
 
224
- def github_ssh_configured
225
+ def github_ssh_configured?
225
226
  !`ssh -T git@github.com 2>&1`.include?('Permission denied')
226
227
  end
227
228
 
@@ -230,7 +231,7 @@ module Vtk
230
231
 
231
232
  if File.exist? "#{ssh_config_path}.bak"
232
233
  log "!!! ERROR: Could not make backup of #{pretty_ssh_config_path} as #{pretty_ssh_config_path}.bak " \
233
- 'exists. Aborting.'
234
+ 'exists. Aborting.'
234
235
  exit 1
235
236
  end
236
237
 
@@ -256,7 +257,7 @@ module Vtk
256
257
  IdentityFile #{pretty_ssh_key_path}
257
258
  CFG
258
259
 
259
- IO.write ssh_config_path, keychain_config, mode: 'a'
260
+ File.write ssh_config_path, keychain_config, mode: 'a'
260
261
  end
261
262
 
262
263
  def ssh_config_configured_with_keychain?
@@ -330,7 +331,7 @@ module Vtk
330
331
  def wsl_configure_system_boot
331
332
  return true if File.exist? socks_bat
332
333
 
333
- IO.write socks_bat, 'wsl nohup bash -c "/usr/bin/ssh socks -N &" < nul > nul 2>&1', mode: 'a'
334
+ File.write socks_bat, 'wsl nohup bash -c "/usr/bin/ssh socks -N &" < nul > nul 2>&1', mode: 'a'
334
335
  end
335
336
 
336
337
  def socks_bat
@@ -400,7 +401,7 @@ module Vtk
400
401
  autossh_path: `which autossh`.chomp,
401
402
  port: @port,
402
403
  boot_script_path: File.realpath(boot_script_path),
403
- user: ENV['USER']
404
+ user: ENV.fetch('USER', nil)
404
405
  )
405
406
  end
406
407
 
@@ -429,7 +430,7 @@ module Vtk
429
430
  autossh_path: `which autossh`.chomp,
430
431
  port: @port,
431
432
  ssh_key_path: ssh_key_path,
432
- user: ENV['USER']
433
+ user: ENV.fetch('USER', nil)
433
434
  )
434
435
  end
435
436
 
@@ -459,8 +460,8 @@ module Vtk
459
460
  return true if `gsettings get org.gnome.system.proxy mode` == "'auto'\n"
460
461
 
461
462
  log 'Configuring system proxy to use SOCKS tunnel...' do
462
- `gsettings set org.gnome.system.proxy mode 'auto'` &&
463
- `gsettings set org.gnome.system.proxy autoconfig-url "#{PROXY_URL}"`
463
+ system("gsettings set org.gnome.system.proxy mode 'auto'") &&
464
+ system("gsettings set org.gnome.system.proxy autoconfig-url '#{PROXY_URL}'")
464
465
  end
465
466
  end
466
467
 
@@ -479,10 +480,8 @@ module Vtk
479
480
  end
480
481
 
481
482
  def network_interfaces
482
- @network_interfaces ||= begin
483
- `networksetup -listallnetworkservices`.split("\n").drop(1).select do |network_interface|
484
- `networksetup -getautoproxyurl "#{network_interface}"`.start_with?('URL: (null)')
485
- end
483
+ @network_interfaces ||= `networksetup -listallnetworkservices`.split("\n").drop(1).select do |interface|
484
+ `networksetup -getautoproxyurl "#{interface}"`.start_with?('URL: (null)')
486
485
  end
487
486
  end
488
487
 
@@ -509,7 +508,7 @@ module Vtk
509
508
  end
510
509
 
511
510
  def pretty_path(path)
512
- path.gsub ENV['HOME'], '~'
511
+ path.gsub Dir.home, '~'
513
512
  end
514
513
 
515
514
  def log(message)
data/lib/vtk/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vtk
4
- VERSION = '0.9.5'
4
+ VERSION = '1.1.0'
5
5
  end
@@ -0,0 +1,531 @@
1
+ #!/bin/bash
2
+ #
3
+ # Shai-Hulud Machine Infection Checker
4
+ # =====================================
5
+ #
6
+ # Quick script to check if your machine shows signs of active Shai-Hulud infection.
7
+ # This is a FAST check (~5 seconds) for infection indicators only.
8
+ #
9
+ # WHAT THIS CHECKS:
10
+ #
11
+ # CRITICAL (Active Infection):
12
+ # - ~/.dev-env/ persistence folder (contains GitHub self-hosted runner)
13
+ # - ~/.truffler-cache/ malware cache (NOT legit Trufflehog which uses ~/.trufflehog/)
14
+ # - Running processes: Runner.Listener, SHA1HULUD, suspicious node/bun
15
+ # - Malware files: setup_bun.js, bun_environment.js
16
+ # - Backdoor workflows: .github/workflows/discussion.yaml
17
+ #
18
+ # HIGH (Exfiltration Likely Occurred):
19
+ # - Exfiltration artifacts: cloud.json, truffleSecrets.json, etc.
20
+ # - Unexpected ~/.bun/ installation
21
+ # - Unexpected Trufflehog binary
22
+ #
23
+ # INFO (Credentials to Rotate if Infected):
24
+ # - Lists credential files the malware targets
25
+ # - ~/.npmrc, ~/.aws/, ~/.config/gh/, etc.
26
+ #
27
+ # EXIT CODES:
28
+ # 0 - Clean (no infection indicators found)
29
+ # 1 - INFECTED (critical indicators found)
30
+ # 2 - WARNING (high-risk indicators, needs investigation)
31
+ #
32
+ # USAGE:
33
+ # ./shai-hulud-machine-check.sh # Compact output
34
+ # ./shai-hulud-machine-check.sh --verbose # Detailed output
35
+ # ./shai-hulud-machine-check.sh --quiet # Exit code only
36
+ # ./shai-hulud-machine-check.sh --json # JSON output
37
+ # ./shai-hulud-machine-check.sh --scan-dirs=~/repos,~/work # Additional dirs
38
+ #
39
+ # References:
40
+ # - Wiz.io: https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack
41
+ # - Datadog: https://securitylabs.datadoghq.com/articles/shai-hulud-2.0-npm-worm/
42
+ #
43
+ # Author: Eric Boehs / EERT (with Claude Code)
44
+ # Version: 1.1.0
45
+ # Date: December 2025
46
+ #
47
+
48
+ set -e
49
+
50
+ # Parse arguments
51
+ QUIET=false
52
+ JSON=false
53
+ VERBOSE=false
54
+ EXTRA_SCAN_DIRS=""
55
+ for arg in "$@"; do
56
+ case $arg in
57
+ --quiet|-q) QUIET=true ;;
58
+ --json|-j) JSON=true ;;
59
+ --verbose|-v) VERBOSE=true ;;
60
+ --scan-dirs=*) EXTRA_SCAN_DIRS="${arg#*=}" ;;
61
+ --help|-h)
62
+ echo "Usage: $0 [--verbose|-v] [--quiet|-q] [--json|-j] [--scan-dirs=DIR1,DIR2,...]"
63
+ echo " --verbose Detailed output with all checks"
64
+ echo " --quiet Exit code only, no output"
65
+ echo " --json JSON output format"
66
+ echo " --scan-dirs Additional directories to scan for backdoor workflows (comma-separated)"
67
+ exit 0
68
+ ;;
69
+ esac
70
+ done
71
+
72
+ # Default directories to scan for backdoor workflows
73
+ DEFAULT_SCAN_DIRS=("$HOME/Code" "$HOME/Projects" "$HOME/src" "$HOME/dev" "$HOME/workspace")
74
+
75
+ # Build final scan dirs list
76
+ SCAN_DIRS=("${DEFAULT_SCAN_DIRS[@]}")
77
+ if [ -n "$EXTRA_SCAN_DIRS" ]; then
78
+ IFS=',' read -ra EXTRA_DIRS <<< "$EXTRA_SCAN_DIRS"
79
+ for dir in "${EXTRA_DIRS[@]}"; do
80
+ # Expand ~ to $HOME
81
+ expanded_dir="${dir/#\~/$HOME}"
82
+ SCAN_DIRS+=("$expanded_dir")
83
+ done
84
+ fi
85
+
86
+ # Results tracking
87
+ CRITICAL_FINDINGS=()
88
+ HIGH_FINDINGS=()
89
+ INFO_FINDINGS=()
90
+
91
+ # Colors (disabled in quiet/json mode)
92
+ if [ "$QUIET" = false ] && [ "$JSON" = false ] && [ -t 1 ]; then
93
+ RED='\033[0;31m'
94
+ YELLOW='\033[0;33m'
95
+ GREEN='\033[0;32m'
96
+ CYAN='\033[0;36m'
97
+ BOLD='\033[1m'
98
+ NC='\033[0m' # No Color
99
+ else
100
+ RED='' YELLOW='' GREEN='' CYAN='' BOLD='' NC=''
101
+ fi
102
+
103
+ # Logging functions
104
+ log() {
105
+ if [ "$QUIET" = false ] && [ "$JSON" = false ]; then
106
+ echo -e "$@"
107
+ fi
108
+ }
109
+
110
+ log_verbose() {
111
+ if [ "$VERBOSE" = true ] && [ "$QUIET" = false ] && [ "$JSON" = false ]; then
112
+ echo -e "$@"
113
+ fi
114
+ }
115
+
116
+ log_critical() {
117
+ CRITICAL_FINDINGS+=("$1")
118
+ }
119
+
120
+ log_high() {
121
+ HIGH_FINDINGS+=("$1")
122
+ }
123
+
124
+ log_info() {
125
+ INFO_FINDINGS+=("$1")
126
+ }
127
+
128
+ # Verbose header
129
+ log_verbose ""
130
+ log_verbose "${BOLD}========================================${NC}"
131
+ log_verbose "${BOLD} Shai-Hulud Machine Infection Check${NC}"
132
+ log_verbose "${BOLD}========================================${NC}"
133
+ log_verbose ""
134
+ log_verbose "Checking for active infection indicators..."
135
+ log_verbose ""
136
+
137
+ ###########################################
138
+ # CRITICAL CHECKS
139
+ ###########################################
140
+
141
+ log_verbose "${BOLD}=== Critical Checks ===${NC}"
142
+ log_verbose ""
143
+
144
+ # 1. Check for ~/.dev-env/ persistence folder
145
+ log_verbose "Checking for persistence folder (~/.dev-env/)..."
146
+ if [ -d "$HOME/.dev-env" ]; then
147
+ log_critical "~/.dev-env/ persistence folder found"
148
+ log_verbose " ${RED}[CRITICAL]${NC} PERSISTENCE FOLDER FOUND: ~/.dev-env/"
149
+ log_verbose " This folder contains the malware's self-hosted GitHub runner."
150
+ log_verbose " Contents:"
151
+ ls -la "$HOME/.dev-env" 2>/dev/null | head -10 | while read line; do log_verbose " $line"; done
152
+ else
153
+ log_verbose " ${GREEN}Not found${NC}"
154
+ fi
155
+
156
+ # 1b. Check for ~/.truffler-cache/ (malware-specific Trufflehog cache)
157
+ log_verbose ""
158
+ log_verbose "Checking for malware Trufflehog cache (~/.truffler-cache/)..."
159
+ if [ -d "$HOME/.truffler-cache" ]; then
160
+ log_critical "~/.truffler-cache/ malware cache found"
161
+ log_verbose " ${RED}[CRITICAL]${NC} MALWARE CACHE FOUND: ~/.truffler-cache/"
162
+ log_verbose " This is a MALWARE-SPECIFIC path (legit Trufflehog uses ~/.trufflehog/)."
163
+ log_verbose " The malware stores its Trufflehog binary here to scan for secrets."
164
+ log_verbose " Contents:"
165
+ ls -la "$HOME/.truffler-cache" 2>/dev/null | head -10 | while read line; do log_verbose " $line"; done
166
+ else
167
+ log_verbose " ${GREEN}Not found${NC}"
168
+ fi
169
+
170
+ # 2. Check for malicious running processes
171
+ log_verbose ""
172
+ log_verbose "Checking for malicious processes..."
173
+
174
+ # Runner.Listener (GitHub self-hosted runner - malware installs this)
175
+ if pgrep -f "Runner.Listener" > /dev/null 2>&1; then
176
+ log_critical "Runner.Listener process running"
177
+ log_verbose " ${RED}[CRITICAL]${NC} MALICIOUS PROCESS: Runner.Listener is running"
178
+ log_verbose " PIDs: $(pgrep -f 'Runner.Listener' | tr '\n' ' ')"
179
+ fi
180
+
181
+ # SHA1HULUD process
182
+ if pgrep -f "SHA1HULUD" > /dev/null 2>&1; then
183
+ log_critical "SHA1HULUD process running"
184
+ log_verbose " ${RED}[CRITICAL]${NC} MALICIOUS PROCESS: SHA1HULUD is running"
185
+ log_verbose " PIDs: $(pgrep -f 'SHA1HULUD' | tr '\n' ' ')"
186
+ fi
187
+
188
+ # Check for node processes running from ~/.dev-env
189
+ if pgrep -f "$HOME/.dev-env" > /dev/null 2>&1; then
190
+ log_critical "Process running from ~/.dev-env"
191
+ log_verbose " ${RED}[CRITICAL]${NC} MALICIOUS PROCESS: Process running from ~/.dev-env"
192
+ log_verbose " PIDs: $(pgrep -f "$HOME/.dev-env" | tr '\n' ' ')"
193
+ fi
194
+
195
+ # Check for suspicious bun processes (from ~/.bun if unexpected)
196
+ if pgrep -f "$HOME/.bun/bin/bun" > /dev/null 2>&1; then
197
+ log_high "bun process running from ~/.bun/bin/bun"
198
+ log_verbose " ${YELLOW}[HIGH]${NC} SUSPICIOUS PROCESS: bun running from ~/.bun/bin/bun"
199
+ log_verbose " This could be legitimate if you installed Bun intentionally."
200
+ log_verbose " PIDs: $(pgrep -f "$HOME/.bun/bin/bun" | tr '\n' ' ')"
201
+ fi
202
+
203
+ # If no malicious processes found
204
+ if ! pgrep -f "Runner.Listener" > /dev/null 2>&1 && \
205
+ ! pgrep -f "SHA1HULUD" > /dev/null 2>&1 && \
206
+ ! pgrep -f "$HOME/.dev-env" > /dev/null 2>&1; then
207
+ log_verbose " ${GREEN}No malicious processes detected${NC}"
208
+ fi
209
+
210
+ # 3. Check for malware payload files in home directory
211
+ log_verbose ""
212
+ log_verbose "Checking for malware files in home directory..."
213
+ MALWARE_FILES=("setup_bun.js" "bun_environment.js")
214
+ MALWARE_FOUND=false
215
+ for file in "${MALWARE_FILES[@]}"; do
216
+ if [ -f "$HOME/$file" ]; then
217
+ log_critical "Malware file: ~/$file"
218
+ log_verbose " ${RED}[CRITICAL]${NC} MALWARE FILE FOUND: ~/$file"
219
+ MALWARE_FOUND=true
220
+ fi
221
+ done
222
+
223
+ # Check in common locations
224
+ for dir in "$HOME" "$HOME/Desktop" "$HOME/Downloads" "/tmp"; do
225
+ if [ -d "$dir" ]; then
226
+ for file in "${MALWARE_FILES[@]}"; do
227
+ FOUND=$(find "$dir" -maxdepth 2 -name "$file" -type f 2>/dev/null | head -5)
228
+ if [ -n "$FOUND" ]; then
229
+ while IFS= read -r f; do
230
+ log_critical "Malware file: $f"
231
+ log_verbose " ${RED}[CRITICAL]${NC} MALWARE FILE FOUND: $f"
232
+ MALWARE_FOUND=true
233
+ done <<< "$FOUND"
234
+ fi
235
+ done
236
+ fi
237
+ done
238
+
239
+ if [ "$MALWARE_FOUND" = false ]; then
240
+ log_verbose " ${GREEN}No malware files detected${NC}"
241
+ fi
242
+
243
+ # 4. Check for backdoor workflow files
244
+ log_verbose ""
245
+ log_verbose "Checking for backdoor workflow files..."
246
+ log_verbose " Scanning directories:"
247
+ for dir in "${SCAN_DIRS[@]}"; do
248
+ if [ -d "$dir" ]; then
249
+ log_verbose " - $dir"
250
+ else
251
+ log_verbose " - $dir ${YELLOW}(not found)${NC}"
252
+ fi
253
+ done
254
+ log_verbose ""
255
+ BACKDOOR_FOUND=false
256
+ # Search for discussion.yaml in .github/workflows directories
257
+ # The malicious workflow contains: runs-on: self-hosted AND ${{ github.event.discussion.body }}
258
+ for base_dir in "${SCAN_DIRS[@]}"; do
259
+ if [ -d "$base_dir" ]; then
260
+ FOUND=$(find "$base_dir" -maxdepth 5 -path "*/.github/workflows/discussion.yaml" -type f 2>/dev/null | head -10)
261
+ if [ -n "$FOUND" ]; then
262
+ while IFS= read -r f; do
263
+ # Check if file contains the malicious pattern
264
+ if grep -q "self-hosted" "$f" 2>/dev/null && grep -q "github.event.discussion" "$f" 2>/dev/null; then
265
+ log_critical "Backdoor workflow: $f"
266
+ log_verbose " ${RED}[CRITICAL]${NC} BACKDOOR WORKFLOW FOUND: $f"
267
+ log_verbose " This workflow uses self-hosted runner with discussion body injection."
268
+ BACKDOOR_FOUND=true
269
+ else
270
+ log_high "Unverified discussion.yaml: $f"
271
+ log_verbose " ${YELLOW}[HIGH]${NC} UNVERIFIED WORKFLOW: $f"
272
+ log_verbose " Found discussion.yaml - please verify this is legitimate."
273
+ fi
274
+ done <<< "$FOUND"
275
+ fi
276
+ fi
277
+ done
278
+
279
+ if [ "$BACKDOOR_FOUND" = false ]; then
280
+ log_verbose " ${GREEN}No backdoor workflows detected${NC}"
281
+ fi
282
+
283
+ ###########################################
284
+ # HIGH-RISK CHECKS
285
+ ###########################################
286
+
287
+ log_verbose ""
288
+ log_verbose "${BOLD}=== High-Risk Checks ===${NC}"
289
+ log_verbose ""
290
+
291
+ # 4. Check for exfiltration artifacts
292
+ log_verbose "Checking for exfiltration artifacts..."
293
+ EXFIL_FILES=("cloud.json" "truffleSecrets.json" "environment.json" "actionsSecrets.json" "contents.json")
294
+ EXFIL_FOUND=false
295
+ for file in "${EXFIL_FILES[@]}"; do
296
+ # Check home directory and common locations
297
+ for dir in "$HOME" "/tmp" "$HOME/Desktop" "$HOME/Downloads"; do
298
+ if [ -f "$dir/$file" ]; then
299
+ log_high "Exfiltration artifact: $dir/$file"
300
+ log_verbose " ${YELLOW}[HIGH]${NC} EXFILTRATION ARTIFACT: $dir/$file"
301
+ EXFIL_FOUND=true
302
+ fi
303
+ done
304
+ done
305
+
306
+ if [ "$EXFIL_FOUND" = false ]; then
307
+ log_verbose " ${GREEN}No exfiltration artifacts detected${NC}"
308
+ fi
309
+
310
+ # 5. Check for unexpected Bun installation
311
+ log_verbose ""
312
+ log_verbose "Checking for unexpected Bun installation..."
313
+ if [ -d "$HOME/.bun" ]; then
314
+ log_high "~/.bun/ installation found"
315
+ log_verbose " ${YELLOW}[HIGH]${NC} BUN INSTALLATION FOUND: ~/.bun/"
316
+ log_verbose " If you did NOT install Bun intentionally, this is suspicious."
317
+ log_verbose " Bun version: $(~/.bun/bin/bun --version 2>/dev/null || echo 'unknown')"
318
+ elif command -v bun &> /dev/null; then
319
+ BUN_PATH=$(which bun)
320
+ log_high "Bun found: $BUN_PATH"
321
+ log_verbose " ${YELLOW}[HIGH]${NC} BUN FOUND IN PATH: $BUN_PATH"
322
+ log_verbose " If you did NOT install Bun intentionally, investigate."
323
+ else
324
+ log_verbose " ${GREEN}No unexpected Bun installation${NC}"
325
+ fi
326
+
327
+ # 6. Check for Trufflehog (malware downloads this to scan for secrets)
328
+ log_verbose ""
329
+ log_verbose "Checking for unexpected Trufflehog..."
330
+ if command -v trufflehog &> /dev/null; then
331
+ TH_PATH=$(which trufflehog)
332
+ log_high "Trufflehog found: $TH_PATH"
333
+ log_verbose " ${YELLOW}[HIGH]${NC} TRUFFLEHOG FOUND: $TH_PATH"
334
+ log_verbose " The malware uses Trufflehog to scan for secrets."
335
+ log_verbose " If you did NOT install this intentionally, investigate."
336
+ elif [ -f "$HOME/.local/bin/trufflehog" ] || [ -f "/tmp/trufflehog" ]; then
337
+ log_high "Trufflehog in suspicious location"
338
+ log_verbose " ${YELLOW}[HIGH]${NC} TRUFFLEHOG FOUND in suspicious location"
339
+ else
340
+ log_verbose " ${GREEN}No unexpected Trufflehog installation${NC}"
341
+ fi
342
+
343
+ ###########################################
344
+ # INFORMATIONAL - Credentials at Risk
345
+ ###########################################
346
+
347
+ log_verbose ""
348
+ log_verbose "${BOLD}=== Credential Files (Rotate if Infected) ===${NC}"
349
+ log_verbose ""
350
+ log_verbose "These are files the malware targets for exfiltration."
351
+ log_verbose "If your machine is infected, ROTATE ALL OF THESE:"
352
+ log_verbose ""
353
+
354
+ # NPM tokens
355
+ if [ -f "$HOME/.npmrc" ]; then
356
+ if grep -qi "authtoken\|_auth" "$HOME/.npmrc" 2>/dev/null; then
357
+ log_info "~/.npmrc contains auth tokens"
358
+ log_verbose " ${CYAN}[INFO]${NC} ~/.npmrc contains auth tokens - ROTATE NPM TOKENS"
359
+ else
360
+ log_verbose " ~/.npmrc exists (no tokens detected)"
361
+ fi
362
+ else
363
+ log_verbose " ~/.npmrc not found"
364
+ fi
365
+
366
+ # AWS credentials
367
+ if [ -d "$HOME/.aws" ]; then
368
+ log_info "~/.aws/ exists"
369
+ log_verbose " ${CYAN}[INFO]${NC} ~/.aws/ exists - ROTATE AWS ACCESS KEYS"
370
+ else
371
+ log_verbose " ~/.aws/ not found"
372
+ fi
373
+
374
+ # GCP credentials
375
+ if [ -f "$HOME/.config/gcloud/application_default_credentials.json" ]; then
376
+ log_info "GCP ADC exists"
377
+ log_verbose " ${CYAN}[INFO]${NC} GCP ADC exists - RE-AUTHENTICATE with 'gcloud auth application-default login'"
378
+ else
379
+ log_verbose " GCP ADC not found"
380
+ fi
381
+
382
+ # Azure credentials
383
+ if [ -d "$HOME/.azure" ]; then
384
+ log_info "~/.azure/ exists"
385
+ log_verbose " ${CYAN}[INFO]${NC} ~/.azure/ exists - RE-AUTHENTICATE with 'az login'"
386
+ else
387
+ log_verbose " ~/.azure/ not found"
388
+ fi
389
+
390
+ # GitHub CLI
391
+ if [ -f "$HOME/.config/gh/hosts.yml" ]; then
392
+ log_info "GitHub CLI authenticated"
393
+ log_verbose " ${CYAN}[INFO]${NC} GitHub CLI authenticated - ROTATE TOKEN with 'gh auth logout && gh auth login'"
394
+ else
395
+ log_verbose " GitHub CLI not authenticated"
396
+ fi
397
+
398
+ # SSH keys
399
+ if [ -d "$HOME/.ssh" ] && ls "$HOME/.ssh/"*.pub &> /dev/null; then
400
+ log_info "SSH keys exist"
401
+ log_verbose " ${CYAN}[INFO]${NC} SSH keys exist (~/.ssh/) - Consider rotating if infected"
402
+ fi
403
+
404
+ # Git credentials
405
+ if [ -f "$HOME/.git-credentials" ]; then
406
+ log_info "~/.git-credentials exists"
407
+ log_verbose " ${CYAN}[INFO]${NC} ~/.git-credentials exists - ROTATE stored credentials"
408
+ fi
409
+
410
+ # Environment variables with secrets
411
+ SENSITIVE_VARS=$(env | grep -iE "token|key|secret|password|credential|auth|api" 2>/dev/null | wc -l | tr -d ' ')
412
+ if [ "$SENSITIVE_VARS" -gt 0 ]; then
413
+ log_info "$SENSITIVE_VARS sensitive env vars"
414
+ log_verbose " ${CYAN}[INFO]${NC} $SENSITIVE_VARS sensitive environment variables detected"
415
+ fi
416
+
417
+ ###########################################
418
+ # SUMMARY
419
+ ###########################################
420
+
421
+ log_verbose ""
422
+ log_verbose "${BOLD}========================================${NC}"
423
+
424
+ # Determine final status
425
+ EXIT_CODE=0
426
+ if [ ${#CRITICAL_FINDINGS[@]} -gt 0 ]; then
427
+ EXIT_CODE=1
428
+ if [ "$VERBOSE" = true ]; then
429
+ log "${RED}${BOLD} STATUS: INFECTED${NC}"
430
+ log "${RED}${BOLD}========================================${NC}"
431
+ log ""
432
+ log "${RED}CRITICAL FINDINGS (${#CRITICAL_FINDINGS[@]}):${NC}"
433
+ for finding in "${CRITICAL_FINDINGS[@]}"; do
434
+ log " - $finding"
435
+ done
436
+ log ""
437
+ log "${BOLD}IMMEDIATE ACTIONS:${NC}"
438
+ log " 1. DISCONNECT from network immediately"
439
+ log " 2. Do NOT run npm/yarn/node commands"
440
+ log " 3. Follow the cleanup playbook"
441
+ log " 4. Rotate ALL credentials listed above"
442
+ else
443
+ log "${RED}${BOLD}Shai-Hulud Check: INFECTED${NC}"
444
+ for finding in "${CRITICAL_FINDINGS[@]}"; do
445
+ log " ${RED}-${NC} $finding"
446
+ done
447
+ log ""
448
+ log "Run with ${BOLD}--verbose${NC} for details and remediation steps"
449
+ fi
450
+ elif [ ${#HIGH_FINDINGS[@]} -gt 0 ]; then
451
+ EXIT_CODE=2
452
+ if [ "$VERBOSE" = true ]; then
453
+ log "${YELLOW}${BOLD} STATUS: WARNING - INVESTIGATE${NC}"
454
+ log "${YELLOW}${BOLD}========================================${NC}"
455
+ log ""
456
+ log "${YELLOW}HIGH-RISK FINDINGS (${#HIGH_FINDINGS[@]}):${NC}"
457
+ for finding in "${HIGH_FINDINGS[@]}"; do
458
+ log " - $finding"
459
+ done
460
+ log ""
461
+ log "${BOLD}RECOMMENDED ACTIONS:${NC}"
462
+ log " 1. Verify if Bun/Trufflehog were installed intentionally"
463
+ log " 2. If not intentional, treat as potentially infected"
464
+ log " 3. Investigate further before running any package manager commands"
465
+ else
466
+ log "${YELLOW}${BOLD}Shai-Hulud Check: WARNING${NC}"
467
+ for finding in "${HIGH_FINDINGS[@]}"; do
468
+ log " ${YELLOW}-${NC} $finding"
469
+ done
470
+ log ""
471
+ log "These may be legitimate if you installed them intentionally."
472
+ log "Run with ${BOLD}--verbose${NC} for details or investigate if unexpected."
473
+ fi
474
+ else
475
+ if [ "$VERBOSE" = true ]; then
476
+ log "${GREEN}${BOLD} STATUS: CLEAN${NC}"
477
+ log "${GREEN}${BOLD}========================================${NC}"
478
+ log ""
479
+ log "${GREEN}No active infection indicators detected.${NC}"
480
+ log ""
481
+ log "Next steps:"
482
+ log " - Verify your repos don't have compromised packages"
483
+ log " - Check your lockfiles for known malicious packages"
484
+ else
485
+ log "${GREEN}${BOLD}Shai-Hulud Check: CLEAN${NC}"
486
+ fi
487
+ fi
488
+
489
+ log ""
490
+
491
+ # JSON output mode
492
+ if [ "$JSON" = true ]; then
493
+ # Build JSON array from bash array (no jq dependency)
494
+ json_array() {
495
+ local arr=("$@")
496
+ if [ ${#arr[@]} -eq 0 ]; then
497
+ echo '[]'
498
+ return
499
+ fi
500
+ local result='['
501
+ local first=true
502
+ for item in "${arr[@]}"; do
503
+ if [ "$first" = true ]; then
504
+ first=false
505
+ else
506
+ result+=','
507
+ fi
508
+ # Escape quotes and backslashes for JSON
509
+ item="${item//\\/\\\\}"
510
+ item="${item//\"/\\\"}"
511
+ result+="\"$item\""
512
+ done
513
+ result+=']'
514
+ echo "$result"
515
+ }
516
+
517
+ cat <<EOF
518
+ {
519
+ "status": "$([ ${#CRITICAL_FINDINGS[@]} -gt 0 ] && echo 'INFECTED' || ([ ${#HIGH_FINDINGS[@]} -gt 0 ] && echo 'WARNING' || echo 'CLEAN'))",
520
+ "critical_count": ${#CRITICAL_FINDINGS[@]},
521
+ "high_count": ${#HIGH_FINDINGS[@]},
522
+ "info_count": ${#INFO_FINDINGS[@]},
523
+ "critical_findings": $(json_array "${CRITICAL_FINDINGS[@]}"),
524
+ "high_findings": $(json_array "${HIGH_FINDINGS[@]}"),
525
+ "info_findings": $(json_array "${INFO_FINDINGS[@]}"),
526
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
527
+ }
528
+ EOF
529
+ fi
530
+
531
+ exit $EXIT_CODE
data/vtk.gemspec CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.metadata['documentation_uri'] = spec.homepage
19
19
  spec.metadata['source_code_uri'] = spec.homepage
20
20
  spec.metadata['changelog_uri'] = 'https://github.com/department-of-veterans-affairs/vtk/blob/master/CHANGELOG.md'
21
+ spec.metadata['rubygems_mfa_required'] = 'true'
21
22
 
22
23
  # Specify which files should be added to the gem when it is released.
23
24
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -31,15 +32,4 @@ Gem::Specification.new do |spec|
31
32
  spec.add_dependency 'thor', '> 0.20.3'
32
33
  spec.add_dependency 'tty-command', '~> 0.10.0'
33
34
  spec.add_dependency 'tty-prompt', '~> 0.23.0'
34
-
35
- spec.add_development_dependency 'github_changelog_generator', '~> 1.15.0'
36
- spec.add_development_dependency 'pry', '~> 0.13.0'
37
- spec.add_development_dependency 'rake', '~> 13.0.0'
38
- spec.add_development_dependency 'rspec', '~> 3.10.0'
39
- spec.add_development_dependency 'rubocop', '~> 1.8.0'
40
- spec.add_development_dependency 'rubocop-rake', '~> 0.5.0'
41
- spec.add_development_dependency 'rubocop-rspec', '~> 2.0.0'
42
-
43
- # For more information and examples about making a new gem, checkout our
44
- # guide at: https://bundler.io/guides/creating_gem.html
45
35
  end
metadata CHANGED
@@ -1,16 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vtk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Boehs
8
8
  - Lindsey Hattamer
9
9
  - Travis Hilton
10
- autorequire:
11
10
  bindir: exe
12
11
  cert_chain: []
13
- date: 2023-12-15 00:00:00.000000000 Z
12
+ date: 1980-01-02 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: thor
@@ -54,104 +53,6 @@ dependencies:
54
53
  - - "~>"
55
54
  - !ruby/object:Gem::Version
56
55
  version: 0.23.0
57
- - !ruby/object:Gem::Dependency
58
- name: github_changelog_generator
59
- requirement: !ruby/object:Gem::Requirement
60
- requirements:
61
- - - "~>"
62
- - !ruby/object:Gem::Version
63
- version: 1.15.0
64
- type: :development
65
- prerelease: false
66
- version_requirements: !ruby/object:Gem::Requirement
67
- requirements:
68
- - - "~>"
69
- - !ruby/object:Gem::Version
70
- version: 1.15.0
71
- - !ruby/object:Gem::Dependency
72
- name: pry
73
- requirement: !ruby/object:Gem::Requirement
74
- requirements:
75
- - - "~>"
76
- - !ruby/object:Gem::Version
77
- version: 0.13.0
78
- type: :development
79
- prerelease: false
80
- version_requirements: !ruby/object:Gem::Requirement
81
- requirements:
82
- - - "~>"
83
- - !ruby/object:Gem::Version
84
- version: 0.13.0
85
- - !ruby/object:Gem::Dependency
86
- name: rake
87
- requirement: !ruby/object:Gem::Requirement
88
- requirements:
89
- - - "~>"
90
- - !ruby/object:Gem::Version
91
- version: 13.0.0
92
- type: :development
93
- prerelease: false
94
- version_requirements: !ruby/object:Gem::Requirement
95
- requirements:
96
- - - "~>"
97
- - !ruby/object:Gem::Version
98
- version: 13.0.0
99
- - !ruby/object:Gem::Dependency
100
- name: rspec
101
- requirement: !ruby/object:Gem::Requirement
102
- requirements:
103
- - - "~>"
104
- - !ruby/object:Gem::Version
105
- version: 3.10.0
106
- type: :development
107
- prerelease: false
108
- version_requirements: !ruby/object:Gem::Requirement
109
- requirements:
110
- - - "~>"
111
- - !ruby/object:Gem::Version
112
- version: 3.10.0
113
- - !ruby/object:Gem::Dependency
114
- name: rubocop
115
- requirement: !ruby/object:Gem::Requirement
116
- requirements:
117
- - - "~>"
118
- - !ruby/object:Gem::Version
119
- version: 1.8.0
120
- type: :development
121
- prerelease: false
122
- version_requirements: !ruby/object:Gem::Requirement
123
- requirements:
124
- - - "~>"
125
- - !ruby/object:Gem::Version
126
- version: 1.8.0
127
- - !ruby/object:Gem::Dependency
128
- name: rubocop-rake
129
- requirement: !ruby/object:Gem::Requirement
130
- requirements:
131
- - - "~>"
132
- - !ruby/object:Gem::Version
133
- version: 0.5.0
134
- type: :development
135
- prerelease: false
136
- version_requirements: !ruby/object:Gem::Requirement
137
- requirements:
138
- - - "~>"
139
- - !ruby/object:Gem::Version
140
- version: 0.5.0
141
- - !ruby/object:Gem::Dependency
142
- name: rubocop-rspec
143
- requirement: !ruby/object:Gem::Requirement
144
- requirements:
145
- - - "~>"
146
- - !ruby/object:Gem::Version
147
- version: 2.0.0
148
- type: :development
149
- prerelease: false
150
- version_requirements: !ruby/object:Gem::Requirement
151
- requirements:
152
- - - "~>"
153
- - !ruby/object:Gem::Version
154
- version: 2.0.0
155
56
  description: This is a platform CLI tool for VFS developer usage.
156
57
  email:
157
58
  - eric.boehs@oddball.io
@@ -189,6 +90,8 @@ files:
189
90
  - lib/vtk/commands/module/model.rb
190
91
  - lib/vtk/commands/module/serializer.rb
191
92
  - lib/vtk/commands/module/service.rb
93
+ - lib/vtk/commands/scan.rb
94
+ - lib/vtk/commands/scan/machine.rb
192
95
  - lib/vtk/commands/socks.rb
193
96
  - lib/vtk/commands/socks/off.rb
194
97
  - lib/vtk/commands/socks/on.rb
@@ -199,6 +102,7 @@ files:
199
102
  - lib/vtk/templates/socks/setup/gov.va.socks.plist.erb
200
103
  - lib/vtk/templates/socks/setup/va_gov_socks.service.erb
201
104
  - lib/vtk/version.rb
105
+ - scripts/shai-hulud-machine-check.sh
202
106
  - vtk.gemspec
203
107
  homepage: https://github.com/department-of-veterans-affairs/vtk
204
108
  licenses:
@@ -208,7 +112,7 @@ metadata:
208
112
  documentation_uri: https://github.com/department-of-veterans-affairs/vtk
209
113
  source_code_uri: https://github.com/department-of-veterans-affairs/vtk
210
114
  changelog_uri: https://github.com/department-of-veterans-affairs/vtk/blob/master/CHANGELOG.md
211
- post_install_message:
115
+ rubygems_mfa_required: 'true'
212
116
  rdoc_options: []
213
117
  require_paths:
214
118
  - lib
@@ -223,8 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
223
127
  - !ruby/object:Gem::Version
224
128
  version: '0'
225
129
  requirements: []
226
- rubygems_version: 3.4.14
227
- signing_key:
130
+ rubygems_version: 3.6.9
228
131
  specification_version: 4
229
132
  summary: A CLI for the platform
230
133
  test_files: []