Skip to content

Commit 14196e7

Browse files
authored
Merge branch 'master' into dependabot/npm_and_yarn/braces-3.0.3
2 parents d319527 + 7539300 commit 14196e7

File tree

8 files changed

+310
-61
lines changed

8 files changed

+310
-61
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
* @auth0/dx-sdks-engineer
1+
* @auth0/project-dx-sdks-engineer-codeowner

.github/workflows/semgrep.yml

Lines changed: 0 additions & 49 deletions
This file was deleted.

.github/workflows/snyk.yml

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Snyk
33
on:
44
merge_group:
55
workflow_dispatch:
6-
pull_request_target:
6+
pull_request:
77
types:
88
- opened
99
- synchronize
@@ -21,16 +21,7 @@ concurrency:
2121
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
2222

2323
jobs:
24-
authorize:
25-
name: Authorize
26-
environment: ${{ github.actor != 'dependabot[bot]' && github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository && 'external' || 'internal' }}
27-
runs-on: ubuntu-latest
28-
steps:
29-
- run: true
30-
3124
check:
32-
needs: authorize
33-
3425
name: Check for Vulnerabilities
3526
runs-on: ubuntu-latest
3627

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Password Sheriff
22
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fauth0%2Fpassword-sheriff.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fauth0%2Fpassword-sheriff?ref=badge_shield)
3+
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/auth0/password-sheriff)
34

45

