passential 0.0.0-stage → 1.0.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 +56 -2
- package/package.json +24 -4
- package/src/api/controllers/password.controller.js +27 -0
- package/src/api/middleware/validation.middleware.js +14 -0
- package/src/api/routes/password.routes.js +11 -0
- package/src/api/server.js +20 -0
- package/src/cli.js +71 -0
- package/src/commonPasswords.js +16 -0
- package/src/events.js +5 -0
- package/src/generator.js +50 -0
- package/src/index.js +13 -0
- package/src/patterns.js +29 -0
- package/src/strength.js +41 -0
- package/src/validator.js +59 -0
package/README.md
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
-
#
|
|
1
|
+
# passential
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A lightweight Node.js password validation and strength checking utility.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
npm install passential
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
validatePassword,
|
|
13
|
+
checkStrength,
|
|
14
|
+
generatePassword
|
|
15
|
+
} = require("npmpasspkg");
|
|
16
|
+
|
|
17
|
+
// Validate
|
|
18
|
+
console.log(validatePassword("Hello@World123"));
|
|
19
|
+
|
|
20
|
+
// Check strength
|
|
21
|
+
console.log(checkStrength("Hello@World123"));
|
|
22
|
+
|
|
23
|
+
// Generate password
|
|
24
|
+
console.log(generatePassword({ length: 20 }));
|
|
25
|
+
|
|
26
|
+
## Custom Rules
|
|
27
|
+
|
|
28
|
+
validatePassword("hello123", {
|
|
29
|
+
minLength: 6,
|
|
30
|
+
requireUppercase: false,
|
|
31
|
+
requireNumber: true,
|
|
32
|
+
requireSpecialChar: false
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
## Password Generator
|
|
36
|
+
|
|
37
|
+
generatePassword({
|
|
38
|
+
length: 20,
|
|
39
|
+
uppercase: true,
|
|
40
|
+
lowercase: true,
|
|
41
|
+
numbers: true,
|
|
42
|
+
special: false
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
- Password strength scoring
|
|
48
|
+
- Custom validation rules
|
|
49
|
+
- Common password detection
|
|
50
|
+
- Repeated and sequential pattern detection
|
|
51
|
+
- Secure password generation using crypto
|
|
52
|
+
- Node.js CLI
|
|
53
|
+
- Express API
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "passential",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Password strength checker, validator and secure password generator for Node.js",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"files": [
|
|
8
|
+
"src",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "node --test"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"password",
|
|
17
|
+
"password-checker",
|
|
18
|
+
"password-validator",
|
|
19
|
+
"password-strength",
|
|
20
|
+
"password-generator",
|
|
21
|
+
"nodejs",
|
|
22
|
+
"express"
|
|
23
|
+
],
|
|
24
|
+
"author": "Aarabdh",
|
|
25
|
+
"license": "MIT"
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const {
|
|
2
|
+
validatePassword,
|
|
3
|
+
checkStrength,
|
|
4
|
+
generatePassword,
|
|
5
|
+
} = require("../../index");
|
|
6
|
+
|
|
7
|
+
exports.check = (req, res) => {
|
|
8
|
+
const { password, options } = req.body;
|
|
9
|
+
|
|
10
|
+
const validation = validatePassword(password, options || {});
|
|
11
|
+
const strength = checkStrength(password);
|
|
12
|
+
|
|
13
|
+
res.json({
|
|
14
|
+
success: true,
|
|
15
|
+
validation,
|
|
16
|
+
strength,
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
exports.generate = (req, res) => {
|
|
21
|
+
const password = generatePassword(req.body || {});
|
|
22
|
+
|
|
23
|
+
res.json({
|
|
24
|
+
success: true,
|
|
25
|
+
password,
|
|
26
|
+
});
|
|
27
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function validateRequest(req, res, next) {
|
|
2
|
+
const { password } = req.body;
|
|
3
|
+
|
|
4
|
+
if (typeof password !== "string" || !password) {
|
|
5
|
+
return res.status(400).json({
|
|
6
|
+
success: false,
|
|
7
|
+
message: "Password is required",
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
next();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = validateRequest;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const controller = require("../controllers/password.controller");
|
|
5
|
+
const validateRequest = require("../middleware/validation.middleware");
|
|
6
|
+
|
|
7
|
+
router.post("/check", validateRequest, controller.check);
|
|
8
|
+
|
|
9
|
+
router.post("/generate", controller.generate);
|
|
10
|
+
|
|
11
|
+
module.exports = router;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
const passwordRoutes = require("./routes/password.routes");
|
|
3
|
+
|
|
4
|
+
const app = express();
|
|
5
|
+
|
|
6
|
+
app.use(express.json());
|
|
7
|
+
|
|
8
|
+
app.use("/api/password", passwordRoutes);
|
|
9
|
+
|
|
10
|
+
app.get("/", (req, res) => {
|
|
11
|
+
res.json({
|
|
12
|
+
message: "Password Checker API is running",
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const PORT = 3000;
|
|
17
|
+
|
|
18
|
+
app.listen(PORT, () => {
|
|
19
|
+
console.log(`Server running at http://localhost:${PORT}`);
|
|
20
|
+
});
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const readline = require("readline");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
validatePassword,
|
|
7
|
+
checkStrength,
|
|
8
|
+
generatePassword,
|
|
9
|
+
} = require("./index");
|
|
10
|
+
|
|
11
|
+
const events = require("./events");
|
|
12
|
+
|
|
13
|
+
events.on("password-checked", (result) => {
|
|
14
|
+
console.log("\nValidation completed!");
|
|
15
|
+
console.log("Valid:", result.isValid);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
events.on("password-generated", () => {
|
|
19
|
+
console.log("Password generated successfully!");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const args = process.argv.slice(2);
|
|
23
|
+
|
|
24
|
+
async function startCLI() {
|
|
25
|
+
let password = args[0];
|
|
26
|
+
|
|
27
|
+
if (args[0] === "--generate") {
|
|
28
|
+
const generated = generatePassword();
|
|
29
|
+
|
|
30
|
+
console.log("Generated password:", generated);
|
|
31
|
+
events.emit("password-generated");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!password) {
|
|
36
|
+
const rl = readline.createInterface({
|
|
37
|
+
input: process.stdin,
|
|
38
|
+
output: process.stdout,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
password = await new Promise((resolve) => {
|
|
42
|
+
rl.question("Enter password to check: ", (answer) => {
|
|
43
|
+
rl.close();
|
|
44
|
+
resolve(answer);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const validation = validatePassword(password);
|
|
50
|
+
const strength = checkStrength(password);
|
|
51
|
+
|
|
52
|
+
console.log("\nVALIDATION:");
|
|
53
|
+
console.log(validation);
|
|
54
|
+
|
|
55
|
+
console.log("\nSTRENGTH:");
|
|
56
|
+
console.log(strength);
|
|
57
|
+
|
|
58
|
+
events.emit("password-checked", validation);
|
|
59
|
+
|
|
60
|
+
// Save report without storing the actual password
|
|
61
|
+
const reportPath = path.join(__dirname, "../password-report.txt");
|
|
62
|
+
|
|
63
|
+
fs.appendFileSync(
|
|
64
|
+
reportPath,
|
|
65
|
+
`Valid: ${validation.isValid}, Strength: ${strength.strength}\n`,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
console.log("\nReport saved successfully!");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
startCLI().catch(console.error);
|
package/src/events.js
ADDED
package/src/generator.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const crypto = require("crypto");
|
|
2
|
+
|
|
3
|
+
function generatePassword(options = {}) {
|
|
4
|
+
const length = options.length ?? 16;
|
|
5
|
+
|
|
6
|
+
if (!Number.isInteger(length) || length < 4 || length > 128) {
|
|
7
|
+
throw new RangeError("Length must be between 4 and 128");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const groups = [];
|
|
11
|
+
|
|
12
|
+
if (options.uppercase !== false) groups.push("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
|
13
|
+
|
|
14
|
+
if (options.lowercase !== false) groups.push("abcdefghijklmnopqrstuvwxyz");
|
|
15
|
+
|
|
16
|
+
if (options.numbers !== false) groups.push("0123456789");
|
|
17
|
+
|
|
18
|
+
if (options.special !== false) groups.push("!@#$%^&*()_+-=[]{}");
|
|
19
|
+
|
|
20
|
+
if (groups.length === 0) {
|
|
21
|
+
throw new Error("Enable at least one character type");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (length < groups.length) {
|
|
25
|
+
throw new Error("Length is too short for enabled character types");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const allCharacters = groups.join("");
|
|
29
|
+
const password = [];
|
|
30
|
+
|
|
31
|
+
// Ensure at least one character from each enabled group
|
|
32
|
+
for (const group of groups) {
|
|
33
|
+
password.push(group[crypto.randomInt(group.length)]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Fill remaining positions
|
|
37
|
+
while (password.length < length) {
|
|
38
|
+
password.push(allCharacters[crypto.randomInt(allCharacters.length)]);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Shuffle password using Fisher-Yates
|
|
42
|
+
for (let i = password.length - 1; i > 0; i--) {
|
|
43
|
+
const j = crypto.randomInt(i + 1);
|
|
44
|
+
[password[i], password[j]] = [password[j], password[i]];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return password.join("");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = { generatePassword };
|
package/src/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const { validatePassword } = require("./validator");
|
|
2
|
+
const { checkStrength } = require("./strength");
|
|
3
|
+
const { generatePassword } = require("./generator");
|
|
4
|
+
|
|
5
|
+
const { hasRepeatedCharacters, hasSequentialPattern } = require("./patterns");
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
validatePassword,
|
|
9
|
+
checkStrength,
|
|
10
|
+
generatePassword,
|
|
11
|
+
hasRepeatedCharacters,
|
|
12
|
+
hasSequentialPattern,
|
|
13
|
+
};
|
package/src/patterns.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
function hasRepeatedCharacters(password) {
|
|
2
|
+
return /(.)\1{2,}/.test(password);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function hasSequentialPattern(password) {
|
|
6
|
+
const sequences = [
|
|
7
|
+
"abcdefghijklmnopqrstuvwxyz",
|
|
8
|
+
"0123456789",
|
|
9
|
+
"qwertyuiop",
|
|
10
|
+
"asdfghjkl",
|
|
11
|
+
"zxcvbnm",
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const value = password.toLowerCase();
|
|
15
|
+
|
|
16
|
+
return sequences.some((sequence) => {
|
|
17
|
+
for (let i = 0; i <= sequence.length - 4; i++) {
|
|
18
|
+
if (value.includes(sequence.substring(i, i + 4))) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
hasRepeatedCharacters,
|
|
28
|
+
hasSequentialPattern,
|
|
29
|
+
};
|
package/src/strength.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const { validatePassword } = require("./validator");
|
|
2
|
+
|
|
3
|
+
function checkStrength(password) {
|
|
4
|
+
const result = validatePassword(password, {
|
|
5
|
+
requireUppercase: false,
|
|
6
|
+
requireLowercase: false,
|
|
7
|
+
requireNumber: false,
|
|
8
|
+
requireSpecialChar: false,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const checks = result.checks;
|
|
12
|
+
let score = 0;
|
|
13
|
+
|
|
14
|
+
if (password.length >= 8) score += 20;
|
|
15
|
+
if (password.length >= 12) score += 15;
|
|
16
|
+
if (password.length >= 16) score += 10;
|
|
17
|
+
|
|
18
|
+
if (checks.hasUppercase) score += 10;
|
|
19
|
+
if (checks.hasLowercase) score += 10;
|
|
20
|
+
if (checks.hasNumber) score += 10;
|
|
21
|
+
if (checks.hasSpecialChar) score += 15;
|
|
22
|
+
|
|
23
|
+
if (!checks.hasRepeatedCharacters) score += 5;
|
|
24
|
+
if (!checks.hasSequentialPattern) score += 5;
|
|
25
|
+
|
|
26
|
+
if (checks.isCommonPassword) score = 0;
|
|
27
|
+
|
|
28
|
+
score = Math.min(score, 100);
|
|
29
|
+
|
|
30
|
+
let strength;
|
|
31
|
+
|
|
32
|
+
if (score < 30) strength = "Very Weak";
|
|
33
|
+
else if (score < 50) strength = "Weak";
|
|
34
|
+
else if (score < 70) strength = "Moderate";
|
|
35
|
+
else if (score < 90) strength = "Strong";
|
|
36
|
+
else strength = "Very Strong";
|
|
37
|
+
|
|
38
|
+
return { score, strength };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = { checkStrength };
|
package/src/validator.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const commonPasswords = require("./commonPasswords");
|
|
2
|
+
const { hasRepeatedCharacters, hasSequentialPattern } = require("./patterns");
|
|
3
|
+
|
|
4
|
+
function validatePassword(password, options = {}) {
|
|
5
|
+
if (typeof password !== "string") {
|
|
6
|
+
throw new TypeError("Password must be a string");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const rules = {
|
|
10
|
+
minLength: options.minLength ?? 8,
|
|
11
|
+
maxLength: options.maxLength ?? 128,
|
|
12
|
+
requireUppercase: options.requireUppercase ?? true,
|
|
13
|
+
requireLowercase: options.requireLowercase ?? true,
|
|
14
|
+
requireNumber: options.requireNumber ?? true,
|
|
15
|
+
requireSpecialChar: options.requireSpecialChar ?? true,
|
|
16
|
+
checkCommonPasswords: options.checkCommonPasswords ?? true,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const checks = {
|
|
20
|
+
hasMinLength: password.length >= rules.minLength,
|
|
21
|
+
hasMaxLength: password.length <= rules.maxLength,
|
|
22
|
+
hasUppercase: /[A-Z]/.test(password),
|
|
23
|
+
hasLowercase: /[a-z]/.test(password),
|
|
24
|
+
hasNumber: /[0-9]/.test(password),
|
|
25
|
+
hasSpecialChar: /[^A-Za-z0-9]/.test(password),
|
|
26
|
+
isCommonPassword: commonPasswords.includes(password.toLowerCase()),
|
|
27
|
+
hasRepeatedCharacters: hasRepeatedCharacters(password),
|
|
28
|
+
hasSequentialPattern: hasSequentialPattern(password),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const errors = [];
|
|
32
|
+
|
|
33
|
+
if (!checks.hasMinLength) errors.push(`Minimum length is ${rules.minLength}`);
|
|
34
|
+
|
|
35
|
+
if (!checks.hasMaxLength) errors.push(`Maximum length is ${rules.maxLength}`);
|
|
36
|
+
|
|
37
|
+
if (rules.requireUppercase && !checks.hasUppercase)
|
|
38
|
+
errors.push("An uppercase letter is required");
|
|
39
|
+
|
|
40
|
+
if (rules.requireLowercase && !checks.hasLowercase)
|
|
41
|
+
errors.push("A lowercase letter is required");
|
|
42
|
+
|
|
43
|
+
if (rules.requireNumber && !checks.hasNumber)
|
|
44
|
+
errors.push("A number is required");
|
|
45
|
+
|
|
46
|
+
if (rules.requireSpecialChar && !checks.hasSpecialChar)
|
|
47
|
+
errors.push("A special character is required");
|
|
48
|
+
|
|
49
|
+
if (rules.checkCommonPasswords && checks.isCommonPassword)
|
|
50
|
+
errors.push("This is a commonly used weak password");
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
isValid: errors.length === 0,
|
|
54
|
+
checks,
|
|
55
|
+
errors,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { validatePassword };
|