Validation
Package, implementation and tests
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
.idea/
|
||||
bun.lockb
|
||||
package-lock.json
|
||||
node_modules/
|
||||
._*
|
||||
.DS_Store
|
||||
@@ -0,0 +1,83 @@
|
||||
import {describe, expect, test} from 'bun:test'
|
||||
|
||||
import * as f from '.'
|
||||
|
||||
// TODO : Test logging
|
||||
|
||||
describe('email', () => {
|
||||
const emails: [string, boolean][] = [
|
||||
['pascal@pband.ch', true],
|
||||
['p@p.ch', true],
|
||||
[`${'a'.repeat(64)}@${'b'.repeat(60)}.ch`, true],
|
||||
[`${'a'.repeat(128)}@${'b'.repeat(64)}.ch`, false],
|
||||
['google.ch@test.com', true],
|
||||
['pascal+truc@pband.ch', true],
|
||||
|
||||
['pascal+.@pband.ch', false],
|
||||
['pascal+p.@pband.ch', false],
|
||||
['pascal@pband.', false],
|
||||
['pascal@pband', false],
|
||||
['pascal@.ch', false],
|
||||
['@pband.ch', false],
|
||||
['prout', false],
|
||||
['google.ch', false],
|
||||
]
|
||||
|
||||
for (const [email, valid] of emails) {
|
||||
test(`"${email}" is ${valid ? 'valid' : 'invalid'}`, () => {
|
||||
const res = expect(f.email_is_valid(email))
|
||||
if (valid) {
|
||||
res.toBeTrue()
|
||||
} else {
|
||||
res.toBeFalse()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
describe('Password strength', () => {
|
||||
// TODO : More passwords lol
|
||||
const pwds: [string, f.PasswordStrength][] = [
|
||||
// ['test', f.PasswordStrength.Strong],
|
||||
['OkPassword', f.PasswordStrength.Easy],
|
||||
]
|
||||
|
||||
for (const pwd of pwds) {
|
||||
for (const level of [f.PasswordStrength.Weak, f.PasswordStrength.Easy, f.PasswordStrength.Medium, f.PasswordStrength.Strong, f.PasswordStrength.Hard]) {
|
||||
const should_pass = level <= pwd[1]
|
||||
test(`"${pwd[0]}" should ${should_pass ? '' : 'not '}pass level ${level}`, () => {
|
||||
const res = expect(f.password_is_strong(pwd[0], level as f.PasswordStrength))
|
||||
if (should_pass) {
|
||||
res.toBeTrue()
|
||||
} else {
|
||||
res.toBeFalse()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
describe('Contains HTML', () => {
|
||||
const texts: [string, boolean][] = [
|
||||
['Hello, world!', false],
|
||||
['<html><body><h1>Hello, world!</h1></body></html>', true],
|
||||
['<html><body><h1>Hello, world!</h1></body></html><script>console.log("Hello, world!")</script>', true],
|
||||
['Hello <strong>guy !</strong>', true],
|
||||
['Hello <strong>guy !</strong><script>console.log("Hello, world!")</script>', true],
|
||||
['Simple balise <br/>', true],
|
||||
['Simple balise <br />', true],
|
||||
['Simple balise <br>', true],
|
||||
['Simple math : 1 < 2', false],
|
||||
['Simple math : 1 < 2 > 0', false],
|
||||
]
|
||||
|
||||
for (const i in texts) {
|
||||
const [text, contains] = texts[i]
|
||||
test(`Text ${i} : should ${contains ? '' : 'not '}contain HTML`, () => {
|
||||
const res = expect(f.contains_html(text))
|
||||
if (contains) {
|
||||
res.toBeTrue()
|
||||
} else {
|
||||
res.toBeFalse()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,52 @@
|
||||
import * as EmailValidator from 'email-validator'
|
||||
import logger from 'log'
|
||||
import zxcvbn from 'zxcvbn'
|
||||
import {parseDocument} from 'htmlparser2'
|
||||
|
||||
let _verbose: boolean = false
|
||||
export function verbose(v: boolean) {
|
||||
_verbose = v
|
||||
}
|
||||
|
||||
const log = logger('validation')
|
||||
|
||||
export enum PasswordStrength {
|
||||
Weak = 0,
|
||||
Easy = 1,
|
||||
Medium = 2,
|
||||
Strong = 3,
|
||||
Hard = 4,
|
||||
}
|
||||
|
||||
export function password_is_strong(password: string, strength: PasswordStrength = PasswordStrength.Medium, email?: string) : boolean {
|
||||
log.trace("Verify password's strength")
|
||||
|
||||
if (zxcvbn(password, email !== undefined ? [email] : []).score < strength) {
|
||||
if (_verbose) log.warn("Password is too weak")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export function contains_html(text: string) : boolean {
|
||||
log.trace("Verify if text contains HTML")
|
||||
|
||||
if (parseDocument(text).children.some(node => node.nodeType === 1)) {
|
||||
if (_verbose) log.warn("")
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function email_is_valid(email: string) : boolean {
|
||||
log.trace("Verify email's validity")
|
||||
|
||||
if (!EmailValidator.validate(email)) {
|
||||
if (_verbose) log.warn("Invalid email")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "validation",
|
||||
"description": "Various validation for web usage",
|
||||
"version": "1.0.0",
|
||||
|
||||
"author": "Pascal Perrenoud <pascal@pband.ch>",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"files": ["index.ts"],
|
||||
|
||||
"scripts": {
|
||||
"test": "bun test"
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
"email-validator": "^2.0.4",
|
||||
"htmlparser2": "^9.1.0",
|
||||
"log": "git+https://git.pband.ch/typescript/log",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.2",
|
||||
"@types/zxcvbn": "^4.4.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./src",
|
||||
|
||||
// Enable latest features
|
||||
"lib": ["ESNext","dom"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
},
|
||||
|
||||
"include": [
|
||||
"index.ts",
|
||||
"src/**/*.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user