sym 2.3.0 → 2.4.3
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/CHANGELOG.md +55 -2
- data/README.md +106 -70
- data/Rakefile +3 -2
- data/bin/sym.completion +6 -2
- data/lib/sym.rb +28 -15
- data/lib/sym/app.rb +1 -3
- data/lib/sym/app/args.rb +5 -1
- data/lib/sym/app/cli.rb +54 -6
- data/lib/sym/app/cli_slop.rb +35 -23
- data/lib/sym/app/commands/base_command.rb +12 -19
- data/lib/sym/app/commands/bash_completion.rb +19 -3
- data/lib/sym/app/commands/decrypt.rb +1 -1
- data/lib/sym/app/commands/encrypt.rb +1 -1
- data/lib/sym/app/commands/generate_key.rb +7 -3
- data/lib/sym/app/commands/keychain_add_key.rb +7 -2
- data/lib/sym/app/commands/open_editor.rb +1 -1
- data/lib/sym/app/commands/password_protect_key.rb +9 -4
- data/lib/sym/app/commands/print_key.rb +3 -3
- data/lib/sym/app/commands/show_examples.rb +25 -17
- data/lib/sym/app/commands/show_help.rb +2 -2
- data/lib/sym/app/keychain.rb +5 -0
- data/lib/sym/app/output/base.rb +3 -7
- data/lib/sym/app/output/file.rb +0 -1
- data/lib/sym/app/output/noop.rb +2 -1
- data/lib/sym/app/password/providers.rb +4 -0
- data/lib/sym/app/password/providers/memcached_provider.rb +1 -1
- data/lib/sym/app/private_key/base64_decoder.rb +1 -0
- data/lib/sym/app/private_key/decryptor.rb +1 -0
- data/lib/sym/app/private_key/detector.rb +45 -26
- data/lib/sym/app/private_key/handler.rb +20 -25
- data/lib/sym/app/private_key/key_source_check.rb +89 -0
- data/lib/sym/application.rb +115 -33
- data/lib/sym/configuration.rb +1 -0
- data/lib/sym/constants.rb +24 -0
- data/lib/sym/data/decoder.rb +2 -1
- data/lib/sym/data/wrapper_struct.rb +1 -1
- data/lib/sym/extensions/stdlib.rb +1 -0
- data/lib/sym/version.rb +1 -1
- data/sym.gemspec +1 -1
- metadata +5 -4
- data/lib/sym/encrypted_file.rb +0 -34
data/lib/sym.rb
CHANGED
@@ -2,14 +2,19 @@ require 'colored2'
|
|
2
2
|
require 'zlib'
|
3
3
|
require 'logger'
|
4
4
|
|
5
|
-
|
5
|
+
require 'sym/configuration'
|
6
|
+
require 'sym/constants'
|
7
|
+
require 'sym/version'
|
8
|
+
require 'sym/errors'
|
6
9
|
|
7
10
|
Sym::Configuration.configure do |config|
|
8
|
-
config.password_cipher
|
9
|
-
config.data_cipher
|
10
|
-
config.private_key_cipher
|
11
|
-
config.compression_enabled
|
12
|
-
config.compression_level
|
11
|
+
config.password_cipher = 'AES-128-CBC'
|
12
|
+
config.data_cipher = 'AES-256-CBC'
|
13
|
+
config.private_key_cipher = config.data_cipher
|
14
|
+
config.compression_enabled = true
|
15
|
+
config.compression_level = Zlib::BEST_COMPRESSION
|
16
|
+
config.encrypted_file_extension = 'enc'
|
17
|
+
config.default_key_file = Sym::Constants::SYM_KEY_FILE
|
13
18
|
|
14
19
|
config.password_cache_timeout = 300
|
15
20
|
|
@@ -117,15 +122,23 @@ module Sym
|
|
117
122
|
end
|
118
123
|
end
|
119
124
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
+
class << self
|
126
|
+
def config
|
127
|
+
Sym::Configuration.config
|
128
|
+
end
|
129
|
+
|
130
|
+
def default_key_file
|
131
|
+
config.default_key_file
|
132
|
+
end
|
133
|
+
|
134
|
+
def default_key
|
135
|
+
File.read(default_key_file) rescue nil
|
136
|
+
end
|
137
|
+
|
138
|
+
def default_key?
|
139
|
+
File.exist?(default_key_file)
|
140
|
+
end
|
125
141
|
|
126
|
-
|
127
|
-
file: File.expand_path('../../bin/sym.completion', __FILE__),
|
128
|
-
script: "[[ -f '#{COMPLETION_PATH}' ]] && source '#{COMPLETION_PATH}'",
|
129
|
-
}.freeze
|
142
|
+
end
|
130
143
|
end
|
131
144
|
|
data/lib/sym/app.rb
CHANGED
@@ -26,7 +26,7 @@ module Sym
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def self.log(level, *args, **opts)
|
29
|
-
Sym::
|
29
|
+
Sym::Constants::Log::LOG.send(level, *args) if opts[:debug]
|
30
30
|
end
|
31
31
|
|
32
32
|
def self.error(config: {},
|
@@ -70,9 +70,7 @@ module Sym
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
require 'sym/version'
|
74
73
|
require 'sym/app/short_name'
|
75
|
-
|
76
74
|
require 'sym/app/args'
|
77
75
|
require 'sym/app/cli'
|
78
76
|
require 'sym/app/commands'
|
data/lib/sym/app/args.rb
CHANGED
@@ -5,7 +5,7 @@ module Sym
|
|
5
5
|
|
6
6
|
OPTIONS_REQUIRE_KEY = %i(encrypt decrypt edit)
|
7
7
|
OPTIONS_KEY_CREATED = %i(generate)
|
8
|
-
OPTIONS_SPECIFY_KEY = %i(
|
8
|
+
OPTIONS_SPECIFY_KEY = %i(key interactive keychain)
|
9
9
|
OPTIONS_SPECIFY_OUTPUT = %i(output quiet)
|
10
10
|
|
11
11
|
attr_accessor :opts, :selected_options
|
@@ -32,6 +32,10 @@ module Sym
|
|
32
32
|
Sym::App::Output.outputs[output_type]
|
33
33
|
end
|
34
34
|
|
35
|
+
def provided_options
|
36
|
+
opts.to_hash.keys.reject { |k| !opts[k] }
|
37
|
+
end
|
38
|
+
|
35
39
|
private
|
36
40
|
def do?(list)
|
37
41
|
!(list & selected_options).empty?
|
data/lib/sym/app/cli.rb
CHANGED
@@ -58,12 +58,14 @@ module Sym
|
|
58
58
|
|
59
59
|
attr_accessor :opts, :application, :outputs, :output_proc
|
60
60
|
|
61
|
-
def initialize(
|
62
|
-
env_args = ENV[ENV_ARGS_VARIABLE_NAME]
|
61
|
+
def initialize(argv)
|
63
62
|
begin
|
64
|
-
argv
|
65
|
-
argv << env_args.split(' ') if env_args && !(argv.include?('-M') or argv.include?('--no-environment'))
|
63
|
+
argv << args_from_environment(argv)
|
66
64
|
argv.flatten!
|
65
|
+
argv.compact!
|
66
|
+
argv_original = argv.dup
|
67
|
+
# Re-map any leg acy options to the new options
|
68
|
+
argv = CLI.replace_argv(argv)
|
67
69
|
dict = argv.delete('--dictionary')
|
68
70
|
self.opts = parse(argv)
|
69
71
|
command_dictionary if dict
|
@@ -72,11 +74,23 @@ module Sym
|
|
72
74
|
return
|
73
75
|
end
|
74
76
|
|
75
|
-
|
77
|
+
# Disable coloring if requested, or if piping STDOUT
|
78
|
+
if opts[:no_color] || !STDOUT.tty?
|
79
|
+
command_no_color(argv_original)
|
80
|
+
end
|
81
|
+
|
76
82
|
self.application = ::Sym::Application.new(opts)
|
77
83
|
select_output_stream
|
78
84
|
end
|
79
85
|
|
86
|
+
def args_from_environment(argv)
|
87
|
+
env_args = ENV[Sym::Constants::ENV_ARGS_VARIABLE_NAME]
|
88
|
+
if env_args && !(argv.include?('-M') or argv.include?('--no-environment'))
|
89
|
+
env_args.split(' ')
|
90
|
+
else
|
91
|
+
[]
|
92
|
+
end
|
93
|
+
end
|
80
94
|
|
81
95
|
def execute
|
82
96
|
return Sym::App.exit_code if Sym::App.exit_code != 0
|
@@ -95,6 +109,40 @@ module Sym
|
|
95
109
|
@command ||= self.application&.command
|
96
110
|
end
|
97
111
|
|
112
|
+
def opts_present
|
113
|
+
o = opts.to_hash
|
114
|
+
o.keys.map { |k| opts[k] ? nil : k }.compact.each { |k| o.delete(k) }
|
115
|
+
o
|
116
|
+
end
|
117
|
+
|
118
|
+
class << self
|
119
|
+
# Re-map any legacy options to the new options
|
120
|
+
ARGV_FLAG_REPLACE_MAP = {
|
121
|
+
'C' => 'c'
|
122
|
+
}
|
123
|
+
|
124
|
+
def replace_regex(from)
|
125
|
+
%r{^-([\w]*)#{from}([\w]*)$}
|
126
|
+
end
|
127
|
+
|
128
|
+
def replace_argv(argv)
|
129
|
+
argv = argv.dup
|
130
|
+
replacements = []
|
131
|
+
ARGV_FLAG_REPLACE_MAP.each_pair do |from, to|
|
132
|
+
argv.map! do |a|
|
133
|
+
match = replace_regex(from).match(a)
|
134
|
+
if match
|
135
|
+
replacements << from
|
136
|
+
"-#{match[1]}#{to}#{match[2]}"
|
137
|
+
else
|
138
|
+
a
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
argv
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
98
146
|
private
|
99
147
|
|
100
148
|
def command_dictionary
|
@@ -114,7 +162,7 @@ module Sym
|
|
114
162
|
unless output_klass && output_klass.is_a?(Class)
|
115
163
|
raise "Can not determine output class from arguments #{opts.to_hash}"
|
116
164
|
end
|
117
|
-
self.output_proc = output_klass.new(
|
165
|
+
self.output_proc = output_klass.new(application.opts).output_proc
|
118
166
|
end
|
119
167
|
|
120
168
|
def command_no_color(argv)
|
data/lib/sym/app/cli_slop.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'sym/version'
|
2
|
+
require 'sym/app/password/providers'
|
3
|
+
|
1
4
|
module Sym
|
2
5
|
module App
|
3
6
|
module CLISlop
|
@@ -6,25 +9,34 @@ module Sym
|
|
6
9
|
|
7
10
|
o.banner = "Sym (#{Sym::VERSION}) – encrypt/decrypt data with a private key\n".bold.white
|
8
11
|
o.separator 'Usage:'.yellow
|
9
|
-
o.separator ' # Generate a new key
|
10
|
-
o.separator '
|
12
|
+
o.separator ' # Generate a new key, optionally password protected, and save it'.dark
|
13
|
+
o.separator ' # in one of: keychain, file, or STDOUT (-q turns off STDOUT) '.dark
|
14
|
+
o.separator ' sym -g '.green.bold + '[ -p/--password ] [ -x keychain | -o file | ] [ -q ] '.green
|
15
|
+
o.separator ''
|
16
|
+
o.separator ' # To specify encryption key, provide the key as '.dark
|
17
|
+
o.separator ' # 1) a string, 2) a file path, 3) an OS-X Keychain, 4) env variable, '.dark
|
18
|
+
o.separator ' # 5) use -i to paste/type the key, 6) default key file, if present.'.dark
|
19
|
+
o.separator ' ' + key_spec + ' = -k/--key [ key | file | keychain | env ]'.green.bold
|
20
|
+
o.separator ' -i/--interactive'.green.bold
|
21
|
+
|
11
22
|
o.separator ''
|
12
|
-
o.separator ' #
|
13
|
-
o.separator ' ' + key_spec + '
|
23
|
+
o.separator ' # Encrypt/Decrypt from STDIN/file/args, to STDOUT/file:'.dark
|
24
|
+
o.separator ' sym -e/--encrypt '.green.bold + key_spec + ' [-f [file | - ] | -s string ] [-o file] '.green
|
25
|
+
o.separator ' sym -d/--decrypt '.green.bold + key_spec + ' [-f [file | - ] | -s string ] [-o file] '.green
|
14
26
|
o.separator ''
|
15
|
-
o.separator ' #
|
16
|
-
o.separator ' sym -
|
17
|
-
o.separator ' sym -d '.green.bold + key_spec + ' [-f <file> | -s <string>] [-o <file>] '.green
|
27
|
+
o.separator ' # Auto-detect mode based on a special file extension '.dark + '".enc"'.dark.bold
|
28
|
+
o.separator ' sym -n/--negate '.green.bold + key_spec + ' file[.enc] '.green
|
18
29
|
o.separator ' '
|
19
30
|
o.separator ' # Edit an encrypted file in $EDITOR '.dark
|
20
|
-
o.separator ' sym -t
|
31
|
+
o.separator ' sym -t/--edit '.green.bold + key_spec + ' -f file [ -b/--backup ]'.green.bold
|
21
32
|
|
22
33
|
o.separator ' '
|
23
|
-
o.separator ' #
|
24
|
-
o.separator '
|
34
|
+
o.separator ' # Save commonly used flags in a BASH variable. Below we save KeyChain '.dark
|
35
|
+
o.separator ' # "staging" as the default key source, and enable password caching.'.dark
|
36
|
+
o.separator ' export SYM_ARGS="'.green + '-ck staging'.bold.green + '"'.green
|
25
37
|
o.separator ' '
|
26
|
-
o.separator ' # And now encrypt
|
27
|
-
o.separator ' sym -e '.green.bold '-f
|
38
|
+
o.separator ' # And now encrypt using default key location '.dark + Sym.default_key_file.magenta.bold
|
39
|
+
o.separator ' sym -e '.green.bold '-f file'.green.bold
|
28
40
|
o.separator ' # May need to disable SYM_ARGS with -M, eg for help:'.dark
|
29
41
|
o.separator ' sym -h -M '.green.bold
|
30
42
|
|
@@ -38,36 +50,36 @@ module Sym
|
|
38
50
|
o.separator 'Create a new private key:'.yellow
|
39
51
|
o.bool '-g', '--generate', ' generate a new private key'
|
40
52
|
o.bool '-p', '--password', ' encrypt the key with a password'
|
53
|
+
if Sym::App.is_osx?
|
54
|
+
o.string '-x', '--keychain', '[key-name] '.blue + 'write the key to OS-X Keychain'
|
55
|
+
end
|
41
56
|
|
42
57
|
o.separator ' '
|
43
58
|
o.separator 'Read existing private key from:'.yellow
|
44
|
-
o.string '-k', '--
|
45
|
-
o.string '-K', '--keyfile', '[key-file]'.blue + ' private key from a file'
|
46
|
-
if Sym::App.is_osx?
|
47
|
-
o.string '-x', '--keychain', '[key-name] '.blue + 'add to (or read from) the OS-X Keychain'
|
48
|
-
end
|
59
|
+
o.string '-k', '--key', '[key-spec]'.blue + ' private key, key file, or keychain'
|
49
60
|
o.bool '-i', '--interactive', ' Paste or type the key interactively'
|
50
61
|
|
51
62
|
o.separator ' '
|
52
63
|
o.separator 'Password Cache:'.yellow
|
53
|
-
o.bool '-
|
54
|
-
o.integer '-
|
55
|
-
o.string '-
|
56
|
-
"[ #{Sym::App::Password::Providers.registry.keys.map(&:to_s).join(', ').blue.bold} ]"
|
64
|
+
o.bool '-c', '--cache-passwords', ' enable password cache'
|
65
|
+
o.integer '-u', '--cache-timeout', '[seconds]'.blue + ' expire passwords after'
|
66
|
+
o.string '-r', '--cache-provider', '[provider]'.blue + ' cache provider, one of ' + "#{Sym::App::Password::Providers.provider_list}"
|
57
67
|
|
58
68
|
o.separator ' '
|
59
69
|
o.separator 'Data to Encrypt/Decrypt:'.yellow
|
60
70
|
o.string '-s', '--string', '[string]'.blue + ' specify a string to encrypt/decrypt'
|
61
71
|
o.string '-f', '--file', '[file] '.blue + ' filename to read from'
|
62
72
|
o.string '-o', '--output', '[file] '.blue + ' filename to write to'
|
73
|
+
o.string '-n', '--negate', '[file] '.blue + " encrypts any regular #{'file'.green} into #{'file.enc'.green}" + "\n" +
|
74
|
+
" conversely decrypts #{'file.enc'.green} into #{'file'.green}."
|
63
75
|
|
64
76
|
o.separator ' '
|
65
77
|
o.separator 'Flags:'.yellow
|
66
78
|
o.bool '-b', '--backup', ' create a backup file in the edit mode'
|
67
79
|
o.bool '-v', '--verbose', ' show additional information'
|
68
|
-
o.bool '-A', '--trace', ' print a backtrace of any errors'
|
69
|
-
o.bool '-D', '--debug', ' print debugging information'
|
70
80
|
o.bool '-q', '--quiet', ' do not print to STDOUT'
|
81
|
+
o.bool '-T', '--trace', ' print a backtrace of any errors'
|
82
|
+
o.bool '-D', '--debug', ' print debugging information'
|
71
83
|
o.bool '-V', '--version', ' print library version'
|
72
84
|
o.bool '-N', '--no-color', ' disable color output'
|
73
85
|
o.bool '-M', '--no-environment', ' disable reading flags from SYM_ARGS'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'sym'
|
2
2
|
require 'sym/app'
|
3
|
-
|
3
|
+
require 'forwardable'
|
4
4
|
require 'active_support/inflector'
|
5
5
|
|
6
6
|
module Sym
|
@@ -47,30 +47,21 @@ module Sym
|
|
47
47
|
end
|
48
48
|
|
49
49
|
include Sym
|
50
|
+
extend Forwardable
|
50
51
|
|
51
52
|
attr_accessor :application
|
53
|
+
def_delegators :@application, :opts, :opts_original, :key
|
52
54
|
|
53
55
|
def initialize(application)
|
54
56
|
self.application = application
|
55
57
|
end
|
56
58
|
|
57
|
-
def opts
|
58
|
-
application.opts
|
59
|
-
end
|
60
|
-
def opts_hash
|
61
|
-
application.opts_hash
|
62
|
-
end
|
63
|
-
|
64
|
-
def key
|
65
|
-
@key ||= application.key
|
66
|
-
end
|
67
|
-
|
68
59
|
def execute
|
69
60
|
raise Sym::Errors::AbstractMethodCalled.new(:run)
|
70
61
|
end
|
71
62
|
|
72
63
|
def content
|
73
|
-
@content ||= (opts[:string] || (opts[:file].eql?('-') ? STDIN.read : File.read(opts[:file])))
|
64
|
+
@content ||= (opts[:string] || (opts[:file].eql?('-') ? STDIN.read : File.read(opts[:file]).chomp))
|
74
65
|
end
|
75
66
|
|
76
67
|
def to_s
|
@@ -89,12 +80,14 @@ module Sym
|
|
89
80
|
end
|
90
81
|
end
|
91
82
|
|
92
|
-
def
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
83
|
+
def encrypt_with_password(key)
|
84
|
+
password = application.input_handler.new_password
|
85
|
+
return encr_password(key, password), password
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def add_password_to_the_cache(encrypted_key, password)
|
90
|
+
self.application.password_cache[encrypted_key] = password
|
98
91
|
end
|
99
92
|
end
|
100
93
|
end
|
@@ -11,7 +11,7 @@ module Sym
|
|
11
11
|
install_completion_file
|
12
12
|
file = opts[:bash_completion]
|
13
13
|
if File.exist?(file)
|
14
|
-
if File.read(file).include?(
|
14
|
+
if File.read(file).include?(script)
|
15
15
|
"#{'Hmmm'.bold.yellow}: #{file.bold.yellow} had completion for #{'sym'.bold.red} already installed\n"
|
16
16
|
else
|
17
17
|
append_completion_script(file)
|
@@ -23,15 +23,31 @@ module Sym
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
private
|
27
|
+
|
26
28
|
def install_completion_file
|
27
|
-
FileUtils.cp(
|
29
|
+
FileUtils.cp(source_file, path)
|
28
30
|
end
|
29
31
|
|
30
32
|
def append_completion_script(file)
|
31
33
|
File.open(file, 'a') do |fd|
|
32
|
-
fd.write(
|
34
|
+
fd.write(script)
|
33
35
|
end
|
34
36
|
end
|
37
|
+
|
38
|
+
|
39
|
+
def script
|
40
|
+
Sym::Constants::Completion::Config[:script]
|
41
|
+
end
|
42
|
+
|
43
|
+
def source_file
|
44
|
+
Sym::Constants::Completion::Config[:file]
|
45
|
+
end
|
46
|
+
|
47
|
+
def path
|
48
|
+
Sym::Constants::Completion::PATH
|
49
|
+
end
|
50
|
+
|
35
51
|
end
|
36
52
|
end
|
37
53
|
end
|
@@ -13,15 +13,19 @@ module Sym
|
|
13
13
|
retries ||= 0
|
14
14
|
|
15
15
|
the_key = create_key
|
16
|
-
the_key = encrypt_password_if_needed(the_key)
|
17
|
-
add_to_keychain_if_needed(the_key)
|
18
16
|
|
17
|
+
if opts[:password]
|
18
|
+
encrypted_key, password = encrypt_with_password(the_key)
|
19
|
+
add_password_to_the_cache(encrypted_key, password)
|
20
|
+
the_key = encrypted_key
|
21
|
+
end
|
22
|
+
|
23
|
+
add_to_keychain_if_needed(the_key)
|
19
24
|
the_key
|
20
25
|
rescue Sym::Errors::PasswordsDontMatch, Sym::Errors::PasswordTooShort => e
|
21
26
|
STDERR.puts e.message.bold
|
22
27
|
retry if (retries += 1) < 3
|
23
28
|
end
|
24
|
-
|
25
29
|
end
|
26
30
|
end
|
27
31
|
end
|
@@ -1,17 +1,22 @@
|
|
1
1
|
require 'sym/app/commands/base_command'
|
2
2
|
require 'sym/app/keychain'
|
3
|
+
require 'sym/errors'
|
3
4
|
module Sym
|
4
5
|
module App
|
5
6
|
module Commands
|
6
7
|
class KeychainAddKey < BaseCommand
|
7
8
|
|
8
|
-
required_options [:
|
9
|
+
required_options [:key, :interactive],
|
9
10
|
:keychain
|
10
|
-
|
11
|
+
incompatible_options %i(examples help version bash_completion)
|
11
12
|
try_after :generate_key, :encrypt, :decrypt, :password_protect_key
|
12
13
|
|
13
14
|
def execute
|
15
|
+
if Sym.default_key? && Sym.default_key == self.key
|
16
|
+
raise 'Refusing to import key specified in the default key file ' + Sym.default_key_file.italic
|
17
|
+
end
|
14
18
|
add_to_keychain_if_needed(self.key)
|
19
|
+
self.key unless opts[:quiet]
|
15
20
|
end
|
16
21
|
end
|
17
22
|
end
|