56
Node.js (and browserify supported) library to enforce password policies.
@@ -113,6 +114,16 @@ Password Sheriff includes some default rules:
113114
});
114115
```
115116

117+
* `sequentialChars`: Passwords should not contain more than `max` sequential (increasing or decreasing) alphanumeric characters.
118+
```js
119+
var sequentialCharsPolicy = new PasswordPolicy({
120+
sequentialChars: { max: 3 }
121+
});
122+
// 'abcd' -> false (4 sequential > 3)
123+
// 'dcba' -> false (4 sequential > 3)
124+
// 'abce' -> true (sequence breaks)
125+
```
126+
116127
See the [default-rules example](examples/default-rules.js) section for more information.
117128

118129
## Issue Reporting
@@ -128,4 +139,4 @@ If you have found a bug or if you have a feature request, please report them at
128139
This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info.
129140

130141

131-
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fauth0%2Fpassword-sheriff.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fauth0%2Fpassword-sheriff?ref=badge_large)
142+
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fauth0%2Fpassword-sheriff.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fauth0%2Fpassword-sheriff?ref=badge_large)

examples/default-rules.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ var PasswordPolicy = require('..').PasswordPolicy;
77
* * contains
88
* * containsAtLeast
99
* * identicalChars
10+
* * sequentialChars
1011
*/
1112

1213
/*
@@ -101,3 +102,18 @@ assert.equal(true, identitcalCharsPolicy.check('helllo'));
101102
assert.equal(false, identitcalCharsPolicy.check('hellllo'));
102103
assert.equal(false, identitcalCharsPolicy.check('123333334'));
103104

105+
/*
106+
* sequentialChars
107+
*
108+
* Parameters: max :: Integer
109+
*
110+
* Passwords should not contain more than `max` sequential (increasing or decreasing) characters.
111+
*/
112+
var sequentialCharsPolicy = new PasswordPolicy({
113+
sequentialChars: { max: 3 }
114+
});
115+
116+
assert.equal(true, sequentialCharsPolicy.check('abce')); // sequence breaks before exceeding 3
117+
assert.equal(true, sequentialCharsPolicy.check('cbaZ')); // descending sequence of length 3 allowed
118+
assert.equal(false, sequentialCharsPolicy.check('abcd')); // ascending length 4 not allowed
119+
assert.equal(false, sequentialCharsPolicy.check('dcba1')); // descending length 4 not allowed

lib/policy.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ var defaultRuleset = {
1111
contains: require('./rules/contains'),
1212
containsAtLeast: require('./rules/containsAtLeast'),
1313
identicalChars: require('./rules/identicalChars'),
14+
sequentialChars: require('./rules/sequentialChars')
1415
};
1516

1617
function flatDescriptions (descriptions, index) {

lib/rules/sequentialChars.js

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
var _ = require('../helper');
2+
3+
/**
4+
* @readonly
5+
* @enum {number}
6+
*/
7+
var Direction = {
8+
Ascending: 1,
9+
Descending: -1,
10+
None: 0
11+
};
12+
13+
/**
14+
* @readonly
15+
* @enum {number}
16+
*/
17+
var CharacterCodes = {
18+
LowerCaseA: 'a'.charCodeAt(0),
19+
LowerCaseZ: 'z'.charCodeAt(0),
20+
Zero: '0'.charCodeAt(0),
21+
Nine: '9'.charCodeAt(0),
22+
UpperCaseA: 'A'.charCodeAt(0),
23+
UpperCaseZ: 'Z'.charCodeAt(0),
24+
};
25+
26+
/**
27+
* Determines if a character is alphanumeric
28+
*
29+
* @param {number} code character code
30+
* @return {boolean}
31+
*/
32+
function isAlphanumeric(code) {
33+
if (!_.isNumber(code) || _.isNaN(code)) {
34+
return false;
35+
}
36+
37+
if (code >= CharacterCodes.LowerCaseA && code <= CharacterCodes.LowerCaseZ) {
38+
return true;
39+
} else if (code >= CharacterCodes.UpperCaseA && code <= CharacterCodes.UpperCaseZ) {
40+
return true;
41+
} else if (code >= CharacterCodes.Zero && code <= CharacterCodes.Nine) {
42+
return true;
43+
}
44+
45+
return false;
46+
}
47+
48+
/**
49+
* Returns true if password has more sequential characters the configured max allowed
50+
*
51+
* @param {{max: number}} options
52+
* @param {string} password
53+
* @return {boolean}
54+
*/
55+
function assert(options, password) {
56+
if (!password) {
57+
return false;
58+
}
59+
60+
var prevCode = password.charCodeAt(0);
61+
var seqLen = isAlphanumeric(prevCode) ? 1 : 0;
62+
var currentDirection = Direction.None;
63+
64+
for (var i = 1; i < password.length; i++) {
65+
var currentCode = password.charCodeAt(i);
66+
if (!isAlphanumeric(currentCode) || !isAlphanumeric(prevCode)) {
67+
currentDirection = Direction.None;
68+
seqLen = 1;
69+
prevCode = currentCode;
70+
continue;
71+
}
72+
73+
var diff = currentCode - prevCode;
74+
prevCode = currentCode;
75+
76+
if (diff === Direction.Ascending || diff === Direction.Descending) {
77+
if (currentDirection === diff) {
78+
seqLen += 1;
79+
} else {
80+
// start a new potential sequence of length 2 (prev + current)
81+
currentDirection = diff;
82+
seqLen = 2;
83+
}
84+
} else {
85+
currentDirection = Direction.None;
86+
seqLen = 1;
87+
}
88+
89+
if (seqLen > options.max) {
90+
return false;
91+
}
92+
}
93+
94+
return true;
95+
}
96+
97+
/**
98+
* @param {{max: number}} options
99+
* @param {boolean} verified
100+
* @return {boolean}
101+
*/
102+
function explain(options, verified) {
103+
var example = '';
104+
for (var i = 0; i < options.max + 1; i++) {
105+
example += String.fromCharCode('a'.charCodeAt(0) + i);
106+
}
107+
var d = {
108+
message: 'No more than %d sequential alphanumeric characters (e.g., "%s" not allowed)', // updated
109+
code: 'sequentialChars',
110+
format: [options.max, example]
111+
};
112+
if (verified !== undefined) {
113+
d.verified = verified;
114+
}
115+
return d;
116+
}
117+
118+
/**
119+
* @param {{max: number}} options
120+
* @return {boolean}
121+
*/
122+
function validate(options) {
123+
if (!_.isObject(options)) {
124+
throw new Error('options should be an object');
125+
}
126+
127+
if (!_.isNumber(options.max) || _.isNaN(options.max) || options.max < 2) {
128+
throw new Error('max should be a number greater than or equal to 2');
129+
}
130+
131+
if (!_.isNumber(options.max) || _.isNaN(options.max) || options.max > 26) {
132+
throw new Error('max should be a number less than or equal to 26');
133+
}
134+
135+
return true;
136+
}
137+
138+
module.exports = {
139+
validate,
140+
explain,
141+
missing: function (options, password) {
142+
return explain(options, assert(options, password));
143+
},
144+
assert
145+
};

0 commit comments

Comments
 (0)