crypt_keeper 1.1.0 → 1.1.1
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/lib/crypt_keeper.rb +1 -0
- data/lib/crypt_keeper/helper.rb +31 -3
- data/lib/crypt_keeper/log_subscriber/mysql_aes.rb +0 -2
- data/lib/crypt_keeper/log_subscriber/postgres_pgp.rb +1 -4
- data/lib/crypt_keeper/provider/postgres_base.rb +28 -0
- data/lib/crypt_keeper/provider/postgres_pgp.rb +7 -8
- data/lib/crypt_keeper/provider/postgres_pgp_public_key.rb +4 -20
- data/lib/crypt_keeper/version.rb +1 -1
- data/spec/crypt_keeper/log_subscriber/mysql_aes_spec.rb +0 -6
- data/spec/crypt_keeper/log_subscriber/postgres_pgp_spec.rb +30 -46
- data/spec/crypt_keeper/provider/postgres_pgp_public_key_spec.rb +1 -0
- data/spec/crypt_keeper/provider/postgres_pgp_spec.rb +1 -0
- data/spec/support/logging.rb +2 -5
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78d7a7f1ea679f9a1542a65b537a446065c9cd27
|
4
|
+
data.tar.gz: 518d78e7d4a80e07faa80b9eac9c3b268e2db2c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30b9ccbea99c9ac89ee4bf4c9df73fc0b95e2ae3f65aca3e9936a95bd406d271e672e73cefa2c717a2f0c057c638ca876a238c7058da2c3ad6e3f2d2d12720cb
|
7
|
+
data.tar.gz: 07f33f2caca79c5be2c45f2365601e8939b5c64200fba53c00de3e70a9d6b221a68efe47aad00fc7b42003bce2c964ce68701656966149afe4b65dfc45a4e0a7
|
data/lib/crypt_keeper.rb
CHANGED
@@ -6,6 +6,7 @@ require 'crypt_keeper/helper'
|
|
6
6
|
require 'crypt_keeper/provider/base'
|
7
7
|
require 'crypt_keeper/provider/aes_new'
|
8
8
|
require 'crypt_keeper/provider/mysql_aes_new'
|
9
|
+
require 'crypt_keeper/provider/postgres_base'
|
9
10
|
require 'crypt_keeper/provider/postgres_pgp'
|
10
11
|
require 'crypt_keeper/provider/postgres_pgp_public_key'
|
11
12
|
|
data/lib/crypt_keeper/helper.rb
CHANGED
@@ -3,10 +3,38 @@ module CryptKeeper
|
|
3
3
|
module SQL
|
4
4
|
private
|
5
5
|
|
6
|
-
# Private: Sanitize an sql query and then execute it
|
7
|
-
|
6
|
+
# Private: Sanitize an sql query and then execute it.
|
7
|
+
#
|
8
|
+
# query - the sql query
|
9
|
+
# new_transaction - if the query should run inside a new transaction
|
10
|
+
#
|
11
|
+
# Returns the ActiveRecord response.
|
12
|
+
def escape_and_execute_sql(query, new_transaction: false)
|
8
13
|
query = ::ActiveRecord::Base.send :sanitize_sql_array, query
|
9
|
-
|
14
|
+
|
15
|
+
if CryptKeeper.silence_logs?
|
16
|
+
::ActiveRecord::Base.logger.silence do
|
17
|
+
execute_sql(query, new_transaction: new_transaction)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
execute_sql(query, new_transaction: new_transaction)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Private: Executes the query.
|
25
|
+
#
|
26
|
+
# query - the sql query
|
27
|
+
# new_transaction - if the query should run inside a new transaction
|
28
|
+
#
|
29
|
+
# Returns an Array.
|
30
|
+
def execute_sql(query, new_transaction: false)
|
31
|
+
if new_transaction
|
32
|
+
::ActiveRecord::Base.transaction(requires_new: true) do
|
33
|
+
::ActiveRecord::Base.connection.execute(query).first
|
34
|
+
end
|
35
|
+
else
|
36
|
+
::ActiveRecord::Base.connection.execute(query).first
|
37
|
+
end
|
10
38
|
end
|
11
39
|
end
|
12
40
|
|
@@ -14,8 +14,6 @@ module CryptKeeper
|
|
14
14
|
payload = event.payload[:sql]
|
15
15
|
.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
|
16
16
|
|
17
|
-
return if CryptKeeper.silence_logs? && payload =~ filter
|
18
|
-
|
19
17
|
event.payload[:sql] = payload.gsub(filter) do |_|
|
20
18
|
"#{$1}([FILTERED])"
|
21
19
|
end
|
@@ -4,7 +4,7 @@ require 'active_support/lazy_load_hooks'
|
|
4
4
|
module CryptKeeper
|
5
5
|
module LogSubscriber
|
6
6
|
module PostgresPgp
|
7
|
-
FILTER = /(\(*)
|
7
|
+
FILTER = /(\(*)(?<operation>pgp_sym_encrypt|pgp_sym_decrypt|pgp_pub_encrypt|pgp_pub_decrypt|pgp_key_id)(\(+.*\)+)/im
|
8
8
|
|
9
9
|
# Public: Prevents sensitive data from being logged
|
10
10
|
#
|
@@ -13,9 +13,6 @@ module CryptKeeper
|
|
13
13
|
# Returns a boolean.
|
14
14
|
def sql(event)
|
15
15
|
payload = crypt_keeper_payload_parse(event.payload[:sql])
|
16
|
-
|
17
|
-
return if CryptKeeper.silence_logs? && payload =~ FILTER
|
18
|
-
|
19
16
|
event.payload[:sql] = crypt_keeper_filter_postgres_log(payload)
|
20
17
|
super(event)
|
21
18
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'crypt_keeper/log_subscriber/postgres_pgp'
|
2
|
+
|
3
|
+
module CryptKeeper
|
4
|
+
module Provider
|
5
|
+
class PostgresBase < Base
|
6
|
+
include CryptKeeper::Helper::SQL
|
7
|
+
include CryptKeeper::LogSubscriber::PostgresPgp
|
8
|
+
|
9
|
+
INVALID_DATA_ERROR = "Wrong key or corrupt data".freeze
|
10
|
+
|
11
|
+
# Public: Checks if value is already encrypted.
|
12
|
+
#
|
13
|
+
# Returns boolean
|
14
|
+
def encrypted?(value)
|
15
|
+
begin
|
16
|
+
escape_and_execute_sql(["SELECT pgp_key_id(?)", value.to_s],
|
17
|
+
new_transaction: true)['pgp_key_id'].present?
|
18
|
+
rescue ActiveRecord::StatementInvalid => e
|
19
|
+
if e.message.include?(INVALID_DATA_ERROR)
|
20
|
+
false
|
21
|
+
else
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,11 +1,6 @@
|
|
1
|
-
require 'crypt_keeper/log_subscriber/postgres_pgp'
|
2
|
-
|
3
1
|
module CryptKeeper
|
4
2
|
module Provider
|
5
|
-
class PostgresPgp <
|
6
|
-
include CryptKeeper::Helper::SQL
|
7
|
-
include CryptKeeper::LogSubscriber::PostgresPgp
|
8
|
-
|
3
|
+
class PostgresPgp < PostgresBase
|
9
4
|
attr_accessor :key
|
10
5
|
attr_accessor :pgcrypto_options
|
11
6
|
|
@@ -37,8 +32,12 @@ module CryptKeeper
|
|
37
32
|
# Returns a plaintext string
|
38
33
|
def decrypt(value)
|
39
34
|
rescue_invalid_statement do
|
40
|
-
|
41
|
-
|
35
|
+
if encrypted?(value)
|
36
|
+
escape_and_execute_sql(["SELECT pgp_sym_decrypt(?, ?)",
|
37
|
+
value, key])['pgp_sym_decrypt']
|
38
|
+
else
|
39
|
+
value
|
40
|
+
end
|
42
41
|
end
|
43
42
|
end
|
44
43
|
|
@@ -1,10 +1,6 @@
|
|
1
|
-
require 'crypt_keeper/log_subscriber/postgres_pgp'
|
2
|
-
|
3
1
|
module CryptKeeper
|
4
2
|
module Provider
|
5
|
-
class PostgresPgpPublicKey <
|
6
|
-
include CryptKeeper::Helper::SQL
|
7
|
-
|
3
|
+
class PostgresPgpPublicKey < PostgresBase
|
8
4
|
attr_accessor :key
|
9
5
|
|
10
6
|
def initialize(options = {})
|
@@ -33,25 +29,13 @@ module CryptKeeper
|
|
33
29
|
#
|
34
30
|
# Returns a plaintext string
|
35
31
|
def decrypt(value)
|
36
|
-
if @private_key.present?
|
37
|
-
escape_and_execute_sql(["SELECT pgp_pub_decrypt(?, dearmor(?), ?)",
|
32
|
+
if @private_key.present? && encrypted?(value)
|
33
|
+
escape_and_execute_sql(["SELECT pgp_pub_decrypt(?, dearmor(?), ?)",
|
34
|
+
value, @private_key, @key])['pgp_pub_decrypt']
|
38
35
|
else
|
39
36
|
value
|
40
37
|
end
|
41
38
|
end
|
42
|
-
|
43
|
-
# Public: Attempts to extract a PGP key id. If it's successful, it returns true
|
44
|
-
#
|
45
|
-
# Returns boolean
|
46
|
-
def encrypted?(value)
|
47
|
-
begin
|
48
|
-
ActiveRecord::Base.transaction(requires_new: true) do
|
49
|
-
escape_and_execute_sql(["SELECT pgp_key_id(?)", value.to_s])['pgp_key_id'].present?
|
50
|
-
end
|
51
|
-
rescue ActiveRecord::StatementInvalid
|
52
|
-
false
|
53
|
-
end
|
54
|
-
end
|
55
39
|
end
|
56
40
|
end
|
57
41
|
end
|
data/lib/crypt_keeper/version.rb
CHANGED
@@ -46,11 +46,5 @@ describe CryptKeeper::LogSubscriber::MysqlAes do
|
|
46
46
|
|
47
47
|
should_log_scrubbed_query(input: input_query, output: output_query)
|
48
48
|
end
|
49
|
-
|
50
|
-
it "skips logging if CryptKeeper.silence_logs is set" do
|
51
|
-
CryptKeeper.silence_logs = true
|
52
|
-
|
53
|
-
should_not_log_query(input_query)
|
54
|
-
end
|
55
49
|
end
|
56
50
|
end
|
@@ -13,44 +13,32 @@ describe CryptKeeper::LogSubscriber::PostgresPgp do
|
|
13
13
|
CryptKeeper::Provider::PostgresPgp.new key: 'secret'
|
14
14
|
end
|
15
15
|
|
16
|
-
let(:
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
let(:input_search_query) do
|
25
|
-
"SELECT \"sensitive_data\".* FROM \"sensitive_data\" WHERE ((pgp_sym_decrypt('f'), 'tool') = 'blah')) AND secret = 'testing'"
|
26
|
-
end
|
27
|
-
|
28
|
-
let(:output_search_query) do
|
29
|
-
"SELECT \"sensitive_data\".* FROM \"sensitive_data\" WHERE decrypt([FILTERED]) AND secret = 'testing'"
|
30
|
-
end
|
16
|
+
let(:queries) {
|
17
|
+
{
|
18
|
+
"SELECT pgp_sym_encrypt('encrypt_value', 'encrypt_key') FROM DUAL;" => "SELECT pgp_sym_encrypt([FILTERED]) FROM DUAL;",
|
19
|
+
"SELECT pgp_sym_decrypt('encrypt_value') FROM DUAL;" => "SELECT pgp_sym_decrypt([FILTERED]) FROM DUAL;",
|
20
|
+
"SELECT pgp_key_id('encrypt_value') FROM DUAL;" => "SELECT pgp_key_id([FILTERED]) FROM DUAL;",
|
21
|
+
"SELECT \"sensitive_data\".* FROM \"sensitive_data\" WHERE ((pgp_sym_decrypt('f'), 'tool') = 'blah')) AND secret = 'testing'" => "SELECT \"sensitive_data\".* FROM \"sensitive_data\" WHERE pgp_sym_decrypt([FILTERED]) AND secret = 'testing'",
|
22
|
+
}
|
23
|
+
}
|
31
24
|
|
32
25
|
it "filters pgp functions" do
|
33
|
-
|
26
|
+
queries.each do |k, v|
|
27
|
+
should_log_scrubbed_query(input: k, output: v)
|
28
|
+
end
|
34
29
|
end
|
35
30
|
|
36
31
|
it "filters pgp functions in lowercase" do
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
it "filters pgp functions when searching" do
|
41
|
-
should_log_scrubbed_query(input: input_search_query, output: output_search_query)
|
32
|
+
queries.each do |k, v|
|
33
|
+
should_log_scrubbed_query(input: k.downcase, output: v.downcase.gsub(/filtered/, 'FILTERED'))
|
34
|
+
end
|
42
35
|
end
|
43
36
|
|
44
37
|
it "forces string encodings" do
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
it "skips logging if CryptKeeper.silence_logs is set" do
|
51
|
-
CryptKeeper.silence_logs = true
|
52
|
-
|
53
|
-
should_not_log_query(input_query)
|
38
|
+
queries.each do |k, v|
|
39
|
+
k = "#{k}\255"
|
40
|
+
should_log_scrubbed_query(input: k, output: v)
|
41
|
+
end
|
54
42
|
end
|
55
43
|
end
|
56
44
|
|
@@ -68,27 +56,23 @@ describe CryptKeeper::LogSubscriber::PostgresPgp do
|
|
68
56
|
CryptKeeper::Provider::PostgresPgpPublicKey.new key: 'secret', public_key: public_key, private_key: private_key
|
69
57
|
end
|
70
58
|
|
71
|
-
let(:
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
"SELECT encrypt([FILTERED])"
|
78
|
-
end
|
59
|
+
let(:queries) {
|
60
|
+
{
|
61
|
+
"SELECT pgp_pub_encrypt('test', dearmor('#{public_key}')) FROM DUAL;" => "SELECT pgp_pub_encrypt([FILTERED]) FROM DUAL;",
|
62
|
+
"SELECT pgp_pub_decrypt('test', dearmor('#{public_key}'), '#{private_key}') FROM DUAL;" => "SELECT pgp_pub_decrypt([FILTERED]) FROM DUAL;",
|
63
|
+
}
|
64
|
+
}
|
79
65
|
|
80
66
|
it "filters pgp functions" do
|
81
|
-
|
67
|
+
queries.each do |k, v|
|
68
|
+
should_log_scrubbed_query(input: k, output: v)
|
69
|
+
end
|
82
70
|
end
|
83
71
|
|
84
72
|
it "filters pgp functions in lowercase" do
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
it "skips logging if CryptKeeper.silence_logs is set" do
|
89
|
-
CryptKeeper.silence_logs = true
|
90
|
-
|
91
|
-
should_not_log_query(input_query)
|
73
|
+
queries.each do |k, v|
|
74
|
+
should_log_scrubbed_query(input: k.downcase, output: v.downcase.gsub(/filtered/, 'FILTERED'))
|
75
|
+
end
|
92
76
|
end
|
93
77
|
end
|
94
78
|
end
|
@@ -47,6 +47,7 @@ describe CryptKeeper::Provider::PostgresPgpPublicKey do
|
|
47
47
|
describe "#decrypt" do
|
48
48
|
specify { expect(subject.decrypt(cipher_text)).to eq(plain_text) }
|
49
49
|
specify { expect(subject.decrypt(integer_cipher_text)).to eq(integer_plain_text.to_s) }
|
50
|
+
specify { expect(subject.decrypt(plain_text)).to eq(plain_text) }
|
50
51
|
|
51
52
|
it "does not decrypt w/o private key" do
|
52
53
|
pgp = described_class.new key: ENCRYPTION_PASSWORD, public_key: public_key
|
@@ -44,6 +44,7 @@ describe CryptKeeper::Provider::PostgresPgp do
|
|
44
44
|
describe "#decrypt" do
|
45
45
|
specify { expect(subject.decrypt(cipher_text)).to eq(plain_text) }
|
46
46
|
specify { expect(subject.decrypt(integer_cipher_text)).to eq(integer_plain_text.to_s) }
|
47
|
+
specify { expect(subject.decrypt(plain_text)).to eq(plain_text) }
|
47
48
|
|
48
49
|
it "filters StatementInvalid errors" do
|
49
50
|
begin
|
data/spec/support/logging.rb
CHANGED
@@ -47,11 +47,8 @@ module CryptKeeper
|
|
47
47
|
def should_log_scrubbed_query(input:, output:)
|
48
48
|
queries = sql(input)
|
49
49
|
|
50
|
-
|
51
|
-
expect(
|
52
|
-
|
53
|
-
valid_output = queries.any? { |line| line.include? output }
|
54
|
-
expect(valid_output).to eq(true), "output query was not logged!"
|
50
|
+
expect(queries.join("\n")).to_not include(input)
|
51
|
+
expect(queries.join("\n")).to include(output)
|
55
52
|
end
|
56
53
|
|
57
54
|
# Public: Verifies that the given input query was not logged.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crypt_keeper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Mazzi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-06-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -245,6 +245,7 @@ files:
|
|
245
245
|
- lib/crypt_keeper/provider/aes_new.rb
|
246
246
|
- lib/crypt_keeper/provider/base.rb
|
247
247
|
- lib/crypt_keeper/provider/mysql_aes_new.rb
|
248
|
+
- lib/crypt_keeper/provider/postgres_base.rb
|
248
249
|
- lib/crypt_keeper/provider/postgres_pgp.rb
|
249
250
|
- lib/crypt_keeper/provider/postgres_pgp_public_key.rb
|
250
251
|
- lib/crypt_keeper/version.rb
|