vtk 1.0.0 → 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 +4 -4
- data/.github/workflows/main.yml +1 -1
- data/.tool-versions +1 -1
- data/CHANGELOG.md +29 -0
- data/README.md +34 -0
- data/lib/vtk/cli.rb +3 -0
- data/lib/vtk/commands/scan/machine.rb +60 -0
- data/lib/vtk/commands/scan.rb +37 -0
- data/lib/vtk/commands/socks/setup.rb +4 -4
- data/lib/vtk/version.rb +1 -1
- data/scripts/shai-hulud-machine-check.sh +531 -0
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c41b577facc94e32eb15aeea7f5817a49f54c31a568654c5dfe5da082d0775f9
|
|
4
|
+
data.tar.gz: 87820698519528ff97594bf9e148a0a20ca700026079a0fc2b65ede3bee84b19
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 729f7b3a93dd61661ad557d471796a059ada830970afe16bbeef7bdf84e0e92887a321249c5ffc025db051fe71dfffb00fdec836382e3041392ae3ddcc9e5b59
|
|
7
|
+
data.tar.gz: eb5411ac4f245210470d310767fabd2e877759a5dcb023b00ae4925e8dcb4dec16f7637441433ca978d809d807734b18e4e51d901f69107e8291d186309d2ba3
|
data/.github/workflows/main.yml
CHANGED
data/.tool-versions
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
ruby 3.
|
|
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/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
|
|
@@ -208,7 +208,7 @@ module Vtk
|
|
|
208
208
|
@repo_url ||= begin
|
|
209
209
|
keyscan_github_com
|
|
210
210
|
|
|
211
|
-
if github_ssh_configured
|
|
211
|
+
if github_ssh_configured?
|
|
212
212
|
'git@github.com:department-of-veterans-affairs/devops.git'
|
|
213
213
|
else
|
|
214
214
|
'https://github.com/department-of-veterans-affairs/devops.git'
|
|
@@ -222,7 +222,7 @@ module Vtk
|
|
|
222
222
|
`ssh-keyscan -H github.com >> ~/.ssh/known_hosts 2> /dev/null`
|
|
223
223
|
end
|
|
224
224
|
|
|
225
|
-
def github_ssh_configured
|
|
225
|
+
def github_ssh_configured?
|
|
226
226
|
!`ssh -T git@github.com 2>&1`.include?('Permission denied')
|
|
227
227
|
end
|
|
228
228
|
|
|
@@ -460,8 +460,8 @@ module Vtk
|
|
|
460
460
|
return true if `gsettings get org.gnome.system.proxy mode` == "'auto'\n"
|
|
461
461
|
|
|
462
462
|
log 'Configuring system proxy to use SOCKS tunnel...' do
|
|
463
|
-
|
|
464
|
-
|
|
463
|
+
system("gsettings set org.gnome.system.proxy mode 'auto'") &&
|
|
464
|
+
system("gsettings set org.gnome.system.proxy autoconfig-url '#{PROXY_URL}'")
|
|
465
465
|
end
|
|
466
466
|
end
|
|
467
467
|
|
data/lib/vtk/version.rb
CHANGED
|
@@ -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
|
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: 1.
|
|
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:
|
|
12
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
14
13
|
dependencies:
|
|
15
14
|
- !ruby/object:Gem::Dependency
|
|
16
15
|
name: thor
|
|
@@ -91,6 +90,8 @@ files:
|
|
|
91
90
|
- lib/vtk/commands/module/model.rb
|
|
92
91
|
- lib/vtk/commands/module/serializer.rb
|
|
93
92
|
- lib/vtk/commands/module/service.rb
|
|
93
|
+
- lib/vtk/commands/scan.rb
|
|
94
|
+
- lib/vtk/commands/scan/machine.rb
|
|
94
95
|
- lib/vtk/commands/socks.rb
|
|
95
96
|
- lib/vtk/commands/socks/off.rb
|
|
96
97
|
- lib/vtk/commands/socks/on.rb
|
|
@@ -101,6 +102,7 @@ files:
|
|
|
101
102
|
- lib/vtk/templates/socks/setup/gov.va.socks.plist.erb
|
|
102
103
|
- lib/vtk/templates/socks/setup/va_gov_socks.service.erb
|
|
103
104
|
- lib/vtk/version.rb
|
|
105
|
+
- scripts/shai-hulud-machine-check.sh
|
|
104
106
|
- vtk.gemspec
|
|
105
107
|
homepage: https://github.com/department-of-veterans-affairs/vtk
|
|
106
108
|
licenses:
|
|
@@ -111,7 +113,6 @@ metadata:
|
|
|
111
113
|
source_code_uri: https://github.com/department-of-veterans-affairs/vtk
|
|
112
114
|
changelog_uri: https://github.com/department-of-veterans-affairs/vtk/blob/master/CHANGELOG.md
|
|
113
115
|
rubygems_mfa_required: 'true'
|
|
114
|
-
post_install_message:
|
|
115
116
|
rdoc_options: []
|
|
116
117
|
require_paths:
|
|
117
118
|
- lib
|
|
@@ -126,8 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
126
127
|
- !ruby/object:Gem::Version
|
|
127
128
|
version: '0'
|
|
128
129
|
requirements: []
|
|
129
|
-
rubygems_version: 3.
|
|
130
|
-
signing_key:
|
|
130
|
+
rubygems_version: 3.6.9
|
|
131
131
|
specification_version: 4
|
|
132
132
|
summary: A CLI for the platform
|
|
133
133
|
test_files: []
|