xchange-passgen 1.0.1.a

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,282 @@
1
+ module Passgen
2
+ class StrengthAnalyzer
3
+ MIN_LENGTH = 8
4
+ attr_reader :password, :score, :complexity, :errors
5
+
6
+ def initialize(pw)
7
+ @password = pw
8
+ @score = 0
9
+ @complexity = "Invalid"
10
+ @errors = []
11
+ end
12
+
13
+ def self.analyze(pw)
14
+ sa = StrengthAnalyzer.new(pw)
15
+ sa.analyze
16
+ sa
17
+ end
18
+
19
+ def analyze
20
+ return self unless check_minimum_requirements
21
+
22
+ nScore = 0
23
+ nLength = 0
24
+ nAlphaUC = 0
25
+ nAlphaLC = 0
26
+ nNumber = 0
27
+ nSymbol = 0
28
+ nMidChar = 0
29
+ nRequirements = 0
30
+ nAlphasOnly = 0
31
+ nNumbersOnly = 0
32
+ nUnqChar = 0
33
+ nRepChar = 0
34
+ nRepInc = 0
35
+ nConsecAlphaUC = 0
36
+ nConsecAlphaLC = 0
37
+ nConsecNumber = 0
38
+ nConsecSymbol = 0
39
+ nConsecCharType = 0
40
+ nSeqAlpha = 0
41
+ nSeqNumber = 0
42
+ nSeqSymbol = 0
43
+ nSeqChar = 0
44
+ nReqChar = 0
45
+ nMultConsecCharType = 0
46
+ nMultRepChar = 1
47
+ nMultConsecSymbol = 1
48
+ nMultMidChar = 2
49
+ nMultRequirements = 2
50
+ nMultConsecAlphaUC = 2
51
+ nMultConsecAlphaLC = 2
52
+ nMultConsecNumber = 2
53
+ nReqCharType = 3
54
+ nMultAlphaUC = 3
55
+ nMultAlphaLC = 3
56
+ nMultSeqAlpha = 3
57
+ nMultSeqNumber = 3
58
+ nMultSeqSymbol = 3
59
+ nMultLength = 4
60
+ nMultNumber = 4
61
+ nMultSymbol = 6
62
+ nTmpAlphaUC = ""
63
+ nTmpAlphaLC = ""
64
+ nTmpNumber = ""
65
+ nTmpSymbol = ""
66
+ sAlphas = 'abcdefghijklmnopqrstuvwxyz'
67
+ sNumerics = '01234567890'
68
+ sSymbols = '!@#$%&/()+?*'
69
+ sComplexity = 'Invalid'
70
+ sStandards = 'Below'
71
+ nMinPwdLen = MIN_LENGTH
72
+
73
+ nScore = @password.length * nMultLength
74
+ nLength = @password.length
75
+ arrPwd = @password.gsub(/\s+/, "").split(/\s*/)
76
+ arrPwdLen = arrPwd.length
77
+
78
+ # Loop through password to check for Symbol, Numeric, Lowercase and Uppercase pattern matches
79
+ arrPwdLen.times do |a|
80
+ if /[A-Z]/.match(arrPwd[a])
81
+ if (nTmpAlphaUC != "")
82
+ if (nTmpAlphaUC + 1) == a
83
+ nConsecAlphaUC += 1
84
+ nConsecCharType += 1
85
+ end
86
+ end
87
+ nTmpAlphaUC = a
88
+ nAlphaUC += 1
89
+ elsif /[a-z]/.match(arrPwd[a])
90
+ if nTmpAlphaLC != ""
91
+ if (nTmpAlphaLC + 1) == a
92
+ nConsecAlphaLC += 1
93
+ nConsecCharType += 1
94
+ end
95
+ end
96
+ nTmpAlphaLC = a
97
+ nAlphaLC += 1
98
+ elsif /[0-9]/.match(arrPwd[a])
99
+ if (a > 0 && a < (arrPwdLen - 1))
100
+ nMidChar += 1
101
+ end
102
+ if nTmpNumber != ""
103
+ if ((nTmpNumber + 1) == a)
104
+ nConsecNumber += 1
105
+ nConsecCharType += 1
106
+ end
107
+ end
108
+ nTmpNumber = a
109
+ nNumber += 1
110
+ elsif /[^a-zA-Z0-9_]/.match(arrPwd[a])
111
+ if a > 0 && a < (arrPwdLen - 1)
112
+ nMidChar += 1
113
+ end
114
+ if nTmpSymbol != ""
115
+ if (nTmpSymbol + 1) == a
116
+ nConsecSymbol += 1
117
+ nConsecCharType += 1
118
+ end
119
+ end
120
+ nTmpSymbol = a;
121
+ nSymbol += 1;
122
+ end
123
+
124
+ # Internal loop through password to check for repeat characters
125
+ bCharExists = false
126
+ arrPwdLen.times do |b|
127
+ if arrPwd[a] == arrPwd[b] && a != b # repeat character exists
128
+ bCharExists = true
129
+ # Calculate increment deduction based on proximity to identical characters
130
+ # Deduction is incremented each time a new match is discovered
131
+ # Deduction amount is based on total password length divided by the
132
+ # difference of distance between currently selected match
133
+ nRepInc += (arrPwdLen/(b-a)).abs
134
+ end
135
+ end
136
+ if bCharExists
137
+ nRepChar += 1
138
+ nUnqChar = arrPwdLen - nRepChar;
139
+ nRepInc = (nUnqChar > 0) ? (nRepInc/nUnqChar).ceil : nRepInc.ceil
140
+ end
141
+ end
142
+
143
+ # Check for sequential alpha string patterns (forward and reverse)
144
+ (sAlphas.size - 3).times do |s|
145
+ sFwd = sAlphas[s...s+3]
146
+ sRev = sFwd.reverse
147
+ if @password.downcase.index(sFwd) || @password.downcase.index(sRev)
148
+ nSeqAlpha += 1
149
+ nSeqChar += 1
150
+ end
151
+ end
152
+
153
+ # Check for sequential numeric string patterns (forward and reverse)
154
+ (sNumerics.size - 3).times do |s|
155
+ sFwd = sNumerics[s...s+3]
156
+ sRev = sFwd.reverse
157
+ if @password.downcase.index(sFwd) || @password.downcase.index(sRev)
158
+ nSeqNumber += 1
159
+ nSeqChar += 1
160
+ end
161
+ end
162
+
163
+ # Check for sequential symbol string patterns (forward and reverse)
164
+ (sSymbols.size - 3).times do |s|
165
+ sFwd = sSymbols[s...s+3]
166
+ sRev = sFwd.reverse
167
+ if @password.downcase.index(sFwd) || @password.downcase.index(sRev)
168
+ nSeqSymbol += 1
169
+ nSeqChar += 1
170
+ end
171
+ end
172
+
173
+ # Modify overall score value based on usage vs requirements
174
+ if nAlphaUC > 0 && nAlphaUC < nLength
175
+ nScore += (nLength - nAlphaUC) * 2
176
+ end
177
+
178
+ if nAlphaLC > 0 && nAlphaLC < nLength
179
+ nScore += (nLength - nAlphaLC) * 2
180
+ end
181
+
182
+ if (nNumber > 0 && nNumber < nLength)
183
+ nScore += nNumber * nMultNumber
184
+ end
185
+
186
+ if nSymbol > 0
187
+ nScore += nSymbol * nMultSymbol
188
+ end
189
+
190
+ if nMidChar > 0
191
+ nScore += nMidChar * nMultMidChar
192
+ end
193
+
194
+ # Point deductions for poor practices
195
+ if (nAlphaLC > 0 || nAlphaUC > 0) && nSymbol == 0 && nNumber == 0 # Only Letters
196
+ nScore -= nLength
197
+ nAlphasOnly = nLength
198
+ end
199
+
200
+ if nAlphaLC === 0 && nAlphaUC === 0 && nSymbol === 0 && nNumber > 0 # Only Numbers
201
+ nScore -= nLength
202
+ nNumbersOnly = nLength
203
+ end
204
+
205
+ if nRepChar > 0 # Same character exists more than once
206
+ nScore -= nRepInc
207
+ end
208
+
209
+ if nConsecAlphaUC > 0 # Consecutive Uppercase Letters exist
210
+ nScore -= nConsecAlphaUC * nMultConsecAlphaUC
211
+ end
212
+
213
+ if nConsecAlphaLC > 0 # Consecutive Lowercase Letters exist
214
+ nScore -= nConsecAlphaLC * nMultConsecAlphaLC
215
+ end
216
+
217
+ if nConsecNumber > 0 # Consecutive Numbers exist
218
+ nScore -= nConsecNumber * nMultConsecNumber
219
+ end
220
+
221
+ if nSeqAlpha > 0 # Sequential alpha strings exist (3 characters or more)
222
+ nScore -= nSeqAlpha * nMultSeqAlpha
223
+ end
224
+
225
+ if nSeqNumber > 0 # Sequential numeric strings exist (3 character or more)
226
+ nScore -= nSeqNumber * nMultSeqNumber
227
+ end
228
+
229
+ if nSeqSymbol > 0 # Sequential symbol strings exist (3 character or more)
230
+ nScore -= nSeqSymbol * nMultSeqSymbol
231
+ end
232
+
233
+ # Determine if mandatory requirements have been met and set image indicators accordingly
234
+ arrChars = [nLength, nAlphaUC, nAlphaLC, nNumber, nSymbol]
235
+ arrCharsIds = ["nLength", "nAlphaUC", "nAlphaLC", "nNumber", "nSymbol"]
236
+ arrCharsLen = arrChars.length;
237
+ arrCharsLen.times do |c|
238
+ minVal = arrCharsIds[c] == "nLength" ? MIN_LENGTH - 1 : 0
239
+ if arrChars[c] == (minVal + 1)
240
+ nReqChar += 1
241
+ elsif arrChars[c] > (minVal + 1)
242
+ nReqChar += 1
243
+ end
244
+ end
245
+ nRequirements = nReqChar
246
+ nMinReqChars = @password.length >= nMinPwdLen ? 3 : 4
247
+ if nRequirements > nMinReqChars # One or more required characters exist
248
+ nScore += (nRequirements * 2)
249
+ end
250
+
251
+ # Determine complexity based on overall score
252
+ if (nScore > 100)
253
+ nScore = 100
254
+ elsif (nScore < 0)
255
+ nScore = 0
256
+ end
257
+ @complexity = case nScore
258
+ when 0...20
259
+ "Trivial"
260
+ when 20...40
261
+ "Weak"
262
+ when 40...60
263
+ "Good"
264
+ when 60...80
265
+ "Strong"
266
+ else
267
+ "Very Strong"
268
+ end
269
+
270
+ @score = nScore
271
+ end
272
+
273
+ private
274
+ def check_minimum_requirements
275
+ if @password.length < MIN_LENGTH
276
+ @errors << "Password must be at least #{MIN_LENGTH} characters long"
277
+ return false
278
+ end
279
+ true
280
+ end
281
+ end
282
+ end
data/lib/passgen.rb ADDED
@@ -0,0 +1,328 @@
1
+ #= Passgen
2
+ #
3
+ #Ruby gem for generating passwords quickly and easily. Although it is
4
+ #suitable for use within Rails it has no Rails dependencies and can be used in
5
+ #non-Rails applications as well.
6
+ #
7
+ #== Install
8
+ #
9
+ # gem install cryptice-passgen --source http://gems.github.com
10
+ #
11
+ #== Usage
12
+ #
13
+ #The usage could not be easier. Just require and call the generate method:
14
+ #
15
+ # >> require 'rubygems'
16
+ # >> require 'passgen'
17
+ # >> Passgen::generate
18
+ # => "zLWCeS3xC9"
19
+ #
20
+ #== Examples
21
+ #
22
+ # >> Passgen::generate
23
+ # => "zLWCeS3xC9"
24
+ #
25
+ # >> Passgen::generate(:length => 20)
26
+ # => "6lCcHvkuEW6OuzAtkoAs"
27
+ #
28
+ # >> Passgen::generate(:symbols => true)
29
+ # => "gr)$6bIym1"
30
+ #
31
+ # >> Passgen::generate(:lowercase => :only)
32
+ # => "ysbwuxbcea"
33
+ #
34
+ # >> Passgen::generate(:number => 3)
35
+ # => ["REdOigTkdI", "PQu8DsV9WZ", "qptKLbw8YQ"]
36
+ #
37
+ # >> Passgen::generate(:seed => 5)
38
+ # => "JoV9M2qjiK"
39
+ # >> Passgen::generate(:seed => 5) # Will generate same password again
40
+ # => "JoV9M2qjiK"
41
+ #
42
+ # >> Passgen::generate(:seed => :default) # Will set random seed...
43
+ # => "SI8QDBdV98"
44
+ # >> Passgen::generate(:seed => :default) # and hence give different password
45
+ # => "tHHU5HLBAn"
46
+ #
47
+ #== Options:
48
+ #
49
+ #=== :lowercase => true/false/:only
50
+ #* true - Use lowercase letters in the generated password.
51
+ #* false - Do not use lowercase letters in the generated password.
52
+ #* :only - Only use lowercase letters in the generated password.
53
+ #
54
+ #=== :uppercase => true/false/:only
55
+ #* true - Use uppercase letters in the generated password.
56
+ #* false - Do not use uppercase letters in the generated password.
57
+ #* :only - Only use uppercase letters in the generated password.
58
+ #
59
+ #=== :digits => true/false/:only
60
+ #* true - Use digits in the generated password.
61
+ #* false - Do not use digits in the generated password.
62
+ #* :only - Only use digits in the generated password.
63
+ #
64
+ #=== :symbols => true/false/:only/:list
65
+ #* true - Use symbols in the generated password.
66
+ #* false - Do not use symbols in the generated password.
67
+ #* :only - Only use symbols in the generated password.
68
+ #* :list - A string with the symbols to use. Not implemented yet.
69
+ #
70
+ #=== :pronounceable => true/false
71
+ #Not implmented yet.
72
+ #
73
+ #=== :number => integer
74
+ #Number of passwords to generate. If >1 the result is an Array.
75
+ #
76
+ #=== :length => integer/range
77
+ #The number of characters in the generated passwords. A range results in passwords
78
+ #lengths within the given range.
79
+ #
80
+ #=== :seed => integer/:default
81
+ #Set the srand seed to the given integer prior to generating the passwords.
82
+ #
83
+ #=== Default values:
84
+ #
85
+ #:lowercase => true
86
+ #
87
+ #:uppercase => true
88
+ #
89
+ #:digits => true
90
+ #
91
+ #:symbols => false
92
+ #
93
+ #:pronounceable => Not implemented yet.
94
+ #
95
+ #:number => 1
96
+ #
97
+ #:length => 10
98
+ #
99
+ #:seed => nil
100
+ #
101
+ #== Copyright and license
102
+ #
103
+ #Copyright (c) 2009 Erik Lindblad
104
+ #
105
+ #Permission is hereby granted, free of charge, to any person obtaining
106
+ #a copy of this software and associated documentation files (the
107
+ #"Software"), to deal in the Software without restriction, including
108
+ #without limitation the rights to use, copy, modify, merge, publish,
109
+ #distribute, sublicense, and/or sell copies of the Software, and to
110
+ #permit persons to whom the Software is furnished to do so, subject to
111
+ #the following conditions:
112
+ #
113
+ #The above copyright notice and this permission notice shall be
114
+ #included in all copies or substantial portions of the Software.
115
+ #
116
+ #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
117
+ #EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
118
+ #MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
119
+ #NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
120
+ #LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
121
+ #OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
122
+ #WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
123
+
124
+ require 'digest'
125
+ require 'passgen/probabilities'
126
+ require 'passgen/strength_analyzer'
127
+
128
+ module Passgen
129
+
130
+ VERSION = "1.0.0"
131
+
132
+ DEFAULT_PARAMS = {
133
+ :number => 1,
134
+ :length => 10,
135
+ :lowercase => true,
136
+ :uppercase => true,
137
+ :digits => true,
138
+ :symbols => false,
139
+ :pronounceable => false
140
+ }
141
+
142
+ def self.default_seed
143
+ Digest::MD5.hexdigest("#{rand}#{Time.now}#{Process.object_id}").to_i(16)
144
+ end
145
+
146
+ def self.generate(params={})
147
+ set_options(params)
148
+ tokens = valid_tokens
149
+ set_seed
150
+
151
+ if n == 1
152
+ generate_one(tokens)
153
+ else
154
+ Array.new(n) {|i| generate_one(tokens) }
155
+ end
156
+ end
157
+
158
+ def self.analyze(pw)
159
+ Passgen::StrengthAnalyzer.analyze(pw)
160
+ end
161
+
162
+ private
163
+ def self.alphabet(index)
164
+ if use_lowercase? && !use_uppercase?
165
+ LOWERCASE_TOKENS[index]
166
+ elsif use_uppercase? && !use_lowercase?
167
+ UPPERCASE_TOKENS[index]
168
+ else
169
+ tmp = LOWERCASE_TOKENS[index]
170
+ tmp.upcase! if rand > 0.5
171
+ tmp
172
+ end
173
+ end
174
+
175
+ def self.generate_one(tokens)
176
+ if @options[:pronounceable]
177
+ generate_pronounceable
178
+ else
179
+ Array.new(password_length) {tokens[rand(tokens.size)]}.join
180
+ end
181
+ end
182
+
183
+ def self.generate_pronounceable
184
+ password = ""
185
+
186
+ # Append digits in front
187
+ digits_prefix = if @options[:digits_before]
188
+ @options[:length] -= @options[:digits_before]
189
+ Array.new(@options[:digits_before]) {DIGIT_TOKENS[rand(DIGIT_TOKENS.size)]}.join
190
+ else
191
+ ""
192
+ end
193
+
194
+ # Append digits at the end
195
+ digits_suffix = if @options[:digits_after]
196
+ @options[:length] -= @options[:digits_after]
197
+ Array.new(@options[:digits_after]) {DIGIT_TOKENS[rand(DIGIT_TOKENS.size)]}.join
198
+ else
199
+ ""
200
+ end
201
+
202
+ # Find a random starting point.
203
+ found_start = false
204
+ ranno = rand * SIGMA # random number [0,1[ weighed by sum of frequencies
205
+ sum = 0;
206
+ N_LETTERS.times do |c1|
207
+ N_LETTERS.times do |c2|
208
+ N_LETTERS.times do |c3|
209
+ sum += P[c1][c2][c3]
210
+ if sum > ranno
211
+ password << alphabet(c1)
212
+ password << alphabet(c2)
213
+ password << alphabet(c3)
214
+ found_start = true
215
+ break
216
+ end
217
+ end
218
+ break if found_start
219
+ end
220
+ break if found_start
221
+ end
222
+
223
+ # Do a random walk.
224
+ (3...@options[:length]).each do |nchar|
225
+ c1 = LETTER_INDEXES[password[nchar-2..nchar-2]]
226
+ c2 = LETTER_INDEXES[password[nchar-1..nchar-1]]
227
+ sum = 0
228
+ N_LETTERS.times {|c3| sum += P[c1][c2][c3]}
229
+ break if sum == 0
230
+ ranno = rand * sum
231
+ sum = 0;
232
+ N_LETTERS.times do |c3|
233
+ sum += P[c1][c2][c3]
234
+ if sum > ranno
235
+ password << alphabet(c3)
236
+ break
237
+ end
238
+ end
239
+ end
240
+ digits_prefix + password + digits_suffix
241
+ end
242
+
243
+ def self.password_length
244
+ if @options[:length].is_a?(Range)
245
+ tmp = @options[:length].to_a
246
+ tmp[rand(tmp.size)]
247
+ else
248
+ @options[:length].to_i
249
+ end
250
+ end
251
+
252
+ def self.n
253
+ @options[:number]
254
+ end
255
+
256
+ def self.set_options(params)
257
+ if params[:lowercase] == :only
258
+ params[:uppercase] = false
259
+ params[:digits] = false
260
+ end
261
+
262
+ if params[:uppercase] == :only
263
+ params[:lowercase] = false
264
+ params[:digits] = false
265
+ end
266
+
267
+ if params[:digits] == :only
268
+ params[:lowercase] = false
269
+ params[:uppercase] = false
270
+ end
271
+
272
+ if params[:symbols] == :only
273
+ params[:lowercase] = false
274
+ params[:uppercase] = false
275
+ params[:digits] = false
276
+ params[:symbols] = true
277
+ end
278
+
279
+ if params[:digits_before] == true
280
+ params[:digits_before] = 2
281
+ end
282
+
283
+ if params[:digits_after] == true
284
+ params[:digits_after] = 2
285
+ end
286
+
287
+ @options = DEFAULT_PARAMS.merge(params)
288
+ end
289
+
290
+ def self.set_seed
291
+ if @options[:seed]
292
+ if @options[:seed] == :default
293
+ srand(default_seed)
294
+ else
295
+ srand(@options[:seed])
296
+ end
297
+ end
298
+ end
299
+
300
+ def self.symbol_tokens
301
+ %w{! @ # $ % & / ( ) + ? *}
302
+ end
303
+
304
+ def self.use_lowercase?
305
+ @options[:lowercase]
306
+ end
307
+
308
+ def self.use_uppercase?
309
+ @options[:uppercase]
310
+ end
311
+
312
+ def self.use_digits?
313
+ @options[:digits]
314
+ end
315
+
316
+ def self.use_symbols?
317
+ @options[:symbols]
318
+ end
319
+
320
+ def self.valid_tokens
321
+ tmp = []
322
+ tmp += LOWERCASE_TOKENS if use_lowercase?
323
+ tmp += UPPERCASE_TOKENS if use_uppercase?
324
+ tmp += DIGIT_TOKENS if use_digits?
325
+ tmp += symbol_tokens if use_symbols?
326
+ tmp
327
+ end
328
+ end
data/passgen.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{xchange-passgen}
5
+ s.version = "1.0.1.a"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Erik Lindblad"]
9
+ s.date = %q{2010-12-16}
10
+ s.description = %q{A password generation gem for Ruby and Rails applications.}
11
+ s.email = %q{erik@l2c.se}
12
+ s.extra_rdoc_files = ["CHANGELOG", "README.rdoc", "lib/passgen.rb", "lib/passgen/probabilities.rb", "lib/passgen/strength_analyzer.rb"]
13
+ s.files = ["CHANGELOG", "Manifest", "README.rdoc", "Rakefile", "init.rb", "lib/passgen.rb", "lib/passgen/probabilities.rb", "lib/passgen/strength_analyzer.rb", "passgen.gemspec", "spec/passgen/strength_analyzer_spec.rb", "spec/passgen_spec.rb"]
14
+ s.homepage = %q{http://github.com/cryptice/passgen}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Passgen", "--main", "README.rdoc"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{passgen}
18
+ s.rubygems_version = %q{1.3.7}
19
+ s.summary = %q{A password generation gem for Ruby and Rails applications.}
20
+
21
+ if s.respond_to? :specification_version then
22
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
+ s.specification_version = 3
24
+
25
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
26
+ else
27
+ end
28
+ else
29
+ end
30
+ end
@@ -0,0 +1,75 @@
1
+ require "./lib/passgen"
2
+
3
+ describe "Using strength analyzer" do
4
+
5
+ before do
6
+ srand(2)
7
+ end
8
+
9
+ it "should return a StrengthAnalyzer instance" do
10
+ Passgen.analyze("abcdefg").should be_a(Passgen::StrengthAnalyzer)
11
+ end
12
+
13
+ it "should require minimum of 8 characters" do
14
+ sa = Passgen.analyze("abcdefg")
15
+ sa.score.should == 0
16
+ sa.complexity.should == "Invalid"
17
+ sa.errors.should == ["Password must be at least 8 characters long"]
18
+ end
19
+
20
+ it "should analyze aaaaaaaa correctly" do
21
+ sa = Passgen.analyze("aaaaaaaa")
22
+ sa.score.should == 0
23
+ sa.complexity.should == "Trivial"
24
+ sa.errors.should == []
25
+ end
26
+
27
+ it "should analyze aaaaAAAA correctly" do
28
+ sa = Passgen.analyze("aaaaAAAA")
29
+ sa.score.should == 0
30
+ sa.complexity.should == "Trivial"
31
+ sa.errors.should == []
32
+ end
33
+
34
+ it "should analyze aaaAAA11 correctly" do
35
+ sa = Passgen.analyze("aaaAAA11")
36
+ sa.score.should == 35
37
+ sa.complexity.should == "Weak"
38
+ sa.errors.should == []
39
+ end
40
+
41
+ it "should analyze aaa1AAA1 correctly" do
42
+ sa = Passgen.analyze("aaa1AAA1")
43
+ sa.score.should == 38
44
+ sa.complexity.should == "Weak"
45
+ sa.errors.should == []
46
+ end
47
+
48
+ it "should analyze hht14AAA correctly" do
49
+ sa = Passgen.analyze("hht14AAA")
50
+ sa.score.should == 57
51
+ sa.complexity.should == "Good"
52
+ sa.errors.should == []
53
+ end
54
+
55
+ it "should analyze hie14KOL correctly" do
56
+ sa = Passgen.analyze("hie14KOL")
57
+ sa.score.should == 62
58
+ sa.complexity.should == "Strong"
59
+ sa.errors.should == []
60
+ end
61
+
62
+ it "should analyze hI&14KoL correctly" do
63
+ sa = Passgen.analyze("hI&14KoL")
64
+ sa.score.should == 82
65
+ sa.complexity.should == "Very Strong"
66
+ sa.errors.should == []
67
+ end
68
+
69
+ it "should analyze hI&1#4KoL correctly" do
70
+ sa = Passgen.analyze("hI&1#4KoL")
71
+ sa.score.should == 100
72
+ sa.complexity.should == "Very Strong"
73
+ sa.errors.should == []
74
+ end
75
+ end