shh 0.1.6 → 0.1.7
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.
- data/MIT-LICENSE +20 -20
- data/README.rdoc +110 -113
- data/bin/shh +5 -5
- data/lib/shh/cli.rb +16 -16
- data/lib/shh/command/copy_key.rb +15 -0
- data/lib/shh/command/edit_key.rb +15 -0
- data/lib/shh/command/execute_key.rb +19 -0
- data/lib/shh/command/key_command.rb +13 -0
- data/lib/shh/command/launch_key.rb +14 -0
- data/lib/shh/command/list_entries.rb +16 -0
- data/lib/shh/command/list_keys.rb +16 -0
- data/lib/shh/command/open_entry.rb +23 -0
- data/lib/shh/command/remove_key.rb +13 -0
- data/lib/shh/command/set_key.rb +14 -0
- data/lib/shh/command/show_key.rb +13 -0
- data/lib/shh/commands.rb +10 -0
- data/lib/shh/crypt.rb +25 -25
- data/lib/shh/entries_menu.rb +16 -64
- data/lib/shh/entry_menu.rb +25 -141
- data/lib/shh/prompt.rb +14 -11
- data/lib/shh/repository.rb +44 -39
- metadata +75 -7
data/MIT-LICENSE
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
-
Copyright (c) 2009 Mark Ryall
|
2
|
-
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
-
a copy of this software and associated documentation files (the
|
5
|
-
"Software"), to deal in the Software without restriction, including
|
6
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
-
permit persons to whom the Software is furnished to do so, subject to
|
9
|
-
the following conditions:
|
10
|
-
|
11
|
-
The above copyright notice and this permission notice shall be
|
12
|
-
included in all copies or substantial portions of the Software.
|
13
|
-
|
14
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1
|
+
Copyright (c) 2009 Mark Ryall
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
CHANGED
@@ -1,113 +1,110 @@
|
|
1
|
-
= SHH
|
2
|
-
|
3
|
-
Secret Squirrel
|
4
|
-
|
5
|
-
This is a command line utility for managing secure information such as accounts, passwords as individual files so that they can be easily managed in a version control repository.
|
6
|
-
|
7
|
-
You won't be hiding anything from the NSA with this level of encryption so it isn't recommended that you make your repository publicly accessible.
|
8
|
-
|
9
|
-
Now with more tab completion and multi line editing!
|
10
|
-
|
11
|
-
= Usage
|
12
|
-
|
13
|
-
Here you sit expectantly in front of a computer at the command line.
|
14
|
-
|
15
|
-
== Install
|
16
|
-
|
17
|
-
gem install shh
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
*
|
51
|
-
*
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
(bitbucket) >
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
*
|
68
|
-
*
|
69
|
-
*
|
70
|
-
*
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
*
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
*
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
browser
|
103
|
-
browser.
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
*
|
111
|
-
* Add help
|
112
|
-
* add some source control (hg/git) commands
|
113
|
-
* add commands for displaying history of entries
|
1
|
+
= SHH
|
2
|
+
|
3
|
+
Secret Squirrel
|
4
|
+
|
5
|
+
This is a command line utility for managing secure information such as accounts, passwords as individual files so that they can be easily managed in a version control repository.
|
6
|
+
|
7
|
+
You won't be hiding anything from the NSA with this level of encryption so it isn't recommended that you make your repository publicly accessible.
|
8
|
+
|
9
|
+
Now with more tab completion and multi line editing!
|
10
|
+
|
11
|
+
= Usage
|
12
|
+
|
13
|
+
Here you sit expectantly in front of a computer at the command line.
|
14
|
+
|
15
|
+
== Install
|
16
|
+
|
17
|
+
gem install shh
|
18
|
+
|
19
|
+
== Launch
|
20
|
+
|
21
|
+
Open all 'secrets' stored in ~/.secret
|
22
|
+
|
23
|
+
> shh
|
24
|
+
|
25
|
+
Opens all 'secrets' stored in foo/.secret
|
26
|
+
|
27
|
+
> shh foo
|
28
|
+
|
29
|
+
== Authenticate
|
30
|
+
|
31
|
+
Enter your passphrase
|
32
|
+
|
33
|
+
This passphrase will be used to encrypt and decrypt all of your secrets so don't make it too obvious.
|
34
|
+
|
35
|
+
== Listing mode
|
36
|
+
|
37
|
+
This mode allows you to view and edit 'entries' (which are encrypted hashes stored in files)
|
38
|
+
|
39
|
+
> list
|
40
|
+
|
41
|
+
bitbucket (260f34de-6779-4367-af2a-44184dec1cc1)
|
42
|
+
amazon (291a3e6c-2c7b-4960-a146-1f6635d9a74e)
|
43
|
+
yahoo (2fe408e7-7f2b-4dc0-854a-c92c21f131ae)
|
44
|
+
evernote (41ad0a21-eafc-4f70-9b0d-0251be207b9e)
|
45
|
+
gmail (8b35c1f2-851d-40fc-bd12-c2aad12c370a)
|
46
|
+
rememberthemilk (a499f612-d034-4e49-82a8-6967d29653a1)
|
47
|
+
|
48
|
+
* list - list entries
|
49
|
+
* edit <name> - edit or create entry
|
50
|
+
* view <name> - view entry
|
51
|
+
* quit
|
52
|
+
|
53
|
+
== Viewing mode
|
54
|
+
|
55
|
+
This mode allows you to view (but not edit) an existing entry
|
56
|
+
|
57
|
+
> view bitbucket
|
58
|
+
(bitbucket) > list
|
59
|
+
id,name,password,username
|
60
|
+
(bitbucket) > view username
|
61
|
+
markryall
|
62
|
+
|
63
|
+
list keys, show key, copy key or quit? c password
|
64
|
+
|
65
|
+
* list - show entry keys
|
66
|
+
* view <key> - view entry value on screen
|
67
|
+
* copy <key> - copy entry value to clipboard
|
68
|
+
* launch <key> - launch entry value in default browser (available if it starts with http)
|
69
|
+
* run <key> - execute ruby script (available if it starts with #!/usr/bin/env ruby)
|
70
|
+
* quit
|
71
|
+
|
72
|
+
== Editing mode
|
73
|
+
|
74
|
+
This mode is viewing mode plus some editing commands
|
75
|
+
|
76
|
+
> edit bitbucket
|
77
|
+
(bitbucket) > set foo
|
78
|
+
Enter new value for foo
|
79
|
+
bar
|
80
|
+
(bitbucket) > delete foo
|
81
|
+
|
82
|
+
* edit <key> - edit or create key using text editor (notepad or EDITOR)
|
83
|
+
* set <key> - edit or create key from prompt
|
84
|
+
* delete <key> - remove a key
|
85
|
+
|
86
|
+
== Quitting mode
|
87
|
+
|
88
|
+
You don't want to be a quitter
|
89
|
+
|
90
|
+
== Scripting
|
91
|
+
|
92
|
+
In a script the following objects are available:
|
93
|
+
|
94
|
+
* splat.clipboard = <text> - allows content to be copied to the clipboard
|
95
|
+
* splat.browser - a watir compatible browser
|
96
|
+
* hash - the current selected entry
|
97
|
+
|
98
|
+
Example:
|
99
|
+
|
100
|
+
browser = 'http://www.github.com/login'.to_browser
|
101
|
+
browser.text_field(:id, 'login_field').set hash['username']
|
102
|
+
browser.text_field(:id, 'password').set hash['password']
|
103
|
+
browser.button(:name, 'commit').click
|
104
|
+
|
105
|
+
= Future plans for world domination
|
106
|
+
|
107
|
+
* Add some color
|
108
|
+
* Add help
|
109
|
+
* add some source control (hg/git) commands
|
110
|
+
* add commands for displaying history of entries
|
data/bin/shh
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
$:.unshift File.dirname(__FILE__)+'/../lib'
|
4
|
-
require 'shh/cli'
|
5
|
-
Shh::Cli.execute(*ARGV)
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__)+'/../lib'
|
4
|
+
require 'shh/cli'
|
5
|
+
Shh::Cli.execute(*ARGV)
|
data/lib/shh/cli.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'shh/repository'
|
3
|
-
require 'shh/prompt'
|
4
|
-
require 'shh/entries_menu'
|
5
|
-
|
6
|
-
module Shh
|
7
|
-
class Cli
|
8
|
-
def self.execute *args
|
9
|
-
prompt = Prompt.new
|
10
|
-
passphrase = prompt.
|
11
|
-
path = args.shift || ('~')
|
12
|
-
|
13
|
-
EntriesMenu.new(prompt, Repository.new(passphrase, path)).
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
1
|
+
require 'rubygems'
|
2
|
+
require 'shh/repository'
|
3
|
+
require 'shh/prompt'
|
4
|
+
require 'shh/entries_menu'
|
5
|
+
|
6
|
+
module Shh
|
7
|
+
class Cli
|
8
|
+
def self.execute *args
|
9
|
+
prompt = Prompt.new
|
10
|
+
passphrase = prompt.ask('Enter your passphrase', :silent => true)
|
11
|
+
path = args.shift || ('~')
|
12
|
+
|
13
|
+
EntriesMenu.new(prompt, Repository.new(passphrase, path)).push
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'shh/commands'
|
2
|
+
require 'shh/command/key_command'
|
3
|
+
require 'splat'
|
4
|
+
|
5
|
+
class Shh::Command::CopyKey
|
6
|
+
include Shh::Command::KeyCommand
|
7
|
+
|
8
|
+
def help
|
9
|
+
"Copies the value associated with the specified key to the clipboard"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute key=nil
|
13
|
+
(@entry[key] || '').to_clipboard
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'shh/command/key_command'
|
2
|
+
require 'splat'
|
3
|
+
|
4
|
+
class Shh::Command::EditKey
|
5
|
+
include Shh::Command::KeyCommand
|
6
|
+
|
7
|
+
def help
|
8
|
+
"Launches an external editor (specified by EDITOR environment variable) to edit the value associated with the specified key"
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute key=nil
|
12
|
+
new_value = (@entry[key] || '').to_editor
|
13
|
+
@entry[key] = new_value if new_value.length > 0
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'shh/command/key_command'
|
2
|
+
|
3
|
+
class Shh::Command::ExecuteKey
|
4
|
+
include Shh::Command::KeyCommand
|
5
|
+
|
6
|
+
def help
|
7
|
+
"Executes the contents (ruby code) associated with the specified key\nNote that the entire entry is available as a hash called 'entry'"
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute key=nil
|
11
|
+
begin
|
12
|
+
entry = @entry
|
13
|
+
eval @entry[key]
|
14
|
+
rescue Exception => e
|
15
|
+
@io.say e.message
|
16
|
+
e.backtrace.each {|t| @io.say " > #{t}" }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'shh/command/key_command'
|
2
|
+
require 'splat'
|
3
|
+
|
4
|
+
class Shh::Command::LaunchKey
|
5
|
+
include Shh::Command::KeyCommand
|
6
|
+
|
7
|
+
def help
|
8
|
+
"Launches the value (usually a url) associated with the specified key using default application"
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute key=nil
|
12
|
+
(@entry[key] || '').to_launcher
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Shh::Command::ListEntries
|
2
|
+
attr_reader :usage, :help
|
3
|
+
|
4
|
+
def initialize repository, io
|
5
|
+
@repository, @io = repository, io
|
6
|
+
@usage = "(<substring>)"
|
7
|
+
@help = "Lists all entries or those with a name including the specified substring"
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute text=nil
|
11
|
+
@repository.each do |entry|
|
12
|
+
next if text and !(entry['name'] =~ /#{text}/)
|
13
|
+
@io.say "#{entry['name']} (#{entry['id']})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Shh::Command::ListKeys
|
2
|
+
attr_reader :usage, :help
|
3
|
+
|
4
|
+
def initialize entry, io
|
5
|
+
@entry, @io = entry, io
|
6
|
+
@usage = "(<substring>)"
|
7
|
+
@help = "Lists all keys for the entry or those including the specified substring"
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute text=nil
|
11
|
+
@entry.each_key do |key|
|
12
|
+
next if text and !(key =~ /#{text}/)
|
13
|
+
@io.say key
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'uuidtools'
|
2
|
+
require 'shh/entry_menu'
|
3
|
+
|
4
|
+
class Shh::Command::OpenEntry
|
5
|
+
attr_reader :usage, :help
|
6
|
+
|
7
|
+
def initialize repository, io
|
8
|
+
@repository, @io = repository, io
|
9
|
+
@usage = "<entry name>"
|
10
|
+
@help = "Enters a subshell for editing the specifed entry"
|
11
|
+
end
|
12
|
+
|
13
|
+
def completion text
|
14
|
+
@repository.map{|entry| entry['name']}.grep(/^#{text}/).sort || []
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute name=nil
|
18
|
+
entry = @repository.find_by_name(name)
|
19
|
+
entry ||= {'name' => name, 'id' => UUIDTools::UUID.random_create.to_s}
|
20
|
+
Shh::EntryMenu.new(@io, entry).push
|
21
|
+
@repository.persist entry
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'shh/command/key_command'
|
2
|
+
|
3
|
+
class Shh::Command::SetKey
|
4
|
+
include Shh::Command::KeyCommand
|
5
|
+
|
6
|
+
def help
|
7
|
+
"Prompts for a new value for the specified key\nKeys with a name including 'pass' (eg. password, passphrase) will not echo input"
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute key=nil
|
11
|
+
new_value = @io.ask("Enter new value for #{key}", :silent => (key =~ /pass/))
|
12
|
+
@entry[key] = new_value if new_value.length > 0
|
13
|
+
end
|
14
|
+
end
|
data/lib/shh/commands.rb
ADDED
data/lib/shh/crypt.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
require 'crypt/blowfish'
|
2
|
-
|
3
|
-
module Shh
|
4
|
-
class Crypt
|
5
|
-
def initialize passphrase
|
6
|
-
@blowfish = ::Crypt::Blowfish.new(passphrase)
|
7
|
-
end
|
8
|
-
|
9
|
-
def encrypt text, out_io
|
10
|
-
in_io = StringIO.new(text)
|
11
|
-
while l = in_io.read(8) do
|
12
|
-
while l.size < 8 do l += "\0" end
|
13
|
-
out_io.print @blowfish.encrypt_block(l)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def decrypt in_io
|
18
|
-
out_io = StringIO.new
|
19
|
-
while l = in_io.read(8) do
|
20
|
-
out_io.print @blowfish.decrypt_block(l)
|
21
|
-
end
|
22
|
-
out_io.string.gsub("\0",'')
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
1
|
+
require 'crypt/blowfish'
|
2
|
+
|
3
|
+
module Shh
|
4
|
+
class Crypt
|
5
|
+
def initialize passphrase
|
6
|
+
@blowfish = ::Crypt::Blowfish.new(passphrase)
|
7
|
+
end
|
8
|
+
|
9
|
+
def encrypt text, out_io
|
10
|
+
in_io = StringIO.new(text)
|
11
|
+
while l = in_io.read(8) do
|
12
|
+
while l.size < 8 do l += "\0" end
|
13
|
+
out_io.print @blowfish.encrypt_block(l)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def decrypt in_io
|
18
|
+
out_io = StringIO.new
|
19
|
+
while l = in_io.read(8) do
|
20
|
+
out_io.print @blowfish.decrypt_block(l)
|
21
|
+
end
|
22
|
+
out_io.string.gsub("\0",'')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/shh/entries_menu.rb
CHANGED
@@ -1,64 +1,16 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
line.strip!
|
18
|
-
case line
|
19
|
-
when 'quit'
|
20
|
-
return
|
21
|
-
when 'refresh'
|
22
|
-
refresh
|
23
|
-
when 'list'
|
24
|
-
@entries.sort.each {|entry| say entry }
|
25
|
-
when /^view (.*)/
|
26
|
-
view $1
|
27
|
-
when /^edit (.*)/
|
28
|
-
edit $1
|
29
|
-
end
|
30
|
-
puts
|
31
|
-
end
|
32
|
-
rescue Interrupt => e
|
33
|
-
exit
|
34
|
-
end
|
35
|
-
puts
|
36
|
-
end
|
37
|
-
|
38
|
-
def refresh
|
39
|
-
commands = ['list', 'refresh', 'quit']
|
40
|
-
@entries = []
|
41
|
-
@repository.each_entry do |entry|
|
42
|
-
@entries << "#{entry['name']} (#{entry['id']})"
|
43
|
-
commands << "edit #{entry['name']}"
|
44
|
-
commands << "view #{entry['name']}"
|
45
|
-
end
|
46
|
-
Readline.completion_proc = lambda do |text|
|
47
|
-
commands.grep( /^#{Regexp.escape(text)}/ ).sort
|
48
|
-
end
|
49
|
-
Readline.completer_word_break_characters = ''
|
50
|
-
end
|
51
|
-
|
52
|
-
def edit name
|
53
|
-
entry = @repository.find_entry(name)
|
54
|
-
entry ||= {'name' => name, 'id' => UUIDTools::UUID.random_create.to_s}
|
55
|
-
@repository.persist_entry EntryMenu.new(@prompt, entry).main_loop
|
56
|
-
refresh
|
57
|
-
end
|
58
|
-
|
59
|
-
def view name
|
60
|
-
EntryMenu.new(@prompt, @repository.find_entry(name), true).main_loop
|
61
|
-
refresh
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
1
|
+
require 'shell_shock/context'
|
2
|
+
require 'shh/commands'
|
3
|
+
|
4
|
+
class Shh::EntriesMenu
|
5
|
+
include Shh::Command
|
6
|
+
include ShellShock::Context
|
7
|
+
|
8
|
+
def initialize io, repository
|
9
|
+
@io, @repository = io, repository
|
10
|
+
@prompt_text = 'shh > '
|
11
|
+
@commands = {
|
12
|
+
'ls' => load_command(:list_entries, @repository, @io),
|
13
|
+
'cd' => load_command(:open_entry, @repository, @io)
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
data/lib/shh/entry_menu.rb
CHANGED
@@ -1,141 +1,25 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
when /^copy (.*)/
|
27
|
-
copy $1
|
28
|
-
when /^launch (.*)/
|
29
|
-
launch $1
|
30
|
-
when /^delete (.*)/
|
31
|
-
delete $1
|
32
|
-
when /^view (.*)/
|
33
|
-
view $1
|
34
|
-
when /^run (.*)/
|
35
|
-
run $1
|
36
|
-
else
|
37
|
-
puts "I can't imagine what you mean by that"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
rescue Interrupt => e
|
41
|
-
exit
|
42
|
-
end
|
43
|
-
|
44
|
-
puts
|
45
|
-
@hash
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
def can_edit?
|
50
|
-
!@read_only
|
51
|
-
end
|
52
|
-
|
53
|
-
def can_launch? key
|
54
|
-
@splat.supported? and @hash[key] =~ /^http/
|
55
|
-
end
|
56
|
-
|
57
|
-
def can_run? key
|
58
|
-
@splat.supported? and @hash[key] =~ /^#!\/usr\/bin\/env ruby/
|
59
|
-
end
|
60
|
-
|
61
|
-
def view key
|
62
|
-
say(@hash[key]) if @hash[key]
|
63
|
-
end
|
64
|
-
|
65
|
-
def copy key
|
66
|
-
@splat.clipboard = @hash[key]
|
67
|
-
end
|
68
|
-
|
69
|
-
def run key
|
70
|
-
begin
|
71
|
-
hash = @hash
|
72
|
-
splat = @splat
|
73
|
-
eval @hash[key]
|
74
|
-
rescue Exception => e
|
75
|
-
puts e.message
|
76
|
-
e.backtrace.each {|t| puts " > #{t}" }
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def set key
|
81
|
-
if can_edit?
|
82
|
-
set_value key
|
83
|
-
refresh
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def edit key
|
88
|
-
if can_edit?
|
89
|
-
edit_value key
|
90
|
-
refresh
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def delete key
|
95
|
-
if can_edit?
|
96
|
-
@hash.delete(key)
|
97
|
-
refresh
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def launch key
|
102
|
-
@splat.launch @hash[key] if can_launch?(key)
|
103
|
-
end
|
104
|
-
|
105
|
-
def refresh
|
106
|
-
commands = ['list', 'quit']
|
107
|
-
@hash.keys.each do |key|
|
108
|
-
commands << "edit #{key}" if can_edit?
|
109
|
-
commands << "set #{key}" if can_edit?
|
110
|
-
commands << "delete #{key}" if can_edit?
|
111
|
-
commands << "view #{key}"
|
112
|
-
commands << "copy #{key}" if @splat.supported?
|
113
|
-
commands << "launch #{key}" if can_launch?(key)
|
114
|
-
commands << "run #{key}" if can_run?(key)
|
115
|
-
end
|
116
|
-
Readline.completion_proc = lambda do |text|
|
117
|
-
commands.grep( /^#{Regexp.escape(text)}/ ).sort
|
118
|
-
end
|
119
|
-
Readline.completer_word_break_characters = ''
|
120
|
-
end
|
121
|
-
|
122
|
-
def set_value key
|
123
|
-
new_value = @prompt.get("Enter new value for #{key}", :silent => (key =~ /pass/))
|
124
|
-
@hash[key] = new_value if new_value.length > 0
|
125
|
-
end
|
126
|
-
|
127
|
-
def edit_value key
|
128
|
-
tmp_file = Pathname.new("key.tmp")
|
129
|
-
begin
|
130
|
-
tmp_file.open('w') { |out| out.print @hash[key] }
|
131
|
-
editor = ENV["EDITOR"] || "notepad"
|
132
|
-
system("#{editor} key.tmp")
|
133
|
-
return unless $?.to_i == 0
|
134
|
-
new_value = tmp_file.read
|
135
|
-
@hash[key] = new_value if new_value.length > 0
|
136
|
-
ensure
|
137
|
-
tmp_file.unlink
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
1
|
+
require 'shell_shock/context'
|
2
|
+
require 'shh/commands'
|
3
|
+
|
4
|
+
class Shh::EntryMenu
|
5
|
+
include Shh::Command
|
6
|
+
include ShellShock::Context
|
7
|
+
|
8
|
+
def initialize io, entry
|
9
|
+
@io, @entry = io, entry
|
10
|
+
@commands = {
|
11
|
+
'ls' => load_command(:list_keys, entry, io),
|
12
|
+
'set' => load_command(:set_key, entry, io),
|
13
|
+
'edit' => load_command(:edit_key, entry, io),
|
14
|
+
'cat' => load_command(:show_key, entry, io),
|
15
|
+
'cp' => load_command(:copy_key, entry, io),
|
16
|
+
'launch' => load_command(:launch_key, entry, io),
|
17
|
+
'rm' => load_command(:remove_key, entry, io),
|
18
|
+
'exec' => load_command(:execute_key, entry, io)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def refresh_commands
|
23
|
+
@prompt_text = "shh:#{@entry['name']} > "
|
24
|
+
end
|
25
|
+
end
|
data/lib/shh/prompt.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
-
require 'highline/import'
|
2
|
-
|
3
|
-
module Shh
|
4
|
-
class Prompt
|
5
|
-
def
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
require 'highline/import'
|
2
|
+
|
3
|
+
module Shh
|
4
|
+
class Prompt
|
5
|
+
def ask text, params={}
|
6
|
+
echo = params[:silent] ? false : true
|
7
|
+
Kernel.ask(text) { |q| q.echo = echo }
|
8
|
+
end
|
9
|
+
|
10
|
+
def say text=''
|
11
|
+
puts text
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/shh/repository.rb
CHANGED
@@ -1,39 +1,44 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
require 'shh/crypt'
|
3
|
-
require 'fileutils'
|
4
|
-
|
5
|
-
module Shh
|
6
|
-
class Repository
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
1
|
+
require 'yaml'
|
2
|
+
require 'shh/crypt'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Shh
|
6
|
+
class Repository
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
attr_reader :folder
|
10
|
+
|
11
|
+
def initialize passphrase, path
|
12
|
+
@folder = Pathname.new(File.expand_path(path)) + '.secret'
|
13
|
+
@folder.mkpath
|
14
|
+
@crypt = Crypt.new(passphrase)
|
15
|
+
end
|
16
|
+
|
17
|
+
def each
|
18
|
+
@folder.children.each do |child|
|
19
|
+
entry = load(child)
|
20
|
+
yield entry if entry
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_by_name name
|
25
|
+
each {|e| return e if e['name'] == name}
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def load path
|
30
|
+
return nil if path.directory?
|
31
|
+
yaml = path.open('rb') {|io| @crypt.decrypt(io) }
|
32
|
+
entry = YAML::load(yaml)
|
33
|
+
unless entry and path.basename.to_s == entry['id']
|
34
|
+
$stderr.puts "failed to deserialise #{path}"
|
35
|
+
return nil
|
36
|
+
end
|
37
|
+
entry
|
38
|
+
end
|
39
|
+
|
40
|
+
def persist entry
|
41
|
+
(@folder + entry['id']).open('wb') {|io| @crypt.encrypt(entry.to_yaml, io) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 7
|
9
|
+
version: 0.1.7
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Mark Ryall
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-06-15 00:00:00 +10:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -27,8 +27,8 @@ dependencies:
|
|
27
27
|
segments:
|
28
28
|
- 1
|
29
29
|
- 5
|
30
|
-
-
|
31
|
-
version: 1.5.
|
30
|
+
- 2
|
31
|
+
version: 1.5.2
|
32
32
|
type: :runtime
|
33
33
|
version_requirements: *id001
|
34
34
|
- !ruby/object:Gem::Dependency
|
@@ -68,11 +68,67 @@ dependencies:
|
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
segments:
|
70
70
|
- 0
|
71
|
+
- 1
|
71
72
|
- 0
|
72
|
-
|
73
|
-
version: 0.0.2
|
73
|
+
version: 0.1.0
|
74
74
|
type: :runtime
|
75
75
|
version_requirements: *id004
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: shell_shock
|
78
|
+
prerelease: false
|
79
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ~>
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
- 0
|
86
|
+
- 3
|
87
|
+
version: 0.0.3
|
88
|
+
type: :runtime
|
89
|
+
version_requirements: *id005
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: rake
|
92
|
+
prerelease: false
|
93
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ~>
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
segments:
|
98
|
+
- 0
|
99
|
+
- 8
|
100
|
+
- 7
|
101
|
+
version: 0.8.7
|
102
|
+
type: :development
|
103
|
+
version_requirements: *id006
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: gemesis
|
106
|
+
prerelease: false
|
107
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ~>
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
segments:
|
112
|
+
- 0
|
113
|
+
- 0
|
114
|
+
- 3
|
115
|
+
version: 0.0.3
|
116
|
+
type: :development
|
117
|
+
version_requirements: *id007
|
118
|
+
- !ruby/object:Gem::Dependency
|
119
|
+
name: orangutan
|
120
|
+
prerelease: false
|
121
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
segments:
|
126
|
+
- 0
|
127
|
+
- 0
|
128
|
+
- 7
|
129
|
+
version: 0.0.7
|
130
|
+
type: :development
|
131
|
+
version_requirements: *id008
|
76
132
|
description: |
|
77
133
|
A command line utility that manages accounts and passwords as individual encypted files
|
78
134
|
|
@@ -85,6 +141,18 @@ extra_rdoc_files: []
|
|
85
141
|
|
86
142
|
files:
|
87
143
|
- lib/shh/cli.rb
|
144
|
+
- lib/shh/command/copy_key.rb
|
145
|
+
- lib/shh/command/edit_key.rb
|
146
|
+
- lib/shh/command/execute_key.rb
|
147
|
+
- lib/shh/command/key_command.rb
|
148
|
+
- lib/shh/command/launch_key.rb
|
149
|
+
- lib/shh/command/list_entries.rb
|
150
|
+
- lib/shh/command/list_keys.rb
|
151
|
+
- lib/shh/command/open_entry.rb
|
152
|
+
- lib/shh/command/remove_key.rb
|
153
|
+
- lib/shh/command/set_key.rb
|
154
|
+
- lib/shh/command/show_key.rb
|
155
|
+
- lib/shh/commands.rb
|
88
156
|
- lib/shh/crypt.rb
|
89
157
|
- lib/shh/entries_menu.rb
|
90
158
|
- lib/shh/entry_menu.rb
|