generate-pw 1.1.0 → 1.2.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.
- package/README.md +95 -16
- package/docs/LICENSE.md +14 -0
- package/docs/SECURITY.md +14 -0
- package/generate-pw.js +167 -83
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,11 +1,29 @@
|
|
|
1
|
+
<div align="right">
|
|
2
|
+
<h6>
|
|
3
|
+
<picture>
|
|
4
|
+
<source type="image/svg+xml" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/earth-icon/white/icon32.svg">
|
|
5
|
+
<img height=14 src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/earth-icon/black/icon32.svg">
|
|
6
|
+
</picture>
|
|
7
|
+
English |
|
|
8
|
+
<a href="https://github.com/adamlui/js-utils/tree/main/generate-pw/docs/zh-cn#readme">简体中文</a> |
|
|
9
|
+
<a href="https://github.com/adamlui/js-utils/tree/main/generate-pw/docs/zh-tw#readme">繁體中文</a> |
|
|
10
|
+
<a href="https://github.com/adamlui/js-utils/tree/main/generate-pw/docs/hi#readme">हिंदी</a> |
|
|
11
|
+
<a href="https://github.com/adamlui/js-utils/tree/main/generate-pw/docs/bn#readme">বাংলা</a>
|
|
12
|
+
</h6>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
1
15
|
# > generate-pw
|
|
2
16
|
|
|
3
17
|
### Randomly generate cryptographically-secure passwords.
|
|
4
18
|
|
|
5
|
-
<a href="#%EF%B8%8F-mit-license"><img height=
|
|
6
|
-
<a href="https://
|
|
7
|
-
<a href="https://www.npmjs.com/package/generate-pw?activeTab=code"><img height=
|
|
8
|
-
<a href="https://sonarcloud.io/component_measures?metric=new_vulnerabilities&id=adamlui_js-utils:generate-pw/generate-pw.js"><img height=
|
|
19
|
+
<a href="#%EF%B8%8F-mit-license"><img height=31 src="https://img.shields.io/badge/License-MIT-orange.svg?logo=internetarchive&logoColor=white&labelColor=464646&style=for-the-badge"></a>
|
|
20
|
+
<a href="https://github.com/adamlui/js-utils/releases/tag/generate-pw-1.2.1"><img height=31 src="https://img.shields.io/badge/Latest_Build-1.2.1-44cc11.svg?logo=icinga&logoColor=white&labelColor=464646&style=for-the-badge"></a>
|
|
21
|
+
<a href="https://www.npmjs.com/package/generate-pw?activeTab=code"><img height=31 src="https://img.shields.io/npm/unpacked-size/generate-pw?style=for-the-badge&logo=ebox&logoColor=white&labelColor=464646&color=blue"></a>
|
|
22
|
+
<a href="https://sonarcloud.io/component_measures?metric=new_vulnerabilities&id=adamlui_js-utils:generate-pw/generate-pw.js"><img height=31 src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fsonarcloud.io%2Fapi%2Fmeasures%2Fcomponent%3Fcomponent%3Dadamlui_js-utils%3Agenerate-pw%2Fgenerate-pw.js%26metricKeys%3Dvulnerabilities&query=%24.component.measures.0.value&style=for-the-badge&logo=sonarcloud&logoColor=white&labelColor=464646&label=Vulnerabilities&color=gold"></a>
|
|
23
|
+
|
|
24
|
+
<br>
|
|
25
|
+
|
|
26
|
+
<img height=6px width="100%" src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/aqua-separator.png">
|
|
9
27
|
|
|
10
28
|
## ⚡ Installation
|
|
11
29
|
|
|
@@ -21,6 +39,10 @@ As a **runtime dependency**, from your project root:
|
|
|
21
39
|
$ npm install generate-pw
|
|
22
40
|
```
|
|
23
41
|
|
|
42
|
+
<br>
|
|
43
|
+
|
|
44
|
+
<img height=6px width="100%" src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/aqua-separator.png">
|
|
45
|
+
|
|
24
46
|
## 🔌 API usage
|
|
25
47
|
|
|
26
48
|
**generate-pw** can be imported into your app as an ECMAScript module or a CommonJS module.
|
|
@@ -37,6 +59,8 @@ import * as pw from 'generate-pw';
|
|
|
37
59
|
const pw = require('generate-pw');
|
|
38
60
|
```
|
|
39
61
|
|
|
62
|
+
#
|
|
63
|
+
|
|
40
64
|
### `generatePassword([options])`
|
|
41
65
|
|
|
42
66
|
Generates **one** password if `qty` option is not given, returning a string:
|
|
@@ -44,7 +68,7 @@ Generates **one** password if `qty` option is not given, returning a string:
|
|
|
44
68
|
```js
|
|
45
69
|
const password = pw.generatePassword({ length: 11, numbers: true });
|
|
46
70
|
console.log(password);
|
|
47
|
-
// sample output: '
|
|
71
|
+
// sample output: 'bAsZm3mq6Qn'
|
|
48
72
|
```
|
|
49
73
|
|
|
50
74
|
...or **multiple** passwords if `qty` option is given, returning an array of strings:
|
|
@@ -57,6 +81,10 @@ console.log(passwords);
|
|
|
57
81
|
|
|
58
82
|
**💡 Note:** If no options are passed, passwords will be 8-chars long, consisting of upper/lower cased letters.
|
|
59
83
|
|
|
84
|
+
See: [Available options](#available-options-for-generate-functions)
|
|
85
|
+
|
|
86
|
+
#
|
|
87
|
+
|
|
60
88
|
### `generatePasswords(qty[, options])`
|
|
61
89
|
|
|
62
90
|
Generates **multiple** passwords based on `qty` given, returning an array of strings:
|
|
@@ -67,16 +95,66 @@ console.log(passwords);
|
|
|
67
95
|
// sample output: [ 'yilppxru', 'ckvkyjfp', 'zolcpyfb' ]
|
|
68
96
|
```
|
|
69
97
|
|
|
70
|
-
|
|
98
|
+
**💡 Note:** If no `qty` arg is passed, just one password will be generated, returned as a string.
|
|
99
|
+
|
|
100
|
+
See: [Available options](#available-options-for-generate-functions)
|
|
101
|
+
|
|
102
|
+
#
|
|
103
|
+
|
|
104
|
+
### `strictify(password[, requiredCharTypes])`
|
|
105
|
+
|
|
106
|
+
Modifies `password` given to use at least one character of each `requiredCharTypes` element passed, returning a string:
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
const password = 'abcdef',
|
|
110
|
+
strictPW = pw.strictify(password, ['numbers', 'symbols']);
|
|
111
|
+
|
|
112
|
+
console.log(strictPW);
|
|
113
|
+
// sample output: 'a!c2ef'
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Available `requiredCharTypes` are: `['number', 'symbol', 'lower', 'upper']`
|
|
117
|
+
|
|
118
|
+
**💡 Note:** If no `requiredCharTypes` array is passed, all available types will be required.
|
|
119
|
+
|
|
120
|
+
#
|
|
121
|
+
|
|
122
|
+
### `validateStrength(password)`
|
|
123
|
+
|
|
124
|
+
Validates the strength of a password, returning an object containing:
|
|
125
|
+
- `strengthScore` (0–100)
|
|
126
|
+
- `recommendations` array
|
|
127
|
+
- `isGood` boolean (`true` if `strengthScore` >= 80)
|
|
128
|
+
|
|
129
|
+
Example:
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
const password = 'AawiddsE',
|
|
133
|
+
pwStrength = pw.validateStrength(password);
|
|
134
|
+
|
|
135
|
+
console.log(pwStrength);
|
|
136
|
+
|
|
137
|
+
/* outputs:
|
|
138
|
+
{
|
|
139
|
+
strengthScore: 60,
|
|
140
|
+
recommendations: [ 'Include at least one number.', 'Include at least one symbol.' ],
|
|
141
|
+
isGood: false
|
|
142
|
+
}
|
|
143
|
+
*/
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#
|
|
71
147
|
|
|
72
|
-
|
|
148
|
+
### Available options for `generate*()` functions
|
|
149
|
+
|
|
150
|
+
Any of these can be passed into the options object for each `generate*()` function:
|
|
73
151
|
|
|
74
152
|
Name | Type | Description | Default Value
|
|
75
153
|
------------|---------|--------------------------------------------------------------------------------|---------------
|
|
76
154
|
`length` | Integer | Length of password(s). | `8`
|
|
77
155
|
`qty`* | Integer | Number of passwords to generate. | `1`
|
|
78
|
-
`charset` | String | Characters to include in password(s). | ''
|
|
79
|
-
`exclude` | String | Characters to exclude from password(s). | ''
|
|
156
|
+
`charset` | String | Characters to include in password(s). | `''`
|
|
157
|
+
`exclude` | String | Characters to exclude from password(s). | `''`
|
|
80
158
|
`numbers` | Boolean | Allow numbers in password(s). | `false`
|
|
81
159
|
`symbols` | Boolean | Allow symbols in password(s). | `false`
|
|
82
160
|
`lowercase` | Boolean | Allow lowercase letters in password(s). | `true`
|
|
@@ -87,9 +165,11 @@ Name | Type | Description
|
|
|
87
165
|
|
|
88
166
|
<br>
|
|
89
167
|
|
|
168
|
+
<img height=6px width="100%" src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/aqua-separator.png">
|
|
169
|
+
|
|
90
170
|
## 💻 Command line usage
|
|
91
171
|
|
|
92
|
-
**generate-pw** can also be used from the command line. The basic
|
|
172
|
+
When installed [globally](#-installation), **generate-pw** can also be used from the command line. The basic command is:
|
|
93
173
|
|
|
94
174
|
```
|
|
95
175
|
$ generate-pw
|
|
@@ -97,10 +177,12 @@ $ generate-pw
|
|
|
97
177
|
|
|
98
178
|
**💡 Note:** To generate multiple results, pass `--qty=n` where `n` is the number of passwords to generate.
|
|
99
179
|
|
|
180
|
+
#
|
|
181
|
+
|
|
100
182
|
### Command line options
|
|
101
183
|
|
|
102
184
|
```
|
|
103
|
-
|
|
185
|
+
Parameter options:
|
|
104
186
|
--length=n Generate password(s) of n length.
|
|
105
187
|
--qty=n Generate n password(s).
|
|
106
188
|
--charset=chars Only include chars in password(s).
|
|
@@ -113,16 +195,13 @@ Boolean options:
|
|
|
113
195
|
-U, --no-uppercase Disallow uppercase letters in password(s).
|
|
114
196
|
|
|
115
197
|
Info commands:
|
|
116
|
-
-h, --help Display
|
|
198
|
+
-h, --help Display help screen.
|
|
117
199
|
-v, --version Show version number.
|
|
118
200
|
```
|
|
119
201
|
|
|
120
202
|
<br>
|
|
121
203
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
Please consider [giving a GitHub ⭐](https://github.com/adamlui/js-utils) or [funding](https://github.com/sponsors/adamlui) this project if it helped you!
|
|
125
|
-
<br><br>
|
|
204
|
+
<img height=6px width="100%" src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/aqua-separator.png">
|
|
126
205
|
|
|
127
206
|
## 🏛️ MIT License
|
|
128
207
|
|
package/docs/LICENSE.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
<div align="right">
|
|
2
|
+
<h6>
|
|
3
|
+
<picture>
|
|
4
|
+
<source type="image/svg+xml" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/earth-icon/white/icon32.svg">
|
|
5
|
+
<img height=14 src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/earth-icon/black/icon32.svg">
|
|
6
|
+
</picture>
|
|
7
|
+
English |
|
|
8
|
+
<a href="zh-cn/LICENSE.md">简体中文</a> |
|
|
9
|
+
<a href="zh-tw/LICENSE.md">繁體中文</a> |
|
|
10
|
+
<a href="hi/LICENSE.md">हिंदी</a> |
|
|
11
|
+
<a href="bn/LICENSE.md">বাংলা</a>
|
|
12
|
+
</h6>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
1
15
|
# 🏛️ MIT License
|
|
2
16
|
|
|
3
17
|
**Copyright © 2024 [Adam Lui](https://github.com/adamlui)**
|
package/docs/SECURITY.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
<div align="right">
|
|
2
|
+
<h6>
|
|
3
|
+
<picture>
|
|
4
|
+
<source type="image/svg+xml" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/earth-icon/white/icon32.svg">
|
|
5
|
+
<img height=14 src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/earth-icon/black/icon32.svg">
|
|
6
|
+
</picture>
|
|
7
|
+
English |
|
|
8
|
+
<a href="zh-cn/SECURITY.md">简体中文</a> |
|
|
9
|
+
<a href="zh-tw/SECURITY.md">繁體中文</a> |
|
|
10
|
+
<a href="hi/SECURITY.md">हिंदी</a> |
|
|
11
|
+
<a href="bn/SECURITY.md">বাংলা</a>
|
|
12
|
+
</h6>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
1
15
|
# 🛡️ Security Policy
|
|
2
16
|
|
|
3
17
|
If you find a vulnerability, please open a [draft security advisory](https://github.com/adamlui/js-utils/security/advisories/new).
|
package/generate-pw.js
CHANGED
|
@@ -29,6 +29,12 @@ function generatePassword(options = {}) {
|
|
|
29
29
|
};
|
|
30
30
|
options = { ...defaultOptions, ...options };
|
|
31
31
|
|
|
32
|
+
// Validate integer args
|
|
33
|
+
for (const numArgType of ['length', 'qty'])
|
|
34
|
+
if (isNaN(options[numArgType]) || options[numArgType] < 1)
|
|
35
|
+
return console.error(
|
|
36
|
+
`ERROR: [${ numArgType }] argument must be 1 or greater.`);
|
|
37
|
+
|
|
32
38
|
if (options.qty > 1) { // return array of [qty] password strings
|
|
33
39
|
const { qty, ...otherOptions } = options;
|
|
34
40
|
return generatePasswords(qty, otherOptions);
|
|
@@ -56,34 +62,10 @@ function generatePassword(options = {}) {
|
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
// Enforce strict mode if enabled
|
|
59
|
-
if (options.strict) {
|
|
65
|
+
if (options.strict) {
|
|
60
66
|
const charTypes = ['number', 'symbol', 'lower', 'upper'],
|
|
61
67
|
requiredCharTypes = charTypes.filter(charType => options[charType + 's'] || options[charType + 'case']);
|
|
62
|
-
|
|
63
|
-
// Init flags
|
|
64
|
-
for (const charType of requiredCharTypes)
|
|
65
|
-
global['has' + charType.charAt(0).toUpperCase() + charType.slice(1)] = false;
|
|
66
|
-
for (let i = 0; i < password.length; i++)
|
|
67
|
-
for (const charType of requiredCharTypes)
|
|
68
|
-
if ((charsets[charType] || charsets[charType + 's']).includes(password.charAt(i)))
|
|
69
|
-
global['has' + charType.charAt(0).toUpperCase() + charType.slice(1)] = true;
|
|
70
|
-
|
|
71
|
-
// Modify password if necessary
|
|
72
|
-
const maxReplacements = Math.min(password.length, requiredCharTypes.length),
|
|
73
|
-
replacedPositions = []; let replacementCnt = 0;
|
|
74
|
-
for (const charType of requiredCharTypes) {
|
|
75
|
-
if (replacementCnt < maxReplacements) {
|
|
76
|
-
if (!global['has' + charType.charAt(0).toUpperCase() + charType.slice(1)]) {
|
|
77
|
-
let replacementPos;
|
|
78
|
-
do replacementPos = randomInt(0, password.length); // pick random pos
|
|
79
|
-
while (replacedPositions.includes(replacementPos)); // check if pos already replaced
|
|
80
|
-
replacedPositions.push(replacementPos); // track new replacement pos
|
|
81
|
-
const replacementCharSet = charsets[charType] || charsets[charType + 's'];
|
|
82
|
-
password = password.substring(0, replacementPos) // perform actual replacement
|
|
83
|
-
+ replacementCharSet.charAt(randomInt(0, replacementCharSet.length))
|
|
84
|
-
+ password.substring(replacementPos + 1);
|
|
85
|
-
replacementCnt++;
|
|
86
|
-
}}}
|
|
68
|
+
password = strictify(password, requiredCharTypes);
|
|
87
69
|
}
|
|
88
70
|
|
|
89
71
|
return password;
|
|
@@ -93,16 +75,79 @@ function generatePassword(options = {}) {
|
|
|
93
75
|
function generatePasswords(qty, options) {
|
|
94
76
|
qty = parseInt(qty);
|
|
95
77
|
if (isNaN(qty)) return console.error(
|
|
96
|
-
'ERROR: First argument
|
|
78
|
+
'ERROR: First argument [qty] of generatePasswords() must be an integer');
|
|
97
79
|
const passwords = [];
|
|
98
80
|
for (let i = 0; i < qty; i++) passwords.push(generatePassword(options));
|
|
99
81
|
return passwords;
|
|
100
82
|
}
|
|
101
83
|
|
|
102
|
-
|
|
103
|
-
|
|
84
|
+
function strictify(password, requiredCharTypes = ['number', 'symbol', 'lower', 'upper']) {
|
|
85
|
+
|
|
86
|
+
// Init flags
|
|
87
|
+
for (const charType of requiredCharTypes)
|
|
88
|
+
global['has' + charType.charAt(0).toUpperCase() + charType.slice(1)] = false;
|
|
89
|
+
for (let i = 0; i < password.length; i++)
|
|
90
|
+
for (const charType of requiredCharTypes)
|
|
91
|
+
if ((charsets[charType] || charsets[charType + 's']).includes(password.charAt(i)))
|
|
92
|
+
global['has' + charType.charAt(0).toUpperCase() + charType.slice(1)] = true;
|
|
93
|
+
|
|
94
|
+
// Modify password if necessary
|
|
95
|
+
const maxReplacements = Math.min(password.length, requiredCharTypes.length),
|
|
96
|
+
replacedPositions = [];
|
|
97
|
+
let replacementCnt = 0, strictPW = password;
|
|
98
|
+
for (const charType of requiredCharTypes) {
|
|
99
|
+
if (replacementCnt < maxReplacements) {
|
|
100
|
+
if (!global['has' + charType.charAt(0).toUpperCase() + charType.slice(1)]) {
|
|
101
|
+
let replacementPos;
|
|
102
|
+
do replacementPos = randomInt(0, password.length); // pick random pos
|
|
103
|
+
while (replacedPositions.includes(replacementPos)); // check if pos already replaced
|
|
104
|
+
replacedPositions.push(replacementPos); // track new replacement pos
|
|
105
|
+
const replacementCharSet = charsets[charType] || charsets[charType + 's'];
|
|
106
|
+
strictPW = strictPW.substring(0, replacementPos) // perform actual replacement
|
|
107
|
+
+ replacementCharSet.charAt(randomInt(0, replacementCharSet.length))
|
|
108
|
+
+ strictPW.substring(replacementPos + 1);
|
|
109
|
+
replacementCnt++;
|
|
110
|
+
}}}
|
|
111
|
+
|
|
112
|
+
return strictPW;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function validateStrength(password) {
|
|
116
|
+
const strengthCriteria = { minLength: 8, minLower: 1, minUpper: 1, minNumber: 1, minSymbol: 1 };
|
|
117
|
+
|
|
118
|
+
// Count occurrences of each char type
|
|
119
|
+
const charCnts = { 'lower': 0, 'upper': 0, 'number': 0, 'symbol': 0 };
|
|
120
|
+
for (let i = 0; i < password.length; i++) {
|
|
121
|
+
const char = password[i];
|
|
122
|
+
for (const charType of Object.keys(charCnts))
|
|
123
|
+
if ((charsets[charType] || charsets[charType + 's']).includes(char))
|
|
124
|
+
charCnts[charType]++;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Check criteria + add recommendations
|
|
128
|
+
const recommendations = [];
|
|
129
|
+
if (password.length < strengthCriteria.minLength)
|
|
130
|
+
recommendations.push(`Make it at least ${ strengthCriteria.minLength } characters long.`);
|
|
131
|
+
for (const charType of Object.keys(charCnts))
|
|
132
|
+
if (charCnts[charType] < strengthCriteria['min' + charType.charAt(0).toUpperCase() + charType.slice(1)])
|
|
133
|
+
recommendations.push('Include at least one ' + charType
|
|
134
|
+
+ `${ ['upper', 'lower'].includes(charType) ? 'case letter' : '' }.`);
|
|
135
|
+
|
|
136
|
+
// Calculate strength score based on counts and criteria
|
|
137
|
+
let strengthScore = 0;
|
|
138
|
+
strengthScore += ( // +20 for satisfying min length
|
|
139
|
+
password.length >= strengthCriteria.minLength) ? 20 : 0;
|
|
140
|
+
for (const charType of Object.keys(charCnts))
|
|
141
|
+
strengthScore += ( // +20 per char type included
|
|
142
|
+
charCnts[charType] >= strengthCriteria['min' + charType.charAt(0).toUpperCase() + charType.slice(1)]) ? 20 : 0;
|
|
143
|
+
|
|
144
|
+
return { strengthScore, recommendations, isGood: strengthScore >= 80 };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// EXPORT main functions if script was required
|
|
148
|
+
if (require.main !== module) module.exports = { generatePassword, generatePasswords, strictify, validateStrength };
|
|
104
149
|
|
|
105
|
-
else { // run as CLI
|
|
150
|
+
else { // run as CLI utility
|
|
106
151
|
|
|
107
152
|
// Init UI colors
|
|
108
153
|
const nc = '\x1b[0m', // no color
|
|
@@ -111,47 +156,56 @@ else { // run as CLI tool
|
|
|
111
156
|
bw = '\x1b[1;97m'; // bright white
|
|
112
157
|
|
|
113
158
|
// Load settings from ARGS
|
|
114
|
-
const config = {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
/^--?(?:
|
|
125
|
-
|
|
159
|
+
const config = {};
|
|
160
|
+
const argRegex = {
|
|
161
|
+
paramOptions: {
|
|
162
|
+
'length': /^--?length/,
|
|
163
|
+
'qty': /^--?qu?a?n?ti?t?y=\d+$/,
|
|
164
|
+
'charset': /^--?chars/,
|
|
165
|
+
'excludeChars': /^--?exclude=/
|
|
166
|
+
},
|
|
167
|
+
flags: {
|
|
168
|
+
'includeNums': /^--?(?:n|(?:include-?)?num(?:ber)?s?=?(?:true|1)?)$/,
|
|
169
|
+
'includeSymbols': /^--?(?:s|(?:include-?)?symbols?=?(?:true|1)?)$/,
|
|
170
|
+
'excludeLowerChars': /^--?(?:L|(?:exclude|disable|no)-?lower-?(?:case)?|lower-?(?:case)?=(?:false|0))$/,
|
|
171
|
+
'excludeUpperChars': /^--?(?:U|(?:exclude|disable|no)-?upper-?(?:case)?|upper-?(?:case)?=(?:false|0))$/,
|
|
172
|
+
'strictMode': /^--?s(?:trict)?(?:-?mode)?$/
|
|
173
|
+
},
|
|
174
|
+
cmds: {
|
|
175
|
+
'help': /^--?h(?:elp)?$/,
|
|
176
|
+
'version': /^--?ve?r?s?i?o?n?$/
|
|
177
|
+
}
|
|
126
178
|
};
|
|
179
|
+
process.argv.forEach(arg => {
|
|
180
|
+
if (!arg.startsWith('-')) return;
|
|
181
|
+
const matchedFlag = Object.keys(argRegex.flags).find(flag => argRegex.flags[flag].test(arg)),
|
|
182
|
+
matchedArgOption = Object.keys(argRegex.paramOptions).find(option => argRegex.paramOptions[option].test(arg)),
|
|
183
|
+
matchedCmd = Object.keys(argRegex.cmds).find(cmd => argRegex.cmds[cmd].test(arg));
|
|
184
|
+
if (matchedFlag) config[matchedFlag] = true;
|
|
185
|
+
else if (matchedArgOption) {
|
|
186
|
+
const value = arg.split('=')[1];
|
|
187
|
+
config[matchedArgOption] = parseInt(value) || value;
|
|
188
|
+
} else if (!matchedCmd) {
|
|
189
|
+
console.error(`\n${br}ERROR: Arg [${ arg }] not recognized.${nc}`);
|
|
190
|
+
console.info(`\n${by}Valid arguments are below.${nc}`);
|
|
191
|
+
printHelpSections(['paramOptions', 'booelanOptions', 'infoCmds']);
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}});
|
|
127
194
|
|
|
128
195
|
// Show HELP screen if -h or --help passed
|
|
129
|
-
if (process.argv.some(arg => /^--?h(?:elp)?$/.test(arg)))
|
|
130
|
-
printHelp(`\n${by}generate-pw [options]${nc}`);
|
|
131
|
-
printHelp('\nArgument options:');
|
|
132
|
-
printHelp(' --length=n Generate password(s) of n length.');
|
|
133
|
-
printHelp(' --qty=n Generate n password(s).');
|
|
134
|
-
printHelp(' --charset=chars Only include chars in password(s).');
|
|
135
|
-
printHelp(' --exclude=chars Exclude chars from password(s).');
|
|
136
|
-
printHelp('\nBoolean options:');
|
|
137
|
-
printHelp(' -n, --include-numbers Allow numbers in password(s).');
|
|
138
|
-
printHelp(' -s, --include-symbols Allow symbols in password(s).');
|
|
139
|
-
printHelp(' -L, --no-lowercase Disallow lowercase letters in password(s).');
|
|
140
|
-
printHelp(' -U, --no-uppercase Disallow uppercase letters in password(s).');
|
|
141
|
-
printHelp('\nInfo commands:');
|
|
142
|
-
printHelp(' -h, --help Display this help screen.');
|
|
143
|
-
printHelp(' -v, --version Show version number.');
|
|
196
|
+
if (process.argv.some(arg => /^--?h(?:elp)?$/.test(arg))) printHelpSections();
|
|
144
197
|
|
|
145
198
|
// Show VERSION number if -v or --version passed
|
|
146
|
-
|
|
199
|
+
else if (process.argv.some(arg => /^--?ve?r?s?i?o?n?$/.test(arg)))
|
|
147
200
|
console.info('v' + require('./package.json').version);
|
|
148
201
|
|
|
149
|
-
|
|
202
|
+
else { // run MAIN routine
|
|
150
203
|
for (const numArgType of ['length', 'qty'])
|
|
151
|
-
if (config[numArgType] < 1)
|
|
152
|
-
`\n${br}Error:
|
|
204
|
+
if (config[numArgType] && (isNaN(config[numArgType]) || config[numArgType] < 1))
|
|
205
|
+
return console.error(`\n${br}Error: [${ numArgType }] argument must be 1 or greater.${nc}`);
|
|
153
206
|
const funcOptions = {
|
|
154
|
-
length: config.length, qty: config.qty
|
|
207
|
+
length: config.length || 8, qty: config.qty || 1,
|
|
208
|
+
charset: config.charset, exclude: config.excludeChars,
|
|
155
209
|
numbers: config.includeNums, symbols: config.includeSymbols,
|
|
156
210
|
lowercase: !config.excludeLowerChars, uppercase: !config.excludeUpperChars,
|
|
157
211
|
strict: config.strictMode
|
|
@@ -160,26 +214,56 @@ else { // run as CLI tool
|
|
|
160
214
|
console.log('\n' + bw + ( Array.isArray(pwResult) ? pwResult.join('\n') : pwResult ) + nc);
|
|
161
215
|
}
|
|
162
216
|
|
|
163
|
-
function
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
217
|
+
function printHelpSections(includeSections = ['cmdFormat', 'paramOptions', 'booelanOptions', 'infoCmds']) {
|
|
218
|
+
const helpSections = {
|
|
219
|
+
'cmdFormat': [
|
|
220
|
+
`\n${by}generate-pw [options|commands]${nc}`
|
|
221
|
+
],
|
|
222
|
+
'paramOptions': [
|
|
223
|
+
'\nParameter options:',
|
|
224
|
+
' --length=n Generate password(s) of n length.',
|
|
225
|
+
' --qty=n Generate n password(s).',
|
|
226
|
+
' --charset=chars Only include chars in password(s).',
|
|
227
|
+
' --charset=chars Only include chars in password(s).',
|
|
228
|
+
' --exclude=chars Exclude chars from password(s).'
|
|
229
|
+
],
|
|
230
|
+
'booelanOptions': [
|
|
231
|
+
'\nBoolean options:',
|
|
232
|
+
' -n, --include-numbers Allow numbers in password(s).',
|
|
233
|
+
' -s, --include-symbols Allow symbols in password(s).',
|
|
234
|
+
' -L, --no-lowercase Disallow lowercase letters in password(s).',
|
|
235
|
+
' -U, --no-uppercase Disallow uppercase letters in password(s).'
|
|
236
|
+
],
|
|
237
|
+
'infoCmds': [
|
|
238
|
+
'\nInfo commands:',
|
|
239
|
+
' -h, --help Display help screen.',
|
|
240
|
+
' -v, --version Show version number.'
|
|
241
|
+
]
|
|
242
|
+
};
|
|
243
|
+
includeSections.forEach(section => { // print valid arg elems
|
|
244
|
+
helpSections[section]?.forEach(line => printHelpMsg(line)); });
|
|
245
|
+
|
|
246
|
+
function printHelpMsg(msg) { // wrap msg + indent 2nd+ lines (for --help screen)
|
|
247
|
+
const terminalWidth = process.stdout.columns || 80,
|
|
248
|
+
indentation = 29, lines = [], words = msg.match(/\S+|\s+/g);
|
|
249
|
+
|
|
250
|
+
// Split msg into lines of appropriate lengths
|
|
251
|
+
let currentLine = '';
|
|
252
|
+
words.forEach(word => {
|
|
253
|
+
const lineLength = terminalWidth - ( lines.length === 0 ? 0 : indentation );
|
|
254
|
+
if (currentLine.length + word.length > lineLength) { // cap/store it
|
|
255
|
+
lines.push(lines.length === 0 ? currentLine : currentLine.trimStart());
|
|
256
|
+
currentLine = '';
|
|
257
|
+
}
|
|
258
|
+
currentLine += word;
|
|
259
|
+
});
|
|
260
|
+
lines.push(lines.length === 0 ? currentLine : currentLine.trimStart());
|
|
261
|
+
|
|
262
|
+
// Print formatted msg
|
|
263
|
+
lines.forEach((line, index) => console.info(
|
|
264
|
+
index === 0 ? line // print 1st line unindented
|
|
265
|
+
: ' '.repeat(indentation) + line // print subsequent lines indented
|
|
266
|
+
));
|
|
267
|
+
}
|
|
184
268
|
}
|
|
185
269
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "generate-pw",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Randomly generate cryptographically-secure passwords.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Adam Lui",
|
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
"password",
|
|
32
32
|
"generator",
|
|
33
33
|
"unique",
|
|
34
|
-
"generate"
|
|
34
|
+
"generate",
|
|
35
|
+
"api",
|
|
36
|
+
"cli"
|
|
35
37
|
],
|
|
36
38
|
"bugs": {
|
|
37
39
|
"url": "https://github.com/adamlui/js-utils/issues"
|