my_obfuscate 0.3.0 → 0.3.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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.rdoc +49 -14
- data/Rakefile +4 -56
- data/lib/my_obfuscate.rb +117 -82
- data/lib/my_obfuscate/mysql.rb +91 -0
- data/lib/my_obfuscate/sql_server.rb +81 -0
- data/lib/my_obfuscate/version.rb +3 -0
- data/my_obfuscate.gemspec +12 -44
- data/spec/my_obfuscate_spec.rb +442 -129
- data/spec/mysql_spec.rb +78 -0
- data/spec/spec_helper.rb +5 -5
- data/spec/sql_server_spec.rb +58 -0
- metadata +70 -51
- data/.document +0 -5
- data/VERSION +0 -1
@@ -0,0 +1,91 @@
|
|
1
|
+
class MyObfuscate
|
2
|
+
class Mysql
|
3
|
+
INSERT_REGEX = /^\s*INSERT INTO `(.*?)` \((.*?)\) VALUES\s*/i
|
4
|
+
|
5
|
+
def parse_insert_statement(line)
|
6
|
+
if regex_match = INSERT_REGEX.match(line)
|
7
|
+
{
|
8
|
+
:table_name => regex_match[1].to_sym,
|
9
|
+
:column_names => regex_match[2].split(/`\s*,\s*`/).map { |col| col.gsub('`', "").to_sym }
|
10
|
+
}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def rows_to_be_inserted(line)
|
15
|
+
line = line.gsub(INSERT_REGEX, '').gsub(/\s*;\s*$/, '')
|
16
|
+
context_aware_mysql_string_split(line)
|
17
|
+
end
|
18
|
+
|
19
|
+
def make_valid_value_string(value)
|
20
|
+
if value.nil?
|
21
|
+
"NULL"
|
22
|
+
elsif value =~ /^0x[0-9a-fA-F]+$/
|
23
|
+
value
|
24
|
+
else
|
25
|
+
"'" + value + "'"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def make_insert_statement(table_name, column_names, values_strings)
|
30
|
+
"INSERT INTO `#{table_name}` (`#{column_names.join('`, `')}`) VALUES #{values_strings};"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Be aware, strings must be quoted in single quotes!
|
36
|
+
def context_aware_mysql_string_split(string)
|
37
|
+
in_sub_insert = false
|
38
|
+
in_quoted_string = false
|
39
|
+
escaped = false
|
40
|
+
current_field = nil
|
41
|
+
length = string.length
|
42
|
+
fields = []
|
43
|
+
output = []
|
44
|
+
|
45
|
+
string.each_char do |i|
|
46
|
+
if escaped
|
47
|
+
escaped = false
|
48
|
+
current_field ||= ""
|
49
|
+
current_field << i
|
50
|
+
else
|
51
|
+
if i == "\\"
|
52
|
+
escaped = true
|
53
|
+
current_field ||= ""
|
54
|
+
current_field << i
|
55
|
+
elsif i == "(" && !in_quoted_string && !in_sub_insert
|
56
|
+
in_sub_insert = true
|
57
|
+
elsif i == ")" && !in_quoted_string && in_sub_insert
|
58
|
+
fields << current_field unless current_field.nil?
|
59
|
+
output << fields unless fields.length == 0
|
60
|
+
in_sub_insert = false
|
61
|
+
fields = []
|
62
|
+
current_field = nil
|
63
|
+
elsif i == "'" && !in_quoted_string
|
64
|
+
fields << current_field unless current_field.nil?
|
65
|
+
current_field = ''
|
66
|
+
in_quoted_string = true
|
67
|
+
elsif i == "'" && in_quoted_string
|
68
|
+
fields << current_field unless current_field.nil?
|
69
|
+
current_field = nil
|
70
|
+
in_quoted_string = false
|
71
|
+
elsif i == "," && !in_quoted_string && in_sub_insert
|
72
|
+
fields << current_field unless current_field.nil?
|
73
|
+
current_field = nil
|
74
|
+
elsif i == "L" && !in_quoted_string && in_sub_insert && current_field == "NUL"
|
75
|
+
current_field = nil
|
76
|
+
fields << current_field
|
77
|
+
elsif (i == " " || i == "\t") && !in_quoted_string
|
78
|
+
# Don't add whitespace not in a string
|
79
|
+
elsif in_sub_insert
|
80
|
+
current_field ||= ""
|
81
|
+
current_field << i
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
fields << current_field unless current_field.nil?
|
87
|
+
output << fields unless fields.length == 0
|
88
|
+
output
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
class MyObfuscate
|
2
|
+
class SqlServer
|
3
|
+
INSERT_REGEX = /^\s*INSERT (?:INTO )?\[dbo\]\.\[(.*?)\] \((.*?)\) VALUES\s*/i
|
4
|
+
|
5
|
+
def parse_insert_statement(line)
|
6
|
+
if regex_match = INSERT_REGEX.match(line)
|
7
|
+
{
|
8
|
+
:table_name => regex_match[1].to_sym,
|
9
|
+
:column_names => regex_match[2].split(/\]\s*,\s*\[/).map { |col| col.gsub(/[\[\]]/, "").to_sym }
|
10
|
+
}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def rows_to_be_inserted(line)
|
15
|
+
line = line.gsub(INSERT_REGEX, '').gsub(/\s*;?\s*$/, '').gsub(/^\(/, '').gsub(/\)$/, '')
|
16
|
+
context_aware_sql_server_string_split(line)
|
17
|
+
end
|
18
|
+
|
19
|
+
def make_valid_value_string(value)
|
20
|
+
if value.nil?
|
21
|
+
"NULL"
|
22
|
+
elsif value.match(/^[A-Z]+\(.*?\)$/)
|
23
|
+
value
|
24
|
+
else
|
25
|
+
"N'#{value}'"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def make_insert_statement(table_name, column_names, values_strings)
|
30
|
+
"INSERT [dbo].[#{table_name}] ([#{column_names.join("], [")}]) VALUES #{values_strings};"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def context_aware_sql_server_string_split(string)
|
36
|
+
in_quoted_string = false
|
37
|
+
backslash_escape = false
|
38
|
+
previous_char_single_quote = false
|
39
|
+
current_field_value = nil
|
40
|
+
completed_fields = []
|
41
|
+
|
42
|
+
string.each_char do |char|
|
43
|
+
if char == "'" && !in_quoted_string
|
44
|
+
if current_field_value != "N"
|
45
|
+
completed_fields << current_field_value unless current_field_value.nil?
|
46
|
+
end
|
47
|
+
current_field_value = ""
|
48
|
+
in_quoted_string = true
|
49
|
+
elsif previous_char_single_quote
|
50
|
+
previous_char_single_quote = false
|
51
|
+
if char == "'"
|
52
|
+
current_field_value << "''"
|
53
|
+
else
|
54
|
+
completed_fields << current_field_value unless current_field_value.nil?
|
55
|
+
in_quoted_string = false
|
56
|
+
current_field_value = nil
|
57
|
+
end
|
58
|
+
elsif char == "'" && in_quoted_string
|
59
|
+
previous_char_single_quote = true
|
60
|
+
elsif char == "," && !in_quoted_string
|
61
|
+
completed_fields << current_field_value unless current_field_value.nil?
|
62
|
+
current_field_value = nil
|
63
|
+
elsif char == "L" && !in_quoted_string && current_field_value == "NUL"
|
64
|
+
current_field_value = nil
|
65
|
+
completed_fields << current_field_value
|
66
|
+
elsif (char == " " || char == "\t") && !in_quoted_string
|
67
|
+
if !current_field_value.nil? && current_field_value.start_with?("CAST(")
|
68
|
+
current_field_value << char
|
69
|
+
end
|
70
|
+
# Don't add whitespace not in a string
|
71
|
+
else
|
72
|
+
current_field_value ||= ""
|
73
|
+
current_field_value << char
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
completed_fields << current_field_value unless current_field_value.nil?
|
78
|
+
[completed_fields]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/my_obfuscate.gemspec
CHANGED
@@ -1,54 +1,22 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "my_obfuscate/version"
|
5
4
|
|
6
5
|
Gem::Specification.new do |s|
|
7
6
|
s.name = %q{my_obfuscate}
|
8
|
-
s.version =
|
7
|
+
s.version = MyObfuscate::VERSION
|
9
8
|
|
10
|
-
s.
|
11
|
-
s.authors = ["Andrew Cantino", "Dave Willett", "Mike Grafton", "Mason Glaves"]
|
12
|
-
s.date = %q{2009-10-09}
|
9
|
+
s.authors = ["Andrew Cantino", "Dave Willett", "Mike Grafton", "Mason Glaves", "Greg Bell", "Mavenlink"]
|
13
10
|
s.description = %q{Standalone Ruby code for the selective rewriting of MySQL dumps in order to protect user privacy.}
|
14
|
-
s.email = %q{andrew@
|
15
|
-
s.
|
16
|
-
"LICENSE",
|
17
|
-
"README.rdoc"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
".document",
|
21
|
-
".gitignore",
|
22
|
-
"LICENSE",
|
23
|
-
"README.rdoc",
|
24
|
-
"Rakefile",
|
25
|
-
"VERSION",
|
26
|
-
"lib/my_obfuscate.rb",
|
27
|
-
"my_obfuscate.gemspec",
|
28
|
-
"spec/my_obfuscate_spec.rb",
|
29
|
-
"spec/spec_helper.rb"
|
30
|
-
]
|
31
|
-
s.homepage = %q{http://github.com/honkster/myobfuscate}
|
32
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
33
|
-
s.require_paths = ["lib"]
|
34
|
-
s.rubyforge_project = %q{my-obfuscate}
|
35
|
-
s.rubygems_version = %q{1.3.5}
|
11
|
+
s.email = %q{andrew@iterationlabs.com}
|
12
|
+
s.homepage = %q{http://github.com/iterationlabs/my_obfuscate}
|
36
13
|
s.summary = %q{Standalone Ruby code for the selective rewriting of MySQL dumps in order to protect user privacy.}
|
37
|
-
s.test_files = [
|
38
|
-
"spec/my_obfuscate_spec.rb",
|
39
|
-
"spec/spec_helper.rb"
|
40
|
-
]
|
41
14
|
|
42
|
-
|
43
|
-
|
44
|
-
s.specification_version = 3
|
15
|
+
s.add_development_dependency "rspec"
|
16
|
+
s.add_dependency "faker", ">= 1.0.1"
|
45
17
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
51
|
-
else
|
52
|
-
s.add_dependency(%q<rspec>, [">= 0"])
|
53
|
-
end
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
54
22
|
end
|
data/spec/my_obfuscate_spec.rb
CHANGED
@@ -1,51 +1,19 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
describe MyObfuscate do
|
4
|
-
describe "MyObfuscate.
|
5
|
-
it "should split a mysql string into fields" do
|
6
|
-
string = "('bob@bob.com','bob', 'somethingelse1', 25, '2', 10, 'hi')"
|
7
|
-
fields = [['bob@bob.com', 'bob', 'somethingelse1', '25', '2', '10', "hi"]]
|
8
|
-
MyObfuscate.context_aware_mysql_string_split(string).should == fields
|
9
|
-
end
|
10
|
-
|
11
|
-
it "should work ok with escaped characters" do
|
12
|
-
string = "('bob,@bob.c , om', 'bo\\', b', 'some\"thin\\gel\\\\\\'se1', 25, '2', 10, 'hi', 5)"
|
13
|
-
fields = [['bob,@bob.c , om', 'bo\\\', b', 'some"thin\\gel\\\\\\\'se1', '25', '2', '10', "hi", "5"]]
|
14
|
-
MyObfuscate.context_aware_mysql_string_split(string).should == fields
|
15
|
-
end
|
16
|
-
|
17
|
-
it "should work with multiple subinserts" do
|
18
|
-
string = "(1,2,3, '((m))(oo()s,e'), ('bob,@bob.c , om', 'bo\\', b', 'some\"thin\\gel\\\\\\'se1', 25, '2', 10, 'hi', 5)"
|
19
|
-
fields = [["1", "2", "3", "((m))(oo()s,e"], ['bob,@bob.c , om', 'bo\\\', b', 'some"thin\\gel\\\\\\\'se1', '25', '2', '10', "hi", "5"]]
|
20
|
-
MyObfuscate.context_aware_mysql_string_split(string).should == fields
|
21
|
-
end
|
22
|
-
|
23
|
-
it "should work ok with NULL values" do
|
24
|
-
string = "(NULL , 'bob@bob.com','bob', NULL, 25, '2', NULL, 'hi', NULL )"
|
25
|
-
fields = [[nil, 'bob@bob.com', 'bob', nil, '25', '2', nil, "hi", nil]]
|
26
|
-
MyObfuscate.context_aware_mysql_string_split(string).should == fields
|
27
|
-
end
|
28
|
-
|
29
|
-
it "should work with empty strings" do
|
30
|
-
string = "(NULL , '', '' , '', 25, '2','', 'hi','')"
|
31
|
-
fields = [[nil, '', '', '', '25', '2', '', "hi", '']]
|
32
|
-
MyObfuscate.context_aware_mysql_string_split(string).should == fields
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
describe "MyObfuscate.reasembling_each_insert" do
|
4
|
+
describe "MyObfuscate.reassembling_each_insert" do
|
37
5
|
before do
|
38
6
|
@column_names = [:a, :b, :c, :d]
|
39
7
|
@test_insert = "INSERT INTO `some_table` (`a`, `b`, `c`, `d`) VALUES ('(\\'bob@bob.com','b()ob','some(thingelse1','25)('),('joe@joe.com','joe','somethingelse2','54');"
|
40
8
|
@test_insert_passes = [
|
41
|
-
|
42
|
-
|
9
|
+
["(\\'bob@bob.com", "b()ob", "some(thingelse1", "25)("],
|
10
|
+
["joe@joe.com", "joe", "somethingelse2", "54"]
|
43
11
|
]
|
44
12
|
end
|
45
13
|
|
46
14
|
it "should yield each subinsert and reassemble the result" do
|
47
15
|
count = 0
|
48
|
-
reassembled = MyObfuscate.
|
16
|
+
reassembled = MyObfuscate.new.reassembling_each_insert(@test_insert, "some_table", @column_names) do |sub_insert|
|
49
17
|
sub_insert.should == @test_insert_passes.shift
|
50
18
|
count += 1
|
51
19
|
sub_insert
|
@@ -57,13 +25,13 @@ describe MyObfuscate do
|
|
57
25
|
|
58
26
|
describe "MyObfuscate.apply_table_config" do
|
59
27
|
it "should work on email addresses" do
|
60
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else"], {
|
28
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else"], {:a => {:type => :email}}, [:a, :b])
|
61
29
|
new_row.length.should == 2
|
62
|
-
new_row.first.should =~
|
30
|
+
new_row.first.should =~ /^[\w\.]+\@\w+\.\w+$/
|
63
31
|
end
|
64
32
|
|
65
33
|
it "should work on strings" do
|
66
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "something crazy"], {
|
34
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "something crazy"], {:b => {:type => :string, :length => 7}}, [:a, :b, :c])
|
67
35
|
new_row.length.should == 3
|
68
36
|
new_row[1].length.should == 7
|
69
37
|
new_row[1].should_not == "something_else"
|
@@ -71,67 +39,95 @@ describe MyObfuscate do
|
|
71
39
|
|
72
40
|
describe "conditional directives" do
|
73
41
|
it "should honor :unless conditionals" do
|
74
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {
|
42
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :unless => lambda { |row| row[:a] == "blah" }}}, [:a, :b, :c])
|
75
43
|
new_row[0].should_not == "123"
|
76
44
|
new_row[0].should == "blah"
|
77
45
|
|
78
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {
|
46
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :unless => lambda { |row| row[:a] == "not blah" }}}, [:a, :b, :c])
|
79
47
|
new_row[0].should == "123"
|
48
|
+
|
49
|
+
new_row = MyObfuscate.apply_table_config([nil, "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :unless => :nil}, :b=> {:type => :fixed, :string => "123", :unless => :nil}}, [:a, :b, :c])
|
50
|
+
new_row[0].should == nil
|
51
|
+
new_row[1].should == "123"
|
52
|
+
|
53
|
+
new_row = MyObfuscate.apply_table_config(['', "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :unless => :blank}, :b=> {:type => :fixed, :string => "123", :unless => :blank}}, [:a, :b, :c])
|
54
|
+
new_row[0].should == ''
|
55
|
+
new_row[1].should == "123"
|
80
56
|
end
|
81
57
|
|
82
58
|
it "should honor :if conditionals" do
|
83
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {
|
59
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => lambda { |row| row[:a] == "blah" }}}, [:a, :b, :c])
|
84
60
|
new_row[0].should == "123"
|
85
61
|
|
86
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {
|
62
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if=> lambda { |row| row[:a] == "not blah" }}}, [:a, :b, :c])
|
87
63
|
new_row[0].should_not == "123"
|
88
64
|
new_row[0].should == "blah"
|
65
|
+
|
66
|
+
new_row = MyObfuscate.apply_table_config([nil, "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => :nil}, :b=> {:type => :fixed, :string => "123", :if => :nil}}, [:a, :b, :c])
|
67
|
+
new_row[0].should == "123"
|
68
|
+
new_row[1].should == "something_else"
|
69
|
+
|
70
|
+
new_row = MyObfuscate.apply_table_config(['', "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => :blank}, :b=> {:type => :fixed, :string => "123", :if => :blank}}, [:a, :b, :c])
|
71
|
+
new_row[0].should == "123"
|
72
|
+
new_row[1].should == "something_else"
|
89
73
|
end
|
90
74
|
|
91
75
|
it "should supply the original row values to the conditional" do
|
92
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else"], {
|
76
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else"], {:a => {:type => :fixed, :string => "123"}, :b => {:type => :fixed, :string => "yup", :if => lambda { |row| row[:a] == "blah" }}}, [:a, :b])
|
93
77
|
new_row[0].should == "123"
|
94
78
|
new_row[1].should == "yup"
|
95
79
|
end
|
96
80
|
|
97
81
|
it "should honor combined :unless and :if conditionals" do
|
98
82
|
#both true
|
99
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {
|
83
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => lambda { |row| row[:a] == "blah" }, :unless => lambda { |row| row[:b] == "something_else" }}}, [:a, :b, :c])
|
100
84
|
new_row[0].should == "blah"
|
101
85
|
|
102
86
|
#both false
|
103
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {
|
87
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => lambda { |row| row[:a] == "not blah" }, :unless => lambda { |row| row[:b] == "not something_else" }}}, [:a, :b, :c])
|
104
88
|
new_row[0].should == "blah"
|
105
89
|
|
106
90
|
#if true, #unless false
|
107
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {
|
91
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => lambda { |row| row[:a] == "blah" }, :unless => lambda { |row| row[:b] == "not something_else" }}}, [:a, :b, :c])
|
108
92
|
new_row[0].should == "123"
|
109
93
|
|
110
94
|
#if false, #unless true
|
111
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {
|
95
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => lambda { |row| row[:a] == "not blah" }, :unless => lambda { |row| row[:b] == "something_else" }}}, [:a, :b, :c])
|
112
96
|
new_row[0].should == "blah"
|
113
97
|
end
|
114
98
|
end
|
115
99
|
|
116
100
|
it "should be able to generate random integers in ranges" do
|
117
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {
|
101
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:c => {:type => :integer, :between => 10..100}}, [:a, :b, :c])
|
118
102
|
new_row.length.should == 3
|
119
103
|
new_row[2].to_i.to_s.should == new_row[2] # It should be an integer.
|
120
104
|
new_row[2].should_not == "5"
|
121
105
|
end
|
122
106
|
|
123
107
|
it "should be able to substitute fixed strings" do
|
124
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {
|
108
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => {:type => :fixed, :string => "hello"}}, [:a, :b, :c])
|
125
109
|
new_row.length.should == 3
|
126
110
|
new_row[1].should == "hello"
|
127
111
|
end
|
128
112
|
|
113
|
+
it "should be able to substitute a proc that returns a string" do
|
114
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => {:type => :fixed, :string => proc { "Hello World" }}}, [:a, :b, :c])
|
115
|
+
new_row.length.should == 3
|
116
|
+
new_row[1].should == "Hello World"
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should provide the row to the proc" do
|
120
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => {:type => :fixed, :string => proc { |a| a[:b] }}}, [:a, :b, :c])
|
121
|
+
new_row.length.should == 3
|
122
|
+
new_row[1].should == "something_else"
|
123
|
+
end
|
124
|
+
|
129
125
|
it "should be able to substitute fixed strings from a random set" do
|
130
126
|
looking_for = ["hello", "world"]
|
131
127
|
original_looking_for = looking_for.dup
|
132
128
|
guard = 0
|
133
129
|
while !looking_for.empty? && guard < 1000
|
134
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {
|
130
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => {:type => :fixed, :one_of => ["hello", "world"]}}, [:a, :b, :c])
|
135
131
|
new_row.length.should == 3
|
136
132
|
original_looking_for.should include(new_row[0])
|
137
133
|
looking_for.delete new_row[0]
|
@@ -140,12 +136,135 @@ describe MyObfuscate do
|
|
140
136
|
looking_for.should be_empty
|
141
137
|
end
|
142
138
|
|
139
|
+
it "should treat a symbol in the column definition as an implicit { :type => symbol }" do
|
140
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => :null, :a => :keep}, [:a, :b, :c])
|
141
|
+
new_row.length.should == 3
|
142
|
+
new_row[0].should == "blah"
|
143
|
+
new_row[1].should == nil
|
144
|
+
end
|
145
|
+
|
143
146
|
it "should be able to set things NULL" do
|
144
|
-
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {
|
147
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => {:type => :null}}, [:a, :b, :c])
|
145
148
|
new_row.length.should == 3
|
146
149
|
new_row[1].should == nil
|
147
150
|
end
|
148
151
|
|
152
|
+
it "should be able to :keep the value the same" do
|
153
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => {:type => :keep}}, [:a, :b, :c])
|
154
|
+
new_row.length.should == 3
|
155
|
+
new_row[1].should == "something_else"
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should keep the value when given an unknown type, but should display a warning" do
|
159
|
+
$stderr = error_output = StringIO.new
|
160
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => {:type => :unknown_type}}, [:a, :b, :c])
|
161
|
+
$stderr = STDERR
|
162
|
+
new_row.length.should == 3
|
163
|
+
new_row[1].should == "something_else"
|
164
|
+
error_output.rewind
|
165
|
+
error_output.read.should =~ /Keeping a column value by.*?unknown_type/
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should be able to substitute lorem ipsum text" do
|
169
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :lorem, :b => {:type => :lorem, :number => 2}}, [:a, :b, :c])
|
170
|
+
new_row.length.should == 3
|
171
|
+
new_row[0].should_not == "blah"
|
172
|
+
new_row[0].should_not =~ /\w\.(?!\Z)/
|
173
|
+
new_row[1].should_not == "something_else"
|
174
|
+
new_row[1].should =~ /\w\.(?!\Z)/
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should be able to generate an :company" do
|
178
|
+
new_row = MyObfuscate.apply_table_config(["Smith and Sons", "something_else", "5"], {:a => :company}, [:a, :b, :c])
|
179
|
+
new_row.length.should == 3
|
180
|
+
new_row[0].should_not == "Smith and Sons"
|
181
|
+
new_row[0].should =~ /\w+/
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should be able to generate an :url" do
|
185
|
+
new_row = MyObfuscate.apply_table_config(["http://mystuff.blogger.com", "something_else", "5"], {:a => :url}, [:a, :b, :c])
|
186
|
+
new_row.length.should == 3
|
187
|
+
new_row[0].should_not == "http://mystuff.blogger.com"
|
188
|
+
new_row[0].should =~ /http:\/\/\w+/
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should be able to generate an :ipv4" do
|
192
|
+
new_row = MyObfuscate.apply_table_config(["1.2.3.4", "something_else", "5"], {:a => :ipv4}, [:a, :b, :c])
|
193
|
+
new_row.length.should == 3
|
194
|
+
new_row[0].should_not == "1.2.3.4"
|
195
|
+
new_row[0].should =~ /\d+\.\d+\.\d+\.\d+/
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should be able to generate an :ipv6" do
|
199
|
+
new_row = MyObfuscate.apply_table_config(["fe80:0000:0000:0000:0202:b3ff:fe1e:8329", "something_else", "5"], {:a => :ipv6}, [:a, :b, :c])
|
200
|
+
new_row.length.should == 3
|
201
|
+
new_row[0].should_not == "fe80:0000:0000:0000:0202:b3ff:fe1e:8329"
|
202
|
+
new_row[0].should =~ /[0-9a-f:]+/
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should be able to generate an :address" do
|
206
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :address}, [:a, :b, :c])
|
207
|
+
new_row.length.should == 3
|
208
|
+
new_row[0].should_not == "blah"
|
209
|
+
new_row[0].should =~ /\d+ \w/
|
210
|
+
end
|
211
|
+
|
212
|
+
it "should be able to generate a :name" do
|
213
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :name}, [:a, :b, :c])
|
214
|
+
new_row.length.should == 3
|
215
|
+
new_row[0].should_not == "blah"
|
216
|
+
new_row[0].should =~ / /
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should be able to generate just a street address" do
|
220
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :street_address}, [:a, :b, :c])
|
221
|
+
new_row.length.should == 3
|
222
|
+
new_row[0].should_not == "blah"
|
223
|
+
new_row[0].should =~ /\d+ \w/
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should be able to generate a city" do
|
227
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :city}, [:a, :b, :c])
|
228
|
+
new_row.length.should == 3
|
229
|
+
new_row[0].should_not == "blah"
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should be able to generate a state" do
|
233
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :state}, [:a, :b, :c])
|
234
|
+
new_row.length.should == 3
|
235
|
+
new_row[0].should_not == "blah"
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should be able to generate a zip code" do
|
239
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :zip_code}, [:a, :b, :c])
|
240
|
+
new_row.length.should == 3
|
241
|
+
new_row[0].should_not == "blah"
|
242
|
+
new_row[0].should =~ /\d+/
|
243
|
+
end
|
244
|
+
|
245
|
+
it "should be able to generate a phone number" do
|
246
|
+
new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :phone}, [:a, :b, :c])
|
247
|
+
new_row.length.should == 3
|
248
|
+
new_row[0].should_not == "blah"
|
249
|
+
new_row[0].should =~ /\d+/
|
250
|
+
end
|
251
|
+
|
252
|
+
describe "when faker generates values with quotes in them" do
|
253
|
+
before do
|
254
|
+
Faker::Address.stub(:city).and_return("O'ReillyTown")
|
255
|
+
Faker::Name.stub(:name).and_return("Foo O'Reilly")
|
256
|
+
Faker::Name.stub(:first_name).and_return("O'Foo")
|
257
|
+
Faker::Name.stub(:last_name).and_return("O'Reilly")
|
258
|
+
Faker::Lorem.stub(:sentences).with(any_args).and_return(["Foo bar O'Thingy"])
|
259
|
+
end
|
260
|
+
|
261
|
+
it "should remove single quotes from the value" do
|
262
|
+
new_row = MyObfuscate.apply_table_config(["address", "city", "first", "last", "fullname", "some text"],
|
263
|
+
{:a => :address, :b => :city, :c => :first_name, :d => :last_name, :e => :name, :f => :lorem},
|
264
|
+
[:a, :b, :c, :d, :e, :f])
|
265
|
+
new_row.each {|value| value.should_not include("'")}
|
266
|
+
end
|
267
|
+
end
|
149
268
|
end
|
150
269
|
|
151
270
|
describe "MyObfuscate.row_as_hash" do
|
@@ -155,91 +274,285 @@ describe MyObfuscate do
|
|
155
274
|
end
|
156
275
|
|
157
276
|
describe "#obfuscate" do
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
277
|
+
describe "when using MySQL" do
|
278
|
+
context "when there is nothing to obfuscate" do
|
279
|
+
it "should accept an IO object for input and output, and copy the input to the output" do
|
280
|
+
ddo = MyObfuscate.new
|
281
|
+
string = "hello, world\nsup?"
|
282
|
+
input = StringIO.new(string)
|
283
|
+
output = StringIO.new
|
284
|
+
ddo.obfuscate(input, output)
|
285
|
+
input.rewind
|
286
|
+
output.rewind
|
287
|
+
output.read.should == string
|
288
|
+
end
|
168
289
|
end
|
169
|
-
end
|
170
290
|
|
171
|
-
|
172
|
-
|
173
|
-
|
291
|
+
context "when the dump to obfuscate is missing columns" do
|
292
|
+
before do
|
293
|
+
@database_dump = StringIO.new(<<-SQL)
|
174
294
|
INSERT INTO `some_table` (`email`, `name`, `something`, `age`) VALUES ('bob@honk.com','bob', 'some\\'thin,ge())lse1', 25),('joe@joe.com','joe', 'somethingelse2', 54);
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
295
|
+
SQL
|
296
|
+
@ddo = MyObfuscate.new({
|
297
|
+
:some_table => {
|
298
|
+
:email => {:type => :email, :honk_email_skip => true},
|
299
|
+
:name => {:type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS},
|
300
|
+
:gender => {:type => :fixed, :string => "m"}
|
301
|
+
}})
|
302
|
+
@output = StringIO.new
|
303
|
+
end
|
304
|
+
|
305
|
+
it "should raise an error if a column name can't be found" do
|
306
|
+
lambda {
|
307
|
+
@ddo.obfuscate(@database_dump, @output)
|
308
|
+
}.should raise_error
|
309
|
+
end
|
189
310
|
end
|
190
|
-
end
|
191
311
|
|
192
|
-
|
193
|
-
|
194
|
-
|
312
|
+
context "when there is something to obfuscate" do
|
313
|
+
before do
|
314
|
+
@database_dump = StringIO.new(<<-SQL)
|
195
315
|
INSERT INTO `some_table` (`email`, `name`, `something`, `age`) VALUES ('bob@honk.com','bob', 'some\\'thin,ge())lse1', 25),('joe@joe.com','joe', 'somethingelse2', 54),('dontmurderme@direwolf.com','direwolf', 'somethingelse3', 44);
|
196
316
|
INSERT INTO `another_table` (`a`, `b`, `c`, `d`) VALUES (1,2,3,4), (5,6,7,8);
|
317
|
+
INSERT INTO `some_table_to_keep` (`a`, `b`, `c`, `d`) VALUES (1,2,3,4), (5,6,7,8);
|
197
318
|
INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES ('hello','kjhjd^&dkjh', 'aawefjkafe'), ('hello1','kjhj!', 892938), ('hello2','moose!!', NULL);
|
198
319
|
INSERT INTO `an_ignored_table` (`col`, `col2`) VALUES ('hello','kjhjd^&dkjh'), ('hello1','kjhj!'), ('hello2','moose!!');
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
320
|
+
SQL
|
321
|
+
|
322
|
+
@ddo = MyObfuscate.new({
|
323
|
+
:some_table => {
|
324
|
+
:email => {:type => :email, :skip_regexes => [/^[\w\.\_]+@honk\.com$/i, /^dontmurderme@direwolf.com$/]},
|
325
|
+
:name => {:type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS},
|
326
|
+
:age => {:type => :integer, :between => 10...80}
|
327
|
+
},
|
328
|
+
:another_table => :truncate,
|
329
|
+
:some_table_to_keep => :keep,
|
330
|
+
:one_more_table => {
|
331
|
+
# Note: fixed strings must be pre-SQL escaped!
|
332
|
+
:password => {:type => :fixed, :string => "monkey"},
|
333
|
+
:c => {:type => :null}
|
334
|
+
}
|
335
|
+
})
|
336
|
+
@output = StringIO.new
|
337
|
+
$stderr = @error_output = StringIO.new
|
338
|
+
@ddo.obfuscate(@database_dump, @output)
|
339
|
+
$stderr = STDERR
|
340
|
+
@output.rewind
|
341
|
+
@output_string = @output.read
|
342
|
+
end
|
343
|
+
|
344
|
+
it "should be able to truncate tables" do
|
345
|
+
@output_string.should_not include("INSERT INTO `another_table`")
|
346
|
+
@output_string.should include("INSERT INTO `one_more_table`")
|
347
|
+
end
|
348
|
+
|
349
|
+
it "should be able to declare tables to keep" do
|
350
|
+
@output_string.should include("INSERT INTO `some_table_to_keep` (`a`, `b`, `c`, `d`) VALUES (1,2,3,4), (5,6,7,8);")
|
351
|
+
end
|
352
|
+
|
353
|
+
it "should ignore tables that it doesn't know about, but should warn" do
|
354
|
+
@output_string.should include("INSERT INTO `an_ignored_table` (`col`, `col2`) VALUES ('hello','kjhjd^&dkjh'), ('hello1','kjhj!'), ('hello2','moose!!');")
|
355
|
+
@error_output.rewind
|
356
|
+
@error_output.read.should =~ /an_ignored_table was not specified in the config/
|
357
|
+
end
|
358
|
+
|
359
|
+
it "should obfuscate the tables" do
|
360
|
+
@output_string.should include("INSERT INTO `some_table` (`email`, `name`, `something`, `age`) VALUES (")
|
361
|
+
@output_string.should include("INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES (")
|
362
|
+
@output_string.should include("'some\\'thin,ge())lse1'")
|
363
|
+
@output_string.should include("INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES ('hello','monkey',NULL),('hello1','monkey',NULL),('hello2','monkey',NULL);")
|
364
|
+
@output_string.should_not include("INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES ('hello','kjhjd^&dkjh', 'aawefjkafe'), ('hello1','kjhj!', 892938), ('hello2','moose!!', NULL);")
|
365
|
+
@output_string.should_not include("INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES ('hello','kjhjd^&dkjh','aawefjkafe'),('hello1','kjhj!',892938),('hello2','moose!!',NULL);")
|
366
|
+
@output_string.should_not include("INSERT INTO `some_table` (`email`, `name`, `something`, `age`) VALUES ('bob@honk.com','bob', 'some\\'thin,ge())lse1', 25),('joe@joe.com','joe', 'somethingelse2', 54);")
|
367
|
+
end
|
368
|
+
|
369
|
+
it "honors a special case: on the people table, rows with anything@honk.com in a slot marked with :honk_email_skip do not change this slot" do
|
370
|
+
@output_string.should include("('bob@honk.com',")
|
371
|
+
@output_string.should include("('dontmurderme@direwolf.com',")
|
372
|
+
@output_string.should_not include("joe@joe.com")
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
context "when fail_on_unspecified_columns is set to true" do
|
377
|
+
before do
|
378
|
+
@database_dump = StringIO.new(<<-SQL)
|
379
|
+
INSERT INTO `some_table` (`email`, `name`, `something`, `age`) VALUES ('bob@honk.com','bob', 'some\\'thin,ge())lse1', 25),('joe@joe.com','joe', 'somethingelse2', 54),('dontmurderme@direwolf.com','direwolf', 'somethingelse3', 44);
|
380
|
+
SQL
|
381
|
+
|
382
|
+
@ddo = MyObfuscate.new({
|
383
|
+
:some_table => {
|
384
|
+
:email => {:type => :email, :skip_regexes => [/^[\w\.\_]+@honk\.com$/i, /^dontmurderme@direwolf.com$/]},
|
385
|
+
:name => {:type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS},
|
386
|
+
:age => {:type => :integer, :between => 10...80}
|
387
|
+
}
|
388
|
+
})
|
389
|
+
@ddo.fail_on_unspecified_columns = true
|
390
|
+
end
|
391
|
+
|
392
|
+
it "should raise an exception when an unspecified column is found" do
|
393
|
+
lambda {
|
394
|
+
@ddo.obfuscate(@database_dump, StringIO.new)
|
395
|
+
}.should raise_error(/column 'something' defined/i)
|
396
|
+
end
|
397
|
+
|
398
|
+
it "should accept columns defined in globally_kept_columns" do
|
399
|
+
@ddo.globally_kept_columns = %w[something]
|
400
|
+
lambda {
|
401
|
+
@ddo.obfuscate(@database_dump, StringIO.new)
|
402
|
+
}.should_not raise_error
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
describe "when using MS SQL Server" do
|
408
|
+
context "when there is nothing to obfuscate" do
|
409
|
+
it "should accept an IO object for input and output, and copy the input to the output" do
|
410
|
+
ddo = MyObfuscate.new
|
411
|
+
ddo.database_type = :sql_server
|
412
|
+
string = "hello, world\nsup?"
|
413
|
+
input = StringIO.new(string)
|
414
|
+
output = StringIO.new
|
415
|
+
ddo.obfuscate(input, output)
|
416
|
+
input.rewind
|
417
|
+
output.rewind
|
418
|
+
output.read.should == string
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
context "when the dump to obfuscate is missing columns" do
|
423
|
+
before do
|
424
|
+
@database_dump = StringIO.new(<<-SQL)
|
425
|
+
INSERT [dbo].[some_table] ([email], [name], [something], [age]) VALUES ('bob@honk.com','bob', 'some''thin,ge())lse1', 25);
|
426
|
+
SQL
|
427
|
+
@ddo = MyObfuscate.new({
|
428
|
+
:some_table => {
|
429
|
+
:email => {:type => :email, :honk_email_skip => true},
|
430
|
+
:name => {:type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS},
|
431
|
+
:gender => {:type => :fixed, :string => "m"}
|
432
|
+
}})
|
433
|
+
@ddo.database_type = :sql_server
|
434
|
+
@output = StringIO.new
|
435
|
+
end
|
436
|
+
|
437
|
+
it "should raise an error if a column name can't be found" do
|
438
|
+
lambda {
|
439
|
+
@ddo.obfuscate(@database_dump, @output)
|
440
|
+
}.should raise_error
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
context "when there is something to obfuscate" do
|
445
|
+
before do
|
446
|
+
@database_dump = StringIO.new(<<-SQL)
|
447
|
+
INSERT [dbo].[some_table] ([email], [name], [something], [age], [bday]) VALUES (N'bob@honk.com',N'bob', N'some''thin,ge())lse1', 25, CAST(0x00009E1A00000000 AS DATETIME));
|
448
|
+
INSERT [dbo].[some_table] ([email], [name], [something], [age], [bday]) VALUES (N'joe@joe.com',N'joe', N'somethingelse2', 54, CAST(0x00009E1A00000000 AS DATETIME));
|
449
|
+
INSERT [dbo].[some_table] ([email], [name], [something], [age], [bday]) VALUES (N'dontmurderme@direwolf.com',N'direwolf', N'somethingelse3', 44, CAST(0x00009E1A00000000 AS DATETIME));
|
450
|
+
INSERT [dbo].[another_table] ([a], [b], [c], [d]) VALUES (1,2,3,4);
|
451
|
+
INSERT [dbo].[another_table] ([a], [b], [c], [d]) VALUES (5,6,7,8);
|
452
|
+
INSERT [dbo].[some_table_to_keep] ([a], [b], [c], [d]) VALUES (1,2,3,4);
|
453
|
+
INSERT [dbo].[some_table_to_keep] ([a], [b], [c], [d]) VALUES (5,6,7,8);
|
454
|
+
INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello',N'kjhjd^&dkjh', N'aawefjkafe');
|
455
|
+
INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello1',N'kjhj!', 892938);
|
456
|
+
INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello2',N'moose!!', NULL);
|
457
|
+
INSERT [dbo].[an_ignored_table] ([col], [col2]) VALUES (N'hello',N'kjhjd^&dkjh');
|
458
|
+
INSERT [dbo].[an_ignored_table] ([col], [col2]) VALUES (N'hello1',N'kjhj!');
|
459
|
+
INSERT [dbo].[an_ignored_table] ([col], [col2]) VALUES (N'hello2',N'moose!!');
|
460
|
+
SQL
|
461
|
+
|
462
|
+
@ddo = MyObfuscate.new({
|
463
|
+
:some_table => {
|
464
|
+
:email => {:type => :email, :skip_regexes => [/^[\w\.\_]+@honk\.com$/i, /^dontmurderme@direwolf.com$/]},
|
465
|
+
:name => {:type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS},
|
466
|
+
:age => {:type => :integer, :between => 10...80},
|
467
|
+
:bday => :keep
|
468
|
+
},
|
469
|
+
:another_table => :truncate,
|
470
|
+
:some_table_to_keep => :keep,
|
471
|
+
:one_more_table => {
|
472
|
+
# Note: fixed strings must be pre-SQL escaped!
|
473
|
+
:password => {:type => :fixed, :string => "monkey"},
|
474
|
+
:c => {:type => :null}
|
475
|
+
}
|
476
|
+
})
|
477
|
+
@ddo.database_type = :sql_server
|
478
|
+
|
479
|
+
@output = StringIO.new
|
480
|
+
$stderr = @error_output = StringIO.new
|
481
|
+
@ddo.obfuscate(@database_dump, @output)
|
482
|
+
$stderr = STDERR
|
483
|
+
@output.rewind
|
484
|
+
@output_string = @output.read
|
485
|
+
end
|
486
|
+
|
487
|
+
it "should be able to truncate tables" do
|
488
|
+
@output_string.should_not include("INSERT [dbo].[another_table]")
|
489
|
+
@output_string.should include("INSERT [dbo].[one_more_table]")
|
490
|
+
end
|
491
|
+
|
492
|
+
it "should be able to declare tables to keep" do
|
493
|
+
@output_string.should include("INSERT [dbo].[some_table_to_keep] ([a], [b], [c], [d]) VALUES (1,2,3,4);")
|
494
|
+
@output_string.should include("INSERT [dbo].[some_table_to_keep] ([a], [b], [c], [d]) VALUES (5,6,7,8);")
|
495
|
+
end
|
496
|
+
|
497
|
+
it "should ignore tables that it doesn't know about, but should warn" do
|
498
|
+
@output_string.should include("INSERT [dbo].[an_ignored_table] ([col], [col2]) VALUES (N'hello',N'kjhjd^&dkjh');")
|
499
|
+
@output_string.should include("INSERT [dbo].[an_ignored_table] ([col], [col2]) VALUES (N'hello1',N'kjhj!');")
|
500
|
+
@output_string.should include("INSERT [dbo].[an_ignored_table] ([col], [col2]) VALUES (N'hello2',N'moose!!');")
|
501
|
+
@error_output.rewind
|
502
|
+
@error_output.read.should =~ /an_ignored_table was not specified in the config/
|
503
|
+
end
|
504
|
+
|
505
|
+
it "should obfuscate the tables" do
|
506
|
+
@output_string.should include("INSERT [dbo].[some_table] ([email], [name], [something], [age], [bday]) VALUES (")
|
507
|
+
@output_string.should include("CAST(0x00009E1A00000000 AS DATETIME)")
|
508
|
+
@output_string.should include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (")
|
509
|
+
@output_string.should include("'some''thin,ge())lse1'")
|
510
|
+
@output_string.should include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello',N'monkey',NULL);")
|
511
|
+
@output_string.should include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello1',N'monkey',NULL);")
|
512
|
+
@output_string.should include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello2',N'monkey',NULL);")
|
513
|
+
@output_string.should_not include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello',N'kjhjd^&dkjh', N'aawefjkafe');")
|
514
|
+
@output_string.should_not include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello1',N'kjhj!', 892938);")
|
515
|
+
@output_string.should_not include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello2',N'moose!!', NULL);")
|
516
|
+
@output_string.should_not include("INSERT [dbo].[some_table] ([email], [name], [something], [age]) VALUES (N'bob@honk.com',N'bob', N'some''thin,ge())lse1', 25, CAST(0x00009E1A00000000 AS DATETIME));")
|
517
|
+
@output_string.should_not include("INSERT [dbo].[some_table] ([email], [name], [something], [age]) VALUES (N'joe@joe.com',N'joe', N'somethingelse2', 54, CAST(0x00009E1A00000000 AS DATETIME));")
|
518
|
+
end
|
519
|
+
|
520
|
+
it "honors a special case: on the people table, rows with anything@honk.com in a slot marked with :honk_email_skip do not change this slot" do
|
521
|
+
@output_string.should include("(N'bob@honk.com',")
|
522
|
+
@output_string.should include("(N'dontmurderme@direwolf.com',")
|
523
|
+
@output_string.should_not include("joe@joe.com")
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
context "when fail_on_unspecified_columns is set to true" do
|
528
|
+
before do
|
529
|
+
@database_dump = StringIO.new(<<-SQL)
|
530
|
+
INSERT INTO [dbo].[some_table] ([email], [name], [something], [age]) VALUES ('bob@honk.com','bob', 'some''thin,ge())lse1', 25);
|
531
|
+
SQL
|
532
|
+
|
533
|
+
@ddo = MyObfuscate.new({
|
534
|
+
:some_table => {
|
535
|
+
:email => {:type => :email, :skip_regexes => [/^[\w\.\_]+@honk\.com$/i, /^dontmurderme@direwolf.com$/]},
|
536
|
+
:name => {:type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS},
|
537
|
+
:age => {:type => :integer, :between => 10...80}
|
538
|
+
}
|
539
|
+
})
|
540
|
+
@ddo.database_type = :sql_server
|
541
|
+
@ddo.fail_on_unspecified_columns = true
|
542
|
+
end
|
543
|
+
|
544
|
+
it "should raise an exception when an unspecified column is found" do
|
545
|
+
lambda {
|
546
|
+
@ddo.obfuscate(@database_dump, StringIO.new)
|
547
|
+
}.should raise_error(/column 'something' defined/i)
|
548
|
+
end
|
549
|
+
|
550
|
+
it "should accept columns defined in globally_kept_columns" do
|
551
|
+
@ddo.globally_kept_columns = %w[something]
|
552
|
+
lambda {
|
553
|
+
@ddo.obfuscate(@database_dump, StringIO.new)
|
554
|
+
}.should_not raise_error
|
555
|
+
end
|
243
556
|
end
|
244
557
|
end
|
245
558
|
end
|